Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type HTTP Request Smuggling — CL.TE Detection
Difficulty Practitioner
Objective Smuggle a request prefix that causes the next GET / to return 404
Note Switch Burp Repeater to HTTP/1 manually. Disable "Update Content-Length."

HTTP Request Smuggling — Confirming CL.TE via Differential Responses — Writeup


Initial Observation

Intercepting any request and sending it to Repeater. In the Inspector panel, switching the protocol to HTTP/1 and changing the method to POST. Going to Repeater settings and unchecking "Update Content-Length" — we need to control that value manually.

Enabling "Show non-printable characters" in the editor helps see the \r\n sequences that matter for chunked encoding.

Screenshot

Understanding the CL.TE Setup

The lab tells us the front-end doesn't support chunked encoding — it uses Content-Length. The back-end does support chunked encoding — it uses Transfer-Encoding. That's a CL.TE configuration.

When we send a request with both headers:

  • The front-end reads Content-Length and forwards exactly that many bytes to the back-end.
  • The back-end reads Transfer-Encoding: chunked and processes the body as chunks.

If we send a Content-Length that covers more than what the chunked encoding terminates at, the extra bytes sit in the back-end's buffer unprocessed. The back-end treats those leftover bytes as the start of the next request — that's the smuggled prefix.

Testing the Desync

Confirming the back-end times out when chunked encoding is left open:

POST / HTTP/1.1
Host: 0a6f003803dc41a5807f5dcd00b5004c.web-security-academy.net
Content-Length: 7
Transfer-Encoding: chunked

teto=teto

The back-end is waiting for the 0 chunk that signals end of request — it never arrives, so the connection hangs. This confirms the back-end is parsing chunked encoding.

Screenshot

Sending a well-formed chunked body:

POST / HTTP/1.1
Host: 0a6f003803dc41a5807f5dcd00b5004c.web-security-academy.net
Cookie: session=2yTkSCKxhGj7JkMeRk7hxS6MPx61Ql5T
Content-Length: 14
Transfer-Encoding: chunked

4
teto
0

200 OKContent-Length: 14 matches the 14 bytes we actually sent (4\r\nteto\r\n0\r\n\r\n). Everything is in sync. When the values don't match, the desync happens — excess bytes land in the back-end buffer.

Screenshot

Smuggling the 404 Prefix

Crafting a request where Content-Length covers not just the chunked body but also a partial HTTP request appended after the 0 terminator. The front-end forwards all 44 bytes. The back-end processes 4\r\nteto\r\n0\r\n\r\n as the complete chunked body, then sees GET /miku HTTP/1.1\r\nTeto: teto sitting in the buffer — and treats it as the beginning of the next request:

POST / HTTP/1.1
Host: 0a6f003803dc41a5807f5dcd00b5004c.web-security-academy.net
Cookie: session=2yTkSCKxhGj7JkMeRk7hxS6MPx61Ql5T
Content-Length: 44
Transfer-Encoding: chunked

4
teto
0

GET /miku HTTP/1.1
Teto: teto

The Teto: teto header at the end is necessary — the smuggled prefix needs at least one valid header so that when the next real request arrives and gets appended to it, the combined result is syntactically valid enough for the back-end to process it.

Screenshot

Sending the smuggling request. The response to our POST comes back fine — but the smuggled GET /miku prefix is now sitting in the back-end buffer waiting for the next real request to complete it.

The Keep-Alive Timing

HTTP connections are keep-alive — the back-end hasn't sent the response for the smuggled request yet because it's waiting for the next bytes to complete the smuggled request's headers. When a real browser request for / arrives, it gets appended to our smuggled prefix, forming a mangled request that the back-end processes. Since /miku doesn't exist, the back-end returns 404 — but this is delivered as the response to the next legitimate request.

Sending the smuggling request in Repeater, then immediately refreshing the browser:

Screenshot
Screenshot

The browser gets a 404 Not Found instead of the normal homepage. Refreshing again (once the smuggled prefix is consumed) confirms the site is back to normal — lab solved 0o0

Resources