The lab
here is how portswigger describes it
This lab involves a front-end and back-end server, and the two servers handle duplicate HTTP request headers in different ways. The front-end server rejects requests that aren't using the GET or POST method.
To solve the lab, smuggle a request to the back-end server, so that the next request processed by the back-end server appears to use the method GPOST.
this follows the basic te.cl lab. the goal is the same GPOST, but the setup is different in an important way. here neither server is simply ignoring chunked encoding, both servers understand Transfer-Encoding, so we cannot rely on one of them just not supporting it. instead we exploit the fact that the two servers disagree about duplicate headers, by sending the Transfer-Encoding header twice, once normal and once obfuscated, so that each server reads a different one
The idea#
when a server that supports chunked encoding also sees a Content-Length, the specification says it should prefer the chunked encoding and ignore the length. so to get a te.cl desync out of two chunked capable servers, we need one of them to not recognise the Transfer-Encoding at all and fall back to Content-Length
the way to do that is to send two Transfer-Encoding headers, a clean one and a deliberately broken one, and rely on the two servers picking different ones. the front end reads the valid Transfer-Encoding: chunked and processes the body as chunked. the back end handles the duplicate differently, it trips over the obfuscated second header, fails to apply chunked encoding, and falls back to the Content-Length. that split is the whole trick, one obfuscated header that one server honours and the other rejects, and now we have a te.cl desync between two servers that both, in principle, support chunked
Step 1 - Send the Request with two te headers#
we intercept a request, go to the repeater menu and make sure the update content length option is unchecked, then send this. note the two Transfer-Encoding headers.
POST / HTTP/1.1
Host: your-lab-id.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-length: 4
Transfer-Encoding: chunked
Transfer-Encoding: x
5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
there must also be a trailing \r\n\r\n, an extra blank line, after the final 0, or the chunked body is not terminated properly.
reading it, the front end sees both Transfer-Encoding headers, accepts the valid chunked one, and reads the body as chunks, taking the 5c sized chunk with our whole GPOST block and then the 0 chunk, and forwards the request. the back end also sees both headers but treats the pair differently, the obfuscated Transfer-Encoding: x throws it off so it does not apply chunked encoding, and it falls back to Content-Length: 4. so it reads only the first 4 bytes of the body, stops, and leaves the rest, from GPOST / HTTP/1.1 onward, as the start of the next request.
Step 2 - Send it again to make gpost#
we send the same request a second time on the same connection


our request meets the smuggled GPOST line waiting on the connection, so the back end processes a request with the method GPOST, and that unknown method solves the lab.
with this, we solved the lab!
