Main Content

Heya - HollyGraceful here, I make all of this content in my spare time, like it? Please support me :)
You can donate via Bitcoin or Patreon!

Burp Suite vs CSRF Tokens Part 2: CSRFTEI for Remote Tokens

The following is a version of my CSRF Extractor Burp Extension that works for remote tokens, the original sequential tokens version is available here. The following code is explained here.

 
print "Loading CSRFTEI (Sea-Surf-Tea)"
print "Cross-site Request Forgery Token Extractor and Inserter"
print "Remember to update token details in the extension!n"

# You need to import the interfaces you want to use here there's a list in the
# API documentation! https://portswigger.net/burp/extender/api/
# In this module, I'm just messing with requests so only need the basics:
from burp import IBurpExtender
from burp import IHttpListener

import httplib

# Regex are used for capturing the token value from the response
import re

# In the site I originally wrote this plugin for the token was in a JSON message
# in the strange form name='csrft' value='*', you should change the below so that
# it's appropriate for your target application, taking note of if ' or " are used
# and changing 'csrft' to the name of the token used in your application, e.g.
# RequestVerificationToken, antiCSRF, CSRFtoken, etc.
csrfregex = re.compile(r"name='csrft's+value='(.*?)'")

# The following code was taken from: http://tech.pedersen-live.com/2010/10/trusting-all-certificates-in-jython/
# It's not the only method of achieving this, but I thought it was graceful so used it here :) Kudos to Brandon
if 'java' in sys.platform:
    from javax.net.ssl import TrustManager, X509TrustManager
    from jarray import array
    from javax.net.ssl import SSLContext
    class TrustAllX509TrustManager(X509TrustManager): 
        def checkClientTrusted(self, chain, auth):
            pass
        def checkServerTrusted(self, chain, auth):
            pass
        def getAcceptedIssuers(self):
            return None
    trust_managers = array([TrustAllX509TrustManager()], TrustManager)
    TRUST_ALL_CONTEXT = SSLContext.getInstance("SSL")
    TRUST_ALL_CONTEXT.init(None, trust_managers, None)
    DEFAULT_CONTEXT = SSLContext.getDefault()

class BurpExtender(IBurpExtender, IHttpListener):
    # Variable to hold the token found so that it can be inserted in the next request
    discoveredToken = ''
    cookie = ''
 
    def trust_all_certificates(f):
        def wrapped(*args, **kwargs):
            if 'java' in sys.platform:
                from javax.net.ssl import SSLContext
                SSLContext.setDefault(TRUST_ALL_CONTEXT)
                try:
                    res = f(*args, **kwargs)
                    return res
                finally:
                    SSLContext.setDefault(DEFAULT_CONTEXT)
            else:
                return f(*args, **kwargs)
        return wrapped

    @trust_all_certificates
    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}
        else:
            headers = {}
        conn.request('GET', page, headers=headers)
        response = conn.getresponse()
        return response

    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        callbacks.setExtensionName("CSRFTEI")
        callbacks.registerHttpListener(self)
        print "Extension registered successfully."
        return

    def processHttpMessage(self, toolFlag, messageIsRequest, currentMessage):
        # Operate on all tools other than the proxy
        if toolFlag != self._callbacks.TOOL_PROXY:
            if messageIsRequest:
                self.processRequest(currentMessage)

    def processRequest(self, currentMessage):
        request = currentMessage.getRequest()
        parsedRequest = self._helpers.analyzeRequest(request)
        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

        token = csrfregex.search(data1)
        if token is None:
            print "No token found in response.n"
        else:
            BurpExtender.discoveredToken = token.group(1)
            print "Found a token."

        if BurpExtender.discoveredToken != '':
            # Here we're using the token name as the start point and an ampersand as the end point
            # so your token must be in the request like csrft=xxxxxx& if there's no trailing amp then
            # this won't work!
            updatedBody = re.sub(r'csrft=.*?&', 'csrft={0}&'.format(BurpExtender.discoveredToken), requestBody)
            print "Replaced the token, using: " + BurpExtender.discoveredToken
        else:
            # No token to replace, so leave the body as it is
            updatedBody = requestBody
            print "No token to replace."

        updatedRequest = self._helpers.buildHttpMessage(parsedRequest.getHeaders(), updatedBody)        
        currentMessage.setRequest(updatedRequest)