In a previous post, I wrote about creating a Burp Suite extension in Java using the IBurpExtender interface. When performing web application security testing, I often need to write small pieces of code to help me in automating some tasks and the code is generally specific the the application I am testing. Whereas I like Java, I think that dynamically typed languages are more efficient for creating small pieces of code quickly and efficiently. However, don't misquote me, dynamically typed languages like Python can also be (and are) used for very large development projects.
Having used
Python for about 8 years now, I found very interesting the idea of creating a
Python binding for the Burp Suite. Since Burp is written in Java, I obviously
used Jython, the java
implementation of Python.
My goal was to allow anyone to write the Burp extensions directly in Python using the same BurpExtender interface. Therefore, if you wrote Burp extensions in Java, you already know how to write them in Python.
First example
This very simple extension replaces the string "java" to "python" in all http responses received by the Burp. This is useless; but it is just to show how easy it is to write an extension in Python. Only those few lines of code are needed:
from burp import IBurpExtender
class BurpExtender(IBurpExtender):
def processProxyMessage(self,messageReference, messageIsRequest, remoteHost, remotePort,
serviceIsHttps, httpMethod, url, resourceType, statusCode,
responseContentType, message, interceptAction):
if not messageIsRequest:
message = message.tostring().replace("java","python")
return message
Embedding an interactive python interpreter
Let's look at something a bit more interesting, using an interactive python console to work on some messages proceeded by Burp:
from burp import IBurpExtender
from java.net import URL
from code import InteractiveConsole
class BurpExtender(IBurpExtender):
def processProxyMessage(self,messageReference, messageIsRequest, remoteHost, remotePort,
serviceIsHttps, httpMethod, url, resourceType, statusCode,
responseContentType, message, interceptAction):
if not messageIsRequest:
uUrl = URL("HTTPS" if serviceIsHttps else "HTTP", remoteHost, remotePort, url)
if self.mCallBacks.isInScope(uUrl):
message = message.tostring()
from pprint import pprint
loc=dict(locals())
c = InteractiveConsole(locals=loc)
c.interact("Interactive python interpreter")
for key in loc:
if key != '__builtins__':
exec "%s = loc[%r]" % (key, key)
return message
def registerExtenderCallbacks(self, callbacks):
self.mCallBacks = callbacks
What this code does basically is: launch a Python interpreter, make all the python namespace available (you can access and modify any field and method that is offered by the BurpExtender object). Is this not cool?
Only messages that are in the Burp Suite scope will be intercepted and made available interactively (Target/Scope tab in Burp). This is done by the line:
if self.mCallBacks.isInScope(uUrl):
isInScope is a callback function, the mCallBack
object is registered by the registerExtenderCallbacks python
method.
Below is an example on what is available with the interactive shell. The shell is available on the console used to start Burp suite. When a message is in the scope, the shell is launched.
First, we are within the scope of the processProxyMessage
method and have direct access to the different fields.
Interactive python interpreter >>> pprint(dir()) ['httpMethod', 'interceptAction', 'message', 'messageIsRequest', 'messageReference', 'pprint', 'remoteHost', 'remotePort', 'resourceType', 'responseContentType', 'self', 'serviceIsHttps', 'statusCode', 'uUrl', 'url'] >>> pprint(message) 'HTTP/1.1 200 OK\r\nDate: Mon, 30 Aug 2010 12:16:40 GMT\r\nServer: Apache/2.2.9 (Fedora)\r\nLast-Modified: Mon, 30 Aug 2010 11:12:52 GMT\r\nETag: "2aa3a-4d-48f088ba1f500"\r\nAccept-Ranges: bytes\r\nContent-Length: 77\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<html>\n<head>\n<title>Test!</title>\n</head>\n<body>\nHello all!\n</body>\n</html>\n' >>> print resourceType, responseContentType, statusCode html text/html; charset=utf-8 200
It is also possible to interact with all the BurpExtender fields and methods:
>>> pprint(dir(self)) ['ACTION_DONT_INTERCEPT', [..] 'applicationClosing', 'class', 'classDictInit', 'clone', 'commandLineArgs', 'equals', 'finalize', 'getClass', 'hashCode', 'mCallBacks', 'newScanIssue', 'notify', 'notifyAll', 'processHttpMessage', 'processProxyMessage', 'registerExtenderCallbacks', 'setCommandLineArgs', 'toString', 'wait'] >>>
It is possible for example to call any Burp method provided by the callback object:
>>> for message in self.mCallBacks.getProxyHistory(): ... message.getRequest().tostring() ... 'GET /test.html HTTP/1.1\r\nHost: 127.0.0.1\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Fedora/3.0.5-1.fc9 Firefox/3.0.5\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nProxy-Connection: keep-alive\r\nCache-Control: max-age=0\r\n\r\n'
Adding new options in Burp Suite menus
This only works with the professional version of Burp Suite (minimum 1.3.07)
Now I am going to show how to create a new menu item within Burp that will call new functions written in Python. This code below adds a "Compare parameters" item in the Burp Suite contextual menu. In the Proxy/History tab, you can select two messages, right click and select the new compare function. This code is just an example of what can be done, it compares GET and POST parameters between two requests and tells the differences. It can be useful though because the Burp Suite comparer is not great to compare requests.
from burp import IBurpExtender
from burp import IMenuItemHandler
from cgi import parse_qs
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, callbacks):
self.mCallBacks = callbacks
self.mCallBacks.registerMenuItem("Compare parameters", ArgsDiffMenuItem())
class ArgsDiffMenuItem(IMenuItemHandler):
def menuItemClicked(self, menuItemCaption, messageInfo):
print "--- Diff on arguments ---"
if len(messageInfo) == 2:
# We can do a diff
request1=HttpRequest(messageInfo[0].getRequest())
request2=HttpRequest(messageInfo[1].getRequest())
print "Diff in GET parameters:"
self.diff(request1.query_params,request2.query_params)
print "Diff in POST parameters:"
self.diff(request1.body_params,request2.body_params)
else:
print "You need to select two messages to do an argument diff"
print "\n\n"
def diff(self, params1, params2):
for param in params1:
if param not in params2:
print "Param %s=%s is not is the second request" % \
(param, params1[param])
continue
if params1[param] != params2[param]:
print "Request1 %s=%s Request2 %s=%s" % \
(param, params1[param], param, params2[param])
for param in params2:
if param not in params1:
print "Param %s=%s is not is the first request" % \
(param, params2[param])
class HttpRequest:
def __init__(self, request):
self.request=request.tostring().splitlines()
self.query_params={}
self.getParameters()
def getParameters(self):
# get url parameters
try:
self.query_params=parse_qs(\
''.join(self.request[0].split()[1].split("?")[1:]))
except:
self.query_params={}
# get body parameters
try:
index=++self.request.index('')
self.body_params=parse_qs(\
''.join(self.request[index:]))
except:
self.body_params={}
How to use the python extension
You need the burppython.jar extension. I have created a jar
file that contains the jython interpreter so you don't need to install anything
else.
Steps:
- You need to download the zipfile attached at the end of this article.
- You need to unzip the content in a dedicated folder.
- You need to copy the burpsuite jarfile in this folder (something like
burpsuite_pro_v1.3.07.jarorburpsuite_v1.3.03.jar) - The python extension (
BurpExtender.py) needs to be placed in the Lib subfolder. - You can launch the burp suite using suite.bat or suite.sh
Please send me an email to david@ombrepixel.com for any
questions
To be done
A lot needs to be done,
- Add the capability of using several python and java extensions at the same time and link them together
- Add the capability of dynamicly reload a python extension without having to stop-restart Burp
- Put the project on a tracking version system like GitHub
- Add more Demo that could leverage on the numerous Python libraries that already exist
- ..
Only two
months after version 3.4.0 of the framework, 




