David Kirkpatrick
13 min read

Every so often a web application comes along where a bit of customization is required in your testing strategy to test it properly. The Burp Suite proxy tool is probably one of the most used tools by penetration testers to test web applications. When a situation comes along where its normal customization menu options isn’t sufficient (e.g. using Burp Macros) we can include a custom written Burp Extension to do what we want.

An application I recently tested used an Authorization token which was unique and was checked in every request. Firing off a Burp scan resulted in hundreds of Alerts in Burp in the format:

 

            [120] Authentication Failure from <TARGET.COM>

 

The number of alerts were incrementing with each scan request. Not so good! Further analysis sending the request to Burp Repeater revealed the response:

 

            HTTP/1.1 401 Invalid token

 

Stepping through the HTTP requests revealed that the Authorization: Bearer header was changing with every request and getting renewed with each request. This meant that any form of automated testing would be very painful unless we could resolve the situation.

After trying a number of options with the “Session Handling Rules” in the “Project Options” tab it became apparent that I would need to combine a Burp macro with a custom Burp Extension as follows:

 

1) A Burp Macro would grab a new token with each request from a suitable login sequence into the application.

2) The Burp Extension would  look at each request and replace the token value with the token from the macro.

 

I was fortunate that I’m not the only tester to have faced this challenge, so many thanks to Xenofon Vassilakopoulos and Rory McCune and their blogs for pointing me in the right direction.

I chose to write the extension in Python so this blog is a step-by-step process in getting this problem working in Burp using a Burp Macro and a Burp Extension

Burp Macro Configuration

Burp macros allow you to automatically modify the HTTP request to the application by looking for a set of parameters and making changes according to the macro rule before submitting it to the application. To get a valid authorization token from the application  I performed the following:

  1. Within the Burp Tool “Project Options” a new Session Handling Rule was created.
  2. It was not necessary to create a rule to determine if a session was valid or not as each request had to have a new Authorization token. Instead, within the “Session Handler Rule Editor” a new “Rule Action” was created to “Run Macro”. The macro would then run with every request.
  3. In the “Macro Editor”, the request “https://<TARGET.COM>/api/token” was chosen as the recorded macro. This request in the application created a new token when the user logs in. Obviously this URL will be different for every application. Just find the one that generates a new token (usually during login). To grab the correct token you can just highlight the token and the Start and End delimiters are automatically entered. In my case, the macro item was customized as:

        ◦ Start After Expresion: "token":"

        ◦ End at delimiter: ","rememberMe"

        ◦ This meant the followiing new token would be retrieved (obviously changing every time):

             {"token":"eyJhbG<TRUNCATED>………………...bozA","rememberMe"…..

        ◦ This was saved as “Parameter Name: token”. The naming of the token was important as it would be used in the custom python script.

  1. With every session, this POST request would be performed and the new token would be grabbed.
  2. Ensure that the Scope tab is selected in the new rule and that the URL is included in the scope under “URL Scope”.
  3. Also check that all the Burp tools are ticked for what you want to use the rule with e.g. scanner.

After testing the macro and ensuring  a 200 OK HTTP error code was returned, the next step involved creating a BURP extension that would use this new token retrieved by the macro and replace it into the “Authorization” header in each request.

BURP Extension Creation

Basic Extension Tutorial

This short tutorial will cover creating the extension using Jython (Ruby and Java are other options).

To set up Burp to create Burp extensions with python perform the following:

We can now start creating our extension. To make sure we have our Burp setup correctly, we can write a basic extension and run in. “Hello world” seems appropriate. Using your text editor safe the following into a file helloworld.py.

We need to implement the Burp Extender APIs.  All extensions need the IBurpExtender API (See the API tab in Burp for details). This is written in Java as a Java class and we need to convert to use as a Jython class. Reviewing the API in Burp for IburpExtender  it has a registerExtenderCallbacks method. We can translate this to Jython as:

 

from burp import IBurpExtender

 

class BurpExtender(IBurpExtender):

 

     def registerExtenderCallbacks(self, callbacks):

          return

 

Saving it as helloworld.py this is the very basic template to build on. To import this extension into Burp go to

  • “Extender→Extensions”
  • Click “Add” and in “Extension Details” Select Python as the Type, and choose your file helloworld.py.
  • Clicking OK will attempt to load the extension. Burp will show you any errors at this stage in the “Errors” tab.
  • Hopefully loading this we get no errors and a message “The extension loaded successfully” The “Output” tab will show nothing as it doesnt do anything. But it helps test the environment is setup correctly.
  • To add to this we will use API IburpExtenderCallbacks which has a number of methods. In this instance we can use the setExtensionName which simply changes the name

 

from burp import IBurpExtender

 

class BurpExtender(IBurpExtender):

 

     def registerExtenderCallbacks(self, callbacks):

          callbacks.setExtensionName("Hello World")

          return

 

  • In the “Extender→Extensions→Burp Extensions” find your helloworld.py in the table.
  • Simply untick to unload the extension and tick to reload the extension.
  • The Name in the table should now show “Hello World”
  • Increasing the code again to print an output we add another method printOutput

 

from burp import IBurpExtender

 

class BurpExtender(IBurpExtender):

 

     def registerExtenderCallbacks(self, callbacks):

          callbacks.setExtensionName("Hello World")

          callbacks.printOutput("Hello There!")

          return

  • This returns “Hello There” as the output.

Now we have a basic extension working, lets get back to our task in hand to generating an extension for our problem. 

Automating Token Generation

To link a burp extension with a session action we can use the ISessionHandlingAction

API that uses the performAction method. It takes a Burp request and the result of any macro as its two inputs to perform the required action. So with that said, here’s the final script:

 

import json

import datetime

from java.io import PrintWriter

from burp import IBurpExtender, IBurpExtenderCallbacks, ISessionHandlingAction

 

class BurpExtender(IBurpExtender, ISessionHandlingAction):

 

    NAME = "Bearer Authorization Token"

    

    def registerExtenderCallbacks(self, callbacks):

 

        # Make errors more readable and required for debugger burp-exceptions

        sys.stdout = callbacks.getStdout()

        # reference our callback objects

        self.callbacks = callbacks

        # obtain an extension helpers object

        self.helpers = callbacks.getHelpers()

        # Set the name of the extension fron NAME variable above

        callbacks.setExtensionName(self.NAME)

        # Register the session

        self.callbacks.registerSessionHandlingAction(self)   

        # Use PrintWriter for all output

        self.stdout = PrintWriter(callbacks.getStdout(), True)

        #self.stderr = PrintWriter(callbacks.getStdout(), True)

        self.stdout.println("Bearer Authorization Token \n")

        self.stdout.println('starting at time : {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()))

        self.stdout.println("-----------------------------------------------------------------\n\n")

        return

   

    def getActionName(self):

        return self.NAME

    

    def performAction(self, currentRequest, macroItems):

        request_info = self.helpers.analyzeRequest(currentRequest)

        #Extract the Bearer token from the macro response

        macro_response_info = self.helpers.analyzeResponse(macroItems[0].getResponse())

       

        macro_msg = macroItems[0].getResponse()

        resp_body = macro_msg[macro_response_info.getBodyOffset():]

        macro_body_string = self.helpers.bytesToString(resp_body)

        bearer_token = json.loads(macro_body_string)

        # The "token" is the name of the parameter rule configured in the macro

        bearer = bearer_token["token"]

       

        headers = request_info.getHeaders()

        req_body = currentRequest.getRequest()[request_info.getBodyOffset():]

   

        resp_headers = macro_response_info.getHeaders() 

        headers = request_info.getHeaders()

        auth_to_delete = ''

        # Search for the "Authorization:Bearer" in response to delete

        for head in headers:

            if 'Authorization: Bearer ' in head:

                auth_to_delete = head       

        try:

            headers.remove(auth_to_delete)

        except:

            pass

        # Add new bearer token from macro

        headers.add('Authorization: Bearer ' + bearer)       

        self.stdout.println('Header Checked at time :  {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()))       

        self.stdout.println("-----------------------------------------------------------------"        )

        self.stdout.println("Adding new header - Authorization bearer: " + bearer)               

        self.stdout.println("-----------------------------------------------------------------")               

        self.stdout.println("Geting authorized..done\n\n")               

        # Build request with bypass headers       

        message = self.helpers.buildHttpMessage(headers, req_body)       

        # Update Request with New Header       

        currentRequest.setRequest(message)

        return

 

        FixBurpExceptions()

 

It essentially takes the result of the macro (the “token” variable) which is the new Authorization Bearer to include in every new request.

Getting the Macro and Extension Working Together

  • Simply save the above into a file (e.g. dk_token_manipulation.py) and save into the same location as your jython jar file.
  • Load the extension “Extender→Extensions”
  • Click “Add” and in “Extension Details” Select Python as the Type, and choose your file dk_token_manipulation.py.
  • If all goes well you should get the following output:

 

Bearer Authorization Token

 

starting at time : 2018-08-17 12:01:36

 

  • Go to “Project Options→Sessions→Session Handling Rules” and edit the previously created session rule
  • In the “Rule Action” Edit the “Run Macro” previously created
  • Go to the “After running the macro, invoke a burp extension action handler” and tick it and select the new extension
  • If everything works as planned, we can select any request and send it to Burp Repeater and get a “200 OK”.
  • Looking at the Burp Extension Output screen for our extension we should get:

 

Header Checked at time :  2018-08-17 12:09:40

-----------------------------------------------------------------

Adding new header - Authorization bearer: eyJhbGci….<TRUNCATED>…….Kl6bSmicRbmsIkZ6KfKBY__mrw

-----------------------------------------------------------------

Geting authorized..done

 

  • This will now log every new session created and you should find that all the Burp tools will now work as expected creating a new valid authorization token with every request.
  • We can also use the Burp proxy to redirect any tool session we want to make use of this. Sqlmap, dirbuster and nikto are examples where we can redirect it to Burp via their proxy settings. All we have to do in Burp is under “Proxy→Options→Proxy Listeners” and “Bind to Address: All Interfaces” is selected. It will then take the request from the tool and implement our macro+extension to make a valid request each time.

 Although it took a while to get working, it’s one of those techniques that are going to pop up more often and worth getting to know. Hopefully you found this blog post helpful, if you'd like to know more about the penetration testing services that we offer then click the link below!

put your environment  to the test

 

Subscribe to our Blog

Contact Us

Access cybersecurity advisory services

 

David Kirkpatrick
David Kirkpatrick

David has been involved in the field of networks and security for over 20 years. His professional career began designing systems for IBM, later as a hardware and software and security technical consultant in the UK, where he worked on the design and implementation of secure environments for many high-profile FTSE 100 clients. This experience has provided him with the skills to specialize in penetration testing, where he has performed offensive security testing in many environments for over 14 years. This includes testing complex infrastructures, web applications, hardware and software components to improve customer security.

See All Articles
SUBSCRIBE

Subscribe to our blog

Security never stops. Get the most up-to-date information by subscribing to the Foregenix blog.