Request Smuggling
Sections Request Smuggling
Front-end and back-end servers disagree on where one request ends and the next begins. Hijack other users’ requests, bypass auth, poison caches.
T=https://target.com
i. Where it lives
Anywhere there’s a chain of HTTP proxies / load balancers / CDNs in front of the app:
- CloudFront / Akamai / Cloudflare / Fastly in front of origin
- nginx / HAProxy / AWS ALB → application server
- Reverse proxy with web cache (Varnish)
- WAF appliances doing buffering before forward
- API gateway → microservice
The vuln is in the disagreement between two HTTP parsers. Different software products handle edge cases differently in:
Content-Length(CL) vsTransfer-Encoding: chunked(TE) when both present- Obfuscated
Transfer-Encodingheaders (extra whitespace, line folding, quoted values) - HTTP/2 downgrade to HTTP/1.1 between front-end and back-end
- Newlines and header parsing edge cases
ii. The smuggling variants
| Type | Front-end uses | Back-end uses | Notes |
|---|---|---|---|
| CL.TE | Content-Length | Transfer-Encoding | Front sends full body, back sees chunked stop early |
| TE.CL | Transfer-Encoding | Content-Length | Front uses chunks, back uses CL |
| TE.TE | Both (one obfuscated) | Both (other one ignored) | Header obfuscation drives the desync |
| H2.CL | HTTP/2 | Content-Length on downgrade | HTTP/2 lengths vs HTTP/1.1 CL after downgrade |
| H2.TE | HTTP/2 | Transfer-Encoding | Same idea with TE |
| 0.CL | HTTP/2 with 0-byte body | CL claims content | Newer technique, James Kettle’s 2024 research |
| CL.0 | Content-Length | No body expected | Back-end ignores CL, treats remainder as new request |
Modern HTTP/2 smuggling is the active research area in 2024-2026. Older HTTP/1.1 CL.TE / TE.CL is mostly patched on managed CDNs but still found in self-hosted reverse proxy setups.
iii. Tools
smuggler.py (defniffer / Defiant)
The most reliable HTTP/1.1 smuggling scanner:
git clone https://github.com/defparam/smuggler
python3 smuggler.py -u "$T"
## Adjust technique:
python3 smuggler.py -u "$T" --no-color -t 5 -m TLS
## From a list:
python3 smuggler.py -u "$T" -l urls.txt
Output flags [CRITICAL] for confirmed smuggling. Read the saved request file in payloads/ to verify manually.
HTTP Request Smuggler (Burp extension)
The Burp Pro / Community extension by Albinowax. Best for interactive testing:
- Install via Burp BApp Store
- Right-click a request → Smuggle Probe
- For HTTP/2: Smuggle Probe → “Try HTTP/2 smuggling”
The extension does the parsing-quirk fuzzing automatically.
h2csmuggler
For HTTP/2 cleartext upgrade smuggling specifically:
python3 h2csmuggler.py -x http://proxy.example.com:80 http://internal.example.com/
nuclei templates
nuclei -u "$T" -tags smuggling
Useful for quick first-pass against many hosts.
iv. Detection (manual probes)
The classic CL.TE / TE.CL detection probes - send these manually via netcat (Burp Repeater bypasses some of the timing tricks needed):
CL.TE probe - back-end uses TE, so after the chunked terminator 0\r\n\r\n, anything extra hangs waiting for the next request:
POST / HTTP/1.1
Host: target.com
Content-Length: 6
Transfer-Encoding: chunked
0
X
If the connection times out waiting for X → CL.TE confirmed.
TE.CL probe - front-end uses TE, back-end uses CL. Back-end reads CL bytes:
POST / HTTP/1.1
Host: target.com
Content-Length: 6
Transfer-Encoding: chunked
0
X
If response is immediate → TE.CL confirmed.
Most clients normalize HTTP. Burp Repeater has an “Update Content-Length” toggle - disable it. Connection: close usually needed too.
v. TE header obfuscation tricks
When both front and back parse Transfer-Encoding but one accepts an obfuscated version, you get TE.TE. Common obfuscations:
Transfer-Encoding: xchunked
Transfer-Encoding : chunked ## space before colon
Transfer-Encoding: chunked
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked ## leading space
X: X[\n]Transfer-Encoding: chunked ## line folding
Transfer-Encoding
: chunked ## obs-fold
Transfer-encoding: chunked ## case variations
TRANSFER-ENCODING: chunked
Transfer-Encoding: chunked, identity
Transfer-Encoding: chunked
Content-Length: 4 ## conflict
Smuggler.py and the Burp extension fuzz these automatically.
vi. HTTP/2 smuggling
HTTP/2 doesn’t have textual headers - request bodies are framed. When a HTTP/2 front-end terminates and re-emits HTTP/1.1 to the back-end, the conversion process can introduce desyncs.
HTTP/2 → HTTP/1.1 downgrade scenarios:
- HTTP/2 request with
transfer-encoding: chunked(forbidden in HTTP/2) - front strips, back-end sees re-emitted HTTP/1.1 with the smuggled chunked encoding - HTTP/2 request with explicit
content-lengthmismatching the actual frame length
The smuggler bypass: send HTTP/2 with content-length: 0 but include a body in the data frame. Front-end uses CL=0, back-end uses the data → CL.0 smuggling.
Use Burp’s HTTP Request Smuggler extension with HTTP/2 selected. Manual HTTP/2 smuggling needs a low-level client like nghttp2 or burp-stager.
vii. What to do with smuggling
Bypass front-end controls
Smuggle a request that the front-end never sees, hitting back-end endpoints normally blocked at the edge:
## Smuggled request goes to /admin which front-end blocks
Capture other users’ requests
Append a request prefix that captures the next victim’s headers:
## After your smuggled prefix, the next request to that backend
## connection has its first bytes appended to your "request body"
## You read those bytes via a reflective endpoint
Classic capture chain via search endpoint:
POST /search HTTP/1.1
...
search_query=... ← victim's request gets appended here
When the search endpoint stores or reflects the query, you read the victim’s Cookies and Authorization headers.
Cache poisoning amplification
Combine smuggling with WEB11 Web Cache Poisoning to poison a public asset with attacker-controlled content. Every visitor downstream of the cache receives the malicious content.
Smuggled SSRF
Smuggle a request to an internal-only endpoint not exposed externally. Common targets: admin panels, internal APIs.
Web cache deception via smuggling
Make the cache store sensitive content (like /api/me) under an attacker-friendly URL.
viii. Tricks worth knowing
Connection: keep-alive
HTTP smuggling depends on connection reuse. Force Connection: keep-alive on your probe. Most servers default to keep-alive on HTTP/1.1 - but some normalize away.
CDN behavior fingerprint
Hit /__cf_origin/ (Cloudflare) and similar paths. Front-end responds with diagnostic info revealing the CDN. Knowing the CDN narrows expected smuggling variants:
- Cloudflare → fairly robust, HTTP/2 downgrade attacks more interesting
- Akamai → historically vulnerable to TE.CL
- Fastly → varies by config
- AWS ALB → HTTP/2 downgrade history
Smuggle then poll
After a smuggle attempt, hit the target rapidly with multiple distinct requests. If the back-end is desynced, you’ll see weird responses (404 instead of 200, response from a different endpoint) - confirms desync.
Differential timing
A successful smuggling primer leaves the back-end waiting. Subsequent requests have unusual response times. Watch latency curves while probing.
ix. James Kettle’s research progression
Smuggling research is dense. Order to read:
- HTTP Desync Attacks: Request Smuggling Reborn - 2019 original
- HTTP/2: The Sequel is Always Worse - 2021 HTTP/2 attacks
- Smashing the state machine - state-based attacks
- HTTP/1.1 must die - 2024 CL.0 / 0.CL
The 2024 paper introduced “0-byte content length” desyncs which are the hot new variant. PortSwigger labs include practice for each.
x. References
- PortSwigger - HTTP Request Smuggling
- PortSwigger - Labs - guided practice
- PayloadsAllTheThings - Request Smuggling
- HackTricks - HTTP Request Smuggling
- smuggler tool
- h2csmuggler
xi. Where it leads
- Session theft → account takeover, see WEB02 Auth & Session
- Auth bypass to internal admin paths → privilege escalation
- Cache poisoning → persistent XSS-like attacks for all visitors, see WEB11 Web Cache Poisoning
- SSRF amplification → internal endpoints exposed
- Header rewriting → trust manipulation (smuggled
X-Forwarded-For: 127.0.0.1etc)
Smuggling is high-impact when it works, but rare and hard to confirm. Expect plenty of false positives. Always validate by chaining to a real impact (session capture, cache poisoning).