code

Access Control Bypass via Request Smuggling

Written by Zinoviy Weinstein on

Access Control Bypass via Request Smuggling

Written by Zinoviy Weinstein on


Introduction

HTTP Request Smuggling is an advanced web exploitation technique first documented in 2005 by Watchfire and later popularized in 2019 by the security researcher James Kettle from PortSwigger. At the heart of this technique are two simple facts that are common in modern web applications:

  • Multiple HTTP requests are sent as a continuous stream over a single connection.
  • These requests pass through a multi-tiered architecture, starting with a front-end server (e.g., a reverse proxy, WAF, or load balancer) and ending at the backend server that handles the application logic.

The vulnerability arises when a carefully crafted request exploits a desynchronization between how the front-end and the back-end servers interpret HTTP request boundaries, causing the back-end to split a single request into two. The results are often critical, allowing an attacker to bypass security controls, gain unauthorized access to sensitive data, and directly compromise other application users (i.e, DoS). The following article aims to provide a deeper understanding of this technique and demonstrate one of its many powerful impacts.

Understanding the HTTP Request Smuggling Attack

With the introduction of HTTP/1.1 came new features that enabled the request smuggling attack. The first is the keep-alive mechanism, which allows the HTTP server to reuse established TCP/TLS connections, significantly improving communication performance. This is in contrast to HTTP/1.0, where each user establishes a separate connection every time they want to communicate.  In essence, it means that all users’ requests are merged into a continuous stream, which resembles the following:

Image No. 1 – Multiple HTTP Requests are Routed through a Single TCP/TLS Connection

The second feature is the chunked transfer encoding mechanism, which allows HTTP messages to be sent without knowing the body’s total size in advance, enabling the transmission of dynamically generated content or starting data transmission before the entire response is ready. This is achieved via the Transfer-Encoding (TE) header, which is set to the value ‘chunked’.  Each chunk is preceded by its length in hexadecimal format, and the end of the body is marked by zero. This is in contrast to the previous version of the protocol, where the body length could only be specified by the Content-Length (CL) header and was set to the exact number of bytes in the body.

Since there is no boundary between individual requests in the keep-alive stream, both the front-end and back-end servers must rely on the Content-Length and Transfer-Encoding headers to determine where each request ends, and crucially, they must agree with each other. But what happens when both headers are sent, and what if they have different values? This can lead to desynchronization, in which case a front-end server will interpret the request boundary based on one header, while the back-end server interprets it based on another. The result is a misaligned view of the request, opening the door for an attacker to smuggle additional content past front-end controls. This smuggled content is then prepended to the subsequent request in the queue.

Image No. 2 – Ambiguous Request is Split into Two Distinct HTTP Requests

Real Life Scenario

Front-end servers were initially designed to act as intermediaries between the client (typically a web browser) and the back-end web server, which contains the application’s logic. This design improved performance through caching, load distribution, and other general optimizations. In modern architectures, however, they often assume additional roles, sometimes including sensitive tasks such as access control to restricted interfaces, such as administrative panels. When a desynchronization occurs between the front-end and back-end servers, as described earlier, these access controls can be bypassed.

We can explore this specific scenario hands-on using one of PortSwigger’s labs that implements this exact vulnerable architecture. Our goal is to access the administrative panel at “/admin”, and as a Proof of Concept (PoC), delete the user “carlos”. As we can see, however, simply navigating to this path as an unauthenticated user will not work, as we receive a “403 Forbidden” response code, which typically indicates that access control restrictions are in place. 

Image No. 3 – Administrative Interface is Blocked via Access Control Restrictions

Armed with reasonable suspicion that access controls are implemented only on the front-end server, we can send a probe request to the system. A probe is a specialized HTTP request used to see how different servers in a chain interpret data. Since we cannot determine in advance which server trusts which header, we send targeted probe requests for each case and analyze the responses to identify possible desynchronization types. This particular probe tests for a CL.TE vulnerability, where the front-end server uses the Content-Length (CL) header while the back-end relies on the Transfer-Encoding (TE) header. Non-printable characters like “\r” (carriage return)  and “\n” (newline) are shown explicitly for clarity and byte counting.

Image No. 4 – HTTP Request Smuggling Probe

Let’s break down the probe request. Setting the Content-Length header to 4 means that the front-end server will process exactly 4 bytes, i.e, “1\r\nA”, and treat “0\r\n” as a part of a different request and most likely discard it from the parsing buffer. The back-end server receives the same headers and the body of “1\r\nA”. Relying on the  Transfer-Encoding header, it interprets “1\r\nA” as a chunk of length 1 containing “A” and expects more chunks ending with “0\r\n\r\n”. Since there is no other chunk or the closing “0\r\n\r\n”, the server hangs and eventually times out, which is evident in the error we receive from the server. This confirms that our target might be vulnerable to the CL.TE Request Smuggling. It is recommended to use Burp Suite’s official extension, HTTP Request Smuggler, which automates the discovery and exploitation of Request Smuggling vulnerabilities.

Constructing the Malicious Request

Now that we know exactly what we are dealing with, it is possible to construct a smuggled request that will give us access to the restricted administrative panel. This can be tricky, as we need to send two requests with precise timing and properly construct them so that the first is appended to the second.

Image No. 5 – A Smuggled Request to Access the Administrative Panel

The first time we send this request, the front-end server processes the Content-Length header and forwards the entire body. The backend server, however, processes the Transfer-Concoding header and recognizes chunk 0, meaning that the request has ended. It then fetches the contents of the “/” page, and the response is returned seemingly as if nothing unusual had happened. However, we now have the smuggled request sitting in the HTTP parsing buffer on the backend server, waiting for any incoming request to trigger the request processing queue. When another request comes in, it gets appended to the smuggled request that is already in the buffer and can be visualized as follows:

Image No. 6 – Visual Diagram of the Smuggled Request in the Back-End Parsing Buffer

In essence, the second request we sent in green is appended to the smuggled orange request as the body, not as the headers. This is because the orange smuggled request is correctly set up and constructed, as it contains a non-zero Content-Length header and the “\r\n” required by RFC 9112 to indicate the end of the headers section and the beginning of the body section. Had we not included the “\r\n”, the green request would have been appended as headers, and there would have been a conflict between the host headers and the request methods, resulting in an error (note that the Host: localhost header is the requirement of the lab, but will not necessarily be required in a different scenario).

It is now clear that we need to send a second request for the smuggled request to trigger properly. This can be particularly challenging in real-world systems with multiple users, as any other user can send a request and receive the desired response. This is not a deal-breaker, however, and it may take several attempts before we receive the desired response. After retrieving the deletion URL, we can successfully modify our smuggled request from “/admin” to the desired “/admin/delete?username=wiener” and repeat the same procedure to smuggle our deletion request, thereby deleting the user as an administrator.

Image No. 7 – Resending the Request Grants Access to the Administrative Panel

Mitigation

If you have encountered any of the above scenarios, please follow the below instructions provided by Clear Gate for immediate mitigation and to prevent Request Smuggling attacks further:

  • Use HTTP/2 end-to-end and turn off downgrading to HTTP/1.1 completely.
  • Normalize requests with ambiguous headers and reject those that cannot be normalized.
  • Never assume that requests won’t have a body.
  • Do not rely on front-end servers to handle access controls.
  • Default to discarding the connection if server-level exceptions are triggered when handling requests.

Conclusion

HTTP Request Smuggling is an old and advanced web exploitation technique that has experienced a significant comeback. It was demonstrated that HTTP/1.1 has inherent ambiguities in how different servers interpret request headers, and that these ambiguities can be exploited through request smuggling, resulting in desynchronization and ultimately allowing bypassing security controls.

Organizations should prioritize cybersecurity risk assessments and penetration tests to identify and mitigate vulnerabilities such as HTTP request smuggling, which can arise in how front-end and back-end servers handle HTTP headers.

Request smuggling attacks exploit inconsistencies in parsing headers, such as Content-Length and Transfer-Encoding, potentially allowing attackers to bypass access controls, manipulate requests, or gain unauthorized access to sensitive endpoints in web applications. By understanding the risks of HTTP request smuggling, organizations can enhance their application security. Clear Gate, a trusted cybersecurity provider, offers in-depth manual penetration tests to uncover such vulnerabilities, enabling organizations to strengthen their server and application defenses and protect valuable data against these sophisticated threats.

References