HTTP Request Smuggling: The 0.CL Desync Era
New 2025 desync research revived request smuggling against modern stacks. We explain CL.TE, TE.CL, and the 0.CL early-response gadget.
HTTP request smuggling exploits two servers disagreeing about where one request ends and the next begins — a front-end proxy and a back-end that parse the same bytes differently. The attacker injects a request the back-end attributes to someone else. This is how to detect the desync and prove it.
Finding it
HTTP/1.1 delimits a body two ways — Content-Length and Transfer-Encoding: chunked — and trouble starts when both are present and the two servers prioritize them differently. Detection is about provoking and measuring a parser discrepancy.
The reliable detection methodology is timing-based, because it is safe (it desyncs your own connection, not a victim's). Send a request crafted so that one server waits for bytes that never arrive; a long delay confirms the discrepancy. Burp's HTTP Request Smuggler extension automates exactly this, and h2csmuggler covers the HTTP/2 cleartext path:
# Probe an HTTP/2 endpoint for a downgrade path that re-opens HTTP/1.1 ambiguity
python3 h2csmuggler.py -x https://victim.example.com --test
What to look for:
- A
CL.TEtimeout: front-end usesContent-Length, back-end usesTransfer-Encoding. - A
TE.CLtimeout: the reverse. - HTTP/2-to-HTTP/1.1 downgrades at the edge (re-introduces HTTP/1.1 ambiguity).
- The newer
0.CLclass plus an "early-response gadget" — an endpoint that responds before reading the full body.
Proof of concept
Start with the safe timing probe for CL.TE. The front-end forwards 4 bytes (1\r\n + A) and the back-end, honoring chunked, waits for the next chunk that never comes — so the connection hangs:
POST / HTTP/1.1
Host: victim.example.com
Content-Length: 4
Transfer-Encoding: chunked
1
A
X
If this request consistently takes ~10s longer than a baseline while a normal request returns immediately, the back-end is reading chunked and the front-end is not — CL.TE confirmed.
Now demonstrate the actual smuggle. Prefix a request onto the next connection user. The 0\r\n\r\n terminates the chunked body for the front-end, leaving GPOST... buffered for the back-end to prepend to whoever comes next:
POST / HTTP/1.1
Host: victim.example.com
Content-Length: 6
Transfer-Encoding: chunked
0
GPOST / HTTP/1.1
Host: victim.example.com
Foo: x
To make the proof self-contained, smuggle a request that captures evidence on a benign endpoint you can read back, or smuggle past a front-end access control:
POST /login HTTP/1.1
Host: victim.example.com
Content-Length: 43
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
X-Ignore: X
If the front-end blocks /admin directly (403) but the smuggled /admin reaches the back-end (you observe the admin response prepended to the next request), you have proven a control bypass.
Going further
The 2025 "HTTP/1.1 Must Die" research revived this class with 0.CL desyncs and parser-discrepancy detection, lighting up targets that scanned clean for years — duplicate headers, obsolete line folding, and ambiguous chunk sizes are all fresh hunting ground. "We tested this once" is a stale assurance; rerun current tooling.
Concrete escalations to demonstrate impact:
- Capture another user's request (and their
Cookie/Authorization) by smuggling a request that reflects the buffered bytes into a stored field you can read. - Bypass edge access controls as shown above.
- Poison a shared cache to amplify a single smuggle to many users.
Because smuggling can affect real users' connections, restrict experiments to the timing-based, self-targeting detection unless you have explicit authorization for active exploitation. Capture the request pair, the timing differential, and the smuggled response as evidence.