We can create and load Python/Java utility modules in Burp and then use them in extensions. It's a somewhat unknown/unused capability in Burp's Python/Java extensions.
Note: Alternatively, the modules can be placed in the same path as the extension and loaded/used the same way. For example, instead of putting the Burp Exceptions file in the modules folder, store it in the extension directory.
If you use Burp, you have seen this screen. This is where we set the Jython jar file. Everyone ignores that second input field here (under Python Environment
). Any Python module in this path will be loaded with Burp. Then the extension can use any function/object inside them.
The same capability exists for Java but not for Ruby. In this blog post I will discuss how I use these modules in my Python extensions.
Extender options
Before starting to develop any Python extension, please make sure to use:
The instructions to set it up are easy and straightforward. You need to load a module and then add a few lines to your extension. Be sure to remove them before you release your code.
Let's say we have referenced a non-existent field or variable in the extension (self.nothing
). This is the meaningless error message in we will see Burp:
Burp's error message
This is the error message from the extension (appears in the Output
tab):
Meaningful error message
It actually tells us where the problem is. SO YEAH, USE THE DAMN EXTENSION.
I couldn't find any usage documentation or tutorials about modules. Burp documentation mentions the field but that's about it. If you have setup Burp Exceptions, you have probably figured out how they are imported. Modules are imported by their file name. If our file is named mylibrary.py
, it's can be imported in the extension like this:
from mylibrary import *
# or a single function
from mylibrary import myFunction
Now we can use any function inside mylibrary.py
(or just myFunction
in the second case) in our extension.
The end user needs to setup the modules path and copy extra files there. I am also not sure how modules work when extensions are installed from Burp App store.
My solution (yours could be different) is using modules for development/personal/work extensions and then copying the needed functions (or all of them) from the modules to the actual extension for release.
Anything and everything. Anything you want and think is useful. I have turned it into a utility library. Things like base64 encoding/decoding, encryption (more on that in the next blog post) and functions that use the Burp's IExtensionHelpers methods. It has very helpful methods.
The only way to get an object to use these methods is through the IBurpExtenderCallbacks.getHelpers() method. As a result, I pass an instance of it manually as a function parameter to each helper function that uses it.
Let's look at a minimal example based on the custom editor tab
example at https://github.com/PortSwigger/example-custom-editor-tab/blob/master/python/CustomEditorTab.py.
We will see a complete example in the next blog post. The callbacks
object is available inside the registerExtenderCallbacks
class BurpExtender(IBurpExtender, IMessageEditorTabFactory):
#
# implement IBurpExtender
#
def registerExtenderCallbacks(self, callbacks):
# keep a reference to our callbacks object
self._callbacks = callbacks
# obtain an extension helpers object
self._helpers = callbacks.getHelpers()
Here we are saving the helper
object as field. This saves it to the extender
object that is passed to __init__
of the tab object. Here we are assigning extender
to the _extender
. What I usually do is, add helpers
directly to a field.
class Base64InputTab(IMessageEditorTab):
def __init__(self, extender, controller, editable):
self._extender = extender
self._editable = editable
# adding helpers as a direct field.
self.helpers = extender._helpers
# create an instance of Burp's text editor, to display our deserialized data
self._txtInput = extender._callbacks.createTextEditor()
self._txtInput.setEditable(editable)
Now we can use self.helpers
inside the extension and use its methods like this:
self.helpers.analyzeRequest(content)
self.helpers.base64Encode(someBytes)
Base64 is an exception because Jython already has a base64 module, so it can be written without the need for helpers
. You could also write your own implementation of IExtensionHelpers
methods but why re-implement when it's already been done.
Note: You cannot use anything that is not in the Jython standard library. For example, pycrypto
for encryption/decryption is not available inside Burp extensions. We will discuss a couple of workarounds in the next blog post.
I also pass it directly as a parameter to helper functions inside my Python module:
# inside mylibrary.py
# getInfo processes the request/response and returns info
def getInfo(content, isRequest, helpers):
if isRequest:
return helpers.analyzeRequest(content)
else:
return helpers.analyzeResponse(content)
# getBody returns the body of a request/response
def getBody(content, isRequest, helpers):
info = getInfo(content, isRequest, helpers)
return content[info.getBodyOffset():]
Now instead of manually doing it, we can just call a function and get the body of what we have.
# inside the extension
# import the module
from mylibrary import getInfo, getBody
# later inside one of the tab functions
def setMessage(self, content, isRequest):
if content is None:
# clear our display
self._txtInput.setText(None)
self._txtInput.setEditable(False)
else:
# get the body of the message
body = getBody(content, isRequest, self.helpers)
We learned about Burp modules in Python and Java. In the next blog post, I will use this as a building block to show what I learned from creating a Burp extension to decrypt some custom protocol.