Skip to content

Burp Suite Extension In Python – Part 4

Burp Suite Extension

So far we have created UI for our logger burp suite extension in Python. In the final part, we will work on burp APIs to get the request and response and show it on our table. Also, we need to replace the table with a custom model and give action to our buttons.

We will follow the same code which we created from the previous article which has all the UI for our extension. Firstly we need to replace the table with a custom table model. Also this time we want dynamic data instead of static data for our table. So we will look at how we can store the dynamic data for our table.

Saving Table Data

We have seen how to create a custom table in part 2. The issue was it had static data and in our case, we want dynamic data. In order to make make our table data dynamic, we need something where we can save the table data. Creating a list or array is the best possible way to save table data. We can use python lists or java arrays as we are working in Jython. for easy understanding we will use a python list to store the data.

First, we need to find out what we need to save. So let’s look at what our table will contain.

  • Request
  • Response
  • URL
  • HTTP Method
  • HostName

There are a lot of things we can add but for now, it’s good enough. So in order to save all the above data in a single list, it will be very difficult to manage the data. So we will create a python class-based list

self.saveoutput =  []

class OutputList: 
    def __init__(self, request, response,url,method,hostname): 
        self.request = request
        self.response = response
        self.url = url
        self.method = method
        self.hostname = hostname

Custom Table with Dynamic Data

Now we have a class-based list where we can store the data. Now let’s remove all the table code and will add a custom table.

self.mytable = CustomTable(self)
jScrollPane1 =  JScrollPane()
jScrollPane1.setViewportView(self.mytable)


    def getRowCount(self):
        try:
            return len(self.saveoutput)
        except:
            return 0

    def getColumnCount(self):
        return 3

    def getColumnName(self, columnIndex):
        
        if columnIndex == 0:
            return "URL"
        if columnIndex == 1:
            return "Method"
        if columnIndex == 2:
            return "Hostname"
        return ""

    def getValueAt(self, rowIndex, columnIndex):
        
        Tabledata = self.saveoutput[rowIndex]
        if columnIndex == 0:
            return Tabledata.url
        if columnIndex == 1:
            return Tabledata.method
        if columnIndex == 2:
            return Tabledata.hostname
        return ""


class CustomTable(JTable):
  
    def __init__(self, tableextend):
        self.tableextend = tableextend
        self.setModel(tableextend)
        self.setRowSelectionAllowed(True)
        self.setAutoCreateRowSorter(True)

Remember the above code should be part of the BurpExtender class except for the CustomTable class. The first 3 lines are as usual. We have a table and are giving it to the CustomTable class and the self here is for AbstractTableModel. Remember to import and add the AbstractTableModel inside the BurpExtender extender class.

JTable
JTable

The getRowCount is not giving the number of rows from the list we have created rather than a static value. The getColumnCount is static as we won’t be changing the number of columns. The getValueAt now takes the values from the class-based list and adds them to the table automatically. You can view the source code for the changes we have done till now from GitHub.

Now the CustomTable class is the same as we saw in part 2. So wherever this class is called it will take one argument and we are passing it within the table tableextend. So in the above code, we are calling this class on the 1st line with a self keyword which refers to the BurpExtender class. So we will be able to call the method and variable from the BurpExtender class inside the CustomTable class.

Burp Suite JMenu

Now we have our table ready we want to add data dynamically to the table. If you remember from part 1, we want our logger to log only requests that users want as there are multiple loggers available that log all the requests from the burp suite. So what we want is to add a menu, so basically every time you right-click in the burp suite repeater or proxy section you can see lots of options like send to repeater send to extension etc. We want the same function for our extension.

Every time users right-click h will see our extension name and once they select our option we will fetch the selected request and will log it into our table. The first task is to create a menu and add it to the burp suite with burp suite APIs.

from burp import IContextMenuFactory
from burp import IContextMenuInvocation
from javax.swing import JMenuItem

class BurpExtender(IBurpExtender, ITab, AbstractTableModel, IContextMenuFactory):
<<Rest of the code>>
self.callbacks.registerContextMenuFactory(self)
self.helpers = callbacks.getHelpers()
def createMenuItems(self,invocation):
        menu = []
        menu.append(JMenuItem("Send To test Extension", None,actionPerformed=lambda x, inv=invocation: self.sendtotable(inv)))
        return menu

def sendtotable(self, invocation):
    

The above code is using the 2 burp suite APIs which allow us to register our extension for the menu inside the burp suite. Also, we have the menu list with the name of our menu and a lambda function with an action listener that will be triggered once the user clicks on the menu from the proxy or repeater tab. Apart from this, we have a helper who will allow us to get information about the request.

JMenu
JMenu

If you look at the above code our sendtotable is not doing anything. Remember the invocation is part of the menu API from the burp suite which will give us the information above the request when the menu was clicked. We want the request, response, URL, HTTP method and hostname once we click on the menu.

def sendtotable(self, invocation):
    	requestinformation = invocation.getSelectedMessages()
    	for items in requestinformation:
    		req = self.helpers.analyzeRequest(items)
    		#req = self.helpers.analyzeRequest(items)
    		self.httpmethodmethod = req.getMethod()
    		self.url = items.getUrl()
    		self.hostname = items.getHost()
    		requestinbytes = items.getRequest()
    		self.requestinstring = self.helpers.bytesToString(requestinbytes)
    		responseinbytes = items.getResponse()
    		self.responseinstring = self.helpers.bytesToString(responseinbytes)
    		self.saveoutput.append(OutputList(self.requestinstring,self.responseinstring,self.url,self.httpmethodmethod,self.hostname))
    		row = len(self.saveoutput)
    		self.fireTableRowsInserted(row, row)

We have now the actual code to add the data to our table. As I said the invocation will get the request information for us so we are looping through the request information and getting the information which we required. Also by default, the burp suite will provide the request and response in bytes instead of sting so we are converting it into sting using the burp suite API itself.

Once we got all the details we are saying the data in our class-based list and then informing the table that the data is updated. You can see the source code here.

JTable Data
JTable Data

Export Button

Now our extension is almost completed we have some buttons and we want to perform the action based on the button name. One of the reasons to add import and export is burp suite doesn’t allow extensions to save data within the burp suite project file so it’s better to have our own solution to save the data.

from javax.swing import JFileChooser
import csv

Exportbutton =  JButton("Export", actionPerformed=self.exportclicked)

def exportclicked(self,e):
    	chooseFile = JFileChooser()
        chooseFile.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
        returnedFile = chooseFile.showDialog(self.Customtabui, "Output Path")
        if returnedFile == JFileChooser.APPROVE_OPTION:
            fileLoad = chooseFile.getSelectedFile()
            #self.filepath = fileLoad.getAbsolutePath()
            self.filepath = fileLoad.getPath()
            fname = "Output"+"."+"csv"
            fnameWithPath = os.path.join(self.filepath,fname)
           
            with open(fnameWithPath, 'wb') as loggerdata:
                writer = csv.writer(loggerdata)
                for tableentry in self.saveoutput:
                    writer.writerow([str(tableentry.request), str(tableentry.response) ,str(tableentry.url) ,str(tableentry.method) ,str(tableentry.hostname),])
            loggerdata.close()

Firstly we have imported the CSV and JFilechooser which will allow us to select the system files. Then we have a method that will be called once the button is clicked. The method will allow users to select the directory and then will create an output.csv file. As our table data is stored in the list we can fetch the data and write it into our CSV file.

Import Button

The below code is again similar to the export button. This time instead of directly we are only allowed to select the CSV file and then instead of reading the data from the list we will add the data in the list from the CSV file.

from javax.swing.filechooser import FileNameExtensionFilter

Importbutton =  JButton("Import", actionPerformed=self.importclicked)
def importclicked(self,e):
    	chooseFile = JFileChooser()
        filter = FileNameExtensionFilter("csv files", ["csv"])
        chooseFile.addChoosableFileFilter(filter)
        ret = chooseFile.showDialog(self.Customtabui, "Choose file")
        if ret == JFileChooser.APPROVE_OPTION:
            fileLoad = chooseFile.getSelectedFile()
            self.filepath = fileLoad.getAbsolutePath()
            with open(self.filepath, 'rb') as f:
                reader = csv.reader(f,  delimiter=',')
                for rows in reader:
                    request = rows[0]
                    response = rows[1]
                    url = rows[2]
                    method = rows[3]
                    hostname = rows[4]
                    self.saveoutput.append(OutputList(request,response, url,method,hostname))
                f.close()
                self.fireTableDataChanged()

Clear Button

The clear button is simple. As our table is getting data from the list all we need to do is clear the list with just 3-4 lines of code. You can find the source code of our buttons here.

Clearbutton =  JButton("Clear", actionPerformed=self.clearclicked)
def clearclicked(self,e):
    	for item in self.saveoutput:
    		self.saveoutput.remove(item)
    		self.fireTableDataChanged()

Request Response Handling

Now, this is our final task for the extension. We have everything running fine only issue is we are not able to see the request and response when we select the row. To resolve this we need to tell our table to set the text of the request and response text box from the list based on the row selected. Before that, we need to replace our text box as we are working within the class

##Old
RequestTextbox = self.callbacks.createMessageEditor(None, True)
jSplitPane3.setLeftComponent(RequestTextbox.getComponent())
ResponseTextbox = self.callbacks.createMessageEditor(None, True)
jSplitPane3.setRightComponent(ResponseTextbox.getComponent())

##New

self.RequestTextbox = self.callbacks.createMessageEditor(None, True)
jSplitPane3.setLeftComponent(self.RequestTextbox.getComponent())
self.ResponseTextbox = self.callbacks.createMessageEditor(None, True)
jSplitPane3.setRightComponent(self.ResponseTextbox.getComponent())

Now we need a method inside the CustomTable class. We have a changeSelection method that will set the text based on the row selected.

def changeSelection(self, row, col, toggle, extend):
    	listentry = self.tableextend.saveoutput[row]
    	self.tableextend.RequestTextbox.setMessage(
            listentry.request, True)
    	self.tableextend.ResponseTextbox.setMessage(
            listentry.response, True)

We can see in the below image that we have created a fully working extension for our burp suite.

Burp extension
Burp extension

There are a lot of different things we can do with our extension in terms of configuration and burp suite APIs. There are soo many APIs available from the burp suite which you can use. For now, it’s good enough for our goal. You can find the source code here.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.