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/downloads.html)
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:
would be converted into:
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:
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: