Burp Suite vs CSRF Tokens: Round Two

So recently I wrote about writing burp extensions and I taught this through writing an extension to deal with CSRF tokens that are in each page, so as you navigate the site or fuzz a function you have to extract a token from each page to include it in the next request.

That’s not the only way to implement tokens though, and today I came across “the other way” during a Penetration Test so modified my original code and figured I’d share this version too!

This particular application had two functions, like /api/edit_user and /api/view_user. The CSRF token was found by going to view_user and it could then be sent to edit_user, but the token was only valid once. Therefore every time I wanted to send a payload to edit_user I first had to request a valid token from view_user (and so I had to teach burp scanner how to do this too!)

Example code for this is here! Again I’ll explain how it works below:

The way that I achieved this was to modify my original burp extension so that before it sent each request it would send a preliminary request to the token granting function but this request must also include the cookie supplied in the main request.

A further hurdle in this task was the fact that I was testing in a pre-production environment and the server I was testing had a self-signed certificate, something that Jython doesn’t like so much. Luckily I found a really nice solution for this written by Brandon of pedersen-live.com who has a nice write up about it here.

So to capture a new CSRF token first of all I have to determine the cookie in use for the original request and then use that cookie to request a new token from the view page, that’s covered by the following code:

        requestHeaders = self._helpers.bytesToString(request)
        #print requestHeaders
        for header in requestHeaders.split("n"):
            if header.startswith("Cookie"):
                print "Found cookie: " + header[8:]
                BurpExtender.cookie = header[8:]
        requestBody = self._helpers.bytesToString(request[parsedRequest.getBodyOffset():])

        resp = self.connect_to_untrusted_host("target.example.net", "/api/view_user")
        print "Requested new token from target page"
	data1 = resp.read()
        print data1

This code takes the original request’s headers, pulls them apart and looks for the header that starts with “Cookie” and then takes that whole line and stores it so that it can include those cookies in the request to the view page. Here that request to the view page is made by the function connect_to_untrusted_host which is a function that comes directly from Brandon’s code, I’ve just snuck into it the ability to inject the stored cookies, it looks like this:

    def connect_to_untrusted_host(self, host, page):
        conn = httplib.HTTPSConnection(host)
        # Cookies required are pulled out of the request, this is good for most sites
        # but if you're using HTTP Basic, you can change this section here:
        print "Hacked a cookie into request"
        if BurpExtender.cookie != '':
            headers = {"Cookie": BurpExtender.cookie}
            headers = {}
        conn.request('GET', page, headers=headers)
        response = conn.getresponse()
        return response

Here you can see that it simply requests the given page but if a cookie was extracted earlier it sticks that into the new request and returns the response for processing! The response is handled in the exact some way that the original extension I blogged about handles tokens! The TL;DR of that is it extracts the CSRF token out of the response and injects it into the new request so that the request is valid!
Example code is all here.