This is blog post #3 in a series that covers HTTP Request Smuggling or HTTP Desync attacks. This post focuses on the 3rd HTTP Request Smuggling lab on the PortSwigger Web Security Academy focused on obfuscating the TE header. The goal of the obfuscation is to cause a server handling the request to mishandle the request in a way such that request will be handled differently by at least one server as the request moves through the various components of the web application.
Again, the goal of this post is to not only help you solve the associated lab on the PortSwigger Web Security Academy site, but to understand the vulnerability, how to construct the payloads, and how to reproduce when testing against different web applications.
Posts in the HTTP Request Smuggling Series:
A standard POST request leaving client side will either or both of the following headers:
Per the HTTP Specification, both headers are allowed to be in the same request, however, Transfer-Encoding should always take precedence if both headers are present.
For valid Transfer-Encoding values (comes into play later in the post), check the link above.
The Web Security Academy lab is here: https://portswigger.net/web-security/request-smuggling/lab-obfuscating-te-header
The goal of the lab is to once again cause a GPOST request. This is going to be very similar to the TE.CL lab, but we’ll have to mess with the headers to get the backend to process the request using Content-Length rather than transfer encoding.
Step 1: Open the Burp built in browser. Again, this is amazing as we no longer have to fiddle with getting Burp to proxy the traffic from an external browser.
Step 2: Browser to the lab URL and the traffic will start flowing into the proxy logs. Grab the GET request to / and send that to repeater.
Step 3: We need to be able to send a request body if the page has the potential to be vulnerable. In Repeater, change the request from a GET to a POST and resend.
We get a 200 and it accepts a payload in the body so we can move on to the next step.
Step 4: Next is to test to see if we can use both Content-Length and Transfer-Encoding headers within the same request.
I let Burp set the Content-Length for me. With both sets of headers present and the payload constructed to conform to Transfer-Encoding: chunked, the request is processed successfully and we receive a 200 response.
Step 5: Here’s the step where we would test for CL.TE and TE.CL vulnerabilities. Force setting the Content-Length to an incorrect value (either too long or too short) always yields a 200 response.
This means we can assume both the backend and frontend are conforming to the HTTP specification are both are honoring transfer encoding. If I do misconfigure the payload, I immediately get a 500 error.
Step 6: Here is now where we test if we can get the web application to ignore the Transfer-Encoding header and fall back to Content-Length. By placing a second Transfer-Encoding header, it’s possible a part of the web application infrastructure will mishandle the request.
Double TE header with multiple submits:
200 response, so now we change the TE header to have an odd value:
500 response. This might mean that either the frontend or backend honors the first TE header it encounters and does not fall back to Content-Length. Here is where we try reordering the headers:
This hangs for a very long time and then returns a 500. This is great! I still have a Content-Length of 300 in the payload. Let’s set it to something less than the length of the provided payload and try multiple submits:
Bingo! It appears that the frontend is handling the request per the first Transfer-Encoding header. It appears that the backend is attempting to handle the request per the second Transfer-Encoding header, the header has a bad value, so the backend then processes the request per the Content-Length header. Now, the goal is to get the HTTP verb to the GPOST.
Step 7: With the obfuscated TE header, the vulnerability becomes a TE.CL vulnerability exactly as in the previous lab.
With the Content-Length set to 4, the backend processes the request payload ‘6e\r\n’ and leaves the rest of the payload for the next request. Since the Content-Length in the GPOST request is longer than the provided payload, the request queues and is released on the next request.
Obfuscation options: There are many different ways you can try and mess with the Transfer-Encoding header. There are a list here (about 2/3rds of the way down) that are a great starting point.
Transfer-Encoding : chunked
X: X[\n]Transfer-Encoding: chunked
Any these (and probably more) have the potential to be mishandled and potentially expose a full request smuggling vulnerability as in the lab above.