tl;dr: HTTP Request Smuggling

Posted on Aug 20, 2021

What is it?

When a web application uses a chain of servers (e.g. a load balancer or reverse proxy that forwards a user’s request to one or more backend servers over the same connection), an attacker can hide multiple HTTP requests in one single request.

The frontend server forwards the request, and the backend server(s) interpret it as multiple requests. Chaos ensues.

How, though?

This attack is possible if the front and backend servers use different headers to ascertain the length of a request. RFC 2616 defines two headers for this: Content-Length and Transfer-Encoding.

Content-Length simply states the length of the request body like so:

POST /search HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

0123456789 

Transfer-Encoding groups the data into different ‘chunks’. On the line above each chunk is its size in hexadecimal, and there’s a 0 after the last chunk. E.g.:

POST /search HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

a
0123456789
0

By sending a request that includes both headers, the backend server can be tricked into perceiving multiple requests.

May I see it?

Ok. In this example, the frontend server processes Content-Length and the backend processes Transfer-Encoding (aka a CL.TE vulnerability).

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

0

SMUGGLED 

The frontend server passes the whole communication (13 bytes) to the backend who thinks it is chunked. It interprets the 0 as the end of the communication and the S of SMUGGLED as the start of a new, separate communication.

The inverse scenario also exists, where the headers are reversed between the servers (TE.CL)

So What?

Among other things, an attacker can use this technique to bypass authorisation controls by sending a single request:

POST / HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

0

GET /confidential HTTP/1.1
Foo: foo

The backend interprets this as two separate requests and by sending another request straight after, the attacker will receive the contents of /confidential

James Kettle’s brilliant research into the HTTP2 flavour of this vulnerability shows how he was able to redirect live Netflix users to his ‘malicious’ domain with a simple smuggled payload.

How can I fix it?

  • use a different connection for sending each request from frontend to backend
  • avoid using different server software between front and backend
  • use a WAF that detects anomalous header configurations

References & Further Reading

https://portswigger.net/web-security/request-smuggling

https://portswigger.net/research/http2

https://cobalt.io/blog/a-pentesters-guide-to-http-request-smuggling

https://datatracker.ietf.org/doc/html/rfc2616