Attribution
This Python 201 guide is based on our notes from the TCM Security - Python 201 for Hackers video course by Riley Kidd.
We highly recommend this course as a great starting point for learning Python; it was instrumental in our understanding of the subject. We would like to extend a big thank you to the TCM Security Team for providing such valuable resources!
Extending Burp
Introduction
Burp Suite is a popular web application proxy frequently used for security testing. The Burp Extender allows you to add custom functionality to the tool, and many popular extensions already exist to automate security testing.
You can interface with Burp Suite using various APIs. While Burp is written in Java, you can create extensions in Python by using Jython (a Java implementation of Python).
Steps to create an extension
- Install Jython: Download the standalone JAR and configure its location in the Extender options tab.
- Create a Python script containing at least a
BurpExtenderclass. - Add your desired functionality within the script.
- Load the script via the Extender tab.
Custom Burp Extension
Preparing Burp Suite
- Download: Visit portswigger.net and navigate to Products > Burp Suite Community Edition.
- Install: Use the default settings.
- Jython settings: Go to Burp Suite > Extensions > Extensions settings > Extension > Python environment and point the “Location of Jython standalone JAR file” to your
.jarfile. - Helper Addition: To make debugging easier, download the helper from github.com/securityMB/burp-exceptions. Save the raw guide file to your extensions directory and ensure it has a
.pyextension.
Preparing Jython
- Download: Visit jython.org, go to Download > Jython Standalone > Versions, and download via the browser.
- Burp Suite API: Navigate to Burp Suite > Extensions > APIs and search for
ITab. - Adding the module to Burp:
- Go to Burp Suite > Extensions > Installed > Add.
- Select Python as the extension type.
- Select your
burp_module.pyfile.
Demo Example
Feel free to experiment by commenting and uncommenting sections as you see fit.
# for creating the gui in burp
from burp import IBurpExtender, ITab
from javax import swing
from java.awt import BorderLayout
# others
import sys, time, socket, threading
from exceptions_fix import FixBurpExceptions
# implement the burp extender interface
# -- required by burp for an extension
class BurpExtender(IBurpExtender, ITab):
def registerExtenderCallbacks(self, callbacks):
self.clicked = False
self.response_data = None
self.kill_threads = False
sys.stdout = callbacks.getStdout()
self.callbacks = callbacks
self.callbacks.setExtensionName("Bind shell")
self.tab = swing.JPanel(BorderLayout())
# adding more components onto our tab
# creating the panel
text_panel = swing.JPanel()
# create the boxes
box_vertical = swing.Box.createVerticalBox()
box_horizontal = swin.Box.createHorizontalBox()
# text area to interact with -- specifying the target
self.ip_address = swing.JTextArea('', 2, 100)
# wrap the lines
self.ip_address.setLineWrap(True)
# adding a border
self.ip_address.border = swing.BorderFactory.createTitledBorder("IP Address:")
# add to the horizontal box
box_horizontal.add(self.ip_address)
# add horizontal box to the vertical box
box_vertical.add(box_horizontal)
# text area to interact with -- specifying the command
# create horizontal box
box_horizontal = swing.Box.createHorizontalBox()
self.user_command = swing.JTextArea('', 2, 100)
self.user_command.setLineWrap(True)
self.user_command.border = swing.BorderFactory.createTitledBorder("Command:")
box_horizontal.add(self.user_command)
box_vertical.add(box_horizontal)
# Creating the buttons
box_horizontal = swing.Box.createHorizontalBox()
button_panel = swing.JPanel()
# adding the buttons -- connect;send;disconnect
self.connect_button = swing.JButton('[ --- Connect --- ]', actionPerformed=self.connect)
self.send_button = swing.JButton('[ --- Send Command --- ]', actionPerformed=self.send)
self.disconnect_button = swing.JButton('[ --- Disconnect --- ]', actionPerformed=self.disconnect)
# by default, we do not want to be able to press the disconnect button
self.disconnect_button.enabled = False
# also do not want to be sending anything to nowhere
self.send_button.enabled = False
# add the buttons to the panel
button_panel.add(self.connect_button)
button_panel.add(self.send_button)
button_panel.add(self.disconnect_button)
# adding the button panel
box_horizontal.add(button_panel)
box_vertical.add(box_horizontal)
# adding the text area -- to see the interaction between burp and the bind shell
box_horizontal = swing.Box.createHorizontalBox()
self.output = swing.JTextArea('', 25, 100)
self.output = setLineWrap(True)
self.output = setEditable(False )
# add the scroll pane
scroll = swing.JScrollPane(self.output)
box_horizontal.add(scroll)
box_vertical.add(box_horizontal)
# add the vertical box to the panel
text_panel.add(box_vertical)
# add the text panel to the tab
self.tab.add(text_panel)
callbacks.addSuiteTab(self)
return
def getTabCaption(self):
return "Bind shell"
def getUIComponent(self):
return self.tab
# adding the button functions
# button clicks are associated with a type of event
def send(self, event):
# variable used to later talk to our threads
self.clicked = True
# sleep for half a sec -- race condition
# prevent the burp gui from locking up
time.sleep(0.5)
self.output.text = self.response_data
# create the threads
def send_thread(self):
while True:
if self.kill_threads:
sys.exit()
if self.clicked:
self.clicked = False
# process the message -- send the user's command
self.s.send(self.user_command.text)
def recv_thread(self):
while True:
if self.kill_threads:
sys.exit()
data = self.s.recv(4096).replace("Enter Command> ", "")
if data:
self.response_data = data
def connect(self, event):
try:
# create the socket
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect to the ip the user enters into the text field
self.s.connect((self.ip_address.text, 1234))
# tell the process not to kill our threads
self.kill_threads = False
# set up a few threads -- we want to send and receive data at the same time
# -- without hanging the already existing burp gui
threading.Thread(target=self.send_thread).start()
threading.Thread(target=self.recv_thread).start()
# clean up the gui
self.connect_button.enabled = False
self.disconnect_button.enabled = True
self.send_button.enabled = True
self.ip_address.enabled = False
# print out some useful msg
self.output.text = "Connected to bind shell!"
except:
self.output.text = "Could not connect, try again!"
def disconnect(self, event):
# send exit to the bind shell
self.s.send("exit")
# close the socket
self.s.close()
# tell the process we want to kill our threads
self.kill_threads = True
# clean up the gui
self.connect_button.enabled = True
self.disconnect_button.enabled = False
self.send_button.enabled = False
self.ip_address.enabled = True
self.output.text = "Disconnected from bind shell!"
FixBurpExceptions()
Debugging
General
- Accept that bugs happen all the time.
- Debugging is the process of finding, isolating, and fixing bugs when something is not working as expected.
- Fixing syntax errors is a form of debugging.
- Using print statements (e.g.,
print("here")) is a common debugging technique. - Breakpoints in a debugger are more effective for larger, complex programs.
Breakpoints
- Breakpoints freeze or break an application at a specific point, allowing you to inspect or change variables, memory, or registers.
- Conditional breakpoints only trigger when a specific condition is met.
- Breakpoints help identify the source of a problem and track down bugs.
- Testing identifies problems; debugging fixes them (e.g., identifying an SQLi vulnerability is testing, while patching it is debugging).
Terminology
- Static analysis: Reading the program without executing it.
- Source code analysis: Automatically checking for known bugs.
- Tracing or Print Debugging: Using sequential print statements to track execution.
- Remote Debugging: Debugging a program running on a different system.
- Post-mortem Debugging: Debugging based on crash dumps and error logs.
Notes
- Debugging is hard, and tracing root causes can be complex.
- It is often easy to find the effect of a bug, but difficult to find the cause.
- Some bugs are hard to reproduce.
- Interdependencies can cause a fix to introduce a new bug.
- Debugging can often take longer than the time it took to create the bug.
- While print statements are useful, they have limitations.
Debugging a Script
pdb – The Python Debugger
- Comes with the standard Python distribution.
- No additional installation is required.
Demo Example
Feel free to experiment by commenting and uncommenting sections as you see fit.
print("-"*80)
## --------------- Loading PDB --------------- ##
breakpoint()
test = "test"
print(test)
# NOTE -- by default, python will import pdb and call pdb.set_trace()
# -- set_trace hardcodes a breakpoint where the method was called
# # adding a breakpoint
# # NOTE -- runnig it -- once you run it -- breakpoint will be hit
# breakpoint()
# NOTE -- loading the debugger in a different way
# -- use `python -m pdb pdb_demo.py`
# -- will load the debugger before even starting the program
## --------------- PDB CONTROL commands --------------- ##
# PDB CONTROL commands
# -- you will have access to the program within the debugger
# PDB CONTROL commands -- exiting
# -- use `quit` or `exit` to exit it
# PDB CONTROL commands -- restarting the program
# -- use `run` to restart the program at any time
# PDB CONTROL commands -- continue execution
# -- use `c` / `cont` / `continue` to continue execution
# -- (till the next breakpoint is found)
# PDB CONTROL commands -- stepping through the code -- next
# -- stepping through the code -- `n` or `next`
# -- until the next line and stays within the current function
# PDB CONTROL commands -- stepping through the code -- step
# -- stepping through the code -- `s` or `step` -- (step into)
# -- execute the current line and will stop an a foreign function if one is called
# PDB CONTROL commands -- help
# -- use `help` to display all the documented commands in pdb
# -- getting more info about a command -- `help c`
# PDB CONTROL commands -- breakpoint
# -- use `b` or `break` to list all the break points
# -- with a line number argument --> set a break at the line
# -- `b 3`
# PDB CONTROL commands -- list the source code of the current file
# -- use `l` or `list`
# -- viewing only the firs 4 lines -- `l 1,4`
# -- use `ll` or `longlist` to list the source code for the whole file
# PDB CONTROL commands -- delete/disable a breakpoint
# -- use `disable` with the bpnumber -- `disable 1`
## --------------- DEBUGGING with PDB --------------- ##
# breakpoint()
# once run -- use `p test` to view the value of the variable test at this point of execution
# -- or `print(test)` -- you are passing an expression to be evaluated to python
# -- or modifying the variable -- `test="123"` and `print(test)`
# -- or introduce a new variable -- `new="456"` and `print(new)`
# NOTE -- breakpoint numbers
# -- whenever you add a breakpoint, the debugger assings a number to it
# -- starting with 1 and incrementing for each additional breakpoint which is added
# NOTE -- jumping during execution
# -- change the flow of the program during run time
# -- `j` or `jump` to skip or jump to certain areas within your code
# -- like to jump to line 6 -- `j 6`
# CAREFUL -- you can not jump into a function before the arguments are defined
# -- or into the middle of the try-except block
## --------------- DEBUGGING with PDB -- loop example --------------- ##
for i in range(10):
print(i)
print("-"*80)
## --------------- END --------------- ##
Closing
General Recommendations and Next Steps
- Practice consistently.
- Read and understand advanced scripts and tooling.
- Modify, improve, and port existing scripts and tooling.
- Create your own scripts and tooling.
- Approach testing from a programmer’s perspective.