Most modern websites are forced to use a cluster of servers to deliver a better user experience. Website users can’t see that implementation and they are not required to make different requests for each server in the chain, all the communication is done in the background. The flaw known as HTTP Request Smuggling or abbreviated as HRS lies in that communication.
HTTP Request Smuggling is an attack which abuses the communication between front-end, commonly known as a load balancer, and back-end server. It exploits the HTTP protocol that uses inconsistency between an interpretation of Content-Type and Transfer-Encoding headers in an HTTP communication. There are other types of HRS attacks which I will cover later in this blog.
In the past, there was only a very old version of HTTP available (HTTP/0.9). Smuggling was not available. The only way to send 3 queries was by opening 3 TCP/IP connections to the server at the same time and each time asking for the targeted document.
The first big study on this attack was done back in 2005. Even though today HTTP/1.1 RFC 7230 contains guidelines on how to defend, these are just references and in reality, things are very different when you check actual implementations.
Let’s quickly review HTTP/1.1, the most commonly used HTTP protocol.
Several new features on HTTP/1.1 will allow very bad behaviors:
– Keep Alive mode
– Pipelined queries
– Chunked queries and responses
Below we will show you how to generate attacks using these features with three requests in action.
But before that, I want to show you how these three features work by themselves and in a combined manner with a load balancer between the end-user and back-end server.
Keep alive mode
With this feature, we can open one TCP/IP connection and send more than one request over it. The goal of this is to retrieve all the assets coming with an HTML page faster, by reusing the TCP/IP connection opened for the document, avoiding the slow TCP/IP connection opening. Using keep-alive between HTTP servers and proxies (in the middleware or between the middleware and the backend) is less common.
The other big thing in HTTP/1.1 is pipelining. Pipelining means sending several queries before having the responses of these requests.
The communication between middleware and the back-end server is not using pipelining, but it does use keep-alive, and there lies a problem of HTTP Request Smuggling.
Sometimes the communication between these two does not use Keep Alive and that is the first step in defending against HRS attacks. Eventually, the server is never expected to respond to all requests in a pipeline. You can get the first response with a Connection: close header, cutting the Keep Alive connection right after the response like shown down below.
Chunk transfer is another way of sending messages over the HTTP protocol. Instead of sending a full size of Content Length, messages can be transferred by small chunks, each one announcing a size.
A special last chunk, empty chunk, marks the end of the message. The important thing with chunks is that it is another way of manipulating the size of the message. Chunks can be used on HTTP responses (usually) but also on queries.
So after introducing features of HTTP protocol, now it’s time to show how to craft malformed requests using these features to execute HRS attacks. There are different ways to exploit HRS vulnerabilities and they differ on how these features are implemented on the servers.
I will cover some of the techniques below. After that, I will show what kind of exploitation can be conducted on vulnerable websites.
- CL-TE: the front-end server uses the Content-Length header and the back-end server uses the Transfer-Encoding header.
- TE-CL: the front-end server uses the Transfer-Encoding header and the back-end server uses the Content-Length header.
- TE-TE: the front-end and back-end servers both support the Transfer-Encoding header, but one of the servers can be induced not to process it by obfuscating the header in some way.
In this case, the front-end server uses the Content-Length header and the back-end server uses the Transfer-Encoding header. We can perform a simple HTTP request smuggling attack as follows:
POST / HTTP/1.1
The front-end server processes the Content-Length header and determines that this request is 13 bytes long, up to the end of SMUGGLED. This request is forwarded on to the back-end server.
The back-end server processes the Transfer-Encoding header and treats the message body as using chunked encoding. It processes the first chunk, which is stated to be zero-length, and so is treated as terminating the request. The following bytes, SMUGGLED, are left unprocessed, and the back-end server will treat these as being the start of the next request in the sequence.
Here, the front-end server uses the Transfer-Encoding header and the back-end server uses the Content-Length header. We can perform a simple HTTP request smuggling attack as follows:
POST / HTTP/1.1
The front-end server processes the Transfer-Encoding header and treats the message body as using chunked encoding. It processes the first chunk, which is stated to be 8 bytes long, up to the start of the line following SMUGGLED. It processes the second chunk, which is stated to be zero-length, and so is treated as terminating the request. This request is forwarded to the back-end server.
The back-end server processes the Content-Length header and determines that the request body is 3 bytes long, up to the start of the line following 8. The following bytes, starting with SMUGGLED, are left unprocessed, and the back-end server will treat these as being the start of the next request in the sequence.
TE-TE behavior: obfuscating the TE header
Here, the front-end and back-end servers both support the Transfer-Encoding header, but one of the servers can be induced not to process it by obfuscating the header in some way.
There are potentially endless ways to obfuscate the Transfer-Encoding header. For example:
Transfer-Encoding : chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding : chunked
X: X[\n]Transfer-Encoding: chunked
Each of these techniques involves a subtle departure from the HTTP specification. Real-world code that implements a protocol specification rarely adheres to it with absolute precision, and it is common for different implementations to tolerate different variations from the specification. To uncover a TE-TE vulnerability, it is necessary to find some variation of the Transfer-Encoding header such that only one of the front-end or back-end servers processes it, while the other server ignores it.
Depending whether it is the front-end or the back-end server that can be induced not to process the obfuscated Transfer-Encoding header, the remainder of the attack will take the same form as for the CL-TE or TE-CL vulnerabilities already described.
The techniques mentioned above can be used to cause serious damage to the website. Exploiting can cause bypassing security filters, replacement of regular response, credentials hijacking, etc.
Bypassing Security Filters
The first type of attack is bypassing security filters on Smuggle forbidden query.
In this type of attack, Smuggle query is forbidden, but the first query is hiding the Smuggle query from the middleware filters. Eventually, the Smuggle query is executed on the target behind the filters by the end server.
Replacement of Regular Response
The second type of attack is if the middleware is a cache server. The goal of the attack is cache poisoning, where the faked response is stored on the wrong cache entry. On a successful attack by First request, anyone requesting anything from the server would get Smuggle response. A successful attack will deface the responses for everybody, not only for the attacker. This attack could lead to Deny of Service for the servers.
The trick was to inject a partial query in the stream and wait for the regular user query, coming in the same backend connection and added to the partial request. It means the proxy is able to add some data in [+] to a TCP/IP connection with the backend that was unfinished in [-]. But the proxy does not know that two queries were sent. For the proxy, there was only one query and the response is already received.
This was a complex scheme, but for example, the Second request could contain a valid session that Smuggle did not have (cookies, HTTP Authentication). Also, this valid session was needed for Smuggle query to succeed. Credentials used in Second query are stolen (hijacked) for a Smuggle query.
Damages of such issues are very high (you can make a user perform unwanted POST actions, using his own credentials and rights).
If your website is not using any CDNs, reverse proxy or load balancer then it is safe from this attack (Security is in simplicity). As you can see, an HRS attack can make a serious impact if not patched correctly. If you are using these technologies some of the generic ways to prevent HTTP request smuggling vulnerabilities arising are:
– Always make sure to use latest version of their software,
– Disable reuse of back-end connections, so that each back-end request is sent over separate network connection
– Use HTTP/2 for back-end connection
– Use exactly the same web server software for the front-end and back-end servers, so they agree about boundaries between requests.