Skip to content

HTTP request smuggling

What is Http Request Smuggling

HTTP request smuggling is a web application vulnerability that exploits inconsistencies in how front-end and back-end servers interpret and process HTTP requests. By manipulating the headers and content of HTTP requests, attackers can trick intermediaries like proxies or web servers into misinterpreting the boundaries between requests, leading to discrepancies in how requests are handled. This can result in various attacks such as cache poisoning, session fixation, or unauthorized access to sensitive data.

Background

Before even going to start about HTTP request smuggling we first need to understand how modern websites work.

  1. The user makes a request to a front-end server(Can be a reverse proxy or load balancer).
  2. For efficiency and performance, multiple HTTP requests are sent in one TCP network connection.
  3. The front-end server forwards the request to the backend server.
  4. The backend server parses the multiple HTTP requests on the basis of HTTP request headers to determine the new request.

Headers to watch out

There are two important headers in the HTTP request that tells the server where the request ends.

  1. The Content-Length header
  2. The Transfer-Encoding header

These same headers are important to perform a successful HTTP request smuggling attack.

Transfer-Encoding Header

The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user. Almost all the time we will use chunked encoding to perform the attack.

Chunked

As per Mozilla,

Data is sent in a series of chunks. The Content-Length header is omitted in this case and at the beginning of each chunk you need to add the length of the current chunk in hexadecimal format, followed by '\r\n' and then the chunk itself, followed by another '\r\n'.

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
7\r\n
Network\r\n
0\r\n
\r\n

Content-Length Header

As the name suggests content-length header specifies the number of bytes present in the message body.

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

 x=anything

As per the HTTP specifications, both the "Content-Length" and "Transfer-Encoding" header can be used in a single HTTP request. Although in the specification it is also mentioned that the "Content-Length" header should be ignored by the backend server if transfer encoding header is used. This can be useful only if one server is in the picture.

Attack types

Type Front-end Backend
CL.CL Content-Length Content-Length
CL.TE Content-Length Transfer-Encoding
TE.CL Transfer-Encoding Content-Length
TE.TE Transfer-Encoding Transfer-Encoding

CL.CL

In this type of attack two "Content-Length" header is used. According to RFC, if two "Content-Length" headers are used with no sign of the "transfer-Encoding" header then the server should throw an unrecoverable error. But modern web servers especially which sit behind the reverse proxies might support this and causes the smuggling attack to be a successful vector.

GET /ping HTTP/1.1
Content-Length: 35
Content-Length: 0
Host: unstable.com

GET /smuggled HTTP/1.1
Host: unstable.com

In the above HTTP request, the proxy will prioritize the first "Content-Length" header and will treat the smuggled request as a normal part of the body. Yes, even though it's a GET request and two "Content-Length" headers are provided. When This is parsed by the backend server, the first "Content-Length" header will be ignored, and the second will be given as a priority.

Since we mentioned the value of the second header is "0" the server will not expect any request body and it will be treated as a normal pipeline request. Therefore the response to this smuggled request will be received by another user.

CL.TE

In this attack, the front-end server uses the "Content-Length" header, and the backend server uses the "Transfer-Encoding" header.

POST / HTTP/1.1
Host: unstable.com
Content-Length: 7
Transfer-Encoding: chunked

0

asdasd
  1. The front-end server will process the "Content-Length" header and will determine the length "7" up to the "asdasd".
  2. The backend server will process the request by prioritizing the "Transfer-Encoding" header and sees the message body is chunked encoded. It will process the first chunk which is "0" and will terminate the request. The remaining body "asdasd" will remain unprocessed and will be used as part of the next pipeline HTTP request.

Response

HTTP/1.1 404 Not Found
Connection: Close
Content-Length: 0

"Unrecognized method asdasdPOST"

Example

POST / HTTP/1.1
Host: unstable.com
Content-Length: 7
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: unstable.com
Content-Length: 7

asdasd

TE.CL

As of now, you might have guessed that the front-end server will use "Transfer-Encoding" and the backend server will use the "Content-Length" header.

POST / HTTP/1.1
Host: unstable.com
Content-Length: 3
Transfer-Encoding: chunked

6
asdasd
0
  1. The front-end server will process the "Transfer-Encoding" header and will parse the message body using chunked encoding. The first chunk is 6 bytes long which is "asdasd". It then goes to the next chunk which is "0" in the size and hence it terminates the request. Then the request will be forwarded to the backend server.
  2. The backend server will use the "Content-Length" header and determines that the request body is 3 bytes long, up to the start of the line following 6. The remaining part of the body will remain unprocessed.
  3. The backend server will treat the remaining bytes as a start of the new HTTP request.

TE.TE

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. 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 on 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.

Obfuscation and bypasses

There are potentially endless ways to obfuscate the "Transfer-Encoding" header, here are some of the examples:

Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
Transfer-Encoding : \x00chunked
Transfer-Encoding: chùnked
Foo: bar\r\n\rTransfer-Encoding: chunked

XSS using HTTP request smuggling

The obvious prerequisite is webpage should be vulnerable to Reflected XSS. Then you can use HTTP request smuggling to perform and increase the impact of the attack.\ When you have XSS on "User-Agent" header you can use something like below,

POST / HTTP/1.1
Host: unstable.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
Cookie: xyz=asd
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked
Content-Length: 213
Connection: keep-alive

0

GET /xss HTTP/1.1
Host: unstable.com
User-Agent: '"><script>alert("xss")</script>
Content-Length: 10
Content-Type: application/x-www-form-urlencoded

asdasd

Tools

References