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.
- Part 1
- Extension Part 2
- Part 3
- Saving Table Data
- Custom Table with Dynamic Data
- Burp Suite JMenu
- Export Button
- Import Button
- Clear Button
- Request Response Handling
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.
- HTTP Method
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.
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.
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
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.
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.
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.
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 response = rows url = rows method = rows hostname = rows self.saveoutput.append(OutputList(request,response, url,method,hostname)) f.close() self.fireTableDataChanged()
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.
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.