Simple trick to increase readability of exceptions raised by Burp extensions written in Python.
Have you ever written a Burp Extender extension in Python to end up with a completely unlegible exception stack trace? Something like:
java.lang.RuntimeException: org.python.core.PyException at burp.fl.a(Unknown Source) at burp.edd.a(Unknown Source) at burp.e2g.a(Unknown Source) at burp.e2g.g(Unknown Source) at burp.i1c.stateChanged(Unknown Source) at javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:416) at javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:270) ...
Would you rather see something you can actually understand? Just like:
*** PYTHON EXCEPTION Traceback (most recent call last): File "/Users/mb/Desktop/burp extension/exceptions_fix.py", line 8, in decorated_function return original_function(*args, **kwargs) File "/Users/mb/Desktop/burp extension/CustomEditorTab.py", line 78, in setMessage self._txtInput.setEsditable(self._editable) AttributeError: 'burp.ul' object has no attribute 'setEsditable'
I'm presenting a neat solution to the problem!
- Grab https://github.com/securityMB/burp-exceptions/blob/master/exceptions_fix.py and save it to your Folder for loading modules.
Hint: If you don't know what folder it is, open your Burp and go to Extender→Options→Python Environment→Folder for loading modules. If you haven't picked any folder yet, just set it now.
- Modify your Burp extension a bit. Firstly, add the following lines in the beginning of your Python file:
from exceptions_fix import FixBurpExceptions import sys
then find your
registerExtenderCallbacks method and add one line to it:
sys.stdout = callbacks.getStdout()
and then also add one new line at the very end of the file:
If you're not sure about the changes you're supposed to make, grab https://github.com/securityMB/burp-exceptions/blob/master/AlteredCustomEditorTab.py and look for lines with
# ADDED LINEcomment.
Just let the magic happen.
Alternatively, you might add
@FixBurpExceptionForClass decorator just before the class you'd like to have exceptions fixed.
@FixBurpExceptionForClass class SomeClass(IMessageEditorTab): ....
How does it work?
The code is a fine example of what one can achieve with metaprogramming in some programming languages. Let's start with the most outer function, namely
def FixBurpExceptions(): for name, cls in inspect.getmembers(sys.modules['__main__'], predicate=inspect.isclass): FixBurpExceptionsForClass(cls)
We're just iterating over all classes defined in
__main__ module (which is just the main file of your Burp extension) and calling
def FixBurpExceptionsForClass(cls): for name, method in inspect.getmembers(cls, inspect.ismethod): setattr(cls, name, decorate_function(method)) return cls
Here, for a change, we're iterating over all methods defined in a given class and overwrite them with results of
decorate_function(method). So what does it do?
def decorate_function(original_function): @functools.wraps(original_function) def decorated_function(*args, **kwargs): try: return original_function(*args, **kwargs) except: sys.stdout.write('\n\n*** PYTHON EXCEPTION\n') traceback.print_exc(file=sys.stdout) raise return decorated_function
To better understand the code, you need to be familiar with Python decorators. At first, we use a @functools.wraps decorator so that the wrapper function will look just like the wrapped function (
original_function). Then, we call the original function in
try: ... except block so that we're able to catch any exception that might be raised. If some exception is raised, we're just writing the stack trace to
sys.stdout and re-raise the exception.
Feel free to contact me via GitHub or Twitter (@SecurityMB) if you have any questions or remarks.