In Blog

All pen testers use the Burp suite at some point, but few really exploit it to its full potential. One of the most extraordinary features of Burp is the ability to write your own custom handler.

Just because Burp is written in Java and the extender APIs uses a Java interface, it doesn’t mean we as pentester’s are limited to Java thanks to the wonderful world of Jython! Just as its name implies, Jython is a Python implementation in Java. What does that mean?

It means one can do the following from Python code:

from Burp import ISomeWidget

Lets get started and create a Burp extensions using Jython! As a reference, here is the documentation for the Burp interfaces: https://portswigger.net/burp/extender/api/burp/package-summary.html

First, we need to download the latest version (herein I am using version 2.5.3 from: https://www.jython.org)

In the Burp menu Extender -> Options, specify the path to the JAR file under Python Environment:

The code itself is loaded via the Burp menu Extender -> Extensions by using the Add button. Be sure to select the Extension type Python. Here is a screen capture of my code being loaded as extension.py:

Hit Next and Burp will take care of loading your code. If there are errors, those will be shown.

For starters, lets write an extension which loads properly, runs without errors, spews a few messages to the console to show that things are running as expected, and as an exercise appends a custom HTTP header to each request.

Firstly, the plumbing which will look essentially the same for all extensions is as follows:

from burp import IBurpExtender
from burp import IHttpListener
from datetime import datetime

class BurpExtender(IBurpExtender, IHttpListener):

 def registerExtenderCallbacks(self, callbacks):
 self._callbacks = callbacks
 self._helpers = callbacks.getHelpers()
 callbacks.setExtensionName("Marios plugin")
 callbacks.registerHttpListener(self)
 return

IBurpExtender must be implemented by all extensions, and implementations must be called BurpExtender. Fail to do so and you will not get passed the extension loading phase!

The second critical piece of the puzzle is the IHttpListener interface; this is the interface which gives our extension the chance to view all the HTTP traffic going through the Burp proxy. When we intercept the traffic, we will be able to see it and modify it as well.

The IBurpExtenderCallbacks::registerHttpListener call above is what makes the magic happen. Once that is done, we only need to setup the callback for the IHttpListener::processHttpMessage (see https://portswigger.net/burp/extender/api/burp/IHttpListener.html)

# currentRequest is a IHttpRequestResponse
 def processHttpMessage(self, toolFlag, messageIsRequest, currentRequest):
  # only process requests
  if not messageIsRequest:
   return

The parameters here are straightforward, toolFlag is an enumeration specifying which Burp tool the traffic is from. I don’t explain it further here, but it could be one of the following values: https://portswigger.net/burp/extender/api/burp/IBurpExtenderCallbacks.html

The parameter I do check is messageIsRequest, since I don’t care about responses. I want to make sure only requests are manipulated by our extension.

At this point we are in a request and ready to tweak it to our needs. I like to start out with some output:

requestInfo = self._helpers.analyzeRequest(currentRequest.getRequest()) # analyzeRequest() returns a IRequestInfo
timestamp = datetime.now()
print "Intercepting message at:", timestamp.isoformat()</pre>
Now we gather the HTTP headers in a python list
<pre>headers = requestInfo.getHeaders()
newHeaders = list(headers)
newHeaders.append("Timestamp: " + timestamp.isoformat())</pre>
Notice we have appended a Timestamp HTTP header at this point.
<pre>parameters = requestInfo.getParameters()
for parameter in parameters:
 print "parameter:"
 print parameter.getName()
 if 'HEADER' in parameter.getName():
  newHeaders.append("via: " + parameter.getValue())
  testmario = self._helpers.removeParameter(currentRequest.getRequest(), parameter)
  currentRequest.setRequest(testmario)

The logic here is straightforward. Get the URL parameters. Print them out. If one of the URL parameters is named HEADER remove it, but not before adding a new HTTP header into the python list named via and we giving it the value of this HTTP header whatever the URL value was. For example:

?HEADER=batman

would be converted into:

via: batman

Finally we can do the setRequest call specifying the new message:

bodyBytes = currentRequest.getRequest()[requestInfo.getBodyOffset():]

 bodyStr = self._helpers.bytesToString(bodyBytes)
 newMsgBody = bodyStr + timestamp.isoformat()
 newMessage = self._helpers.buildHttpMessage(newHeaders, newMsgBody)

 print "Sending modified message:"
 print self._helpers.bytesToString(newMessage)
 currentRequest.setRequest(newMessage)
 return

Note the use of getrequests/setrequests (see the https://portswigger.net/burp/extender/api/burp/IHttpRequestResponse.html documentation).

This basic extension code should be a good start to help you create your own Burp extensions and provide some great integration opportunities. Be sure to launch Burp with extra memory, for example I use

java -XX:MaxPermSize=1G -jar burpsuite_pro_v1.5.20.jar

If we run this, and navigate to an URL such as:

www.superconfigure.com?HEADER=bacon

The Output tab should display:

Intercepting message at: 2013-12-12T16:40:16.375999
parameter:
HEADER
Sending modified message:
GET /?HEADER=bacon HTTP/1.1
Host: www.superconfigure.com
Timestamp: 2013-12-12T16:40:16.375999
via: bacon
Content-Length: 26

The headers in red above are new as added by our extension. Here’s what it looks like in Burp:

Leave a Comment

Start typing and press Enter to search