HTTP Request Smuggling: Abusing Reverse Proxies

HTTP request smuggling is a special web application attack that tries to exploit differences between web servers and their reverse proxies. When successful, it can allow an attacker to submit an HTTP request in the context of another user’s session. In a way, it’s analogous to sneaking malicious traffic past a firewall with overlapping fragments when the firewall and target host interpret that kind of traffic differently – but let’s back up for a minute.

Any website that serves a lot of visitors (for certain values of $A_LOT) uses a reverse proxy. Website visitors hit this reverse proxy first, whether they realize it or not. This device may block attacks, load balance across a set of web servers, modify request parameters, or shape traffic in some other way. During normal business, no one even notices that they’re communicating with two distinct servers.

A typical reverse proxy, web server relationship

This all works very well under normal conditions, but what do hackers care about “normal conditions??” Specifically, we’re interested here in what happens with malformed headers in HTTP requests. A normal request might look like one of these:

POST /admin HTTP/1.1Host: curl/7.68.0Accept: */*Content-Length: 9Content-Type: application/x-www-form-urlencodedaction=do
POST /admin HTTP/1.1Host: curl/7.68.0Accept: */*Transfer-Encoding: chunkedContent-Type: application/x-www-form-urlencoded9action=do
Here we see a POST request to a web server using Content-Length: 15 alongside a POST request to the same web server using Transfer-Encoding: chunked

The first uses Content-Length to tell the server that there are 9 bytes of data after the last header. The second uses Transfer-Encoding to say that a chunk of 9 bytes follows. They effectively do the same thing. But what happens when we send both?

POST /admin HTTP/1.1Host: curl/7.68.0Accept: */*Content-Length: 18Transfer-Encoding: chunkedContent-Type: application/x-www-form-urlencoded9action=dothings
A POST request to a web server using Content-Length: 18 and Transfer-Encoding: chunked

So what happens? The answer here is our attack path: “it depends.” By Nerd Law 2616 (sometimes called [RFC 2616](, only Content-Length or Transfer-Encoding should be used. Since developers, logically, expect only one or the other, the behavior of any given web server or proxy is anyone’s guess. It may obey Content-Length and see “9rnaction=dothings”. It may obey Transfer-Encoding and only see “action=do”.

The fun starts when the reverse proxy and the web server disagree!

In our previous example, we might get something mildly interesting to happen when the two servers interpret our message differently. But what about a request like this?

POST /admin HTTP/1.1Host: curl/7.68.0Accept: */*Cookie: session=8675309jigynContent-Length: 38Transfer-Encoding: chunkedContent-Type: application/x-www-form-urlencoded0GET /admin?action=dothings HTTP/1.1
A POST request where Content-Length and Transfer-Encoding have differing lengths

If we’re lucky, the reverse proxy and the web server will each interpret this request in a different way. If we’re extra lucky, our request will be followed up by an authenticated request from someone with privileges. If we’re really, super lucky, part of our request will be interpreted as part of that authenticated request!

POST /admin HTTP/1.1Host: curl/7.68.0Accept: */*Content-Length: 38Transfer-Encoding: chunkedContent-Type: application/x-www-form-urlencoded0GET /admin?action=dothings HTTP/1.1
GET /admin?action=nothing HTTP/1.1Host: SafariMaybe/77Accept: */*Cookie: session=8675309jigynContent-Type: application/x-www-form-urlencoded
Here we see a malicious web request with an orphan line and the authenticated user’s web request

Now what happens with our POST request? We don’t care. We don’t have a session, so we don’t expect to be able to accomplish anything directly. But that second request? We’re hoping
the last line of our request gets orphaned and then adopted by the authenticated request, resulting in an effective request that looks like this:

GET /admin?action=dothings HTTP/1.1GET /admin?action=nothing HTTP/1.1Host: SafariMaybe/77Accept: */*Cookie: session=8675309jigynContent-Type: application/x-www-form-urlencoded
Effective request processed by web server
Extra GET request smuggled into an otherwise legitimate web request

Neat! Now we have a GET to do our things, and it’s going in with the authenticated user’s cookie! As long as that web server doesn’t drop the request for having two GET lines (Nerd Law illegal in 147 countries), we just might get our action to fire!

A smuggled request associating with a legitimate request

So there you have it! HTTP request smuggling isn’t as cut and dry as Shell Shock or other branded vulnerabilities with theme songs, but, at the same time, the automated scanner is not going to beat you to finding it. Happy hunting!

*** This is a Security Bloggers Network syndicated blog from SANS Blog authored by SANS Blog. Read the original post at: