HTTP request smuggling
What is HTTP request smuggling?
HTTP request smuggling is a technique for interfering with the way a web site processes sequences of HTTP requests that are received from one or more users. Request smuggling vulnerabilities are often critical in nature, allowing an attacker to bypass security controls, gain unauthorized access to sensitive data, and directly compromise other application users.
Request smuggling is primarily associated with HTTP/1 requests. However, websites that support HTTP/2 may be vulnerable, depending on their back-end architecture.
HTTP request smuggling was first documented in 2005, and repopularized by PortSwigger's extensive research on the topic. For details, check out the following whitepapers:
What happens in an HTTP request smuggling attack?
Today's web applications frequently employ chains of HTTP servers between users and the ultimate application logic. Users send requests to a front-end server (sometimes called a load balancer or reverse proxy) and this server forwards requests to one or more back-end servers. This type of architecture is increasingly common, and in some cases unavoidable, in modern cloud-based applications.
When the front-end server forwards HTTP requests to a back-end server, it typically sends several requests over the same back-end network connection, because this is much more efficient and performant. The protocol is very simple; HTTP requests are sent one after another, and the receiving server has to determine where one request ends and the next one begins.
In this situation, it is crucial that the front-end and back-end systems agree about the boundaries between requests. Otherwise, an attacker might be able to send an ambiguous request that gets interpreted differently by the front-end and back-end systems.
Here, the attacker causes part of their front-end request to be interpreted by the back-end server as the start of the next request. It is effectively prepended to the next request, and so can interfere with the way the application processes that request. This is a request smuggling attack, and it can have devastating results.
How do HTTP request smuggling vulnerabilities arise?
Most HTTP request smuggling vulnerabilities arise because the HTTP/1 specification provides two different ways to specify where a request ends: the Content-Length
header and the Transfer-Encoding
header.
The Content-Length
header is straightforward: it specifies the length of the message body in bytes. For example:
The Transfer-Encoding
header can be used to specify that the message body uses chunked encoding. This means that the message body contains one or more chunks of data. Each chunk consists of the chunk size in bytes (expressed in hexadecimal), followed by a newline, followed by the chunk contents. The message is terminated with a chunk of size zero. For example:
Note
Many security testers are unaware that chunked encoding can be used in HTTP requests, for two reasons:
Burp Suite automatically unpacks chunked encoding to make messages easier to view and edit.
Browsers do not normally use chunked encoding in requests, and it is normally seen only in server responses.
As the HTTP/1 specification provides two different methods for specifying the length of HTTP messages, it is possible for a single message to use both methods at once, such that they conflict with each other. The specification attempts to prevent this problem by stating that if both the Content-Length
and Transfer-Encoding
headers are present, then the Content-Length
header should be ignored. This might be sufficient to avoid ambiguity when only a single server is in play, but not when two or more servers are chained together. In this situation, problems can arise for two reasons:
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 behave differently in relation to the (possibly obfuscated) Transfer-Encoding
header, then they might disagree about the boundaries between successive requests, leading to request smuggling vulnerabilities.
Note
Websites that use HTTP/2 end-to-end are inherently immune to request smuggling attacks. As the HTTP/2 specification introduces a single, robust mechanism for specifying the length of a request, there is no way for an attacker to introduce the required ambiguity.
However, many websites have an HTTP/2-speaking front-end server, but deploy this in front of back-end infrastructure that only supports HTTP/1. This means that the front-end effectively has to translate the requests it receives into HTTP/1. This process is known as HTTP downgrading. For more information, see Advanced request smuggling.
How to perform an HTTP request smuggling attack
Classic request smuggling attacks involve placing both the Content-Length
header and the Transfer-Encoding
header into a single HTTP/1 request and manipulating these so that the front-end and back-end servers process the request differently. The exact way in which this is done depends on the behavior of the two servers:
CL.TE: the front-end server uses the
Content-Length
header and the back-end server uses theTransfer-Encoding
header.TE.CL: the front-end server uses the
Transfer-Encoding
header and the back-end server uses theContent-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.
Note
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.
CL.TE vulnerabilities
Here, 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:
The front-end server processes the Content-Length
header and determines that the request body 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 so 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.
Lab: HTTP request smuggling, basic CL.TE vulnerability
TE.CL vulnerabilities
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:
Note
To send this request using Burp Repeater, you will first need to go to the Repeater menu and ensure that the "Update Content-Length" option is unchecked.
You need to include the trailing sequence \r\n\r
following the final 0
.
The front-end server processes the Transfer-Encoding
header, and so 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 on 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.
Lab: HTTP request smuggling, basic TE.CL vulnerability
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:
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 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.
Lab: HTTP request smuggling, obfuscating the TE header
Finding HTTP request smuggling vulnerabilities
Timing techniques
The most generally effective way to detect HTTP request smuggling vulnerabilities is to send requests that will cause a time delay in the application's responses if a vulnerability is present. This technique is used by Burp Scanner to automate the detection of request smuggling vulnerabilities.
Finding CL.TE vulnerabilities using timing techniques
If an application is vulnerable to the CL.TE variant of request smuggling, then sending a request like the following will often cause a time delay:
Since the front-end server uses the Content-Length
header, it will forward only part of this request, omitting the X
. The back-end server uses the Transfer-Encoding
header, processes the first chunk, and then waits for the next chunk to arrive. This will cause an observable time delay.
Finding TE.CL vulnerabilities using timing techniques
If an application is vulnerable to the TE.CL variant of request smuggling, then sending a request like the following will often cause a time delay:
Since the front-end server uses the Transfer-Encoding
header, it will forward only part of this request, omitting the X
. The back-end server uses the Content-Length
header, expects more content in the message body, and waits for the remaining content to arrive. This will cause an observable time delay.
Note
The timing-based test for TE.CL vulnerabilities will potentially disrupt other application users if the application is vulnerable to the CL.TE variant of the vulnerability. So to be stealthy and minimize disruption, you should use the CL.TE test first and continue to the TE.CL test only if the first test is unsuccessful.
Confirming HTTP request smuggling vulnerabilities using differential responses
When a probable request smuggling vulnerability has been detected, you can obtain further evidence for the vulnerability by exploiting it to trigger differences in the contents of the application's responses. This involves sending two requests to the application in quick succession:
An "attack" request that is designed to interfere with the processing of the next request.
A "normal" request.
If the response to the normal request contains the expected interference, then the vulnerability is confirmed.
For example, suppose the normal request looks like this:
This request normally receives an HTTP response with status code 200, containing some search results.
The attack request that is needed to interfere with this request depends on the variant of request smuggling that is present: CL.TE vs TE.CL.
Confirming CL.TE vulnerabilities using differential responses
To confirm a CL.TE vulnerability, you would send an attack request like this:
If the attack is successful, then the last two lines of this request are treated by the back-end server as belonging to the next request that is received. This will cause the subsequent "normal" request to look like this:
Since this request now contains an invalid URL, the server will respond with status code 404, indicating that the attack request did indeed interfere with it.
Lab: HTTP request smuggling, confirming a CL.TE vulnerability via differential responses
Confirming TE.CL vulnerabilities using differential responses
To confirm a TE.CL vulnerability, you would send an attack request like this:
Note
To send this request using Burp Repeater, you will first need to go to the Repeater menu and ensure that the "Update Content-Length" option is unchecked.
You need to include the trailing sequence \r\n\r
following the final 0
.
If the attack is successful, then everything from GET /404
onwards is treated by the back-end server as belonging to the next request that is received. This will cause the subsequent "normal" request to look like this:
Since this request now contains an invalid URL, the server will respond with status code 404, indicating that the attack request did indeed interfere with it.
Lab: HTTP request smuggling, confirming a TE.CL vulnerability via differential responses
Note
Some important considerations should be kept in mind when attempting to confirm request smuggling vulnerabilities via interference with other requests:
The "attack" request and the "normal" request should be sent to the server using different network connections. Sending both requests through the same connection won't prove that the vulnerability exists.
The "attack" request and the "normal" request should use the same URL and parameter names, as far as possible. This is because many modern applications route front-end requests to different back-end servers based on the URL and parameters. Using the same URL and parameters increases the chance that the requests will be processed by the same back-end server, which is essential for the attack to work.
When testing the "normal" request to detect any interference from the "attack" request, you are in a race with any other requests that the application is receiving at the same time, including those from other users. You should send the "normal" request immediately after the "attack" request. If the application is busy, you might need to perform multiple attempts to confirm the vulnerability.
In some applications, the front-end server functions as a load balancer, and forwards requests to different back-end systems according to some load balancing algorithm. If your "attack" and "normal" requests are forwarded to different back-end systems, then the attack will fail. This is an additional reason why you might need to try several times before a vulnerability can be confirmed.
If your attack succeeds in interfering with a subsequent request, but this wasn't the "normal" request that you sent to detect the interference, then this means that another application user was affected by your attack. If you continue performing the test, this could have a disruptive effect on other users, and you should exercise caution.
Exploiting HTTP request smuggling vulnerabilities
Using HTTP request smuggling to bypass front-end security controls
In some applications, the front-end web server is used to implement some security controls, deciding whether to allow individual requests to be processed. Allowed requests are forwarded to the back-end server, where they are deemed to have passed through the front-end controls.
For example, suppose an application uses the front-end server to implement access control restrictions, only forwarding requests if the user is authorized to access the requested URL. The back-end server then honors every request without further checking. In this situation, an HTTP request smuggling vulnerability can be used to bypass the access controls, by smuggling a request to a restricted URL.
Suppose the current user is permitted to access /home
but not /admin
. They can bypass this restriction using the following request smuggling attack:
The front-end server sees two requests here, both for /home
, and so the requests are forwarded to the back-end server. However, the back-end server sees one request for /home
and one request for /admin
. It assumes (as always) that the requests have passed through the front-end controls, and so grants access to the restricted URL.
Lab: Exploiting HTTP request smuggling to bypass front-end security controls, CL.TE vulnerability
Lab: Exploiting HTTP request smuggling to bypass front-end security controls, TE.CL vulnerability
Revealing front-end request rewriting
In many applications, the front-end server performs some rewriting of requests before they are forwarded to the back-end server, typically by adding some additional request headers. For example, the front-end server might:
terminate the TLS connection and add some headers describing the protocol and ciphers that were used;
add an
X-Forwarded-For
header containing the user's IP address;determine the user's ID based on their session token and add a header identifying the user; or
add some sensitive information that is of interest for other attacks.
In some situations, if your smuggled requests are missing some headers that are normally added by the front-end server, then the back-end server might not process the requests in the normal way, resulting in smuggled requests failing to have the intended effects.
There is often a simple way to reveal exactly how the front-end server is rewriting requests. To do this, you need to perform the following steps:
Find a POST request that reflects the value of a request parameter into the application's response.
Shuffle the parameters so that the reflected parameter appears last in the message body.
Smuggle this request to the back-end server, followed directly by a normal request whose rewritten form you want to reveal.
Suppose an application has a login function that reflects the value of the email
parameter:
This results in a response containing the following:
Here you can use the following request smuggling attack to reveal the rewriting that is performed by the front-end server:
The requests will be rewritten by the front-end server to include the additional headers, and then the back-end server will process the smuggled request and treat the rewritten second request as being the value of the email
parameter. It will then reflect this value back in the response to the second request:
Note
Since the final request is being rewritten, you don't know how long it will end up. The value in the Content-Length
header in the smuggled request will determine how long the back-end server believes the request is. If you set this value too short, you will receive only part of the rewritten request; if you set it too long, the back-end server will time out waiting for the request to complete. Of course, the solution is to guess an initial value that is a bit bigger than the submitted request, and then gradually increase the value to retrieve more information, until you have everything of interest.
Once you have revealed how the front-end server is rewriting requests, you can apply the necessary rewrites to your smuggled requests, to ensure they are processed in the intended way by the back-end server.
Lab: Exploiting HTTP request smuggling to reveal front-end request rewriting
Bypassing client authentication
As part of the TLS handshake, servers authenticate themselves with the client (usually a browser) by providing a certificate. This certificate contains their "common name" (CN), which should match their registered hostname. The client can then use this to verify that they're talking to a legitimate server belonging to the expected domain.
Some sites go one step further and implement a form of mutual TLS authentication, where clients must also present a certificate to the server. In this case, the client's CN is often a username or suchlike, which can be used in the back-end application logic as part of an access control mechanism, for example.
The component that authenticates the client typically passes the relevant details from the certificate to the application or back-end server via one or more non-standard HTTP headers. For example, front-end servers sometimes append a header containing the client's CN to any incoming requests:
As these headers are supposed to be completely hidden from users, they are often implicitly trusted by back-end servers. Assuming you're able to send the right combination of headers and values, this may enable you to bypass access controls.
In practice, this behavior isn't usually exploitable because front-end servers tend to overwrite these headers if they're already present. However, smuggled requests are hidden from the front-end altogether, so any headers they contain will be sent to the back-end unchanged.
Capturing other users' requests
If the application contains any kind of functionality that allows you to store and later retrieve textual data, you can potentially use this to capture the contents of other users' requests. These may include session tokens or other sensitive data submitted by the user. Suitable functions to use as the vehicle for this attack would be comments, emails, profile descriptions, screen names, and so on.
To perform the attack, you need to smuggle a request that submits data to the storage function, with the parameter containing the data to store positioned last in the request. For example, suppose an application uses the following request to submit a blog post comment, which will be stored and displayed on the blog:
Now consider what would happen if you smuggle an equivalent request with an overly long Content-Length
header and the comment
parameter positioned at the end of the request as follows:
The Content-Length
header of the smuggled request indicates that the body will be 400 bytes long, but we've only sent 144 bytes. In this case, the back-end server will wait for the remaining 256 bytes before issuing the response, or else issue a timeout if this doesn't arrive quick enough. As a result, when another request is sent to the back-end server down the same connection, the first 256 bytes are effectively appended to the smuggled request as follows:
As the start of the victim's request is contained in the comment
parameter, this will be posted as a comment on the blog, enabling you to read it simply by visiting the relevant post.
To capture more of the victim's request, you just need to increase the value of the smuggled request's Content-Length
header accordingly, but note that this will involve a certain amount of trial and error. If you encounter a timeout, this probably means that the Content-Length
you've specified is higher than the actual length of the victim's request. In this case, simply reduce the value until the attack works again.
Note
One limitation with this technique is that it will generally only capture data up until the parameter delimiter that is applicable for the smuggled request. For URL-encoded form submissions, this will be the &
character, meaning that the content that is stored from the victim user's request will end at the first &
, which might even appear in the query string
Lab: Exploiting HTTP request smuggling to capture other users' requests
Using HTTP request smuggling to exploit reflected XSS
If an application is vulnerable to HTTP request smuggling and also contains reflected XSS, you can use a request smuggling attack to hit other users of the application. This approach is superior to normal exploitation of reflected XSS in two ways:
It requires no interaction with victim users. You don't need to feed them a URL and wait for them to visit it. You just smuggle a request containing the XSS payload and the next user's request that is processed by the back-end server will be hit.
It can be used to exploit XSS behavior in parts of the request that cannot be trivially controlled in a normal reflected XSS attack, such as HTTP request headers.
For example, suppose an application has a reflected XSS vulnerability in the User-Agent
header. You can exploit this in a request smuggling attack as follows:
The next user's request will be appended to the smuggled request, and they will receive the reflected XSS payload in the response.
Lab: Exploiting HTTP request smuggling to deliver reflected XSS
Using HTTP request smuggling to turn an on-site redirect into an open redirect
Many applications perform on-site redirects from one URL to another and place the hostname from the request's Host
header into the redirect URL. An example of this is the default behavior of Apache and IIS web servers, where a request for a folder without a trailing slash receives a redirect to the same folder including the trailing slash:
This behavior is normally considered harmless, but it can be exploited in a request smuggling attack to redirect other users to an external domain. For example:
The smuggled request will trigger a redirect to the attacker's website, which will affect the next user's request that is processed by the back-end server. For example:
Here, the user's request was for a JavaScript file that was imported by a page on the web site. The attacker can fully compromise the victim user by returning their own JavaScript in the response.
Turning root-relative redirects into open redirects
In some cases, you may encounter server-level redirects that use the path to construct a root-relative URL for the Location
header, for example:
This can potentially still be used for an open redirect if the server lets you use a protocol-relative URL in the path:
Using HTTP request smuggling to perform web cache poisoning
In a variation of the preceding attack, it might be possible to exploit HTTP request smuggling to perform a web cache poisoning attack. If any part of the front-end infrastructure performs caching of content (generally for performance reasons), then it might be possible to poison the cache with the off-site redirect response. This will make the attack persistent, affecting any user who subsequently requests the affected URL.
In this variant, the attacker sends all of the following to the front-end server:
The smuggled request reaches the back-end server, which responds as before with the off-site redirect. The front-end server caches this response against what it believes is the URL in the second request, which is /static/include.js
:
From this point onwards, when other users request this URL, they receive the redirection to the attacker's web site.
Lab: Exploiting HTTP request smuggling to perform web cache poisoning
Using HTTP request smuggling to perform web cache deception
In yet another variant of the attack, you can leverage HTTP request smuggling to perform web cache deception. This works in a similar way to the web cache poisoning attack but with a different purpose.
What is the difference between web cache poisoning and web cache deception?
In web cache poisoning, the attacker causes the application to store some malicious content in the cache, and this content is served from the cache to other application users.
In web cache deception, the attacker causes the application to store some sensitive content belonging to another user in the cache, and the attacker then retrieves this content from the cache.
In this variant, the attacker smuggles a request that returns some sensitive user-specific content. For example:
The next request from another user that is forwarded to the back-end server will be appended to the smuggled request, including session cookies and other headers. For example:
The back-end server responds to this request in the normal way. The URL in the request is for the user's private messages and the request is processed in the context of the victim user's session. The front-end server caches this response against what it believes is the URL in the second request, which is /static/some-image.png
:
The attacker then visits the static URL and receives the sensitive content that is returned from the cache.
An important caveat here is that the attacker doesn't know the URL against which the sensitive content will be cached, since this will be whatever URL the victim user happened to be requesting when the smuggled request took effect. The attacker might need to fetch a large number of static URLs to discover the captured content.
Lab: Exploiting HTTP request smuggling to perform web cache deception
Last updated