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:
- Within the Burp Tool “Project Options” a new Session Handling Rule was created.
- 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.
- 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.
- With every session, this POST request would be performed and the new token would be grabbed.
- Ensure that the Scope tab is selected in the new rule and that the URL is included in the scope under “URL Scope”.
- 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!