def addCenterPane(self, atfAreaView, consoleView): splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT) splitPane.setTopComponent(atfAreaView) splitPane.setBottomComponent(consoleView) splitPane.setDividerSize(5) self.getContentPane().add(splitPane, BorderLayout.CENTER) # Make console's high remain smaller compared to edit area splitPane.setResizeWeight(0.9)
def addCenterPane(self, atfAreaView, consoleView): splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT) splitPane.setTopComponent(atfAreaView) splitPane.setBottomComponent(consoleView) splitPane.setDividerSize(5) self.getContentPane().add(splitPane, BorderLayout.CENTER) # Make console's high remain smaller compared to edit area splitPane.setResizeWeight(0.9)
class TypeDefinitionTab(burp.ITab): """Implements an interface for editing known message type definitions.""" def __init__(self, burp_callbacks): self._burp_callbacks = burp_callbacks self._type_list_component = JList( blackboxprotobuf.known_messages.keys()) self._type_list_component.setSelectionMode( ListSelectionModel.SINGLE_SELECTION) self._component = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._component.setLeftComponent(JScrollPane( self._type_list_component)) self._component.setRightComponent(self.createButtonPane()) self._component.setResizeWeight(0.9) def getTabCaption(self): """Returns name on tab""" return "Protobuf Type Editor" def getUiComponent(self): """Returns Java AWT component for tab""" return self._component def createButtonPane(self): """Create AWT window panel for buttons""" self._button_listener = TypeDefinitionButtonListener(self) panel = JPanel() panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS)) panel.add(self.createButton("New Type", "new-type")) panel.add(self.createButton("Edit Type", "edit-type")) panel.add(self.createButton("Delete Type", "delete-type")) panel.add(self.createButton("Save All Types To File", "save-types")) panel.add(self.createButton("Load All Types To File", "load-types")) return panel def createButton(self, text, command): """Generate new button with the given text and command string""" button = JButton(text) button.setAlignmentX(Component.CENTER_ALIGNMENT) button.setActionCommand(command) button.addActionListener(self._button_listener) return button def updateList(self): """Let the UI know that the list of message types has been updated""" self._type_list_component.setListData( blackboxprotobuf.known_messages.keys())
def __init__(self, burp_callbacks, typedef, callback): self._burp_callbacks = burp_callbacks self._type_callback = callback self.setSize(1000,700) self._original_typedef = typedef self._type_editor = burp_callbacks.createTextEditor() self._type_editor.setEditable(True) self._type_editor.setText(json.dumps(self._original_typedef, indent=4)) splitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) splitPane.setLeftComponent(self._type_editor.getComponent()) splitPane.setRightComponent(self.createButtonPane()) splitPane.setResizeWeight(0.8) self.add(splitPane)
def start(self): self.frame = JDialog() #self.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) self.frame.setLocation(0, 1500) self.frame.setSize(1000, 200) self.tableDataModel = DefaultTableModel([], [ "URL", "Code", "Content-Length", "Location", "Mutated Id", "Orig Id" ]) self.jtable = JTable(self.tableDataModel) scrollPane = JScrollPane(self.jtable) self.jtable.setFillsViewportHeight(True) messageEditorOrig = self.callbacks.createMessageEditor(None, False) messageEditorModified = self.callbacks.createMessageEditor( None, False) self.editorSplitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, messageEditorOrig.getComponent(), messageEditorModified.getComponent()) self.editorSplitPane.setResizeWeight(0.5) splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, self.editorSplitPane) splitPane.setResizeWeight(0.5) class TableSelector(ListSelectionListener): def __init__(self, plugin): self.plugin = plugin def valueChanged(self, event): if not event.getValueIsAdjusting(): selectedRowIndex = self.plugin.jtable.getSelectedRows()[0] self.plugin._rowSelected(selectedRowIndex) self.jtable.getSelectionModel().addListSelectionListener( TableSelector(self)) self.frame.add(splitPane) self.frame.setVisible(True) self.callbacks.registerHttpListener(self) self.callbacks.setExtensionName("Custom Plugin") return
def __init__(self, extension, burp_callbacks): self._burp_callbacks = burp_callbacks self._extension = extension self._type_list_component = JList(extension.known_message_model) self._type_list_component.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) self._component = JPanel() self._component.setLayout(BoxLayout(self._component, BoxLayout.Y_AXIS)) splitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) splitPane.setRightComponent(JScrollPane(self._type_list_component)) splitPane.setLeftComponent(self.createButtonPane()) splitPane.setResizeWeight(0.03) splitPane.setMaximumSize(Dimension(1000, 1000)) self._component.add(splitPane) self._component.add(Box.createVerticalGlue()) self._component.setBorder(EmptyBorder(10, 10, 10, 10))
def __init__(self, burp_callbacks, typedef, callback): burp_window = None for frame in Frame.getFrames(): if "Burp Suite" in frame.getName(): burp_window = frame break JDialog.__init__(self, burp_window) self._burp_callbacks = burp_callbacks self._type_callback = callback self.setSize(1000, 700) self._original_typedef = typedef self._type_editor = burp_callbacks.createTextEditor() self._type_editor.setEditable(True) self._type_editor.setText(json.dumps(self._original_typedef, indent=4)) splitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) splitPane.setLeftComponent(self._type_editor.getComponent()) splitPane.setRightComponent(self.createButtonPane()) splitPane.setResizeWeight(0.8) self.add(splitPane) self.is_open = True
class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory, IHttpRequestResponseWithMarkers, ITextEditor): def registerExtenderCallbacks(self, callbacks): self._callbacks = callbacks #Initialize callbacks to be used later self._helpers = callbacks.getHelpers() callbacks.setExtensionName("Trishul") self._log = ArrayList() #_log used to store our outputs for a URL, which is retrieved later by the tool self._lock = Lock() #Lock is used for locking threads while updating logs in order such that no multiple updates happen at once self.intercept = 0 self.FOUND = "Found" self.CHECK = "Possible! Check Manually" self.NOT_FOUND = "Not Found" #Static Values for output #Initialize GUI self.issuesTab() self.advisoryReqResp() self.configTab() self.tabsInit() self.definecallbacks() print("Thank You for Installing Trishul") return # #Initialize Issues Tab displaying the JTree # def issuesTab(self): self.root = DefaultMutableTreeNode('Issues') frame = JFrame("Issues Tree") self.tree = JTree(self.root) self.rowSelected = '' self.tree.addMouseListener(mouseclick(self)) self.issuepanel = JScrollPane() self.issuepanel.setPreferredSize(Dimension(300,450)) self.issuepanel.getViewport().setView((self.tree)) frame.add(self.issuepanel,BorderLayout.CENTER) # #Adding Issues to Issues TreePath # def addIssues(self, branch, branchData=None): if branchData == None: branch.add(DefaultMutableTreeNode('No valid data')) else: for item in branchData: branch.add(DefaultMutableTreeNode(item)) # #Initialize the Config Tab to modify tool settings # def configTab(self): Config = JLabel("Config") self.startButton = JToggleButton("Intercept Off", actionPerformed=self.startOrStop) self.startButton.setBounds(40, 30, 200, 30) self.autoScroll = JCheckBox("Auto Scroll") self.autoScroll.setBounds(40, 80, 200, 30) self.xsscheck = JCheckBox("Detect XSS") self.xsscheck.setSelected(True) self.xsscheck.setBounds(40, 110, 200, 30) self.sqlicheck = JCheckBox("Detect SQLi") self.sqlicheck.setSelected(True) self.sqlicheck.setBounds(40, 140, 200, 30) self.ssticheck = JCheckBox("Detect SSTI") self.ssticheck.setSelected(True) self.ssticheck.setBounds(40, 170, 200, 30) self.blindxss = JCheckBox("Blind XSS") self.blindxss.setBounds(40, 200, 200, 30) self.BlindXSSText = JTextArea("", 5, 30) scrollbxssText = JScrollPane(self.BlindXSSText) scrollbxssText.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED) scrollbxssText.setBounds(40, 250, 400, 110) self.configtab = JPanel() self.configtab.setLayout(None) self.configtab.setBounds(0, 0, 300, 300) self.configtab.add(Config) self.configtab.add(self.startButton) self.configtab.add(self.autoScroll) self.configtab.add(self.xsscheck) self.configtab.add(self.sqlicheck) self.configtab.add(self.ssticheck) self.configtab.add(self.blindxss) self.configtab.add(scrollbxssText) # #Turn Intercept from Proxy on or off # def startOrStop(self, event): if self.startButton.getText() == "Intercept Off": self.startButton.setText("Intercept On") self.startButton.setSelected(True) self.intercept = 1 else: self.startButton.setText("Intercept Off") self.startButton.setSelected(False) self.intercept = 0 # #Intialize the Advisory, Request and Response Tabs # def advisoryReqResp(self): self.textfield = JEditorPane("text/html", "") self.kit = HTMLEditorKit() self.textfield.setEditorKit(self.kit) self.doc = self.textfield.getDocument() self.textfield.setEditable(0) self.advisorypanel = JScrollPane() self.advisorypanel.getVerticalScrollBar() self.advisorypanel.setPreferredSize(Dimension(300,450)) self.advisorypanel.getViewport().setView((self.textfield)) self.selectedreq = [] self._requestViewer = self._callbacks.createMessageEditor(self, False) self._responseViewer = self._callbacks.createMessageEditor(self, False) self._texteditor = self._callbacks.createTextEditor() self._texteditor.setEditable(False) # #Initialize Trishul Tabs # def tabsInit(self): self.logTable = Table(self) tableWidth = self.logTable.getPreferredSize().width self.logTable.getColumn("#").setPreferredWidth(Math.round(tableWidth / 50 * 0.1)) self.logTable.getColumn("Method").setPreferredWidth(Math.round(tableWidth / 50 * 3)) self.logTable.getColumn("URL").setPreferredWidth(Math.round(tableWidth / 50 * 40)) self.logTable.getColumn("Parameters").setPreferredWidth(Math.round(tableWidth / 50 * 1)) self.logTable.getColumn("XSS").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.logTable.getColumn("SQLi").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.logTable.getColumn("SSTI").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.logTable.getColumn("Request Time").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.tableSorter = TableRowSorter(self) self.logTable.setRowSorter(self.tableSorter) self._bottomsplit = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._bottomsplit.setDividerLocation(500) self.issuetab = JTabbedPane() self.issuetab.addTab("Config",self.configtab) self.issuetab.addTab("Issues",self.issuepanel) self._bottomsplit.setLeftComponent(self.issuetab) self.tabs = JTabbedPane() self.tabs.addTab("Advisory",self.advisorypanel) self.tabs.addTab("Request", self._requestViewer.getComponent()) self.tabs.addTab("Response", self._responseViewer.getComponent()) self.tabs.addTab("Highlighted Response", self._texteditor.getComponent()) self._bottomsplit.setRightComponent(self.tabs) self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) self._splitpane.setDividerLocation(450) self._splitpane.setResizeWeight(1) self.scrollPane = JScrollPane(self.logTable) self._splitpane.setLeftComponent(self.scrollPane) self.scrollPane.getVerticalScrollBar().addAdjustmentListener(autoScrollListener(self)) self._splitpane.setRightComponent(self._bottomsplit) # #Initialize burp callbacks # def definecallbacks(self): self._callbacks.registerHttpListener(self) self._callbacks.customizeUiComponent(self._splitpane) self._callbacks.customizeUiComponent(self.logTable) self._callbacks.customizeUiComponent(self.scrollPane) self._callbacks.customizeUiComponent(self._bottomsplit) self._callbacks.registerContextMenuFactory(self) self._callbacks.addSuiteTab(self) # #Menu Item to send Request to Trishul # def createMenuItems(self, invocation): responses = invocation.getSelectedMessages() if responses > 0: ret = LinkedList() requestMenuItem = JMenuItem("Send request to Trishul") for response in responses: requestMenuItem.addActionListener(handleMenuItems(self,response, "request")) ret.add(requestMenuItem) return ret return None # #Highlighting Response # def markHttpMessage( self, requestResponse, responseMarkString ): responseMarkers = None if responseMarkString: response = requestResponse.getResponse() responseMarkBytes = self._helpers.stringToBytes( responseMarkString ) start = self._helpers.indexOf( response, responseMarkBytes, False, 0, len( response ) ) if -1 < start: responseMarkers = [ array( 'i',[ start, start + len( responseMarkBytes ) ] ) ] requestHighlights = [array( 'i',[ 0, 5 ] )] return self._callbacks.applyMarkers( requestResponse, requestHighlights, responseMarkers ) def getTabCaption(self): return "Trishul" def getUiComponent(self): return self._splitpane # #Table Model to display URL's and results based on the log size # def getRowCount(self): try: return self._log.size() except: return 0 def getColumnCount(self): return 8 def getColumnName(self, columnIndex): data = ['#','Method', 'URL', 'Parameters', 'XSS', 'SQLi', "SSTI", "Request Time"] try: return data[columnIndex] except IndexError: return "" def getColumnClass(self, columnIndex): data = [Integer, String, String, Integer, String, String, String, String] try: return data[columnIndex] except IndexError: return "" #Get Data stored in log and display in the respective columns def getValueAt(self, rowIndex, columnIndex): logEntry = self._log.get(rowIndex) if columnIndex == 0: return rowIndex+1 if columnIndex == 1: return logEntry._method if columnIndex == 2: return logEntry._url.toString() if columnIndex == 3: return len(logEntry._parameter) if columnIndex == 4: return logEntry._XSSStatus if columnIndex == 5: return logEntry._SQLiStatus if columnIndex == 6: return logEntry._SSTIStatus if columnIndex == 7: return logEntry._req_time return "" def getHttpService(self): return self._currentlyDisplayedItem.getHttpService() def getRequest(self): return self._currentlyDisplayedItem.getRequest() def getResponse(self): return self._currentlyDisplayedItem.getResponse() #For Intercepted requests perform tests in scope def processHttpMessage(self, toolFlag, messageIsRequest, messageInf): if self.intercept == 1: if toolFlag == self._callbacks.TOOL_PROXY: if not messageIsRequest: requestInfo = self._helpers.analyzeRequest(messageInf) requeststr = requestInfo.getUrl() parameters = requestInfo.getParameters() param_new = [p for p in parameters if p.getType() != 2] if len(param_new) != 0: if self._callbacks.isInScope(URL(str(requeststr))): start_new_thread(self.sendRequestToTrishul,(messageInf,)) return # #Main processing of Trishul # def sendRequestToTrishul(self,messageInfo): request = messageInfo.getRequest() req_time = datetime.datetime.today() requestURL = self._helpers.analyzeRequest(messageInfo).getUrl() messageInfo = self._callbacks.makeHttpRequest(self._helpers.buildHttpService(str(requestURL.getHost()), int(requestURL.getPort()), requestURL.getProtocol() == "https"), request) resp_time = datetime.datetime.today() time_taken = (resp_time - req_time).total_seconds() response = messageInfo.getResponse() #initialozations of default value SQLiimp = self.NOT_FOUND SSTIimp = self.NOT_FOUND XSSimp = self.NOT_FOUND Comp_req = messageInfo requestInfo = self._helpers.analyzeRequest(messageInfo) self.content_resp = self._helpers.analyzeResponse(response) requestURL = requestInfo.getUrl() parameters = requestInfo.getParameters() requeststring = self._helpers.bytesToString(request) headers = requestInfo.getHeaders() #Used to obtain GET, POST and JSON parameters from burp api param_new = [p for p in parameters if p.getType() == 0 or p.getType() == 1 or p.getType() == 6] i = 0 xssflag=0 sqliflag=0 sstiflag=0 resultxss = [] resultsqli = [] resultssti = [] xssreqresp = [] sqlireqresp = [] sstireqresp = [] ssti_description = [] sqli_description = [] xss_description = [] for i in range(len(param_new)): name = param_new[i].getName() ptype = param_new[i].getType() param_value = param_new[i].getValue() #check XSS if ticked if self.xsscheck.isSelected(): score = 0 flag1 = 0 XSSimp = self.NOT_FOUND payload_array = ["<", ">", "\\\\'asd", "\\\\\"asd", "\\", "'\""] json_payload_array = ["<", ">", "\\\\'asd", "\\\"asd", "\\", "\'\\\""] payload_all = "" json_payload = "" rand_str = "testtest" for payload in payload_array: payload_all = payload_all+rand_str+payload payload_all = URLEncoder.encode(payload_all, "UTF-8") for payload in json_payload_array: json_payload = json_payload+rand_str+payload json_payload = URLEncoder.encode(json_payload, "UTF-8") if ptype == 0 or ptype == 1: new_paramters_value = self._helpers.buildParameter(name, payload_all, ptype) updated_request = self._helpers.updateParameter(request, new_paramters_value) else: jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) new = jsonreq.split(name+"\":",1)[1] if new.startswith('\"'): newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+json_payload) else: newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+json_payload+"\"") updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) attack = self.makeRequest(Comp_req, updated_request) response = attack.getResponse() response_str = self._helpers.bytesToString(response) xssreqresp.append(attack) if_found_payload = "" non_encoded_symbols = "" for check_payload in payload_array: if_found_payload = rand_str+check_payload if if_found_payload in response_str: non_encoded_symbols = non_encoded_symbols+"<br>"+check_payload.replace('<', '<') score = score+1 flag1 = 1 if score > 2: XSSimp = self.CHECK if score > 3: XSSimp = self.FOUND xssflag = self.checkBetterScore(score,xssflag) if non_encoded_symbols == " \\\\'asd": XSSimp = self.NOT_FOUND if non_encoded_symbols != '': xss_description.append("The Payload <b>" + payload_all.replace('<', '<') + "</b> was passed in the request for the paramater <b>" + self._helpers.urlDecode(name) + "</b>. Some Tags were observed in the output unfiltered. A payload can be generated with the observed tags.<br>Symbols not encoded for parameter <b>" + name + "</b>: " + non_encoded_symbols) else: xss_description.append("") else: XSSimp = "Disabled" resultxss.append(XSSimp) if self.sqlicheck.isSelected(): SQLiimp = self.NOT_FOUND score = 0 value = "%27and%28select%2afrom%28select%28sleep%285%29%29%29a%29--" orig_time = datetime.datetime.today() if ptype == 0 or ptype == 1: new_paramters_value = self._helpers.buildParameter(name, value, ptype) updated_request = self._helpers.updateParameter(request, new_paramters_value) else: jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) new = jsonreq.split(name+"\":",1)[1] if new.startswith('\"'): newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+value) else: newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+value+"\"") updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) attack1 = self.makeRequest(Comp_req, updated_request) response1 = attack1.getResponse() new_time = datetime.datetime.today() response_str1 = self._helpers.bytesToString(response1) sqlireqresp.append(attack1) diff = (new_time - orig_time).total_seconds() if (diff - time_taken) > 3: score = 4 self.error_array = ["check the manual that corresponds to your", "You have an error", "syntax error", "SQL syntax", "SQL statement", "ERROR:", "Error:", "MySQL","Warning:","mysql_fetch_array()"] found_text = "" for error in self.error_array: if error in response_str1: found_text = found_text + error score = score + 1 if score > 1: SQLiimp = self.CHECK if score > 2: SQLiimp = self.FOUND sqliflag = self.checkBetterScore(score,sqliflag) if found_text != '': sqli_description.append("The payload <b>"+self._helpers.urlDecode(value)+"</b> was passed in the request for parameter <b>"+self._helpers.urlDecode(name)+"</b>. Some errors were generated in the response which confirms that there is an Error based SQLi. Please check the request and response for this parameter") elif (diff - time_taken) > 3: sqli_description.append("The payload <b>"+self._helpers.urlDecode(value)+"</b> was passed in the request for parameter <b>"+self._helpers.urlDecode(name)+"</b>. The response was in a delay of <b>"+str(diff)+"</b> seconds as compared to original <b>"+str(time_taken)+"</b> seconds. This indicates that there is a time based SQLi. Please check the request and response for this parameter") else: sqli_description.append("") else: SQLiimp = "Disabled" resultsqli.append(SQLiimp) if self.ssticheck.isSelected(): score = 0 SSTIimp = self.NOT_FOUND payload_array = ["${123*456}", "<%=123*567%>", "{{123*678}}"] json_payload_array = ["$\{123*456\}", "<%=123*567%>", "\{\{123*678\}\}"] payload_all = "" rand_str = "jjjjjjj" json_payload = "" for payload in payload_array: payload_all = payload_all+rand_str+payload for payload in json_payload_array: json_payload = json_payload+rand_str+payload payload_all = URLEncoder.encode(payload_all, "UTF-8") json_payload = URLEncoder.encode(json_payload, "UTF-8") if ptype == 0 or ptype == 1: new_paramters_value = self._helpers.buildParameter(name, payload_all, ptype) updated_request = self._helpers.updateParameter(request, new_paramters_value) else: jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) new = jsonreq.split(name+"\":",1)[1] if new.startswith('\"'): newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+json_payload) else: newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+json_payload+"\"") updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) attack = self.makeRequest(Comp_req, updated_request) response = attack.getResponse() response_str = self._helpers.bytesToString(response) self.expected_output = ["56088","69741","83394","3885","777777777777777"] for output in self.expected_output: if_found_payload = rand_str+output if if_found_payload in response_str: if output == self.expected_output[0]: sstireqresp.append(attack) ssti_description.append("Parameter <b>" + self._helpers.urlDecode(name) + "</b> is using <b>Java</b> Template<br>The value <b>" + payload_new + "</b> was passed which gave result as <b>56088</b>") score = 2 if output == self.expected_output[1]: sstireqresp.append(attack) ssti_description.append("Parameter <b>" + self._helpers.urlDecode(name) + "</b> is using <b>Ruby</b> Template<br>The value <b>" + payload_new + "</b> was passed which gave result as <b>69741</b>") score = 2 if output == self.expected_output[2]: payload_new = "{{5*'777'}}" json_payload_ssti = "\{\{5*'777'\}\}" payload = URLEncoder.encode("{{5*'777'}}", "UTF-8") json_ssti = URLEncoder.encode("\{\{5*'777'\}\}", "UTF-8") if ptype == 0 or ptype == 1: new_paramters = self._helpers.buildParameter(name, payload, ptype) ssti_updated_request = self._helpers.updateParameter(request, new_paramters) else: jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) new = jsonreq.split(name+"\":",1)[1] if new.startswith('\"'): newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+json_ssti) else: newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+json_ssti+"\"") ssti_updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) self.ssti_attack = self.makeRequest(Comp_req, ssti_updated_request) ssti_response = self.ssti_attack.getResponse() ssti_response_str = self._helpers.bytesToString(ssti_response) if self.expected_output[3] in ssti_response_str: sstireqresp.append(self.ssti_attack) ssti_description.append("Parameter <b>" + self._helpers.urlDecode(name) + "</b> is using <b>Twig</b> Template<br>The value <b>" + payload_new + "</b> was passed which gave result as <b>3885</b>") score = 2 elif self.expected_output[4] in ssti_response_str: sstireqresp.append(self.ssti_attack) self.responseMarkString = "777777777777777" ssti_description.append("Parameter <b>" + self._helpers.urlDecode(name) + "</b> is using <b>Jinja2</b> Template<br>The value <b>" + payload_new + "</b> was passed which gave result as <b>777777777777777</b>") score = 2 if score > 0: SSTIimp = self.CHECK if score > 1: SSTIimp = self.FOUND sstiflag = self.checkBetterScore(score,sstiflag) else: SSTIimp = "Disabled" resultssti.append(SSTIimp) if self.blindxss.isSelected(): blindxss_value = self.BlindXSSText.getText() if ptype == 0 or ptype == 1: new_paramters_value = self._helpers.buildParameter(name, blindxss_value, ptype) updated_request = self._helpers.updateParameter(request, new_paramters_value) else: jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) new = jsonreq.split(name+"\":",1)[1] if new.startswith('\"'): newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+blindxss_value) else: newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+blindxss_value+"\"") updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) attack = self.makeRequest(Comp_req, updated_request) if XSSimp != "Disabled": if xssflag > 3: XSSimp = self.FOUND elif xssflag > 2: XSSimp = self.CHECK else: XSSimp = self.NOT_FOUND if SSTIimp != "Disabled": if sstiflag > 1: SSTIimp = self.FOUND elif sstiflag > 0: SSTIimp = self.CHECK else: SSTIimp = self.NOT_FOUND if SQLiimp != "Disabled": if sqliflag > 3: SQLiimp = self.FOUND elif sqliflag > 2: SQLiimp = self.CHECK else: SQLiimp = self.NOT_FOUND self.addToLog(messageInfo, XSSimp, SQLiimp, SSTIimp, param_new, resultxss, resultsqli, resultssti, xssreqresp, sqlireqresp, sstireqresp , xss_description, sqli_description, ssti_description, req_time.strftime('%H:%M:%S %m/%d/%y')) # #Function used to check if the score originally and mentioned is better # def checkBetterScore(self, score, ogscore): if score > ogscore: ogscore = score return ogscore def makeRequest(self, messageInfo, message): request = messageInfo.getRequest() requestURL = self._helpers.analyzeRequest(messageInfo).getUrl() return self._callbacks.makeHttpRequest(self._helpers.buildHttpService(str(requestURL.getHost()), int(requestURL.getPort()), requestURL.getProtocol() == "https"), message) def addToLog(self, messageInfo, XSSimp, SQLiimp, SSTIimp, parameters, resultxss, resultsqli, resultssti, xssreqresp, sqlireqresp, sstireqresp, xss_description, sqli_description, ssti_description, req_time): requestInfo = self._helpers.analyzeRequest(messageInfo) method = requestInfo.getMethod() self._lock.acquire() row = self._log.size() self._log.add(LogEntry(self._callbacks.saveBuffersToTempFiles(messageInfo), requestInfo.getUrl(),method,XSSimp,SQLiimp,SSTIimp,req_time, parameters,resultxss, resultsqli, resultssti, xssreqresp, sqlireqresp, sstireqresp, xss_description, sqli_description, ssti_description)) # same requests not include again. SwingUtilities.invokeLater(UpdateTableEDT(self,"insert",row,row)) self._lock.release()
class BurpExtender(IBurpExtender, IContextMenuFactory, ITab, IExtensionStateListener, IMessageEditorController, IHttpListener): ''' IBurpExtender: Hook into burp and inherit base classes ITab: Create new tabs inside burp IMessageEditorTabFactory: Access createNewInstance ''' def registerExtenderCallbacks(self, callbacks): # Set encoding to utf-8 to avoid some errors reload(sys) sys.setdefaultencoding('utf8') # Keep a reference to callback object and helper object self._callbacks = callbacks self._helpers = callbacks.getHelpers() # Set the extension name that shows in the burp extension menu callbacks.setExtensionName("InjectionScanner") # Create the log and a lock on which to synchronize when adding log entries self._log = ArrayList() self._logLock = Lock() self._httpLock = Lock() # The length of the basis used to fetch abnormal data, default to zero self._basisLen = 0 # 1: {POST. GET}; 2: {urlencoded, json, xml} self._postGet = 'NaN' self._dataType = 'NaN' # Scan list self._simpleList = [ '\'', '\"', '/', '/*', '#', ')', '(', ')\'', '(\'', 'and 1=1', 'and 1=2', 'and 1>2', 'and 12', '+', 'and+12', '/**/and/**/1' ] self._xmlList = ['a', 'b', 'c', 'd', 'e'] # Not setted # Response mutex: True = is blocking; False = free to go # self._mutexR = False # Other classes instance self._dataTable = Guis_DefaultTM() self._logTable = Guis_AbstractTM(self) self._xh = XMLHandler() listeners = Guis_Listeners(self, self._logTable) ''' Setting GUIs ''' # Divide the whole pane two: one upper and one lower pane self._mainSplitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) self._mainSplitpane.setResizeWeight(0.4) # Initizlize request table dataTable = JTable(self._dataTable) dataScrollPane = JScrollPane(dataTable) dataScrollPane.setPreferredSize(Dimension(0, 125)) self._dataTable.addTableModelListener(listeners) # Initialize log table logTable = Guis_LogTable(self._logTable) logScrollPane = JScrollPane(logTable) logScrollPane.setPreferredSize(Dimension(0, 125)) # Split the upper pane to two panes tableSplitpane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) tableSplitpane.setResizeWeight(0.5) # Set the data table to the left and log to the right tableSplitpane.setLeftComponent(dataScrollPane) tableSplitpane.setRightComponent(logScrollPane) # Tabs with request/response viewers tabs = JTabbedPane() self._requestViewer = callbacks.createMessageEditor(self, False) self._responseViewer = callbacks.createMessageEditor(self, False) tabs.addTab("Request", self._requestViewer.getComponent()) tabs.addTab("Response", self._responseViewer.getComponent()) # Create buttons that do operation with the test self._basisLabel = JLabel('Basis: ' + str(self._basisLen)) self._levelLabel = JLabel('Level:') self._setBasisButton = JButton('Set Basis') self._hitOnceButton = JButton('Hit Once') self._autoScanButton = JButton('Auto Scan') self._clearLogButton = JButton('Clear Log') self._cancelButton = JButton('Cancel') self._levelSelection = JComboBox() self._levelSelection.addItem('1') self._levelSelection.addItem('2') self._levelSelection.addItem('3') self._hitOnceButton.addActionListener(listeners) self._autoScanButton.addActionListener(listeners) self._clearLogButton.addActionListener(listeners) self._setBasisButton.addActionListener(listeners) self._cancelButton.addActionListener(listeners) self._basisLabel.setPreferredSize(Dimension(100, 20)) # Create bottom pane for holding the buttons buttonPane = JPanel() buttonPane.setLayout(BorderLayout()) centerPane = JPanel() leftPane = JPanel() rightPane = JPanel() leftPane.add(self._basisLabel) centerPane.add(self._setBasisButton) centerPane.add(self._hitOnceButton) centerPane.add(self._autoScanButton) centerPane.add(self._cancelButton) centerPane.add(self._clearLogButton) rightPane.add(self._levelLabel) rightPane.add(self._levelSelection) buttonPane.add(centerPane, BorderLayout.CENTER) buttonPane.add(leftPane, BorderLayout.WEST) buttonPane.add(rightPane, BorderLayout.EAST) # Create and set the bottom panel that holds viewers and buttons utilPane = JPanel() utilPane.setLayout(BorderLayout()) utilPane.add(tabs, BorderLayout.CENTER) utilPane.add(buttonPane, BorderLayout.SOUTH) self._mainSplitpane.setLeftComponent(tableSplitpane) self._mainSplitpane.setRightComponent(utilPane) # Customize UI components callbacks.customizeUiComponent(self._mainSplitpane) callbacks.customizeUiComponent(dataTable) callbacks.customizeUiComponent(dataScrollPane) callbacks.customizeUiComponent(logTable) callbacks.customizeUiComponent(logScrollPane) callbacks.customizeUiComponent(tabs) callbacks.customizeUiComponent(buttonPane) callbacks.customizeUiComponent(utilPane) callbacks.customizeUiComponent(self._basisLabel) callbacks.customizeUiComponent(self._setBasisButton) callbacks.customizeUiComponent(self._hitOnceButton) callbacks.customizeUiComponent(self._autoScanButton) callbacks.customizeUiComponent(self._clearLogButton) callbacks.customizeUiComponent(self._levelSelection) callbacks.customizeUiComponent(self._cancelButton) # Add the custom tab to Burp's UI callbacks.addSuiteTab(self) # Register the context menu and message editor for new tabs callbacks.registerContextMenuFactory(self) # Register as a HTTP listener callbacks.registerHttpListener(self) return ''' ITab implementation ''' def getTabCaption(self): return 'InjectionScanner' def getUiComponent(self): return self._mainSplitpane ''' IContextMenuFactory implementation ''' def createMenuItems(self, invocation): menu = [] # Which part of the interface the user selects ctx = invocation.getInvocationContext() # Message viewer request will show menu item if selected by the user if ctx == 0 or ctx == 2: menu.append( swing.JMenuItem("Send to InjectionScanner", None, actionPerformed=lambda x, inv=invocation: self. sendToExtender(inv))) return menu if menu else None ''' IMessageEditorController Implementation ''' def getHttpService(self): return self._currentlyDisplayedItem.getHttpService() def getRequest(self): return self._currentlyDisplayedItem.getRequest() def getResponse(self): return self._currentlyDisplayedItem.getResponse() ''' IHttpListener implementation ''' def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): # Skip this function if the message is request if messageIsRequest: return # Lock the log entry in case race condition self._logLock.acquire() row = self._log.size() # Fetch request message requestBody = messageInfo.getRequest() requestInfo = self._helpers.analyzeResponse(requestBody) requestHeaders = requestInfo.getHeaders() if self._postGet == 'POST': requestData = self._helpers.bytesToString( requestBody[requestInfo.getBodyOffset():]) elif self._postGet == 'GET': for header in requestHeaders: if 'GET' in header: # If the request is GET, update the GET data requestUrl = re.sub('^GET\s+', '', header, re.IGNORECASE) requestUrl = re.sub('\sHTTP/1.1\S*', '', requestUrl, re.IGNORECASE) if '?' in requestUrl: requestData = re.sub('\S*\?', '', requestUrl, re.IGNORECASE) else: print('processHttpMessage: no parameter in GET url') else: print('processHttpMessage: _postGet not defined') self._logLock.release() return # Fetch the http type (GET/POST) httpType = requestHeaders[0].split(' ') # Fetch response message responseBody = messageInfo.getResponse() responseInfo = self._helpers.analyzeResponse(responseBody) responseHeaders = responseInfo.getHeaders() self._responseLength = '' # Fetch the content length self._responseLength = self.fetchContentLength(responseHeaders) # If the response message is auto-generated, ignore it. If not, add it into the log list if self._callbacks.getToolName(toolFlag) != 'Proxy': self._log.add( LogEntry(httpType[0], requestData, self._callbacks.saveBuffersToTempFiles(messageInfo), self._responseLength)) self._logTable.fireTableRowsInserted(row, row) self._logLock.release() ''' Fetch content length from the headers given ''' def fetchContentLength(self, fromHeaders): for header in fromHeaders: if re.search('^Content-Length', header, re.IGNORECASE) is not None: return re.sub('^Content-Length\:\s+', '', header, re.IGNORECASE) ''' When the user select 'Send to InjectionScanner', call this function ''' def sendToExtender(self, invocation): # Init/reset request data before sending to extender self.initRequestInfo() try: # Initialize basic information invMessage = invocation.getSelectedMessages() requestMessage = invMessage[0] requestInfo = self._helpers.analyzeRequest(requestMessage) self._requestBody = requestMessage.getRequest() # Set the _currentlyDisplayedItem so each time the data is sent to the extender self._currentlyDisplayedItem = self._callbacks.saveBuffersToTempFiles( requestMessage) # Fetch the request data bodyLen = len(self._helpers.bytesToString(self._requestBody)) if requestInfo.getBodyOffset() < bodyLen: self._requestData = self._helpers.bytesToString( self._requestBody[requestInfo.getBodyOffset():]) elif requestInfo.getBodyOffset() == bodyLen: self._requestData = '' else: print('sendToExtender: body length < body offset') # Fetch the headers and Http service requestHeaders = list(requestInfo.getHeaders()) self._httpService = requestMessage.getHttpService() # Initialize POST/GET identifier and User-Agent for header in requestHeaders: if re.search('^POST', header, re.IGNORECASE) is not None: self._postGet = 'POST' elif re.search('^GET', header, re.IGNORECASE) is not None: self._postGet = 'GET' # If the request is GET, initialize the url and GET data self._requestUrl = re.sub('^GET\s+', '', header, re.IGNORECASE) self._requestUrl = re.sub('\sHTTP/1.1\S*', '', self._requestUrl, re.IGNORECASE) if '?' in self._requestUrl: self._requestDataGet = re.sub('\S*\?', '', self._requestUrl, re.IGNORECASE) else: print('sendToExtender: no parameter in GET url') # If the request if POST, fetch the request data type by content type if self._postGet == 'POST' and re.search( '^Content-Type', header, re.IGNORECASE) is not None: contentType = re.sub('^Content-Type', '', header, re.IGNORECASE) if 'urlencoded' in contentType: self._dataType = 'urlencoded' elif 'json' in contentType: self._dataType = 'json' elif 'xml' in contentType or 'http' in conentType: self._dataType = 'xml' else: print( 'sendToExtender: _dataType is not supported, do not scan' ) # Initialze the User-Agent if it exists if re.search('^User-Agent', header, re.IGNORECASE) is not None: self._userAgent = re.sub('^User-Agent\:\s+', '', header, re.IGNORECASE) # If there's no content type in the header,fetch from data if self._postGet == 'POST' and self._dataType == '': if self._requestData != '': if self._requestData[ 0] == '{' and '}' in self._requestData and ':' in self._requestData: self._dataType = 'json' elif self._requestData[0] == '<' and self._requestData[ -1] == '>': self._dataType = 'xml' else: self._dataType = 'urlencoded' else: print( 'sendToExtender: _postGet is POST but _requestData is null' ) # Clear the table before adding elements self._dataTable.setRowCount(0) # Update request viewer self.updateRequestViewer() # Fill request data self.fillRequestData() except Exception as e: print(e) ''' Fill the data into the request table ''' def fillRequestData(self): # If _postGet is GET, also adds URL to the table if self._postGet == 'GET': dataList = self._requestDataGet.split('&') for data in dataList: if '=' in data: x = data.split('=', 1) self._dataDict[str(x[0])] = str(x[1]) self._dataTable.addRow([str(x[0]), str(x[1])]) self._dataLen += 1 self._dataTable.addRow(['URL', self._requestUrl]) self._UrlRow = self._dataLen if self._userAgent != '': self._dataTable.addRow(['User-Agent', self._userAgent]) elif self._postGet == 'POST': if self._dataType == 'urlencoded': dataList = self._requestData.split('&') for data in dataList: if '=' in data: x = data.split('=', 1) self._dataDict[str(x[0])] = str(x[1]) self._dataTable.addRow([str(x[0]), str(x[1])]) self._dataLen += 1 elif self._dataType == 'json': self._dataDict = json.loads(self._requestData) for key in self._dataDict: # Convert '"' to '\"' to be the same as that in the data value = str(self._dataDict[key]) if '\"' in value: value = value.replace('\"', '\\\"') self._dataDict[key] = value self._dataTable.addRow([str(key), self._dataDict[key]]) self._dataLen += 1 elif self._dataType == 'xml': # Use xml package to convert the xml string to dict # Note1: the xml dict will be in reverse order # Note2: the arrtibute will also be added into dict, need to be pop # Note3: special characters like \" will be considered as " xml.sax.parseString(self._requestData, self._xh) self._attr = re.sub('\>(\S*\s*)*', '', self._requestData[1:], re.IGNORECASE) self._dataDict = self._xh.getDict() self._dataDict.pop(self._attr) for key in self._dataDict: self._dataTable.addRow( [str(key), str(self._dataDict[key])]) self._dataLen += 1 else: print('fillRequestData: _dataType not defined') if self._userAgent != '': self._dataTable.addRow(['User-Agent', self._userAgent]) self._savedUserAgent = self._userAgent else: print('fillRequestData: _postGet not defined') ''' Receive & update the response after sending request to the server ''' def receiveResponse(self): # Init/reset response data before receiving response self.initResponseInfo() # Launch the http thread self._httpThread = Thread(target=self.makeRequest, args=( self._httpService, self._requestBody, )) self._httpThread.start() ''' Make Http request to a service ''' def makeRequest(self, httpService, requestBody): self._httpLock.acquire() # Disable the hit buttons before starting the thread self._hitOnceButton.setEnabled(False) self._autoScanButton.setEnabled(False) self._responseMessage = self._callbacks.makeHttpRequest( httpService, requestBody) # Enable the hit buttons self._hitOnceButton.setEnabled(True) self._autoScanButton.setEnabled(True) # Unblock the mutex self._httpLock.release() ''' updateRequestViewer ''' def updateRequestViewer(self): self._requestViewer.setMessage(self.getRequest(), True) ''' updateResponseViewer ''' def updateResponseViewer(self): self._responseViewer.setMessage(self.getResponse(), False) ''' Level 1 auto: only loop through the data, do not modify the 'submit' section ''' def autoScan1(self): # TODO: Add a 'cancel' button to stop when the user think it takes too long # TODO: Add XML support if self._postGet == 'GET': for i in range(0, self._dataLen): title = self._dataTable.getValueAt(i, 0) baseValue = self._dataDict[title] for value in self._simpleList: # TODO: update more value that should not be changed if 'submit' not in title.lower( ) and 'submit' not in self._dataDict[title].lower( ) and 'search' not in title.lower( ) and 'search' not in self._dataDict[title].lower(): # Update the table in case the loop interrupt in the middle # Note that the URL will be automatically updated due to this code, so no need to manually update the URL section self._dataTable.setValueAt(value, i, 1) # Send & request the HTTP request/response self.updateRequestViewer() self.receiveResponse() # Reset the table self._dataTable.setValueAt(baseValue, i, 1) if self._postGet == 'POST': if self._dataType == 'urlencoded' or self._dataType == 'json': for i in range(0, self._dataLen): title = self._dataTable.getValueAt(i, 0) baseValue = self._dataDict[title] if 'submit' in title.lower() or 'submit' in self._dataDict[ title].lower() or 'search' in title.lower( ) or 'search' in self._dataDict[title].lower(): continue for value in self._simpleList: self._dataTable.setValueAt(value, i, 1) self.updateRequestViewer() self.receiveResponse() # Reset the table self._dataTable.setValueAt(baseValue, i, 1) elif self._dataType == 'xml': for i in range(0, self._dataLen): title = self._dataTable.getValueAt(i, 0) baseValue = self._dataDict[title] for value in self._xmlList: # Update the table in case the loop interrupt in the middle self._dataTable.setValueAt(value, i, 1) # Send & request the HTTP request/response self.updateRequestViewer() self.receiveResponse() # Reset the table self._dataTable.setValueAt(baseValue, i, 1) ''' Level 2 auto: loop through the data as well as the user agent (if exist) ''' def autoScan2(self): # If the User-Agent does not exist, only performs level 1 auto if self._userAgent != '': baseUserAgent = self._userAgent baseExpression = 'User-Agent: ' + baseUserAgent for value in self._simpleList: oldExpression = 'User-Agent: ' + self._userAgent newExpression = 'User-Agent: ' + value # Update the values accordingly requestBodyString = self._helpers.bytesToString( self._requestBody) self._requestBody = requestBodyString.replace( oldExpression, newExpression) self._userAgent = value self.updateRequestViewer() self.receiveResponse() # Reset the value back to original after each loop requestBodyString = self._helpers.bytesToString(self._requestBody) self._requestBody = requestBodyString.replace( newExpression, baseExpression) self._savedUserAgent = baseUserAgent self.updateRequestViewer() # Perform level 1 scan also self.autoScan1() ''' Level 3 auto: Alpha: use the timer to perform blind insertion ''' # TODO: 目前只支持GET/urlencoded,后续添加更多支持 def autoScan3(self): self._timeReach = False timer = Timer(5, self.timeReach) # Modify the first element to perform blind injection title = self._dataTable.getValueAt(i, 0) oldExpression = title + '=' + self._dataDict[title] newExpression = title + '=' + '1\' and if(1=0,1, sleep(10)) --+' if self._postGet == 'GET': # Update the values accordingly requestBodyString = self._helpers.bytesToString(self._requestBody) self._requestBody = requestBodyString.replace( oldExpression, newExpression) self._requestDataGet = self._requestDataGet.replace( oldExpression, newExpression) self._requestUrl = self._requestUrl.replace( oldExpression, newExpression) self._dataDict[title] = '1\' and if(1=0,1, sleep(10)) --+' self._requestModel.setValueAt('1\' and if(1=0,1, sleep(10)) --+', 0, 1) elif self._postGet == 'POST': if self._dataType == 'urlencoded': # Update the values accordingly requestBodyString = self._helpers.bytesToString( self._requestBody) self._requestBody = requestBodyString.replace( oldExpression, newExpression) self._requestData = self._requestData.replace( oldExpression, newExpression) self._dataDict[title] = '1\' and if(1=0,1, sleep(10)) --+' self._requestModel.setValueAt( '1\' and if(1=0,1, sleep(10)) --+', 0, 1) else: print('autoScan3: _dataType not supported') else: print('autoScan3: _postGet not defined') timer.start() self.updateRequestViewer() self.receiveResponse() # Print the result if self._timeReach: print('Delay scan succeed') else: print('Delay scan failed') # Cancel the timer timer.cancel() def timeReach(self): self._timeReach = True ''' Fetch the 'abnormal' payloads that shows very different response length from the normal ones ''' def getAbnormal(self, basis, coefficient): # If the basis is not set, do nothing abnormList = ArrayList() if basis == 0: return None # Fetch the abnormals from the log list for log in self._log: if float(log._responseLen) / float(basis) < coefficient or float( basis) / float(log._responseLen) < coefficient: abnormList.append(log._payload) return abnormList ''' Turn a simple dict of key/value pairs into XML ''' def dictToXml(self, tag, d): elem = Element(tag) for key, val in d.items(): child = Element(key) child.text = str(val) # Add element in reverse order so that the result is correct elem.insert(0, child) return elem ''' initRequestInfo ''' def initRequestInfo(self): self._postGet = '' self._userAgent = '' self._requestUrl = '' self._requestBody = '' self._requestData = '' self._requestDataGet = '' self._httpService = None self._dataDict = {} self._dataType = '' self._dataLen = 0 self._attr = '' self._contentLength = 0 self._currentlyDisplayedItem = None ''' initResponseInfo ''' def initResponseInfo(self): self._responseBody = None self._responseMessage = None self._responseLength = '' ''' printRequest ''' def printRequest(self): print('----------------') print(self._postGet) print('----------------') print(self._userAgent) print('----------------') print(self._requestUrl) print('----------------') print(self._requestBody) print('----------------') print(self._requestData) print('----------------') print(self._requestDataGet) print('----------------') print(self._httpService) print('----------------') print(self._dataDict) print('----------------') print(self._dataLen) print('----------------') print(self._attr) print('----------------') ''' printResponse ''' def printResponse(self): print('----------------') print(self._responseBody) print('----------------') print(self._responseMessage) print('----------------') print(self._responseLength) print('----------------')
def registerExtenderCallbacks(self, callbacks): print("[*] Loading Jaeles beta v0.1") # keep a reference to our callbacks object self._callbacks = callbacks # obtain an extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("Jaeles") # main split pane self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) # table of log entries # logTable = Table(self) # scrollPane = JScrollPane(logTable) # _toppane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) _mainpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) _mainpane.setResizeWeight(0.5) # _mainpane = JPanel() _toppane = JPanel() # top pane self.banner = JLabel("Jaeles - The Swiss Army knife for automated Web Application Testing. ") self.banner.setBounds(50, 30, 200, 400) self.banner2 = JLabel("Official Documentation: https://jaeles-project.github.io/") self.banner2.setBounds(100, 50, 200, 400) _toppane.add(self.banner) _toppane.add(self.banner2) # _botpane = JPanel() _botpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) # bot pane self.HostLabel = JLabel("Jaeles Endpoint: ") self.HostLabel.setBounds(100, 150, 200, 400) self.Jaeles_endpoint = 'http://127.0.0.1:5000/api/parse' self.jwt = 'Jaeles token_here' # just prevent plugin error when you doesn't have server running try: self.initial() jwt, endpoint = self.get_config() if endpoint: self.Jaeles_endpoint = endpoint if jwt: self.jwt = jwt except: pass endpoint_pane = JPanel() # end point to submit request self.EndpointText = JTextArea(self.Jaeles_endpoint, 3, 100) self.jwtLabel = JLabel("Jaeles JWT token: ") self.jwtLabel.setBounds(100, 300, 250, 450) self.jwtText = JTextArea(self.jwt, 3, 100, lineWrap=True) buttons = JPanel() self.buttonLabel = JLabel("Actions: ") self.buttonLabel.setBounds(150, 200, 200, 400) self._saveButton = JButton("Save", actionPerformed=self.saveToken) self._loadButton = JButton( "Test Connection", actionPerformed=self.butClick) self._reloadButton = JButton("Reload", actionPerformed=self.butClick) oob_control = JPanel() self.oobLabel = JLabel("OOB: ") self.oobLabel.setBounds(150, 200, 200, 400) self._saveoob = JButton("Save OOB", actionPerformed=self.saveToken) self._pollingBox = JCheckBox("Polling") self._pollingBox.setBounds(290, 25, 300, 30) oob_control.add(self.oobLabel) oob_control.add(self._saveoob) oob_control.add(self._pollingBox) # _botpane.add(self.banner) # endpoint_pane.add(self.blankLabel) endpoint_pane.add(self.HostLabel) endpoint_pane.add(self.EndpointText) endpoint_pane.add(self.jwtLabel) endpoint_pane.add(self.jwtText) buttons.add(self.buttonLabel) buttons.add(self._saveButton) buttons.add(self._loadButton) buttons.add(self._reloadButton) _botpane.setLeftComponent(oob_control) _botpane.setLeftComponent(endpoint_pane) _botpane.setRightComponent(buttons) _botpane.setResizeWeight(0.7) # set _mainpane.setLeftComponent(_toppane) _mainpane.setRightComponent(_botpane) self._splitpane.setLeftComponent(_mainpane) ########### # tabs with request/response viewers tabs = JTabbedPane() self.log_area = JTextArea("", 5, 30) # self._requestViewer = callbacks.createMessageEditor(self, False) tabs.addTab("Log", self.log_area) # tabs.addTab("Config", self._requestViewer.getComponent()) self._splitpane.setRightComponent(tabs) self._splitpane.setResizeWeight(0.5) callbacks.customizeUiComponent(self._splitpane) callbacks.customizeUiComponent(tabs) callbacks.registerContextMenuFactory(self) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) # register ourselves as an HTTP listener # callbacks.registerHttpListener(self) self.print_log("[*] Jaeles Loaded ...") return
class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory): def registerExtenderCallbacks(self, callbacks): # keep a reference to our callbacks object self._callbacks = callbacks # obtain an extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("Autorize") # create the log and a lock on which to synchronize when adding log entries self._log = ArrayList() self._lock = Lock() self._enfocementStatuses = ["Authorization bypass!","Authorization enforced??? (please configure enforcement detector)","Authorization enforced!"] self.intercept = 0 self.initInterceptionFilters() self.initEnforcementDetector() self.initEnforcementDetectorUnauthorized() self.initExport() self.initConfigurationTab() self.initTabs() self.initCallbacks() self.currentRequestNumber = 1 print "Thank you for installing Autorize v0.12 extension" print "Created by Barak Tawily" print "Contributors: Barak Tawily, Federico Dotta" print "\nGithub:\nhttps://github.com/Quitten/Autorize" return def initExport(self): # ## init enforcement detector tab # exportLType = JLabel("File Type:") exportLType.setBounds(10, 10, 100, 30) exportLES = JLabel("Enforcement Statuses:") exportLES.setBounds(10, 50, 160, 30) exportFileTypes = ["HTML","CSV"] self.exportType = JComboBox(exportFileTypes) self.exportType.setBounds(100, 10, 200, 30) exportES = ["All Statuses", self._enfocementStatuses[0], self._enfocementStatuses[1], self._enfocementStatuses[2]] self.exportES = JComboBox(exportES) self.exportES.setBounds(100, 50, 200, 30) exportLES = JLabel("Statuses:") exportLES.setBounds(10, 50, 100, 30) self.exportButton = JButton("Export",actionPerformed=self.export) self.exportButton.setBounds(390, 25, 100, 30) self.exportPnl = JPanel() self.exportPnl.setLayout(None); self.exportPnl.setBounds(0, 0, 1000, 1000); self.exportPnl.add(exportLType) self.exportPnl.add(self.exportType) self.exportPnl.add(exportLES) self.exportPnl.add(self.exportES) self.exportPnl.add(self.exportButton) def initEnforcementDetector(self): # ## init enforcement detector tab # # These two variable appears to be unused... self.EDFP = ArrayList() self.EDCT = ArrayList() EDLType = JLabel("Type:") EDLType.setBounds(10, 10, 140, 30) EDLContent = JLabel("Content:") EDLContent.setBounds(10, 50, 140, 30) EDLabelList = JLabel("Filter List:") EDLabelList.setBounds(10, 165, 140, 30) EDStrings = ["Headers (simple string): (enforced message headers contains)", "Headers (regex): (enforced messege headers contains)", "Body (simple string): (enforced messege body contains)", "Body (regex): (enforced messege body contains)", "Full request (simple string): (enforced messege contains)", "Full request (regex): (enforced messege contains)", "Content-Length: (constant Content-Length number of enforced response)"] self.EDType = JComboBox(EDStrings) self.EDType.setBounds(80, 10, 430, 30) self.EDText = JTextArea("", 5, 30) self.EDText.setBounds(80, 50, 300, 110) self.EDModel = DefaultListModel(); self.EDList = JList(self.EDModel); self.EDList.setBounds(80, 175, 300, 110) self.EDList.setBorder(LineBorder(Color.BLACK)) self.EDAdd = JButton("Add filter",actionPerformed=self.addEDFilter) self.EDAdd.setBounds(390, 85, 120, 30) self.EDDel = JButton("Remove filter",actionPerformed=self.delEDFilter) self.EDDel.setBounds(390, 210, 120, 30) self.EDPnl = JPanel() self.EDPnl.setLayout(None); self.EDPnl.setBounds(0, 0, 1000, 1000); self.EDPnl.add(EDLType) self.EDPnl.add(self.EDType) self.EDPnl.add(EDLContent) self.EDPnl.add(self.EDText) self.EDPnl.add(self.EDAdd) self.EDPnl.add(self.EDDel) self.EDPnl.add(EDLabelList) self.EDPnl.add(self.EDList) def initEnforcementDetectorUnauthorized(self): # ## init enforcement detector tab # EDLType = JLabel("Type:") EDLType.setBounds(10, 10, 140, 30) EDLContent = JLabel("Content:") EDLContent.setBounds(10, 50, 140, 30) EDLabelList = JLabel("Filter List:") EDLabelList.setBounds(10, 165, 140, 30) EDStrings = ["Headers (simple string): (enforced message headers contains)", "Headers (regex): (enforced messege headers contains)", "Body (simple string): (enforced messege body contains)", "Body (regex): (enforced messege body contains)", "Full request (simple string): (enforced messege contains)", "Full request (regex): (enforced messege contains)", "Content-Length: (constant Content-Length number of enforced response)"] self.EDTypeUnauth = JComboBox(EDStrings) self.EDTypeUnauth.setBounds(80, 10, 430, 30) self.EDTextUnauth = JTextArea("", 5, 30) self.EDTextUnauth.setBounds(80, 50, 300, 110) self.EDModelUnauth = DefaultListModel(); self.EDListUnauth = JList(self.EDModelUnauth); self.EDListUnauth.setBounds(80, 175, 300, 110) self.EDListUnauth.setBorder(LineBorder(Color.BLACK)) self.EDAddUnauth = JButton("Add filter",actionPerformed=self.addEDFilterUnauth) self.EDAddUnauth.setBounds(390, 85, 120, 30) self.EDDelUnauth = JButton("Remove filter",actionPerformed=self.delEDFilterUnauth) self.EDDelUnauth.setBounds(390, 210, 120, 30) self.EDPnlUnauth = JPanel() self.EDPnlUnauth.setLayout(None); self.EDPnlUnauth.setBounds(0, 0, 1000, 1000); self.EDPnlUnauth.add(EDLType) self.EDPnlUnauth.add(self.EDTypeUnauth) self.EDPnlUnauth.add(EDLContent) self.EDPnlUnauth.add(self.EDTextUnauth) self.EDPnlUnauth.add(self.EDAddUnauth) self.EDPnlUnauth.add(self.EDDelUnauth) self.EDPnlUnauth.add(EDLabelList) self.EDPnlUnauth.add(self.EDListUnauth) def initInterceptionFilters(self): # ## init interception filters tab # IFStrings = ["Scope items only: (Content is not required)","URL Contains (simple string): ","URL Contains (regex): ","URL Not Contains (simple string): ","URL Not Contains (regex): "] self.IFType = JComboBox(IFStrings) self.IFType.setBounds(80, 10, 430, 30) self.IFModel = DefaultListModel(); self.IFList = JList(self.IFModel); self.IFList.setBounds(80, 175, 300, 110) self.IFList.setBorder(LineBorder(Color.BLACK)) self.IFText = JTextArea("", 5, 30) self.IFText.setBounds(80, 50, 300, 110) IFLType = JLabel("Type:") IFLType.setBounds(10, 10, 140, 30) IFLContent = JLabel("Content:") IFLContent.setBounds(10, 50, 140, 30) IFLabelList = JLabel("Filter List:") IFLabelList.setBounds(10, 165, 140, 30) self.IFAdd = JButton("Add filter",actionPerformed=self.addIFFilter) self.IFAdd.setBounds(390, 85, 120, 30) self.IFDel = JButton("Remove filter",actionPerformed=self.delIFFilter) self.IFDel.setBounds(390, 210, 120, 30) self.filtersPnl = JPanel() self.filtersPnl.setLayout(None); self.filtersPnl.setBounds(0, 0, 1000, 1000); self.filtersPnl.add(IFLType) self.filtersPnl.add(self.IFType) self.filtersPnl.add(IFLContent) self.filtersPnl.add(self.IFText) self.filtersPnl.add(self.IFAdd) self.filtersPnl.add(self.IFDel) self.filtersPnl.add(IFLabelList) self.filtersPnl.add(self.IFList) def initConfigurationTab(self): # ## init configuration tab # self.prevent304 = JCheckBox("Prevent 304 Not Modified status code") self.prevent304.setBounds(290, 25, 300, 30) self.ignore304 = JCheckBox("Ignore 304/204 status code responses") self.ignore304.setBounds(290, 5, 300, 30) self.ignore304.setSelected(True) self.autoScroll = JCheckBox("Auto Scroll") #self.autoScroll.setBounds(290, 45, 140, 30) self.autoScroll.setBounds(160, 40, 140, 30) self.doUnauthorizedRequest = JCheckBox("Check unauthenticated") self.doUnauthorizedRequest.setBounds(290, 45, 300, 30) self.doUnauthorizedRequest.setSelected(True) startLabel = JLabel("Authorization checks:") startLabel.setBounds(10, 10, 140, 30) self.startButton = JButton("Autorize is off",actionPerformed=self.startOrStop) self.startButton.setBounds(160, 10, 120, 30) self.startButton.setBackground(Color(255, 100, 91, 255)) self.clearButton = JButton("Clear List",actionPerformed=self.clearList) self.clearButton.setBounds(10, 40, 100, 30) self.replaceString = JTextArea("Cookie: Insert=injected; header=here;", 5, 30) self.replaceString.setWrapStyleWord(True); self.replaceString.setLineWrap(True) self.replaceString.setBounds(10, 80, 470, 180) self.filtersTabs = JTabbedPane() self.filtersTabs.addTab("Enforcement Detector", self.EDPnl) self.filtersTabs.addTab("Detector Unauthenticated", self.EDPnlUnauth) self.filtersTabs.addTab("Interception Filters", self.filtersPnl) self.filtersTabs.addTab("Export", self.exportPnl) self.filtersTabs.setBounds(0, 280, 2000, 700) self.pnl = JPanel() self.pnl.setBounds(0, 0, 1000, 1000); self.pnl.setLayout(None); self.pnl.add(self.startButton) self.pnl.add(self.clearButton) self.pnl.add(self.replaceString) self.pnl.add(startLabel) self.pnl.add(self.autoScroll) self.pnl.add(self.ignore304) self.pnl.add(self.prevent304) self.pnl.add(self.doUnauthorizedRequest) self.pnl.add(self.filtersTabs) def initTabs(self): # ## init autorize tabs # self.logTable = Table(self) self.logTable.setAutoCreateRowSorter(True) tableWidth = self.logTable.getPreferredSize().width self.logTable.getColumn("ID").setPreferredWidth(Math.round(tableWidth / 50 * 2)) self.logTable.getColumn("URL").setPreferredWidth(Math.round(tableWidth / 50 * 24)) self.logTable.getColumn("Orig. Length").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.logTable.getColumn("Modif. Length").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.logTable.getColumn("Unauth. Length").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.logTable.getColumn("Authorization Enforcement Status").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self.logTable.getColumn("Authorization Unauth. Status").setPreferredWidth(Math.round(tableWidth / 50 * 4)) self._splitpane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._splitpane.setResizeWeight(1) self.scrollPane = JScrollPane(self.logTable) self._splitpane.setLeftComponent(self.scrollPane) self.scrollPane.getVerticalScrollBar().addAdjustmentListener(autoScrollListener(self)) self.menuES0 = JCheckBoxMenuItem(self._enfocementStatuses[0],True) self.menuES1 = JCheckBoxMenuItem(self._enfocementStatuses[1],True) self.menuES2 = JCheckBoxMenuItem(self._enfocementStatuses[2],True) self.menuES0.addItemListener(menuTableFilter(self)) self.menuES1.addItemListener(menuTableFilter(self)) self.menuES2.addItemListener(menuTableFilter(self)) copyURLitem = JMenuItem("Copy URL"); copyURLitem.addActionListener(copySelectedURL(self)) self.menu = JPopupMenu("Popup") self.menu.add(copyURLitem) self.menu.add(self.menuES0) self.menu.add(self.menuES1) self.menu.add(self.menuES2) self.tabs = JTabbedPane() self._requestViewer = self._callbacks.createMessageEditor(self, False) self._responseViewer = self._callbacks.createMessageEditor(self, False) self._originalrequestViewer = self._callbacks.createMessageEditor(self, False) self._originalresponseViewer = self._callbacks.createMessageEditor(self, False) self._unauthorizedrequestViewer = self._callbacks.createMessageEditor(self, False) self._unauthorizedresponseViewer = self._callbacks.createMessageEditor(self, False) self.tabs.addTab("Modified Request", self._requestViewer.getComponent()) self.tabs.addTab("Modified Response", self._responseViewer.getComponent()) self.tabs.addTab("Original Request", self._originalrequestViewer.getComponent()) self.tabs.addTab("Original Response", self._originalresponseViewer.getComponent()) self.tabs.addTab("Unauthenticated Request", self._unauthorizedrequestViewer.getComponent()) self.tabs.addTab("Unauthenticated Response", self._unauthorizedresponseViewer.getComponent()) self.tabs.addTab("Configuration", self.pnl) self.tabs.setSelectedIndex(6) self._splitpane.setRightComponent(self.tabs) def initCallbacks(self): # ## init callbacks # # customize our UI components self._callbacks.customizeUiComponent(self._splitpane) self._callbacks.customizeUiComponent(self.logTable) self._callbacks.customizeUiComponent(self.scrollPane) self._callbacks.customizeUiComponent(self.tabs) self._callbacks.customizeUiComponent(self.filtersTabs) self._callbacks.registerContextMenuFactory(self) # add the custom tab to Burp's UI self._callbacks.addSuiteTab(self) # ## Events functions # def startOrStop(self, event): if self.startButton.getText() == "Autorize is off": self.startButton.setText("Autorize is on") self.startButton.setBackground(Color.GREEN) self.intercept = 1 self._callbacks.registerHttpListener(self) else: self.startButton.setText("Autorize is off") self.startButton.setBackground(Color(255, 100, 91, 255)) self.intercept = 0 self._callbacks.removeHttpListener(self) def addEDFilter(self, event): typeName = self.EDType.getSelectedItem().split(":")[0] self.EDModel.addElement(typeName + ": " + self.EDText.getText()) def delEDFilter(self, event): index = self.EDList.getSelectedIndex(); if not index == -1: self.EDModel.remove(index); def addEDFilterUnauth(self, event): typeName = self.EDTypeUnauth.getSelectedItem().split(":")[0] self.EDModelUnauth.addElement(typeName + ": " + self.EDTextUnauth.getText()) def delEDFilterUnauth(self, event): index = self.EDListUnauth.getSelectedIndex(); if not index == -1: self.EDModelUnauth.remove(index); def addIFFilter(self, event): typeName = self.IFType.getSelectedItem().split(":")[0] self.IFModel.addElement(typeName + ": " + self.IFText.getText()) def delIFFilter(self, event): index = self.IFList.getSelectedIndex(); if not index == -1: self.IFModel.remove(index); def clearList(self, event): self._lock.acquire() oldSize = self._log.size() self._log.clear() self.fireTableRowsDeleted(0, oldSize - 1) self._lock.release() def export(self, event): if self.exportType.getSelectedItem() == "HTML": self.exportToHTML() else: self.exportToCSV() def exportToCSV(self): parentFrame = JFrame() fileChooser = JFileChooser() fileChooser.setSelectedFile(File("AutorizeReprort.csv")); fileChooser.setDialogTitle("Save Autorize Report") userSelection = fileChooser.showSaveDialog(parentFrame) if userSelection == JFileChooser.APPROVE_OPTION: fileToSave = fileChooser.getSelectedFile() enforcementStatusFilter = self.exportES.getSelectedItem() csvContent = "id\tURL\tOriginal length\tModified length\tUnauthorized length\tAuthorization Enforcement Status\tAuthorization Unauthenticated Status\n" for i in range(0,self._log.size()): if enforcementStatusFilter == "All Statuses": csvContent += "%d\t%s\t%d\t%d\t%d\t%s\t%s\n" % (self._log.get(i)._id,self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse != None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse != None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse != None else 0, self._log.get(i)._enfocementStatus, self._log.get(i)._enfocementStatusUnauthorized) else: if (enforcementStatusFilter == self._log.get(i)._enfocementStatus) or (enforcementStatusFilter == self._log.get(i)._enfocementStatusUnauthorized): csvContent += "%d\t%s\t%d\t%d\t%d\t%s\t%s\n" % (self._log.get(i)._id,self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse != None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse != None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse != None else 0, self._log.get(i)._enfocementStatus, self._log.get(i)._enfocementStatusUnauthorized) f = open(fileToSave.getAbsolutePath(), 'w') f.writelines(csvContent) f.close() def exportToHTML(self): parentFrame = JFrame() fileChooser = JFileChooser() fileChooser.setSelectedFile(File("AutorizeReprort.html")); fileChooser.setDialogTitle("Save Autorize Report") userSelection = fileChooser.showSaveDialog(parentFrame) if userSelection == JFileChooser.APPROVE_OPTION: fileToSave = fileChooser.getSelectedFile() enforcementStatusFilter = self.exportES.getSelectedItem() htmlContent = """<html><title>Autorize Report by Barak Tawily</title> <style> .datagrid table { border-collapse: collapse; text-align: left; width: 100%; } .datagrid {font: normal 12px/150% Arial, Helvetica, sans-serif; background: #fff; overflow: hidden; border: 1px solid #006699; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .datagrid table td, .datagrid table th { padding: 3px 10px; } .datagrid table thead th {background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #006699), color-stop(1, #00557F) );background:-moz-linear-gradient( center top, #006699 5%, #00557F 100% );filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#006699', endColorstr='#00557F');background-color:#006699; color:#FFFFFF; font-size: 15px; font-weight: bold; border-left: 1px solid #0070A8; } .datagrid table thead th:first-child { border: none; }.datagrid table tbody td { color: #00496B; border-left: 1px solid #E1EEF4;font-size: 12px;font-weight: normal; }.datagrid table tbody .alt td { background: #E1EEF4; color: #00496B; }.datagrid table tbody td:first-child { border-left: none; }.datagrid table tbody tr:last-child td { border-bottom: none; }.datagrid table tfoot td div { border-top: 1px solid #006699;background: #E1EEF4;} .datagrid table tfoot td { padding: 0; font-size: 12px } .datagrid table tfoot td div{ padding: 2px; }.datagrid table tfoot td ul { margin: 0; padding:0; list-style: none; text-align: right; }.datagrid table tfoot li { display: inline; }.datagrid table tfoot li a { text-decoration: none; display: inline-block; padding: 2px 8px; margin: 1px;color: #FFFFFF;border: 1px solid #006699;-webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #006699), color-stop(1, #00557F) );background:-moz-linear-gradient( center top, #006699 5%, #00557F 100% );filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#006699', endColorstr='#00557F');background-color:#006699; }.datagrid table tfoot ul.active, .datagrid table tfoot ul a:hover { text-decoration: none;border-color: #006699; color: #FFFFFF; background: none; background-color:#00557F;}div.dhtmlx_window_active, div.dhx_modal_cover_dv { position: fixed !important; } table { width: 100%; table-layout: fixed; } td { border: 1px solid #35f; overflow: hidden; text-overflow: ellipsis; } td.a { width: 13%; white-space: nowrap; } td.b { width: 9%; word-wrap: break-word; } </style> <body> <h1>Autorize Report<h1> <div class="datagrid"><table> <thead><tr><th width=\"3%\">ID</th><th width=\"48%\">URL</th><th width=\"9%\">Original length</th><th width=\"9%\">Modified length</th><th width=\"9%\">Unauthorized length</th><th width=\"11%\">Authorization Enforcement Status</th><th width=\"11%\">Authorization Unauthenticated Status</th></tr></thead> <tbody>""" for i in range(0,self._log.size()): color_modified = "" if self._log.get(i)._enfocementStatus == self._enfocementStatuses[0]: color_modified = "red" if self._log.get(i)._enfocementStatus == self._enfocementStatuses[1]: color_modified = "yellow" if self._log.get(i)._enfocementStatus == self._enfocementStatuses[2]: color_modified = "LawnGreen" color_unauthorized = "" if self._log.get(i)._enfocementStatusUnauthorized == self._enfocementStatuses[0]: color_unauthorized = "red" if self._log.get(i)._enfocementStatusUnauthorized == self._enfocementStatuses[1]: color_unauthorized = "yellow" if self._log.get(i)._enfocementStatusUnauthorized == self._enfocementStatuses[2]: color_unauthorized = "LawnGreen" if enforcementStatusFilter == "All Statuses": htmlContent += "<tr><td>%d</td><td><a href=\"%s\">%s</a></td><td>%d</td><td>%d</td><td>%d</td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td></tr>" % (self._log.get(i)._id,self._log.get(i)._url,self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse != None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse != None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse != None else 0, color_modified, self._log.get(i)._enfocementStatus, color_unauthorized, self._log.get(i)._enfocementStatusUnauthorized) else: if (enforcementStatusFilter == self._log.get(i)._enfocementStatus) or (enforcementStatusFilter == self._log.get(i)._enfocementStatusUnauthorized): htmlContent += "<tr><td>%d</td><td><a href=\"%s\">%s</a></td><td>%d</td><td>%d</td><td>%d</td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td></tr>" % (self._log.get(i)._id,self._log.get(i)._url,self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse != None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse != None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse != None else 0, color_modified, self._log.get(i)._enfocementStatus, color_unauthorized, self._log.get(i)._enfocementStatusUnauthorized) htmlContent += "</tbody></table></div></body></html>" f = open(fileToSave.getAbsolutePath(), 'w') f.writelines(htmlContent) f.close() # # implement IContextMenuFactory # def createMenuItems(self, invocation): responses = invocation.getSelectedMessages(); if responses > 0: ret = LinkedList() requestMenuItem = JMenuItem("Send request to Autorize"); cookieMenuItem = JMenuItem("Send cookie to Autorize"); requestMenuItem.addActionListener(handleMenuItems(self,responses[0], "request")) cookieMenuItem.addActionListener(handleMenuItems(self, responses[0], "cookie")) ret.add(requestMenuItem); ret.add(cookieMenuItem); return(ret); return null; # # implement ITab # def getTabCaption(self): return "Autorize" def getUiComponent(self): return self._splitpane # # extend AbstractTableModel # def getRowCount(self): try: return self._log.size() except: return 0 def getColumnCount(self): return 7 def getColumnName(self, columnIndex): if columnIndex == 0: return "ID" if columnIndex == 1: return "URL" if columnIndex == 2: return "Orig. Length" if columnIndex == 3: return "Modif. Length" if columnIndex == 4: return "Unauth. Length" if columnIndex == 5: return "Authorization Enforcement Status" if columnIndex == 6: return "Authorization Unauth. Status" return "" def getColumnClass(self, columnIndex): if columnIndex == 0: return Integer if columnIndex == 1: return String if columnIndex == 2: return Integer if columnIndex == 3: return Integer if columnIndex == 4: return Integer if columnIndex == 5: return String if columnIndex == 6: return String return String def getValueAt(self, rowIndex, columnIndex): logEntry = self._log.get(rowIndex) if columnIndex == 0: return logEntry._id if columnIndex == 1: return logEntry._url.toString() if columnIndex == 2: return len(logEntry._originalrequestResponse.getResponse()) if columnIndex == 3: return len(logEntry._requestResponse.getResponse()) if columnIndex == 4: if logEntry._unauthorizedRequestResponse != None: return len(logEntry._unauthorizedRequestResponse.getResponse()) else: #return "-" return 0 if columnIndex == 5: return logEntry._enfocementStatus if columnIndex == 6: return logEntry._enfocementStatusUnauthorized return "" # # implement IMessageEditorController # this allows our request/response viewers to obtain details about the messages being displayed # def getHttpService(self): return self._currentlyDisplayedItem.getHttpService() def getRequest(self): return self._currentlyDisplayedItem.getRequest() def getResponse(self): return self._currentlyDisplayedItem.getResponse() # # implement IHttpListener # def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): #if (self.intercept == 1) and (toolFlag != self._callbacks.TOOL_EXTENDER): if (self.intercept == 1) and (toolFlag == self._callbacks.TOOL_PROXY): if self.prevent304.isSelected(): if messageIsRequest: requestHeaders = list(self._helpers.analyzeRequest(messageInfo).getHeaders()) newHeaders = list() found = 0 for header in requestHeaders: if not "If-None-Match:" in header and not "If-Modified-Since:" in header: newHeaders.append(header) found = 1 if found == 1: requestInfo = self._helpers.analyzeRequest(messageInfo) bodyBytes = messageInfo.getRequest()[requestInfo.getBodyOffset():] bodyStr = self._helpers.bytesToString(bodyBytes) messageInfo.setRequest(self._helpers.buildHttpMessage(newHeaders, bodyStr)) if not messageIsRequest: if not self.replaceString.getText() in self._helpers.analyzeRequest(messageInfo).getHeaders(): if self.ignore304.isSelected(): firstHeader = self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders()[0] if "304" in firstHeader or "204" in firstHeader: return if self.IFList.getModel().getSize() == 0: self.checkAuthorization(messageInfo,self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) else: urlString = str(self._helpers.analyzeRequest(messageInfo).getUrl()) do_the_check = 1 for i in range(0,self.IFList.getModel().getSize()): if self.IFList.getModel().getElementAt(i).split(":")[0] == "Scope items only": currentURL = URL(urlString) if not self._callbacks.isInScope(currentURL): do_the_check = 0 if self.IFList.getModel().getElementAt(i).split(":")[0] == "URL Contains (simple string)": if self.IFList.getModel().getElementAt(i)[30:] not in urlString: do_the_check = 0 if self.IFList.getModel().getElementAt(i).split(":")[0] == "URL Contains (regex)": regex_string = self.IFList.getModel().getElementAt(i)[22:] p = re.compile(regex_string, re.IGNORECASE) if not p.search(urlString): do_the_check = 0 if self.IFList.getModel().getElementAt(i).split(":")[0] == "URL Not Contains (simple string)": if self.IFList.getModel().getElementAt(i)[34:] in urlString: do_the_check = 0 if self.IFList.getModel().getElementAt(i).split(":")[0] == "URL Not Contains (regex)": regex_string = self.IFList.getModel().getElementAt(i)[26:] p = re.compile(regex_string, re.IGNORECASE) if p.search(urlString): do_the_check = 0 if do_the_check: self.checkAuthorization(messageInfo,self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) return def sendRequestToAutorizeWork(self,messageInfo): if messageInfo.getResponse() == None: message = self.makeMessage(messageInfo,False,False) requestResponse = self.makeRequest(messageInfo, message) self.checkAuthorization(requestResponse,self._helpers.analyzeResponse(requestResponse.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) else: self.checkAuthorization(messageInfo,self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) def makeRequest(self, messageInfo, message): requestURL = self._helpers.analyzeRequest(messageInfo).getUrl() return self._callbacks.makeHttpRequest(self._helpers.buildHttpService(str(requestURL.getHost()), int(requestURL.getPort()), requestURL.getProtocol() == "https"), message) def makeMessage(self, messageInfo, removeOrNot, authorizeOrNot): requestInfo = self._helpers.analyzeRequest(messageInfo) headers = requestInfo.getHeaders() if removeOrNot: headers = list(headers) removeHeaders = ArrayList() removeHeaders.add(self.replaceString.getText()[0:self.replaceString.getText().index(":")]) for header in headers[:]: for removeHeader in removeHeaders: if removeHeader in header: headers.remove(header) if authorizeOrNot: headers.append(self.replaceString.getText()) msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] return self._helpers.buildHttpMessage(headers, msgBody) def checkBypass(self,oldStatusCode,newStatusCode,oldContentLen,newContentLen,filters,requestResponse): analyzedResponse = self._helpers.analyzeResponse(requestResponse.getResponse()) impression = "" if oldStatusCode == newStatusCode: if oldContentLen == newContentLen: impression = self._enfocementStatuses[0] else: auth_enforced = 1 for filter in filters: if str(filter).startswith("Headers (simple string): "): if not(filter[25:] in self._helpers.bytesToString(requestResponse.getResponse()[0:analyzedResponse.getBodyOffset()])): auth_enforced = 0 if str(filter).startswith("Headers (regex): "): regex_string = filter[17:] p = re.compile(regex_string, re.IGNORECASE) if not p.search(self._helpers.bytesToString(requestResponse.getResponse()[0:analyzedResponse.getBodyOffset()])): auth_enforced = 0 if str(filter).startswith("Body (simple string): "): if not(filter[22:] in self._helpers.bytesToString(requestResponse.getResponse()[analyzedResponse.getBodyOffset():])): auth_enforced = 0 if str(filter).startswith("Body (regex): "): regex_string = filter[14:] p = re.compile(regex_string, re.IGNORECASE) if not p.search(self._helpers.bytesToString(requestResponse.getResponse()[analyzedResponse.getBodyOffset():])): auth_enforced = 0 if str(filter).startswith("Full request (simple string): "): if not(filter[30:] in self._helpers.bytesToString(requestResponse.getResponse())): auth_enforced = 0 if str(filter).startswith("Full request (regex): "): regex_string = filter[22:] p = re.compile(regex_string, re.IGNORECASE) if not p.search(self._helpers.bytesToString(requestResponse.getResponse())): auth_enforced = 0 if str(filter).startswith("Content-Length: "): if newContentLen != filter: auth_enforced = 0 if auth_enforced: impression = self._enfocementStatuses[2] else: impression = self._enfocementStatuses[1] else: impression = self._enfocementStatuses[2] return impression def checkAuthorization(self, messageInfo, originalHeaders, checkUnauthorized): message = self.makeMessage(messageInfo,True,True) requestResponse = self.makeRequest(messageInfo, message) analyzedResponse = self._helpers.analyzeResponse(requestResponse.getResponse()) oldStatusCode = originalHeaders[0] newStatusCode = analyzedResponse.getHeaders()[0] oldContentLen = self.getContentLength(originalHeaders) newContentLen = self.getContentLength(analyzedResponse.getHeaders()) # Check unauthorized request if checkUnauthorized: messageUnauthorized = self.makeMessage(messageInfo,True,False) requestResponseUnauthorized = self.makeRequest(messageInfo, messageUnauthorized) analyzedResponseUnauthorized = self._helpers.analyzeResponse(requestResponseUnauthorized.getResponse()) statusCodeUnauthorized = analyzedResponseUnauthorized.getHeaders()[0] contentLenUnauthorized = self.getContentLength(analyzedResponseUnauthorized.getHeaders()) EDFilters = self.EDModel.toArray() impression = self.checkBypass(oldStatusCode,newStatusCode,oldContentLen,newContentLen,EDFilters,requestResponse) if checkUnauthorized: EDFiltersUnauth = self.EDModelUnauth.toArray() impressionUnauthorized = self.checkBypass(oldStatusCode,statusCodeUnauthorized,oldContentLen,contentLenUnauthorized,EDFiltersUnauth,requestResponseUnauthorized) self._lock.acquire() row = self._log.size() if checkUnauthorized: self._log.add(LogEntry(self.currentRequestNumber,self._callbacks.saveBuffersToTempFiles(requestResponse), self._helpers.analyzeRequest(requestResponse).getUrl(),messageInfo,impression,self._callbacks.saveBuffersToTempFiles(requestResponseUnauthorized),impressionUnauthorized)) # same requests not include again. else: self._log.add(LogEntry(self.currentRequestNumber,self._callbacks.saveBuffersToTempFiles(requestResponse), self._helpers.analyzeRequest(requestResponse).getUrl(),messageInfo,impression,None,"Disabled")) # same requests not include again. self.fireTableRowsInserted(row, row) self.currentRequestNumber = self.currentRequestNumber + 1 self._lock.release() def getContentLength(self, analyzedResponseHeaders): for header in analyzedResponseHeaders: if "Content-Length:" in header: return header; return "null" def getCookieFromMessage(self, messageInfo): headers = list(self._helpers.analyzeRequest(messageInfo.getRequest()).getHeaders()) for header in headers: if "Cookie:" in header: return header return None
def registerExtenderCallbacks(self, callbacks): # keep a reference to our Burp callbacks object self._callbacks = callbacks # obtain an Burp extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("AuthMatrix - v0.4") # DB that holds everything users, roles, and messages self._db = MatrixDB() # For saving/loading config self._fc = JFileChooser() # Used by ActionListeners selfExtender = self self._selectedColumn = -1 self._selectedRow = -1 # Table of User entries self._userTable = UserTable(self, model=UserTableModel(self._db)) roleScrollPane = JScrollPane(self._userTable) self._userTable.redrawTable() # Table of Request (AKA Message) entries self._messageTable = MessageTable(self, model=MessageTableModel(self._db)) messageScrollPane = JScrollPane(self._messageTable) self._messageTable.redrawTable() # Semi-Generic Popup stuff def addPopup(component, popup): class genericMouseListener(MouseAdapter): def mousePressed(self, e): if e.isPopupTrigger(): self.showMenu(e) def mouseReleased(self, e): if e.isPopupTrigger(): self.showMenu(e) def showMenu(self, e): if type(component) is JTableHeader: table = component.getTable() column = component.columnAtPoint(e.getPoint()) if type( table ) is MessageTable and column >= selfExtender._db.STATIC_MESSAGE_TABLE_COLUMN_COUNT or type( table ) is UserTable and column >= selfExtender._db.STATIC_USER_TABLE_COLUMN_COUNT: selfExtender._selectedColumn = column else: return else: selfExtender._selectedRow = component.rowAtPoint( e.getPoint()) popup.show(e.getComponent(), e.getX(), e.getY()) component.addMouseListener(genericMouseListener()) class actionRunMessage(ActionListener): def actionPerformed(self, e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows( ): indexes = [ selfExtender._db.getMessageByRow( selfExtender._selectedRow)._index ] else: indexes = [ selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows() ] t = Thread(target=selfExtender.runMessagesThread, args=[indexes]) t.start() selfExtender._selectedColumn = -1 # Redrawing the table happens in colorcode within the thread class actionRemoveMessage(ActionListener): def actionPerformed(self, e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows( ): indexes = [ selfExtender._db.getMessageByRow( selfExtender._selectedRow)._index ] else: indexes = [ selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows() ] for i in indexes: selfExtender._db.deleteMessage(i) selfExtender._selectedColumn = -1 selfExtender._messageTable.redrawTable() class actionRemoveUser(ActionListener): def actionPerformed(self, e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._userTable.getSelectedRows( ): indexes = [ selfExtender._db.getUserByRow( selfExtender._selectedRow)._index ] else: indexes = [ selfExtender._db.getUserByRow(rowNum)._index for rowNum in selfExtender._userTable.getSelectedRows() ] for i in indexes: selfExtender._db.deleteUser(i) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() # TODO combine these next two classes # TODO Also, clean up the variable names where M and U are in place of MessageTable and UserTable class actionRemoveRoleHeaderFromM(ActionListener): def actionPerformed(self, e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole( selfExtender._db.getRoleByMColumn( selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() class actionRemoveRoleHeaderFromU(ActionListener): def actionPerformed(self, e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole( selfExtender._db.getRoleByUColumn( selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() # Message Table popups messagePopup = JPopupMenu() addPopup(self._messageTable, messagePopup) messageRun = JMenuItem("Run Request(s)") messageRun.addActionListener(actionRunMessage()) messagePopup.add(messageRun) messageRemove = JMenuItem("Remove Request(s)") messageRemove.addActionListener(actionRemoveMessage()) messagePopup.add(messageRemove) messageHeaderPopup = JPopupMenu() addPopup(self._messageTable.getTableHeader(), messageHeaderPopup) roleRemoveFromM = JMenuItem("Remove Role") roleRemoveFromM.addActionListener(actionRemoveRoleHeaderFromM()) messageHeaderPopup.add(roleRemoveFromM) # User Table popup userPopup = JPopupMenu() addPopup(self._userTable, userPopup) userRemove = JMenuItem("Remove Users(s)") userRemove.addActionListener(actionRemoveUser()) userPopup.add(userRemove) userHeaderPopup = JPopupMenu() addPopup(self._userTable.getTableHeader(), userHeaderPopup) roleRemoveFromU = JMenuItem("Remove Role") roleRemoveFromU.addActionListener(actionRemoveRoleHeaderFromU()) userHeaderPopup.add(roleRemoveFromU) # Top pane topPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, roleScrollPane, messageScrollPane) topPane.setResizeWeight(0.3) # request tabs added to this tab on click in message table self._tabs = JTabbedPane() # Button pannel buttons = JPanel() runButton = JButton("Run", actionPerformed=self.runClick) newUserButton = JButton("New User", actionPerformed=self.getInputUserClick) newRoleButton = JButton("New Role", actionPerformed=self.getInputRoleClick) #debugButton = JButton("Debug", actionPerformed=self.printDB) saveButton = JButton("Save", actionPerformed=self.saveClick) loadButton = JButton("Load", actionPerformed=self.loadClick) clearButton = JButton("Clear", actionPerformed=self.clearClick) buttons.add(runButton) buttons.add(newUserButton) buttons.add(newRoleButton) #buttons.add(debugButton) buttons.add(saveButton) buttons.add(loadButton) buttons.add(clearButton) bottomPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, self._tabs, buttons) bottomPane.setResizeWeight(0.95) # Main Pane self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT, topPane, bottomPane) self._splitpane.setResizeWeight(0.5) # customize our UI components callbacks.customizeUiComponent(self._splitpane) callbacks.customizeUiComponent(topPane) callbacks.customizeUiComponent(bottomPane) callbacks.customizeUiComponent(messageScrollPane) callbacks.customizeUiComponent(roleScrollPane) callbacks.customizeUiComponent(self._messageTable) callbacks.customizeUiComponent(self._userTable) callbacks.customizeUiComponent(self._tabs) callbacks.customizeUiComponent(buttons) # Handles checkbox color coding # Must be bellow the customizeUiComponent calls self._messageTable.setDefaultRenderer(Boolean, SuccessBooleanRenderer(self._db)) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) # register SendTo option callbacks.registerContextMenuFactory(self) return
class BobGui(JFrame): def __init__(self): super(BobGui, self).__init__('BobPy') # cls = self.getClass() # print(cls) # self.setLayout(MigLayout()) self.setLayout(BorderLayout()) self.main_panel = JPanel() self.main_panel.setLayout(MigLayout()) dir_panel = JPanel() dir_panel.setLayout(BoxLayout(dir_panel, BoxLayout.X_AXIS)) dir_label = JLabel('Experiment Folder:') dir_panel.add(dir_label) self.dir_text_field = JTextField(10) self.dir_text_field.addActionListener( ActionListenerFactory(self, self.text_field_al)) dir_panel.add(self.dir_text_field) dir_button = JButton('open') dir_button.addActionListener( ActionListenerFactory(self, self.choose_dir_al)) dir_panel.add(dir_button) self.main_panel.add(dir_panel, 'growx, spanx, pushx, wrap') add_key_args(self.main_panel, 'close_w', ActionListenerFactory(self, self.close_al), KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) self.add(self.main_panel, BorderLayout.CENTER) self.setPreferredSize(Dimension(500, 400)) self.pack() self.setLocationRelativeTo(None) self.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE) self.setVisible(True) def show_exper_info_old(self): # sb = br.SimilarityBuilder() cab = br.CollectionArchetypeBuilder() for hseg in self.exper.hsegs(): all_file_dict = hseg.file_dict() all_file_dict.update(hseg.cell_file_dict()) all_file_dict.update(hseg.bin_file_dict()) cab.add_collection(hseg.name, all_file_dict) hseg_at, hseg_at_deviations = cab.get_archetype_info() at_str = '' for val in hseg_at: at_str += str(val) + '\n' chf_panel = self.make_chf_panel(at_str) hseg_tree_panel = self.make_hseg_tree_panel(hseg_at_deviations) self.split_pane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self.split_pane.setOneTouchExpandable(True) self.split_pane.setContinuousLayout(True) self.split_pane.setResizeWeight(0.5) self.split_pane.add(chf_panel) self.split_pane.add(hseg_tree_panel) self.main_panel.add(self.split_pane, 'grow') self.revalidate() def show_exper_info(self): # sb = br.SimilarityBuilder() # cab = br.CollectionArchetypeBuilder() # # for hseg in self.exper.hsegs() : # all_file_dict = hseg.file_dict() # all_file_dict.update(hseg.cell_file_dict()) # all_file_dict.update(hseg.bin_file_dict()) # cab.add_collection(hseg.name, all_file_dict) # # hseg_at, hseg_at_deviations = cab.get_archetype_info() # # at_str = '' # for val in hseg_at : # at_str += str(val) + '\n' chf_panel = self.make_chf_panel() hseg_tree_panel = self.make_hseg_tree_panel() self.split_pane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self.split_pane.setOneTouchExpandable(True) self.split_pane.setContinuousLayout(True) self.split_pane.setResizeWeight(0.5) self.split_pane.add(chf_panel) self.split_pane.add(hseg_tree_panel) self.main_panel.add(self.split_pane, 'grow') self.revalidate() def make_chf_panel_old(self, at_str): """ cf --> common hseg files """ chf_panel = JPanel() # chf_panel.setLayout(BoxLayout(chf_panel, BoxLayout.Y_AXIS)) chf_panel.setLayout(MigLayout('insets 0')) # chf_panel.setAlignmentX(Component.LEFT_ALIGNMENT) chf_label = JLabel('Common Hemeisegment Files') # chf_label.setAlignmentX(Component.LEFT_ALIGNMENT) chf_panel.add(chf_label, 'grow, wrap') chf_text_area = JTextArea(at_str) chf_panel.add(chf_text_area, 'grow, push, span') return chf_panel def make_chf_panel(self): """ chf --> common hseg files """ chf_panel = JPanel() # chf_panel.setLayout(BoxLayout(chf_panel, BoxLayout.Y_AXIS)) chf_panel.setLayout(MigLayout('insets 0')) # chf_panel.setAlignmentX(Component.LEFT_ALIGNMENT) chf_files_label = JLabel('Hemisegment cells') chf_files_text = JTextArea( BobGui.archetype_to_str( self.exper.hseg_cell_files_cab().archetype)) chf_panel.add(chf_files_label, 'growx, wrap') chf_panel.add(chf_files_text, 'grow, wrap') chf_files_label = JLabel('Hemisegment binary image files') chf_files_text = JTextArea( BobGui.archetype_to_str(self.exper.hseg_bin_files_cab().archetype)) chf_panel.add(chf_files_label, 'growx, wrap') chf_panel.add(chf_files_text, 'grow, wrap') chf_files_label = JLabel('Other hemisegment files') chf_files_text = JTextArea( BobGui.archetype_to_str(self.exper.hseg_files_cab().archetype)) chf_panel.add(chf_files_label, 'growx, wrap') chf_panel.add(chf_files_text, 'grow') # chf_label = JLabel('Common Hemeisegment Files') # # chf_label.setAlignmentX(Component.LEFT_ALIGNMENT) # # chf_panel.add(chf_label, 'grow, wrap') # # chf_text_area = JTextArea(at_str) # chf_panel.add(chf_text_area, 'grow, push, span') return chf_panel @staticmethod def archetype_to_str(archetype): at_str = '' for val in archetype: at_str += str(val) + '\n' return at_str # def make_hseg_tree_panel(self, hseg_at_deviations) : def make_hseg_tree_panel(self): root = DefaultMutableTreeNode(self.exper.name) for hseg in self.exper.hsegs(): hseg_node = DefaultMutableTreeNode(hseg.name) root.add(hseg_node) hseg_at_deviations = self.exper.hseg_all_files_cab( ).archetype_deviations if len(hseg_at_deviations[hseg.name]) > 0: for definer, file_names in hseg_at_deviations[ hseg.name].items(): for file_name in file_names: node_str = definer + ': ' + file_name temp = DefaultMutableTreeNode(node_str) hseg_node.add(temp) hseg_tree = JTree(root) hseg_tree.setCellRenderer(BobPyTreeCellRenderer()) hseg_scroll_pane = JScrollPane() hseg_scroll_pane.getViewport().setView((hseg_tree)) return hseg_scroll_pane def text_field_al(self, e): self.dir_path = self.dir_text_field.getText() self.got_exper(self.dir_path) def choose_dir_al(self, e): dc = DirectoryChooser('Choose a bob_py experiment folder') self.dir_path = dc.getDirectory() self.dir_text_field.setText(self.dir_path) self.got_exper(self.dir_path) def close_al(self, e): self.dispatchEvent(WindowEvent(self, WindowEvent.WINDOW_CLOSING)) def got_exper(self, dir_path): self.exper = bob_py.Exper(dir_path) self.show_exper_info()
class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory): # # implement IBurpExtender # def registerExtenderCallbacks(self, callbacks): # PDB debugging: connect sys.stdout and sys.stderr to Burp # sys.stdout = callbacks.getStdout() # sys.stderr = callbacks.getStderr() # keep a reference to our callbacks object self._callbacks = callbacks # obtain an extension helpers object self._helpers = callbacks.getHelpers() # set extension name callbacks.setExtensionName("To Do") # create the log and a lock on which to synchronize when adding # log entries self._log = ArrayList() self._lock = Lock() # main split pane self._splitpane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) # Configuration Tab self.initConfigTab() # table of to do entries logTable = Table(self) scrollPane = JScrollPane(logTable) self._splitpane.setLeftComponent(scrollPane) # Config tab self.tabs = JTabbedPane() self._configuration = self._callbacks.createMessageEditor(self, False) self.tabs.addTab("Configuration", self._configuration.getComponent()) self._splitpane.setRightComponent(self.panel) # customize our UI components callbacks.customizeUiComponent(self._splitpane) callbacks.customizeUiComponent(logTable) callbacks.customizeUiComponent(scrollPane) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) # register ourselves as an HTTP listener callbacks.registerHttpListener(self) # initialize tabs self.initTabs() # Print thank you, contact info, etc print("Thank you for installing Burp To Do List") print("created by Chris Lockard") print("https://github.com/chrislockard/BurpToDoList") return # # implement ITab # def getTabCaption(self): return "To Do" def getUiComponent(self): return self._splitpane def initConfigTab(self): # Init configuration tab self.test = JLabel("Configuration") self.test.setBounds(10, 10, 140, 30) self.panel = JPanel() self.panel.setBounds(0, 0, 1000, 1000) self.panel.setLayout(None) self.panel.add(self.test) def initTabs(self): # Init ToDo List Tabs self.logTable = Table(self) tableWidth = self.logTable.getPreferredSize().width self.logTable.getColumn("Complete?").setPreferredWidth( Math.round(tableWidth / 10 * 1)) self.logTable.getColumn("Section").setPreferredWidth( Math.round(tableWidth / 10 * 3)) self.logTable.getColumn("Task").setPreferredWidth( Math.round(tableWidth / 10 * 3)) self.logTable.getColumn("Notes").setPreferredWidth( Math.round(tableWidth / 10 * 3)) self.tableSorter = TableRowSorter(self) self.logTable.setRowSorter(self.tableSorter) self._splitpane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._splitpane.setResizeWeight(1) self.scrollPane = JScrollPane(self.logTable) self._splitpane.setLeftComponent(self.scrollPane) self.scrollPane.getVerticalScrollBar().addAdjustmentListener( autoScrollListener(self)) def getRowCount(self): try: return self._log.size() except: return 0 def getColumnCount(self): return 4 def getColumnName(self, columnIndex): if columnIndex == 0: return "Complete?" if columnIndex == 1: return "Section" if columnIndex == 2: return "Task" if columnIndex == 3: return "Notes" return "" def getColumnClass(self, columnIndex): if columnIndex == 0: return checkbox if columnIndex == 1: return String if columnIndex == 2: return String if columnIndex == 3: return String return "" def getValueAt(self, rowIndex, columnIndex): logEntry = self._log.get(rowIndex) if columnIndex == 0: return self._callbacks.getToolName(logEntry._tool) if columnIndex == 1: return logEntry._url.toString() if columnIndex == 2: pass if columnIndex == 3: pass return ""
class BurpExtender(IBurpExtender, ITab, IHttpListener, AbstractTableModel, IContextMenuFactory): # # implement IBurpExtender # def registerExtenderCallbacks(self, callbacks): # set default values ## Pre-defined callbacks.setExtensionName("Body2Header") self._imenu_description = "Add this URL to the scope of Body2Header" self._remove_description = "Remove this URL from the scope" self._scopes = ArrayList() ## User-defined self._header_name_default = "X-CSRF-Token" self._value_source_regex_default = re.compile("<meta name=\"csrf-token\" content=\"(.*?)\">", re.MULTILINE) self._csrf_token = "" # store callbacks set an alias for stdout and helpers self._callbacks = callbacks self._out = callbacks.getStdout() self._helpers = callbacks.getHelpers() # initialize GUI callbacks.registerContextMenuFactory(self) self.initializeGUI() callbacks.addSuiteTab(self) # register ourselves as an HTTP listener callbacks.registerHttpListener(self) def log(self, message): self._out.write("[{0}] {1}\n".format(datetime.now().isoformat(),message)) # # implement IHttpListener # def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): # only process if tools are in the setting if not self._checkboxes[toolFlag].isSelected(): return None request_url = self._helpers.analyzeRequest(messageInfo).getUrl() if not messageIsRequest: body = self._helpers.bytesToString(messageInfo.getResponse()).encode('utf-8') for scope in self._scopes: if not scope.isMatch(request_url): continue csrf_token_b = scope.value_regex.search(body) if csrf_token_b != None: scope.stored_value = csrf_token_b.group(1) self.log("New value for {0}: {1}".format(scope.header_name, scope.stored_value)) # only process requests/responses in the scope for scope in self._scopes: if not scope.isMatch(request_url): continue # Intercept and modify the request if messageIsRequest: request = messageInfo.getRequest() requestInfo = self._helpers.analyzeRequest(request) # update headers headers = requestInfo.getHeaders() headers = [h for h in headers if not h.startswith(scope.header_name+':')] if scope.header_name != "" and scope.stored_value != "": headers.append(scope.header_name + ': ' + scope.stored_value) self.log("{0} was added to the current request.".format(scope.header_name)) # fetching body to rebubild the request body = request[requestInfo.getBodyOffset():] updatedRequest = self._helpers.buildHttpMessage(headers, body) messageInfo.setRequest(updatedRequest) # Utilities def updateTokenSourceRegex(self, e): row = self._url_table.getSelectedRow() if row == -1: return self._scopes[row].value_regex = re.compile(self._form_value_regex.getText(), re.MULTILINE) self._label_value_regex_now_2.setText(self._scopes[row].value_regex.pattern) self.fireTableRowsUpdated(row, row) def updateHeaderName(self, e): row = self._url_table.getSelectedRow() if row == -1: return self._scopes[row].header_name = self._form_header.getText() self._label_header_now_2.setText(self._scopes[row].header_name) self.fireTableRowsUpdated(row, row) def addURLDirectly(self, e): row = self._scopes.size() self._scopes.add(ScopeInfo(self._form_add_url.getText(), self._value_source_regex_default, url_regex = re.compile(self._form_add_url.getText(), re.MULTILINE), header_name = self._header_name_default)) self._form_add_url.setText("") self.fireTableRowsInserted(row, row) def removeFromScope(self, invocation): index_to_delete = self._url_table.getSelectedRow() self._scopes.pop(index_to_delete) self.fireTableRowsDeleted(index_to_delete, index_to_delete) def addToScope(self, invocation): messagesInfo = self._add_invocation.getSelectedMessages() row = self._scopes.size() for messageInfo in messagesInfo: self._scopes.add(ScopeInfo(self._helpers.analyzeRequest(messageInfo).getUrl(), self._value_source_regex_default, header_name = self._header_name_default)) self.fireTableRowsInserted(row, row) # # implement IContextMenuFactory # def createMenuItems(self, invocation): self._add_invocation = invocation self._imenu = JMenuItem(self._imenu_description, actionPerformed=self.addToScope) return [self._imenu] # # extend AbstractTableModel # def getRowCount(self): try: return self._scopes.size() except: return 0 def getColumnCount(self): return 3 def getColumnName(self, columnIndex): if columnIndex == 0: return "URL Regex" if columnIndex == 1: return "Value Regex" if columnIndex == 2: return "Header Name" return "" def getValueAt(self, rowIndex, columnIndex): if columnIndex == 0: return self._scopes[rowIndex].url_regex.pattern if columnIndex == 1: return self._scopes[rowIndex].value_regex.pattern if columnIndex == 2: return self._scopes[rowIndex].header_name return "" # # implement ITab # def getTabCaption(self): return "Body2Header" def getUiComponent(self): return self._splitpane # # GUI settings # def initializeGUI(self): # table panel of scope entries self._url_table = Table(self) table_popup = JPopupMenu(); remove_item_menu = JMenuItem(self._remove_description, actionPerformed=self.removeFromScope) table_popup.add(remove_item_menu) self._url_table.setComponentPopupMenu(table_popup) self._url_table.addMouseListener(TableMouseListener(self._url_table)) scrollPane = JScrollPane(self._url_table) # setting panel ## locate checkboxes ### for constants, see: https://portswigger.net/burp/extender/api/constant-values.html#burp.IBurpExtenderCallbacks.TOOL_PROXY self._checkboxes = { 2: JCheckBox('Target'), 4: JCheckBox('Proxy'), 8: JCheckBox('Spider'), 16: JCheckBox('Scanner'), 32: JCheckBox('Intruder'), 64: JCheckBox('Repeater'), 128: JCheckBox('Sequencer'), 1024: JCheckBox('Extender') } checkboxes_components = {0: dict(zip(range(1,len(self._checkboxes) + 1), self._checkboxes.values()))} self._label_value_regex_now_1 = JLabel("(1) Regex for the value to store: ") self._label_value_regex_now_2 = JLabel("") self._label_value_regex = JLabel("(1) New regex:") self._form_value_regex = JTextField("", 64) self._button_value_regex = JButton('Update', actionPerformed=self.updateTokenSourceRegex) self._label_header_now_1 = JLabel("(2) Header for sending the value: ") self._label_header_now_2 = JLabel("") self._label_header = JLabel("(2) New header key: ") self._form_header = JTextField("", 64) self._button_header = JButton('Update', actionPerformed=self.updateHeaderName) self._label_add_url = JLabel("Add this URL: ") self._form_add_url = JTextField("", 64) self._button_add_url = JButton('Add', actionPerformed=self.addURLDirectly) ## logate regex settings ui_components_for_settings_pane = { 0: { 0: JLabel("Local Settings:") }, 1: { 0: self._label_value_regex_now_1, 1: self._label_value_regex_now_2 }, 2: { 0: self._label_value_regex, 1: self._form_value_regex, 2: self._button_value_regex}, 3: { 0: self._label_header_now_1, 1: self._label_header_now_2 }, 4: { 0: self._label_header, 1: self._form_header, 2: self._button_header}, 5: { 0: {'item': JSeparator(JSeparator.HORIZONTAL), 'width': 3, }}, 6: { 0: JLabel("General Settings:") }, 7: { 0: self._label_add_url, 1: self._form_add_url, 2: self._button_add_url}, 8: { 0: JLabel("Use this extender in:"), 1: {'item': self.compose_ui(checkboxes_components), 'width': 3} } } # build a split panel & set UI component self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) self._splitpane.setResizeWeight(0.85) self._splitpane.setLeftComponent(scrollPane) self._splitpane.setRightComponent(self.compose_ui(ui_components_for_settings_pane)) self._callbacks.customizeUiComponent(self._splitpane) def compose_ui(self, components): panel = JPanel() panel.setLayout(GridBagLayout()) constraints= GridBagConstraints() constraints.fill = GridBagConstraints.HORIZONTAL constraints.insets = Insets(2, 1, 2, 1) for i in components: for j in components[i]: constraints.gridy, constraints.gridx = i, j constraints.gridwidth = components[i][j]['width'] if type(components[i][j]) == dict and 'width' in components[i][j] else 1 constraints.gridheight = components[i][j]['height'] if type(components[i][j]) == dict and 'height' in components[i][j] else 1 item = components[i][j]['item'] if type(components[i][j]) == dict and 'item' in components[i][j] else components[i][j] panel.add(item, constraints) return panel
class AtfAreaView(JPanel): ''' Initializes the ATF (edit/model) view and sets its layout. ''' def __init__(self, controller): ''' Creates default empty text area in a panel for ATF edition. It has syntax highlighting based on the ATF parser (pyoracc). It also highlights line numbers where there are validations errors returned by the ORACC server. ''' # Give reference to controller to delegate action response self.controller = controller # Make text area occupy all available space and resize with parent # window self.setLayout(BorderLayout()) # Short hand for edit area and line numbers area self.edit_area = self.controller.edit_area self.line_numbers_area = self.controller.line_numbers_area # Create secondary text area for split view self.secondary_area = self.controller.secondary_area self.secondary_line_numbers = self.controller.secondary_line_numbers # Set undo/redo manager to edit area self.undo_manager = UndoManager() self.undo_manager.limit = 3000 self.edit_listener = AtfUndoableEditListener(self.undo_manager) self.edit_area.getDocument().addUndoableEditListener( self.edit_listener) # Sort out layout by synch-ing line numbers and text area and putting # only the text area in a scroll pane as indicated in the # TextLineNumber tutorial. self.edit_area.setPreferredSize(Dimension(1, 500)) self.container = JScrollPane(self.edit_area) self.container.setRowHeaderView(self.line_numbers_area) self.add(self.container, BorderLayout.CENTER) # Key listener that triggers syntax highlighting, etc. upon key release self.edit_area.addKeyListener(AtfAreaKeyListener(self.controller)) # Also needed in secondary area: self.secondary_area.addKeyListener(AtfAreaKeyListener(self.controller)) def toggle_split(self, split_orientation=None): ''' Clear ATF edit area and repaint chosen layout (splitscreen/scrollpane). ''' # Remove all existent components in parent JPanel self.removeAll() # Check what editor view to toggle self.setup_edit_area(split_orientation) # Revalitate is needed in order to repaint the components self.revalidate() self.repaint() def setup_edit_area(self, split_orientation=None): ''' Check if the ATF text area is being displayed in a split editor. If so, resets to normal JScrollPane. If not, splits the screen. ''' if isinstance(self.container, JSplitPane): # If Nammu is already displaying a split pane, reset to original # setup self.container = JScrollPane(self.edit_area) self.container.setRowHeaderView(self.line_numbers_area) self.container.setVisible(True) self.add(self.container, BorderLayout.CENTER) else: # If there is not a split pane, create both panels and setup view main_editor = JScrollPane(self.edit_area) main_editor.setRowHeaderView(self.line_numbers_area) secondary_editor = JScrollPane(self.secondary_area) secondary_editor.setRowHeaderView(self.secondary_line_numbers) self.container = JSplitPane(split_orientation, main_editor, secondary_editor) self.container.setDividerSize(5) self.container.setVisible(True) self.container.setDividerLocation(0.5) self.container.setResizeWeight(0.5) self.add(self.container, BorderLayout.CENTER)
class AtfAreaView(JPanel): ''' Initializes the ATF (edit/model) view and sets its layout. ''' def __init__(self, controller): ''' Creates default empty text area in a panel for ATF edition. It has syntax highlighting based on the ATF parser (pyoracc). It also highlights line numbers where there are validations errors returned by the ORACC server. ''' # Give reference to controller to delegate action response self.controller = controller # Make text area occupy all available space and resize with parent # window self.setLayout(BorderLayout()) # Short hand for edit area and line numbers area self.edit_area = self.controller.edit_area self.line_numbers_area = self.controller.line_numbers_area # Create secondary text area for split view self.secondary_area = self.controller.secondary_area self.secondary_line_numbers = self.controller.secondary_line_numbers # Set undo/redo manager to edit area self.undo_manager = UndoManager() self.undo_manager.limit = 3000 self.edit_listener = AtfUndoableEditListener(self.undo_manager) self.edit_area.getDocument().addUndoableEditListener( self.edit_listener) # Sort out layout by synch-ing line numbers and text area and putting # only the text area in a scroll pane as indicated in the # TextLineNumber tutorial. self.edit_area.setPreferredSize(Dimension(1, 500)) self.container = JScrollPane(self.edit_area) self.container.setRowHeaderView(self.line_numbers_area) self.add(self.container, BorderLayout.CENTER) self.vert_scroll = self.container.getVerticalScrollBar() self.vert_scroll.addAdjustmentListener(atfAreaAdjustmentListener(self)) # Key listener that triggers syntax highlighting, etc. upon key release self.edit_area.addKeyListener(AtfAreaKeyListener(self)) # Also needed in secondary area: self.secondary_area.addKeyListener(AtfAreaKeyListener(self)) # Add a document listener to track changes to files docListener = atfAreaDocumentListener(self) self.edit_area.getDocument().addDocumentListener(docListener) # instance variable to store a record of the text contents prior to the # most recent change. Needed so that the different listeners can access # this to handle error line updating. self.oldtext = '' def toggle_split(self, split_orientation=None): ''' Clear ATF edit area and repaint chosen layout (splitscreen/scrollpane). ''' # Remove all existent components in parent JPanel self.removeAll() # Check what editor view to toggle self.setup_edit_area(split_orientation) # Revalitate is needed in order to repaint the components self.revalidate() self.repaint() def setup_edit_area(self, split_orientation=None): ''' Check if the ATF text area is being displayed in a split editor. If so, resets to normal JScrollPane. If not, splits the screen. ''' if isinstance(self.container, JSplitPane): # If Nammu is already displaying a split pane, reset to original # setup self.container = JScrollPane(self.edit_area) self.container.setRowHeaderView(self.line_numbers_area) self.container.setVisible(True) self.add(self.container, BorderLayout.CENTER) else: # If there is not a split pane, create both panels and setup view main_editor = JScrollPane(self.edit_area) main_editor.setRowHeaderView(self.line_numbers_area) secondary_editor = JScrollPane(self.secondary_area) secondary_editor.setRowHeaderView(self.secondary_line_numbers) self.container = JSplitPane(split_orientation, main_editor, secondary_editor) self.container.setDividerSize(5) self.container.setVisible(True) self.container.setDividerLocation(0.5) self.container.setResizeWeight(0.5) self.add(self.container, BorderLayout.CENTER) def get_viewport_carets(self): ''' Get the top left and bottom left caret position of the current viewport ''' extent = self.container.getViewport().getExtentSize() top_left_position = self.container.getViewport().getViewPosition() top_left_char = self.edit_area.viewToModel(top_left_position) bottom_left_position = Point(top_left_position.x, top_left_position.y + extent.height) bottom_left_char = self.edit_area.viewToModel(bottom_left_position) # Something has gone wrong. Assume that top_left should be at the start # of the file if top_left_char >= bottom_left_char: top_left_char = 0 # Get the text in the full edit area text = self.controller.edit_area.getText() # Pad the top of the viewport to capture up to the nearest header and # the bottom by 2 lines top_ch = self.controller.pad_top_viewport_caret(top_left_char, text) bottom_ch = self.controller.pad_bottom_viewport_caret(bottom_left_char, text) return top_ch, bottom_ch def refresh(self): ''' Restyle edit area using user selected appearance settings. ''' config = self.controller.controller.config # Create a new font with the new size font = set_font(config['edit_area_style']['fontsize']['user']) # Update the sytnax highlighter font params, so our changes are not # superceded self.controller.syntax_highlighter.font = font self.controller.syntax_highlighter.setup_attribs() attrs = self.controller.edit_area.getInputAttributes() StyleConstants.setFontSize(attrs, font.getSize()) # Get the Styledoc so we can update it doc = self.controller.edit_area.getStyledDocument() # Apply the new fontsize to the whole document doc.setCharacterAttributes(0, doc.getLength() + 1, attrs, False)
def registerExtenderCallbacks(self, callbacks): # keep a reference to our Burp callbacks object self._callbacks = callbacks # obtain an Burp extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("AuthMatrix - v0.5.2") # DB that holds everything users, roles, and messages self._db = MatrixDB() # For saving/loading config self._fc = JFileChooser() # Used by ActionListeners selfExtender = self self._selectedColumn = -1 self._selectedRow = -1 # Table of User entries self._userTable = UserTable(self, model = UserTableModel(self._db)) roleScrollPane = JScrollPane(self._userTable) self._userTable.redrawTable() # Table of Request (AKA Message) entries self._messageTable = MessageTable(self, model = MessageTableModel(self._db)) messageScrollPane = JScrollPane(self._messageTable) self._messageTable.redrawTable() # Semi-Generic Popup stuff def addPopup(component, popup): class genericMouseListener(MouseAdapter): def mousePressed(self, e): if e.isPopupTrigger(): self.showMenu(e) def mouseReleased(self, e): if e.isPopupTrigger(): self.showMenu(e) def showMenu(self, e): if type(component) is JTableHeader: table = component.getTable() column = component.columnAtPoint(e.getPoint()) if type(table) is MessageTable and column >= selfExtender._db.STATIC_MESSAGE_TABLE_COLUMN_COUNT or type(table) is UserTable and column >= selfExtender._db.STATIC_USER_TABLE_COLUMN_COUNT: selfExtender._selectedColumn = column else: return else: selfExtender._selectedRow = component.rowAtPoint(e.getPoint()) popup.show(e.getComponent(), e.getX(), e.getY()) component.addMouseListener(genericMouseListener()) class actionRunMessage(ActionListener): def actionPerformed(self,e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows(): indexes = [selfExtender._db.getMessageByRow(selfExtender._selectedRow)._index] else: indexes = [selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows()] t = Thread(target=selfExtender.runMessagesThread, args = [indexes]) t.start() selfExtender._selectedColumn = -1 # Redrawing the table happens in colorcode within the thread class actionRemoveMessage(ActionListener): def actionPerformed(self,e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows(): indexes = [selfExtender._db.getMessageByRow(selfExtender._selectedRow)._index] else: indexes = [selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows()] for i in indexes: selfExtender._db.deleteMessage(i) selfExtender._selectedColumn = -1 selfExtender._messageTable.redrawTable() class actionRemoveUser(ActionListener): def actionPerformed(self,e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._userTable.getSelectedRows(): indexes = [selfExtender._db.getUserByRow(selfExtender._selectedRow)._index] else: indexes = [selfExtender._db.getUserByRow(rowNum)._index for rowNum in selfExtender._userTable.getSelectedRows()] for i in indexes: selfExtender._db.deleteUser(i) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() # TODO combine these next two classes class actionRemoveRoleHeaderFromMessageTable(ActionListener): def actionPerformed(self,e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole(selfExtender._db.getRoleByMessageTableColumn(selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() class actionRemoveRoleHeaderFromUserTable(ActionListener): def actionPerformed(self,e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole(selfExtender._db.getRoleByUserTableColumn(selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() # Message Table popups messagePopup = JPopupMenu() addPopup(self._messageTable,messagePopup) messageRun = JMenuItem("Run Request(s)") messageRun.addActionListener(actionRunMessage()) messagePopup.add(messageRun) messageRemove = JMenuItem("Remove Request(s)") messageRemove.addActionListener(actionRemoveMessage()) messagePopup.add(messageRemove) messageHeaderPopup = JPopupMenu() addPopup(self._messageTable.getTableHeader(),messageHeaderPopup) roleRemoveFromMessageTable = JMenuItem("Remove Role") roleRemoveFromMessageTable.addActionListener(actionRemoveRoleHeaderFromMessageTable()) messageHeaderPopup.add(roleRemoveFromMessageTable) # User Table popup userPopup = JPopupMenu() addPopup(self._userTable,userPopup) userRemove = JMenuItem("Remove Users(s)") userRemove.addActionListener(actionRemoveUser()) userPopup.add(userRemove) userHeaderPopup = JPopupMenu() addPopup(self._userTable.getTableHeader(),userHeaderPopup) roleRemoveFromUserTable = JMenuItem("Remove Role") roleRemoveFromUserTable.addActionListener(actionRemoveRoleHeaderFromUserTable()) userHeaderPopup.add(roleRemoveFromUserTable) # Top pane topPane = JSplitPane(JSplitPane.VERTICAL_SPLIT,roleScrollPane,messageScrollPane) # request tabs added to this tab on click in message table self._tabs = JTabbedPane() # Button pannel buttons = JPanel() runButton = JButton("Run", actionPerformed=self.runClick) newUserButton = JButton("New User", actionPerformed=self.getInputUserClick) newRoleButton = JButton("New Role", actionPerformed=self.getInputRoleClick) #debugButton = JButton("Debug", actionPerformed=self.printDB) saveButton = JButton("Save", actionPerformed=self.saveClick) loadButton = JButton("Load", actionPerformed=self.loadClick) clearButton = JButton("Clear", actionPerformed=self.clearClick) buttons.add(runButton) buttons.add(newUserButton) buttons.add(newRoleButton) #buttons.add(debugButton) buttons.add(saveButton) buttons.add(loadButton) buttons.add(clearButton) bottomPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, self._tabs, buttons) # Main Pane self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT, topPane, bottomPane) # customize our UI components callbacks.customizeUiComponent(self._splitpane) callbacks.customizeUiComponent(topPane) callbacks.customizeUiComponent(bottomPane) callbacks.customizeUiComponent(messageScrollPane) callbacks.customizeUiComponent(roleScrollPane) callbacks.customizeUiComponent(self._messageTable) callbacks.customizeUiComponent(self._userTable) callbacks.customizeUiComponent(self._tabs) callbacks.customizeUiComponent(buttons) self._splitpane.setResizeWeight(0.5) topPane.setResizeWeight(0.3) bottomPane.setResizeWeight(0.95) # Handles checkbox color coding # Must be bellow the customizeUiComponent calls self._messageTable.setDefaultRenderer(Boolean, SuccessBooleanRenderer(self._db)) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) # register SendTo option callbacks.registerContextMenuFactory(self) return
class BurpExtender(IBurpExtender, ITab, IMessageEditorController, IContextMenuFactory): # # implement IBurpExtender # def registerExtenderCallbacks(self, callbacks): # keep a reference to our Burp callbacks object self._callbacks = callbacks # obtain an Burp extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("AuthMatrix - v0.5.2") # DB that holds everything users, roles, and messages self._db = MatrixDB() # For saving/loading config self._fc = JFileChooser() # Used by ActionListeners selfExtender = self self._selectedColumn = -1 self._selectedRow = -1 # Table of User entries self._userTable = UserTable(self, model = UserTableModel(self._db)) roleScrollPane = JScrollPane(self._userTable) self._userTable.redrawTable() # Table of Request (AKA Message) entries self._messageTable = MessageTable(self, model = MessageTableModel(self._db)) messageScrollPane = JScrollPane(self._messageTable) self._messageTable.redrawTable() # Semi-Generic Popup stuff def addPopup(component, popup): class genericMouseListener(MouseAdapter): def mousePressed(self, e): if e.isPopupTrigger(): self.showMenu(e) def mouseReleased(self, e): if e.isPopupTrigger(): self.showMenu(e) def showMenu(self, e): if type(component) is JTableHeader: table = component.getTable() column = component.columnAtPoint(e.getPoint()) if type(table) is MessageTable and column >= selfExtender._db.STATIC_MESSAGE_TABLE_COLUMN_COUNT or type(table) is UserTable and column >= selfExtender._db.STATIC_USER_TABLE_COLUMN_COUNT: selfExtender._selectedColumn = column else: return else: selfExtender._selectedRow = component.rowAtPoint(e.getPoint()) popup.show(e.getComponent(), e.getX(), e.getY()) component.addMouseListener(genericMouseListener()) class actionRunMessage(ActionListener): def actionPerformed(self,e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows(): indexes = [selfExtender._db.getMessageByRow(selfExtender._selectedRow)._index] else: indexes = [selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows()] t = Thread(target=selfExtender.runMessagesThread, args = [indexes]) t.start() selfExtender._selectedColumn = -1 # Redrawing the table happens in colorcode within the thread class actionRemoveMessage(ActionListener): def actionPerformed(self,e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows(): indexes = [selfExtender._db.getMessageByRow(selfExtender._selectedRow)._index] else: indexes = [selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows()] for i in indexes: selfExtender._db.deleteMessage(i) selfExtender._selectedColumn = -1 selfExtender._messageTable.redrawTable() class actionRemoveUser(ActionListener): def actionPerformed(self,e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._userTable.getSelectedRows(): indexes = [selfExtender._db.getUserByRow(selfExtender._selectedRow)._index] else: indexes = [selfExtender._db.getUserByRow(rowNum)._index for rowNum in selfExtender._userTable.getSelectedRows()] for i in indexes: selfExtender._db.deleteUser(i) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() # TODO combine these next two classes class actionRemoveRoleHeaderFromMessageTable(ActionListener): def actionPerformed(self,e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole(selfExtender._db.getRoleByMessageTableColumn(selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() class actionRemoveRoleHeaderFromUserTable(ActionListener): def actionPerformed(self,e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole(selfExtender._db.getRoleByUserTableColumn(selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() # Message Table popups messagePopup = JPopupMenu() addPopup(self._messageTable,messagePopup) messageRun = JMenuItem("Run Request(s)") messageRun.addActionListener(actionRunMessage()) messagePopup.add(messageRun) messageRemove = JMenuItem("Remove Request(s)") messageRemove.addActionListener(actionRemoveMessage()) messagePopup.add(messageRemove) messageHeaderPopup = JPopupMenu() addPopup(self._messageTable.getTableHeader(),messageHeaderPopup) roleRemoveFromMessageTable = JMenuItem("Remove Role") roleRemoveFromMessageTable.addActionListener(actionRemoveRoleHeaderFromMessageTable()) messageHeaderPopup.add(roleRemoveFromMessageTable) # User Table popup userPopup = JPopupMenu() addPopup(self._userTable,userPopup) userRemove = JMenuItem("Remove Users(s)") userRemove.addActionListener(actionRemoveUser()) userPopup.add(userRemove) userHeaderPopup = JPopupMenu() addPopup(self._userTable.getTableHeader(),userHeaderPopup) roleRemoveFromUserTable = JMenuItem("Remove Role") roleRemoveFromUserTable.addActionListener(actionRemoveRoleHeaderFromUserTable()) userHeaderPopup.add(roleRemoveFromUserTable) # Top pane topPane = JSplitPane(JSplitPane.VERTICAL_SPLIT,roleScrollPane,messageScrollPane) # request tabs added to this tab on click in message table self._tabs = JTabbedPane() # Button pannel buttons = JPanel() runButton = JButton("Run", actionPerformed=self.runClick) newUserButton = JButton("New User", actionPerformed=self.getInputUserClick) newRoleButton = JButton("New Role", actionPerformed=self.getInputRoleClick) #debugButton = JButton("Debug", actionPerformed=self.printDB) saveButton = JButton("Save", actionPerformed=self.saveClick) loadButton = JButton("Load", actionPerformed=self.loadClick) clearButton = JButton("Clear", actionPerformed=self.clearClick) buttons.add(runButton) buttons.add(newUserButton) buttons.add(newRoleButton) #buttons.add(debugButton) buttons.add(saveButton) buttons.add(loadButton) buttons.add(clearButton) bottomPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, self._tabs, buttons) # Main Pane self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT, topPane, bottomPane) # customize our UI components callbacks.customizeUiComponent(self._splitpane) callbacks.customizeUiComponent(topPane) callbacks.customizeUiComponent(bottomPane) callbacks.customizeUiComponent(messageScrollPane) callbacks.customizeUiComponent(roleScrollPane) callbacks.customizeUiComponent(self._messageTable) callbacks.customizeUiComponent(self._userTable) callbacks.customizeUiComponent(self._tabs) callbacks.customizeUiComponent(buttons) self._splitpane.setResizeWeight(0.5) topPane.setResizeWeight(0.3) bottomPane.setResizeWeight(0.95) # Handles checkbox color coding # Must be bellow the customizeUiComponent calls self._messageTable.setDefaultRenderer(Boolean, SuccessBooleanRenderer(self._db)) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) # register SendTo option callbacks.registerContextMenuFactory(self) return ## ## implement ITab ## def getTabCaption(self): return "AuthMatrix" def getUiComponent(self): return self._splitpane ## ## Creates the sendto tab in other areas of Burp ## def createMenuItems(self, invocation): def addRequestsToTab(e): for messageInfo in messages: # saveBuffers is required since modifying the original from its source changes the saved objects, its not a copy messageIndex = self._db.createNewMessage(self._callbacks.saveBuffersToTempFiles(messageInfo), self._helpers.analyzeRequest(messageInfo).getUrl()) #self._messageTable.getModel().addRow(row) self._messageTable.redrawTable() ret = [] messages = invocation.getSelectedMessages() # Check if the messages in the target tree have a response valid = True if invocation.getInvocationContext() == invocation.CONTEXT_TARGET_SITE_MAP_TREE: for selected in messages: if not selected.getResponse(): valid = False if valid: menuItem = JMenuItem("Send request(s) to AuthMatrix"); menuItem.addActionListener(addRequestsToTab) ret.append(menuItem) return ret ## ## implement IMessageEditorController ## this allows our request/response viewers to obtain details about the messages being displayed ## def getHttpService(self): return self._currentlyDisplayedItem.getHttpService() def getRequest(self): return self._currentlyDisplayedItem.getRequest() def getResponse(self): return self._currentlyDisplayedItem.getResponse() ## ## Actions on Bottom Row Button Clicks ## def printDB(self, e): out = "" for a in self._db.arrayOfUsers: out += str(a._index)+" "+a._name+" : "+str(a._roles)+"\n" for b in self._db.arrayOfMessages: out += str(b._index)+" "+str(b._roles)+"\n" JOptionPane.showMessageDialog(self._splitpane,out) def getInputUserClick(self, e): newUser = JOptionPane.showInputDialog(self._splitpane,"Enter New User:"******"Enter New Role:") if not newRole is None: self._db.getOrCreateRole(newRole) self._userTable.redrawTable() self._messageTable.redrawTable() def saveClick(self, e): returnVal = self._fc.showSaveDialog(self._splitpane) if returnVal == JFileChooser.APPROVE_OPTION: f = self._fc.getSelectedFile() if f.exists(): result = JOptionPane.showConfirmDialog(self._splitpane, "The file exists, overwrite?", "Existing File", JOptionPane.YES_NO_OPTION) if result != JOptionPane.YES_OPTION: return fileName = f.getPath() outs = ObjectOutputStream(FileOutputStream(fileName)) outs.writeObject(self._db.getSaveableObject()) outs.close() def loadClick(self,e): returnVal = self._fc.showOpenDialog(self._splitpane) if returnVal == JFileChooser.APPROVE_OPTION: warning = """ CAUTION: Loading a saved configuration deserializes data. This action may pose a security threat to the application. Only proceed when the source and contents of this file is trusted. Load Selected File? """ result = JOptionPane.showOptionDialog(self._splitpane, warning, "Caution", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, None, ["OK", "Cancel"],"OK") if result != JOptionPane.YES_OPTION: return f = self._fc.getSelectedFile() fileName = f.getPath() ins = ObjectInputStream(FileInputStream(fileName)) dbData=ins.readObject() ins.close() self._db.load(dbData,self) self._userTable.redrawTable() self._messageTable.redrawTable() def clearClick(self,e): result = JOptionPane.showConfirmDialog(self._splitpane, "Clear AuthMatrix Configuration?", "Clear Config", JOptionPane.YES_NO_OPTION) if result == JOptionPane.YES_OPTION: self._db.clear() self._tabs.removeAll() self._userTable.redrawTable() self._messageTable.redrawTable() def runClick(self,e): t = Thread(target=self.runMessagesThread) self._tabs.removeAll() t.start() def changeDomainPopup(self, oldDomain, index): hostField = JTextField(25) checkbox = JCheckBox() domainPanel = JPanel(GridLayout(0,1)) domainPanel.add(JLabel("Request %s: Domain %s inaccessible. Enter new domain." % (str(index),oldDomain))) firstline = JPanel() firstline.add(JLabel("Host:")) firstline.add(hostField) domainPanel.add(firstline) secondline = JPanel() secondline.add(JLabel("Replace domain for all requests?")) secondline.add(checkbox) domainPanel.add(secondline) result = JOptionPane.showConfirmDialog( self._splitpane,domainPanel, "Domain Inaccessible", JOptionPane.OK_CANCEL_OPTION) cancelled = (result == JOptionPane.CANCEL_OPTION) if cancelled: return (False, None, False) return (True, hostField.getText(), checkbox.isSelected()) ## ## Methods for running messages and analyzing results ## def runMessagesThread(self, messageIndexes=None): self._db.lock.acquire() try: indexes = messageIndexes if not indexes: indexes = self._db.getActiveMessageIndexes() self.clearColorResults(indexes) for index in indexes: self.runMessage(index) #self.colorCodeResults() except: traceback.print_exc(file=self._callbacks.getStderr()) finally: self._db.lock.release() self.colorCodeResults() # Replaces headers/cookies with user's token def getNewHeader(self, requestInfo, userEntry): headers = requestInfo.getHeaders() if userEntry.isCookie(): cookieHeader = "Cookie:" newheader = cookieHeader previousCookies = [] # note: getHeaders has to be called again here cuz of java references for header in requestInfo.getHeaders(): # Find and remove existing cookie header if str(header).startswith(cookieHeader): previousCookies = str(header)[len(cookieHeader):].replace(" ","").split(";") headers.remove(header) newCookies = userEntry._token.replace(" ","").split(";") newCookieVariableNames = [] for newCookie in newCookies: # If its a valid cookie equalsToken = newCookie.find("=") if equalsToken >= 0: newCookieVariableNames.append(newCookie[0:equalsToken+1]) # Add all the old unchanged cookies for previousCookie in previousCookies: # If its a valid cookie equalsToken = previousCookie.find("=") if equalsToken >= 0: if previousCookie[0:equalsToken+1] not in newCookieVariableNames: newCookies.append(previousCookie) # Remove whitespace newCookies = [x for x in newCookies if x] newheader = cookieHeader+" "+";".join(newCookies) else: # TODO: Support multiple headers with a newline somehow newheader = userEntry._token # Remove previous HTTP Header colon = newheader.find(":") if colon >= 0: # getHeaders has to be called again here cuz of java references for header in requestInfo.getHeaders(): # If the header already exists, remove it if str(header).startswith(newheader[0:colon+1]): headers.remove(header) headers.add(newheader) return headers def runMessage(self, messageIndex): messageEntry = self._db.arrayOfMessages[messageIndex] # Clear Previous Results: messageEntry._roleResults = {} messageEntry._userRuns = {} messageInfo = messageEntry._requestResponse requestInfo = self._helpers.analyzeRequest(messageInfo) reqBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] for userIndex in self._db.getActiveUserIndexes(): userEntry = self._db.arrayOfUsers[userIndex] headers = self.getNewHeader(requestInfo, userEntry) # Add static CSRF token if available # TODO: Kinda hacky, but for now it will add the token as long as there is some content in the post body # Even if its a GET request. This screws up when original requests have no body though... oh well... newBody = reqBody if userEntry._staticcsrf and len(reqBody): delimeter = userEntry._staticcsrf.find("=") if delimeter >= 0: csrfname = userEntry._staticcsrf[0:delimeter] csrfvalue = userEntry._staticcsrf[delimeter+1:] params = requestInfo.getParameters() for param in params: if str(param.getName())==csrfname: # Handle CSRF Tokens in Body if param.getType() == 1: newBody = reqBody[0:param.getValueStart()-requestInfo.getBodyOffset()] + StringUtil.toBytes(csrfvalue) + reqBody[param.getValueEnd()-requestInfo.getBodyOffset():] # Handle CSRF Tokens in Cookies (for Cookie==Body mitigation technique): if param.getType() == 2: # TODO: required moving above portion to a function # TODO: also need to think about when cookie name != postarg name print "Cookie CSRF Tokens are not currently supported" if newBody == reqBody: newBody = reqBody+StringUtil.toBytes("&"+userEntry._staticcsrf) # Construct and send a message with the new headers message = self._helpers.buildHttpMessage(headers, newBody) requestResponse = self._callbacks.makeHttpRequest(messageInfo.getHttpService(),message) messageEntry.addRunByUserIndex(userIndex, self._callbacks.saveBuffersToTempFiles(requestResponse)) # Grab all active roleIndexes that should succeed activeSuccessRoles = [index for index in messageEntry._roles.keys() if messageEntry._roles[index] and not self._db.arrayOfRoles[index].isDeleted()] # Check Role Results of message for roleIndex in self._db.getActiveRoleIndexes(): success = self.checkResult(messageEntry, roleIndex, activeSuccessRoles) messageEntry.setRoleResultByRoleIndex(roleIndex, success) def colorCodeResults(self): self._messageTable.redrawTable() def clearColorResults(self, messageIndexArray = None): if not messageIndexArray: messageIndexes = self._db.getActiveMessageIndexes() else: messageIndexes = messageIndexArray for messageIndex in messageIndexes: messageEntry = self._db.arrayOfMessages[messageIndex] messageEntry._roleResults = {} messageEntry._userRuns = {} self._messageTable.redrawTable() def checkResult(self, messageEntry, roleIndex, activeSuccessRoles): for userIndex in self._db.getActiveUserIndexes(): userEntry = self._db.arrayOfUsers[userIndex] ignoreUser = False # if user is not in this role, ignore it if not userEntry._roles[roleIndex]: ignoreUser = True # If user is in any other role that should succeed, then ignore it for index in userEntry._roles.keys(): if not index == roleIndex and userEntry._roles[index] and index in activeSuccessRoles: ignoreUser = True if not ignoreUser: shouldSucceed = roleIndex in activeSuccessRoles requestResponse = messageEntry._userRuns[userEntry._index] resp = StringUtil.fromBytes(requestResponse.getResponse()) found = re.search(messageEntry._successRegex, resp, re.DOTALL) succeeds = found if shouldSucceed else not found if not succeeds: return False return True
class BurpExtender(IBurpExtender, IContextMenuFactory, ActionListener, IMessageEditorController, ITab, ITextEditor, IHttpService, IScanIssue, IHttpRequestResponseWithMarkers): def __init__(self): self.menuItem = JMenuItem('Generate Finding') self.menuItem.addActionListener(self) # implement IBurpExtender def registerExtenderCallbacks(self, callbacks): # keep a reference to our callbacks object (Burp Extensibility Feature) self._callbacks = callbacks self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("Generate Finding") callbacks.registerContextMenuFactory(self) # -- Request Response Viewers -- # # create the lower half for the Request Response tabs... # Request and response from selection self._tabbedPane = JTabbedPane() tabs = self._tabbedPane self._requestViewer = callbacks.createMessageEditor(self, True) self._responseViewer = callbacks.createMessageEditor(self, True) self._requestHighlight = callbacks.createTextEditor() self._responseHighlight = callbacks.createTextEditor() tabs.addTab("Supporting Request", self._requestViewer.getComponent()) tabs.addTab("Supporting Response", self._responseViewer.getComponent()) tabs.addTab("Request Marker Selection", self._requestHighlight.getComponent()) tabs.addTab("Response Marker Selection", self._responseHighlight.getComponent()) #self._mainFrame.setRightComponent(tabs) # set to the lower split pane print "*" * 60 print "[+] Request/Response tabs created" # -- Define Issue Details GUI & Layout-- # # Labels and Input boxes... # Issue Name self.issueNameLabel = JLabel(" Issue Name:") self.issueNameValue = JTextArea(text = str(issueNamePlaceholder), editable = True, wrapStyleWord = True, lineWrap = True, alignmentX = Component.LEFT_ALIGNMENT, size = (1, 20) ) # Issue Detail self.issueDetailLabel = JLabel(" Issue Detail:") #self.issueDetailValue = JTextField(str(issueDetailPlaceholder), 15) self.issueDetailValue = JTextArea(text = str(issueDetailPlaceholder), editable = True, wrapStyleWord = True, lineWrap = True, alignmentX = Component.LEFT_ALIGNMENT, size = (100, 20) ) # IssueBackground self.issueBackgroundLabel = JLabel(" Issue Background:") self.issueBackgroundValue = JTextArea(text = str(issueBackgroundPlaceholder), editable = True, wrapStyleWord = True, lineWrap = True, alignmentX = Component.LEFT_ALIGNMENT, size = (100, 20) ) # Remediation Detail self.issueRemediationLabel = JLabel(" Remediation Detail:") self.issueRemediationValue = JTextArea(text = str(remediationDetailPlaceholder), editable = True, wrapStyleWord = True, lineWrap = True, alignmentX = Component.LEFT_ALIGNMENT, size = (100, 20) ) # Remediation Background self.issueRemBackgroundLabel = JLabel(" Remediation Background:") self.issueRemBackgroundValue = JTextArea(text = str(remediationBackgroundPlaceholder), editable = True, wrapStyleWord = True, lineWrap = True, alignmentX = Component.LEFT_ALIGNMENT, size = (100, 20) ) # Issue URL self.issueURLLabel = JLabel(" URL (path = http://domain/path):") self.issueURLValue = JTextArea(text = str(issueURLPlaceholder), editable = True, wrapStyleWord = True, lineWrap = True, alignmentX = Component.LEFT_ALIGNMENT, size = (1, 20) ) # Issue Port self.issuePortLabel = JLabel(" Port:") self.issuePortValue = JTextArea(text = str(issuePortPlaceholder), editable = True, wrapStyleWord = True, lineWrap = True, alignmentX = Component.LEFT_ALIGNMENT, size = (1, 20) ) # Confidence self.confidenceValuesList = ("Certain","Firm","Tentative") self.issueConfienceLabel = JLabel(" Confidence [Certain, Firm or Tentative]") self.issueConfidenceValue = JComboBox(self.confidenceValuesList) # Severity self.severityValuesList = ("High","Medium","Low","Information") self.issueSeverityLabel = JLabel(" Severity [High, Medium Low or Informational]") self.issueSeverityValue = JComboBox(self.severityValuesList) # Add Finding button self.addFindingButton = JButton("Generate Finding", actionPerformed=self.createScanIssue, alignmentX=Component.CENTER_ALIGNMENT) # -- Group items for display -- # # Group items self.grpIssueSummary = JPanel(GridLayout(0,1)) self.grpIssueSummary.add(self.issueNameLabel) self.grpIssueSummary.add(self.issueNameValue) self.grpIssueSummary.add(self.issueDetailLabel) self.grpIssueSummary.add(self.issueDetailValue) self.grpIssueSummary.add(self.issueBackgroundLabel) self.grpIssueSummary.add(self.issueBackgroundValue) self.grpIssueSummary.add(self.issueRemediationLabel) self.grpIssueSummary.add(self.issueRemediationValue) self.grpIssueSummary.add(self.issueRemBackgroundLabel) self.grpIssueSummary.add(self.issueRemBackgroundValue) self.grpIssueSummary.add(self.issueURLLabel) self.grpIssueSummary.add(self.issueURLValue) self.grpIssueSummary.add(self.issuePortLabel) self.grpIssueSummary.add(self.issuePortValue) self.grpIssueSummary.add(self.issueURLLabel) self.grpIssueSummary.add(self.issueURLValue) self.grpIssueSummary.add(self.issuePortLabel) self.grpIssueSummary.add(self.issuePortValue) self.grpRatingBoxes = JPanel() self.grpRatingBoxes.add(self.issueSeverityLabel) self.grpRatingBoxes.add(self.issueSeverityValue) self.grpRatingBoxes.add(self.issueConfienceLabel) self.grpRatingBoxes.add(self.issueConfidenceValue) self.grpRatingBoxes.add(self.addFindingButton) # add grps to details frame self._detailsPanel = JPanel(GridLayout(0,1)) self._detailsPanel.add(self.grpIssueSummary) self._detailsPanel.add(self.grpRatingBoxes) self._findingDetailsPane = JScrollPane(self._detailsPanel) # create the main frame to hold details self._detailsViewer = self._findingDetailsPane # creates a form for details #tabs.addTab("Finding Details", self._detailsViewer) self._mainFrame = JSplitPane(JSplitPane.VERTICAL_SPLIT, self._detailsViewer, tabs) self._mainFrame.setOneTouchExpandable(True); self._mainFrame.setDividerLocation(0.5) self._mainFrame.setResizeWeight(0.50) print "[+] Finding details panel created" print "[+] Rendering..." # customize our UI components callbacks.customizeUiComponent(self._mainFrame) callbacks.customizeUiComponent(self._tabbedPane) callbacks.customizeUiComponent(self._detailsPanel) callbacks.customizeUiComponent(tabs) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) print "[+] Done" print "[!] Added suite tab initialize complete!" return def getTabCaption(self): return "Generate Finding" def getUiComponent(self): return self._mainFrame # initiaizes when button is clicked in 'Generate Finding Tab' def createScanIssue(self, event): print "[!] Finding Detail: " print "\t[+] Name:\n\t\t", self.issueNameValue.getText().strip() name = self.issueNameValue.getText() print "\t[+] Description:\n\t\t", self.issueDetailValue.getText().strip() description = self.issueDetailValue.getText() print "\t[+] Background:\n\t\t", self.issueBackgroundValue.getText().strip() background = self.issueBackgroundValue.getText() print "\t[+] Remediation:\n\t\t", self.issueRemediationValue.getText().strip() remediation = self.issueRemediationValue.getText() print "\t[+] Remediation Background:\n\t\t", self.issueRemBackgroundValue.getText().strip() remBackground = self.issueRemBackgroundValue.getText() print "\t[+] URL Detail:\n\t\t", self.issueURLValue.getText() urlDetail = self.issueURLValue.getText() print "\t[+] Port Number:\n\t\t", self.issuePortValue.getText() portNumber = self.issuePortValue.getText() print "\t[+] Confidence Rating:\n\t\t", self.issueConfidenceValue.getSelectedItem() confidenceRating = self.issueConfidenceValue.getSelectedItem() print "\t[+] Severity Rating:\n\t\t", self.issueSeverityValue.getSelectedItem() severityRating = self.issueSeverityValue.getSelectedItem() #print "\t[+] Payload Markers:\n\t\t", self.getSelectionBounds() # get highlighted data from request/response tabs in 'Generate Finding' #print "[!] Request Selected data:", self._requestViewer.getSelectedData() #highRequest = self._requestViewer.getSelectedData() #print "converted:", self._helpers.bytesToString(highRequest) #print "[!] Response Selected data:", self._responseViewer.getSelectedData() #highResponse = self._responseViewer.getSelectedData() #print "converted:", self._helpers.bytesToString(highResponse) # current message is used - should work as long as menu item 'Generate Finding' is not reset or used before finding has been generated. requestResponse = self.current_message print "\t[+] RequestResponse:\n\t\t", requestResponse print "\t[+] Service:\n\t\t", requestResponse.getHttpService() # Collect request and Response Markers... #print "[**] Request Bounds: ", self._requestHighlight.getSelectionBounds() requestBounds = self._requestHighlight.getSelectionBounds() #print "[**] Response Bounds: ", self._responseHighlight.getSelectionBounds() responseBounds = self._responseHighlight.getSelectionBounds() # applyMarkers to request/response # callbacks.applyMarkers(requestResponse, None, [array('i', (data[1], data[2]))]) self.reqMarkers = [requestBounds[0],requestBounds[1]] print "\t[+] Request Reporting Markers:\n\t\t", self.reqMarkers self.resMarkers = [responseBounds[0],responseBounds[1]] print "\t[+] Response Reporting Markers:\n\t\t", self.resMarkers print "*" * 60 print "[!] Attempting to create custom scan issue." # Call AddScanItem class to create scan issue!! finding_array = [urlDetail, name, 134217728, severityRating, confidenceRating, background, remBackground, description, remediation, requestResponse] issue = ScanIssue(self, finding_array, self.current_message, self.reqMarkers, self.resMarkers, self._helpers, self._callbacks) self._callbacks.addScanIssue(issue) # Done print "[+] Finding Generated!" def getRequestResponseText(self): messages = self.ctxMenuInvocation.getSelectedMessages() # parses currently selected finding to a string if len(messages) == 1 : for self.m in messages: requestResponse = self.m # add requestResponseWithMarkers to be global so can be included in scanIssue self.current_message = requestResponse # get request data and convert to string requestDetail = requestResponse.getRequest() try: requestData = self._helpers.bytesToString(requestDetail) # converts & Prints out the entire request as string except: requestData = '[-] No Request Detail in this RequestResponse' pass # get response data and convert to string responseDetail = requestResponse.getResponse() try: responseData = self._helpers.bytesToString(responseDetail) # converts & Prints out the entire request as string except: responseData = '[-] No Response Detail in this RequestResponse' pass requestData = self._helpers.bytesToString(requestDetail) # converts & Prints out the entire request as string # send request string to 'Supporting Request' tab - 'True' because it is a request! self._requestViewer.setMessage(requestData, True) # for higlighting markers.. self._requestHighlight.setText(requestData) # send response string to 'Supporting Response' tab self._responseViewer.setMessage(responseData, False) # set False as is a response not request... # for higlighting markers.. self._responseHighlight.setText(responseData) def getFindingDetails(self): messages = self.ctxMenuInvocation.getSelectedMessages() print "*" * 60 print "[+] Handling selected request: ", self.current_message if len(messages) == 1: for m in messages: # URL #print "[!] Selected Request's URL: \n", self._helpers.analyzeRequest(m).getUrl() self.issueURLValue.setText(str(self._helpers.analyzeRequest(m).getUrl())) # update finding info # Protocol #print "[!] Request's Protocol: \n", m.getProtocol() # Request Port #print "[!] Request's Port: \n", m.getPort() self.issuePortValue.setText(str(m.getPort())) # update finding info print "*" * 60 # API hook... def getHttpMessages(self): return [self.m] # Actions on menu click... def actionPerformed(self, actionEvent): print "*" * 60 print "[+] Request sent to 'Generate Finding'" try: # When clicked!! self.getRequestResponseText() self.getFindingDetails() except: tb = traceback.format_exc() print tb # create Menu def createMenuItems(self, ctxMenuInvocation): self.ctxMenuInvocation = ctxMenuInvocation return [self.menuItem]
class Plugin(IHttpListener): MUTATE_ID_COLUMN_INDEX = 4 ORIG_ID_COLUMN_INDEX = 5 def __init__(self, callbacks): self.callbacks = callbacks self.helpers = self.callbacks.getHelpers() self.origMessageEditorController = MessageEditorController() self.mutatedMessageEditorController = MessageEditorController() self.origSearchString = replacements.origSearchString self.replacements = replacements.replacements self.requestResponseCache = {} def start(self): self.frame = JDialog() #self.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) self.frame.setLocation(0, 1500) self.frame.setSize(1000, 200) self.tableDataModel = DefaultTableModel([], [ "URL", "Code", "Content-Length", "Location", "Mutated Id", "Orig Id" ]) self.jtable = JTable(self.tableDataModel) scrollPane = JScrollPane(self.jtable) self.jtable.setFillsViewportHeight(True) messageEditorOrig = self.callbacks.createMessageEditor(None, False) messageEditorModified = self.callbacks.createMessageEditor( None, False) self.editorSplitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, messageEditorOrig.getComponent(), messageEditorModified.getComponent()) self.editorSplitPane.setResizeWeight(0.5) splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, self.editorSplitPane) splitPane.setResizeWeight(0.5) class TableSelector(ListSelectionListener): def __init__(self, plugin): self.plugin = plugin def valueChanged(self, event): if not event.getValueIsAdjusting(): selectedRowIndex = self.plugin.jtable.getSelectedRows()[0] self.plugin._rowSelected(selectedRowIndex) self.jtable.getSelectionModel().addListSelectionListener( TableSelector(self)) self.frame.add(splitPane) self.frame.setVisible(True) self.callbacks.registerHttpListener(self) self.callbacks.setExtensionName("Custom Plugin") return def stop(self): print("Closing!") self.callbacks.removeHttpListener(self) self.frame.dispose() self.jrame = None return def _rowSelected(self, index): #self.splitPane.setLeftComponent( #self.callbacks.createMessageEditor( origId = self.tableDataModel.getValueAt( index, self.ORIG_ID_COLUMN_INDEX).encode('ascii', 'ignore') mutateId = self.tableDataModel.getValueAt( index, self.MUTATE_ID_COLUMN_INDEX).encode('ascii', 'ignore') self.origMessageEditorController.setRequestResponse( self.requestResponseCache[origId]) messageEditorOrig = self.callbacks.createMessageEditor( self.origMessageEditorController, False) messageEditorOrig.setMessage( self.requestResponseCache[origId].getResponse(), False) self.editorSplitPane.setLeftComponent(messageEditorOrig.getComponent()) self.mutatedMessageEditorController.setRequestResponse( self.requestResponseCache[mutateId]) messageEditorMutated = self.callbacks.createMessageEditor( self.mutatedMessageEditorController, False) messageEditorMutated.setMessage( self.requestResponseCache[mutateId].getResponse(), False) self.editorSplitPane.setRightComponent( messageEditorMutated.getComponent()) print(mutateId) print("Row selected") print(str(index)) def _buildResponseHeadersDictionary(self, headers): """Creates key/value lookup from list of headers. Header names are converted to lowercase. If header is returned multiple time, last header has precedence.""" d = {} #Skip first "header", it's the response code line. for i in range(1, len(headers)): (name, value) = headers[i].split(":", 1) d[name.lower()] = value return d def _getDictValueOrEmptyStr(self, d, key): if key in d: return d[key] else: return "" def handleReceivedResponseForModifiedRequest(self, requestResponse): #Get original HTTP Request requestData = StringUtil.fromBytes(requestResponse.getRequest()) requestId = re.search(b"^X-REQUEST-ID: ([^\r]*)", requestData, flags=re.MULTILINE).group(1).encode('ascii') origRequestId = re.search(b"^X-REQUEST-ORIG-ID: ([^\r]*)", requestData, flags=re.MULTILINE).group(1).encode('ascii') print("Keys") print(requestId) print(origRequestId) print(self.requestResponseCache.keys()) self.requestResponseCache[requestId] = requestResponse origRequestResponse = self.requestResponseCache[origRequestId] analyzedOrigResponse = self.helpers.analyzeResponse( origRequestResponse.getResponse()) analayzeMutatedResponse = self.helpers.analyzeResponse( requestResponse.getResponse()) origResponseHeaders = self._buildResponseHeadersDictionary( analyzedOrigResponse.getHeaders()) mutatedResponseHeaders = self._buildResponseHeadersDictionary( analayzeMutatedResponse.getHeaders()) mutatedRequestInfo = self.helpers.analyzeRequest( requestResponse.getHttpService(), requestResponse.getRequest()) model = self.jtable.getModel() model.addRow([ str(mutatedRequestInfo.getUrl()), str(analayzeMutatedResponse.getStatusCode()), self._getDictValueOrEmptyStr(mutatedResponseHeaders, "content-length"), self._getDictValueOrEmptyStr(mutatedResponseHeaders, "location"), requestId, origRequestId ]) print("Modified Request Found: %s %s" % (requestId, origRequestId)) #Get original request and response object from lookup #Get request from lookup def processHttpMessage(self, toolFlag, messageIsRequest, requestResponse): if not messageIsRequest: requestData = StringUtil.fromBytes(requestResponse.getRequest()) #We generated the request, process it if requestData.find(b"X-REQUEST-ID") != -1: self.handleReceivedResponseForModifiedRequest(requestResponse) #Response received for non-mutated request. #Mutate request and send it. else: origRequestResponseUUID = str(uuid.uuid4()) reload(replacements) print("Looking for replacements") for replacement in self.replacements: newRequestData = re.sub(self.origSearchString, replacement, requestData) #If no replacemnets made, don't send any requests if newRequestData != requestData: newRequestUUID = str(uuid.uuid4()) newRequestData = re.sub( b"Host", b"X-REQUEST-ID: " + newRequestUUID + "\r\nHost", requestData) newRequestData = re.sub( b"Host", b"X-REQUEST-ORIG-ID: " + origRequestResponseUUID + "\r\nHost", newRequestData) print("Sending Mutated Request") print(newRequestData) self.requestResponseCache[ origRequestResponseUUID] = requestResponse httpService = requestResponse.getHttpService() self.callbacks.makeHttpRequest(httpService, newRequestData) print("Got here")
class BobGui(JFrame): def __init__(self): super(BobGui, self).__init__('BobPy') # IJ.log('okay?') # print('okay??') self.setLayout(BorderLayout()) self.main_panel = JPanel() # self.main_panel.setLayout(MigLayout('insets 1 10 1 1')) self.main_panel.setLayout(MigLayout()) dir_panel = JPanel() dir_panel.setLayout(BoxLayout(dir_panel, BoxLayout.X_AXIS)) dir_label = JLabel('Experiment Folder:') dir_panel.add(dir_label) self.dir_text_field = JTextField(10) self.dir_text_field.addActionListener( ActionListenerFactory(self, self.text_field_al)) dir_panel.add(self.dir_text_field) dir_button = JButton('open') dir_button.addActionListener( ActionListenerFactory(self, self.choose_dir_al)) dir_panel.add(dir_button) self.main_panel.add(dir_panel, 'wrap, growx, spanx, pushx') add_key_args(self.main_panel, 'close_w', ActionListenerFactory(self, self.close_al), KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) self.add(self.main_panel, BorderLayout.CENTER) self.setPreferredSize(Dimension(650, 600)) self.pack() self.setLocationRelativeTo(None) self.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE) self.setVisible(True) def show_exper_info(self): chf_panel = self.make_chf_panel() hseg_tree_panel = self.make_hseg_tree_panel() self.split_pane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self.split_pane.setOneTouchExpandable(True) self.split_pane.setContinuousLayout(True) self.split_pane.setResizeWeight(0.5) self.split_pane.add(chf_panel) self.split_pane.add(hseg_tree_panel) self.main_panel.add(self.split_pane, 'grow, wrap') # self.log_text = JTextArea() self.main_panel.add(self.log_text, 'grow, wrap') self.log_text.setLineWrap(True) self.log_text.setWrapStyleWord(True) self.revalidate() def make_chf_panel(self): """ chf --> common hseg files """ chf_panel = JPanel() chf_panel.setLayout(MigLayout('insets 0')) chf_files_label = JLabel('Hemisegment cells') chf_files_text = JTextArea( BobGui.archetype_to_str( self.exper.hseg_cell_files_cab().archetype)) chf_panel.add(chf_files_label, 'growx, wrap') chf_panel.add(chf_files_text, 'grow, wrap') chf_files_label = JLabel('Hemisegment binary image files') chf_files_text = JTextArea( BobGui.archetype_to_str(self.exper.hseg_bin_files_cab().archetype)) chf_panel.add(chf_files_label, 'growx, wrap') chf_panel.add(chf_files_text, 'grow, wrap') chf_files_label = JLabel('Intensity Image Files') # chf_files_text = JTextArea(BobGui.archetype_to_str(self.exper.hseg_intens_im_files_cab().archetype)) # print(self.exper.hseg_intens_im_files_cab().archetype) # chf_panel.add(chf_files_label, 'growx, wrap') # chf_panel.add(chf_files_text, 'grow, wrap') self.intens_im_boxes = [] intens_im_panel = JPanel() intens_im_panel.setLayout(MigLayout('insets 0')) for poss_im_file in self.exper.hseg_intens_im_files_cab().archetype: self.intens_im_boxes.append(JCheckBox(poss_im_file)) intens_im_panel.add(self.intens_im_boxes[-1], 'wrap') chf_panel.add(chf_files_label, 'growx, wrap') chf_panel.add(intens_im_panel, 'grow, wrap') mdf_create_button = JButton( 'Create meta_data file from default outline') # mdf_create_button = JButton('<html>Create meta_data file<br>from default outline</html>') mdf_create_button.addActionListener( ActionListenerFactory(self, self.mdf_create_al)) mdf_open_button = JButton('Open existing meta_data file') mdf_open_button.addActionListener( ActionListenerFactory(self, self.mdf_open_al)) # meta_data_file_buttton = JButton('Open/Create meta_data file') # meta_data_file_buttton.addActionListener(ActionListenerFactory(self, self.meta_data_al)) # chf_panel.add(meta_data_file_buttton) chf_panel.add(mdf_create_button, 'wrap') chf_panel.add(mdf_open_button, 'wrap') chf_scroll_pane = JScrollPane() chf_scroll_pane.getViewport().setView(chf_panel) return chf_scroll_pane @staticmethod def archetype_to_str(archetype): at_str = '' for val in archetype: at_str += str(val) + '\n' return at_str def make_hseg_tree_panel(self): root = DefaultMutableTreeNode(self.exper.name) for hseg in self.exper.hsegs(): hseg_node = DefaultMutableTreeNode(hseg.name) root.add(hseg_node) hseg_at_deviations = self.exper.hseg_files_cab( ).archetype_deviations if len(hseg_at_deviations[hseg.name]) > 0: for definer, file_names in hseg_at_deviations[ hseg.name].items(): for file_name in file_names: node_str = definer + ': ' + file_name temp = DefaultMutableTreeNode(node_str) hseg_node.add(temp) hseg_tree = JTree(root) hseg_tree.setCellRenderer(BobPyTreeCellRenderer()) hseg_scroll_pane = JScrollPane() hseg_scroll_pane.getViewport().setView((hseg_tree)) hseg_panel = JPanel(MigLayout('insets 0')) hseg_panel.add(hseg_scroll_pane, 'grow, span, push, wrap') run_button = JButton('Run') run_button.addActionListener(ActionListenerFactory(self, self.run_al)) rerun_button = JButton('Rerun') rerun_button.addActionListener( ActionListenerFactory(self, self.rerun_al)) hseg_panel.add(run_button) hseg_panel.add(rerun_button) return hseg_panel def log(self, text): self.log_text.append(str(text) + '\n') def text_field_al(self, e): # self.dir_path = self.got_exper(self.dir_text_field.getText()) def choose_dir_al(self, e): dc = DirectoryChooser('Choose a bob_py experiment folder') # self.dir_path = dc.getDirectory() # self.dir_text_field.setText(self.dir_path) # self.dir_text_field.setText('blerg') # IJ.log('blerg') # print('boop') self.got_exper(dc.getDirectory()) def close_al(self, e): self.dispatchEvent(WindowEvent(self, WindowEvent.WINDOW_CLOSING)) def run_al(self, e): # dt = br.dtic('Processed experiment {}'.format(self.exper.name)) t = br.tic() self.exper.make_data() self.exper.output_cell_cols_def() self.exper.output_nuc_cols_def() self.exper.output_new_hdings() self.exper.log('Processed experiment {} in {:.3f} seconds'.format( self.exper.name, br.toc(t))) # br.dtoc(dt) def rerun_al(self, e): # dt = br.dtic('Processed experiment {}'.format(self.exper.name)) # t = br.tic() self.exper = None self.got_exper(self.dir_path) self.run_al(None) # self.exper = # self.exper.make_data() # self.exper.output_cell_cols_def() # self.exper.output_nuc_cols_def() # self.exper.output_new_hdings() # # self.exper.log('Created and processed experiment {} in {:.3f} seconds'.format(self.exper.name, br.toc(t))) # br.dtoc(dt) # def meta_data_al(self, e) : # meta_data_path = self.exper.meta_data_path() # if not os.path.exists(meta_data_path) : # txt = make_meta_data_str(self.exper) # with open(meta_data_path, 'w') as f : # f.write(txt) # # IJ.open(meta_data_path) def mdf_create_al(self, e): meta_data_path = self.exper.meta_data_path() # if not os.path.exists(meta_data_path) : intens_im_list = [] for check_box in self.intens_im_boxes: if check_box.isSelected(): intens_im_list.append(check_box.getLabel()) txt = make_meta_data_str2(self.exper, intens_im_list) with open(meta_data_path, 'w') as f: f.write(txt) # IJ.open(meta_data_path) def mdf_open_al(self, e): meta_data_path = self.exper.meta_data_path() IJ.open(meta_data_path) def got_exper(self, dir_path): # IJ.log('exper') self.dir_path = dir_path self.dir_text_field.setText(self.dir_path) self.log_text = JTextArea() self.exper = bob_py.Exper(dir_path, gui=self) self.show_exper_info()
class BurpExtender(IBurpExtender, ITab, IMessageEditorController, IContextMenuFactory): # # implement IBurpExtender # def registerExtenderCallbacks(self, callbacks): # keep a reference to our Burp callbacks object self._callbacks = callbacks # obtain an Burp extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("AuthMatrix - v0.4") # DB that holds everything users, roles, and messages self._db = MatrixDB() # For saving/loading config self._fc = JFileChooser() # Used by ActionListeners selfExtender = self self._selectedColumn = -1 self._selectedRow = -1 # Table of User entries self._userTable = UserTable(self, model=UserTableModel(self._db)) roleScrollPane = JScrollPane(self._userTable) self._userTable.redrawTable() # Table of Request (AKA Message) entries self._messageTable = MessageTable(self, model=MessageTableModel(self._db)) messageScrollPane = JScrollPane(self._messageTable) self._messageTable.redrawTable() # Semi-Generic Popup stuff def addPopup(component, popup): class genericMouseListener(MouseAdapter): def mousePressed(self, e): if e.isPopupTrigger(): self.showMenu(e) def mouseReleased(self, e): if e.isPopupTrigger(): self.showMenu(e) def showMenu(self, e): if type(component) is JTableHeader: table = component.getTable() column = component.columnAtPoint(e.getPoint()) if type( table ) is MessageTable and column >= selfExtender._db.STATIC_MESSAGE_TABLE_COLUMN_COUNT or type( table ) is UserTable and column >= selfExtender._db.STATIC_USER_TABLE_COLUMN_COUNT: selfExtender._selectedColumn = column else: return else: selfExtender._selectedRow = component.rowAtPoint( e.getPoint()) popup.show(e.getComponent(), e.getX(), e.getY()) component.addMouseListener(genericMouseListener()) class actionRunMessage(ActionListener): def actionPerformed(self, e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows( ): indexes = [ selfExtender._db.getMessageByRow( selfExtender._selectedRow)._index ] else: indexes = [ selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows() ] t = Thread(target=selfExtender.runMessagesThread, args=[indexes]) t.start() selfExtender._selectedColumn = -1 # Redrawing the table happens in colorcode within the thread class actionRemoveMessage(ActionListener): def actionPerformed(self, e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._messageTable.getSelectedRows( ): indexes = [ selfExtender._db.getMessageByRow( selfExtender._selectedRow)._index ] else: indexes = [ selfExtender._db.getMessageByRow(rowNum)._index for rowNum in selfExtender._messageTable.getSelectedRows() ] for i in indexes: selfExtender._db.deleteMessage(i) selfExtender._selectedColumn = -1 selfExtender._messageTable.redrawTable() class actionRemoveUser(ActionListener): def actionPerformed(self, e): if selfExtender._selectedRow >= 0: if selfExtender._selectedRow not in selfExtender._userTable.getSelectedRows( ): indexes = [ selfExtender._db.getUserByRow( selfExtender._selectedRow)._index ] else: indexes = [ selfExtender._db.getUserByRow(rowNum)._index for rowNum in selfExtender._userTable.getSelectedRows() ] for i in indexes: selfExtender._db.deleteUser(i) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() # TODO combine these next two classes # TODO Also, clean up the variable names where M and U are in place of MessageTable and UserTable class actionRemoveRoleHeaderFromM(ActionListener): def actionPerformed(self, e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole( selfExtender._db.getRoleByMColumn( selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() class actionRemoveRoleHeaderFromU(ActionListener): def actionPerformed(self, e): if selfExtender._selectedColumn >= 0: selfExtender._db.deleteRole( selfExtender._db.getRoleByUColumn( selfExtender._selectedColumn)._index) selfExtender._selectedColumn = -1 selfExtender._userTable.redrawTable() selfExtender._messageTable.redrawTable() # Message Table popups messagePopup = JPopupMenu() addPopup(self._messageTable, messagePopup) messageRun = JMenuItem("Run Request(s)") messageRun.addActionListener(actionRunMessage()) messagePopup.add(messageRun) messageRemove = JMenuItem("Remove Request(s)") messageRemove.addActionListener(actionRemoveMessage()) messagePopup.add(messageRemove) messageHeaderPopup = JPopupMenu() addPopup(self._messageTable.getTableHeader(), messageHeaderPopup) roleRemoveFromM = JMenuItem("Remove Role") roleRemoveFromM.addActionListener(actionRemoveRoleHeaderFromM()) messageHeaderPopup.add(roleRemoveFromM) # User Table popup userPopup = JPopupMenu() addPopup(self._userTable, userPopup) userRemove = JMenuItem("Remove Users(s)") userRemove.addActionListener(actionRemoveUser()) userPopup.add(userRemove) userHeaderPopup = JPopupMenu() addPopup(self._userTable.getTableHeader(), userHeaderPopup) roleRemoveFromU = JMenuItem("Remove Role") roleRemoveFromU.addActionListener(actionRemoveRoleHeaderFromU()) userHeaderPopup.add(roleRemoveFromU) # Top pane topPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, roleScrollPane, messageScrollPane) topPane.setResizeWeight(0.3) # request tabs added to this tab on click in message table self._tabs = JTabbedPane() # Button pannel buttons = JPanel() runButton = JButton("Run", actionPerformed=self.runClick) newUserButton = JButton("New User", actionPerformed=self.getInputUserClick) newRoleButton = JButton("New Role", actionPerformed=self.getInputRoleClick) #debugButton = JButton("Debug", actionPerformed=self.printDB) saveButton = JButton("Save", actionPerformed=self.saveClick) loadButton = JButton("Load", actionPerformed=self.loadClick) clearButton = JButton("Clear", actionPerformed=self.clearClick) buttons.add(runButton) buttons.add(newUserButton) buttons.add(newRoleButton) #buttons.add(debugButton) buttons.add(saveButton) buttons.add(loadButton) buttons.add(clearButton) bottomPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, self._tabs, buttons) bottomPane.setResizeWeight(0.95) # Main Pane self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT, topPane, bottomPane) self._splitpane.setResizeWeight(0.5) # customize our UI components callbacks.customizeUiComponent(self._splitpane) callbacks.customizeUiComponent(topPane) callbacks.customizeUiComponent(bottomPane) callbacks.customizeUiComponent(messageScrollPane) callbacks.customizeUiComponent(roleScrollPane) callbacks.customizeUiComponent(self._messageTable) callbacks.customizeUiComponent(self._userTable) callbacks.customizeUiComponent(self._tabs) callbacks.customizeUiComponent(buttons) # Handles checkbox color coding # Must be bellow the customizeUiComponent calls self._messageTable.setDefaultRenderer(Boolean, SuccessBooleanRenderer(self._db)) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) # register SendTo option callbacks.registerContextMenuFactory(self) return ## ## implement ITab ## def getTabCaption(self): return "AuthMatrix" def getUiComponent(self): return self._splitpane ## ## Creates the sendto tab in other areas of Burp ## def createMenuItems(self, invocation): messages = invocation.getSelectedMessages() def addRequestsToTab(e): for messageInfo in messages: # saveBuffers is required since modifying the original from its source changes the saved objects, its not a copy messageIndex = self._db.createNewMessage( self._callbacks.saveBuffersToTempFiles(messageInfo), self._helpers.analyzeRequest(messageInfo).getUrl()) #self._messageTable.getModel().addRow(row) self._messageTable.redrawTable() ret = [] menuItem = JMenuItem("Send request(s) to AuthMatrix") menuItem.addActionListener(addRequestsToTab) ret.append(menuItem) return (ret) ## ## implement IMessageEditorController ## this allows our request/response viewers to obtain details about the messages being displayed ## ## TODO: Is this necessary? The request viewers may not require this since they aren't editable ## def getHttpService(self): return self._currentlyDisplayedItem.getHttpService() def getRequest(self): return self._currentlyDisplayedItem.getRequest() def getResponse(self): return self._currentlyDisplayedItem.getResponse() ## ## Actions on Bottom Row Button Clicks ## def printDB(self, e): out = "" for a in self._db.arrayOfUsers: out += str(a._index) + " " + a._name + " : " + str(a._roles) + "\n" for b in self._db.arrayOfMessages: out += str(b._index) + " " + str(b._roles) + "\n" JOptionPane.showMessageDialog(self._splitpane, out) def getInputUserClick(self, e): newUser = JOptionPane.showInputDialog(self._splitpane, "Enter New User:"******"Enter New Role:") if not newRole is None: self._db.getOrCreateRole(newRole) self._userTable.redrawTable() self._messageTable.redrawTable() def saveClick(self, e): returnVal = self._fc.showSaveDialog(self._splitpane) if returnVal == JFileChooser.APPROVE_OPTION: f = self._fc.getSelectedFile() if f.exists(): result = JOptionPane.showConfirmDialog( self._splitpane, "The file exists, overwrite?", "Existing File", JOptionPane.YES_NO_OPTION) if result != JOptionPane.YES_OPTION: return fileName = f.getPath() outs = io.ObjectOutputStream(io.FileOutputStream(fileName)) outs.writeObject(self._db.getSaveableObject()) outs.close() def loadClick(self, e): returnVal = self._fc.showOpenDialog(self._splitpane) if returnVal == JFileChooser.APPROVE_OPTION: warning = """ CAUTION: Loading a saved configuration deserializes data. This action may pose a security threat to the application. Only proceed when the source and contents of this file is trusted. Load Selected File? """ result = JOptionPane.showOptionDialog(self._splitpane, warning, "Caution", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, None, ["OK", "Cancel"], "OK") if result != JOptionPane.YES_OPTION: return f = self._fc.getSelectedFile() fileName = f.getPath() ins = io.ObjectInputStream(io.FileInputStream(fileName)) dbData = ins.readObject() ins.close() self._db.load(dbData, self._callbacks, self._helpers) self._userTable.redrawTable() self._messageTable.redrawTable() def clearClick(self, e): self._db.clear() self._tabs.removeAll() self._userTable.redrawTable() self._messageTable.redrawTable() def runClick(self, e): t = Thread(target=self.runMessagesThread) self._tabs.removeAll() t.start() ## ## Methods for running messages and analyzing results ## def runMessagesThread(self, messageIndexes=None): self._db.lock.acquire() try: indexes = messageIndexes if not indexes: indexes = self._db.getActiveMessageIndexes() self.clearColorResults(indexes) for index in indexes: self.runMessage(index) #self.colorCodeResults() except: traceback.print_exc(file=self._callbacks.getStderr()) finally: self._db.lock.release() self.colorCodeResults() # TODO: This method is too large. Fix that def runMessage(self, messageIndex): messageEntry = self._db.arrayOfMessages[messageIndex] # Clear Previous Results: messageEntry._roleResults = {} messageEntry._userRuns = {} messageInfo = messageEntry._requestResponse requestInfo = self._helpers.analyzeRequest(messageInfo) reqBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] for userIndex in self._db.getActiveUserIndexes(): userEntry = self._db.arrayOfUsers[userIndex] headers = requestInfo.getHeaders() if userEntry.isCookie(): cookieHeader = "Cookie:" newheader = cookieHeader # getHeaders has to be called again here cuz of java references for header in requestInfo.getHeaders(): if str(header).startswith(cookieHeader): newheader = str(header).strip() headers.remove(header) # If its a valid cookie equals = userEntry._token.find("=") if equals >= 0: cookieIndex = newheader.find(userEntry._token[0:equals + 1]) # Cookie already exists and must be removed if cookieIndex >= 0: semicolon = newheader.find(";", cookieIndex) if semicolon >= 0: # Remove extra whitespace if newheader[semicolon + 1:semicolon + 2] == " ": newheader = newheader.replace( newheader[cookieIndex:semicolon + 2], "") else: newheader = newheader.replace( newheader[cookieIndex:semicolon + 1], "") else: newheader = newheader.replace( newheader[cookieIndex:], "") # Removing tailing semicolon and white space newheader = newheader.rstrip("; ") # Handle when the only cookie is the target cookie if not newheader == cookieHeader: newheader += ";" newheader += " " + userEntry._token # Header else: newheader = userEntry._token colon = newheader.find(":") if colon >= 0: # getHeaders has to be called again here cuz of java references for header in requestInfo.getHeaders(): if str(header).startswith(newheader[0:colon + 1]): headers.remove(header) headers.add(newheader) # Add static CSRF token if available # TODO: Kinda hacky, but for now it will add the token as long as there is some content in the post body # Even if its a GET request. This screws up when original requests have no body though... oh well... newBody = reqBody if userEntry._staticcsrf and len(reqBody): delimeter = userEntry._staticcsrf.find("=") if delimeter >= 0: csrfname = userEntry._staticcsrf[0:delimeter] csrfvalue = userEntry._staticcsrf[delimeter + 1:] params = requestInfo.getParameters() for param in params: if str(param.getName()) == csrfname: # Handle CSRF Tokens in Body if param.getType() == 1: newBody = reqBody[0:param.getValueStart( ) - requestInfo.getBodyOffset( )] + StringUtil.toBytes(csrfvalue) + reqBody[ param.getValueEnd() - requestInfo.getBodyOffset():] # Handle CSRF Tokens in Cookies (for Cookie==Body mitigation technique): if param.getType() == 2: # TODO: required moving above portion to a function # TODO: also need to think about when cookie name != postarg name print "Cookie CSRF Tokens are not currently supported" if newBody == reqBody: newBody = reqBody + StringUtil.toBytes( "&" + userEntry._staticcsrf) # Construct and send a message with the new headers message = self._helpers.buildHttpMessage(headers, newBody) requestResponse = self._callbacks.makeHttpRequest( messageInfo.getHttpService(), message) messageEntry.addRunByUserIndex( userIndex, self._callbacks.saveBuffersToTempFiles(requestResponse)) # Grab all active roleIndexes that should succeed activeSuccessRoles = [ index for index in messageEntry._roles.keys() if messageEntry._roles[index] and not self._db.arrayOfRoles[index].isDeleted() ] # Check Role Results of message for roleIndex in self._db.getActiveRoleIndexes(): success = self.checkResult(messageEntry, roleIndex, activeSuccessRoles) messageEntry.setRoleResultByRoleIndex(roleIndex, success) def colorCodeResults(self): self._messageTable.redrawTable() def clearColorResults(self, messageIndexArray=None): if not messageIndexArray: messageIndexes = self._db.getActiveMessageIndexes() else: messageIndexes = messageIndexArray for messageIndex in messageIndexes: messageEntry = self._db.arrayOfMessages[messageIndex] messageEntry._roleResults = {} messageEntry._userRuns = {} self._messageTable.redrawTable() def checkResult(self, messageEntry, roleIndex, activeSuccessRoles): for userIndex in self._db.getActiveUserIndexes(): userEntry = self._db.arrayOfUsers[userIndex] ignoreUser = False # if user is not in this role, ignore it if not userEntry._roles[roleIndex]: ignoreUser = True # If user is in any other role that should succeed, then ignore it for index in userEntry._roles.keys(): if not index == roleIndex and userEntry._roles[ index] and index in activeSuccessRoles: ignoreUser = True if not ignoreUser: shouldSucceed = roleIndex in activeSuccessRoles requestResponse = messageEntry._userRuns[userEntry._index] resp = StringUtil.fromBytes(requestResponse.getResponse()) found = re.search(messageEntry._successRegex, resp, re.DOTALL) succeeds = found if shouldSucceed else not found if not succeeds: return False return True
class ProtoBufEditorTab(burp.IMessageEditorTab): """Tab in interceptor/repeater for editing protobuf message. Decodes them to JSON and back. The message type is attached to this object. """ def __init__(self, extender, controller, editable): self._extender = extender self._callbacks = extender.callbacks self._helpers = extender.helpers self._controller = controller self._text_editor = self._callbacks.createTextEditor() self._text_editor.setEditable(editable) self._editable = editable self._component = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._component.setLeftComponent(self._text_editor.getComponent()) self._component.setRightComponent(self.createButtonPane()) self._component.setResizeWeight(0.8) self.message_type = None self._is_request = None self._encoder = None self._original_json = None self._content_info = None self._request_content_info = None self._request = None self._original_content = None def getTabCaption(self): """Return message tab caption""" return "Protobuf" def getMessage(self): """Transform the JSON format back to the binary protobuf message""" try: if self.message_type is None or not self.isModified(): return self._original_content json_data = self._text_editor.getText().tostring() protobuf_data = blackboxprotobuf.protobuf_from_json( json_data, self.message_type) protobuf_data = self.encodePayload(protobuf_data) if 'set_protobuf_data' in dir(user_funcs): result = user_funcs.set_protobuf_data( protobuf_data, self._original_content, self._is_request, self._content_info, self._helpers, self._request, self._request_content_info) if result is not None: return result headers = self._content_info.getHeaders() return self._helpers.buildHttpMessage(headers, str(protobuf_data)) except Exception as exc: self._callbacks.printError(traceback.format_exc()) JOptionPane.showMessageDialog( self._component, "Error encoding protobuf: " + str(exc)) # Resets state return self._original_content def setMessage(self, content, is_request, retry=True): """Get the data from the request/response and parse into JSON. sets self.message_type """ # Save original content self._original_content = content if is_request: self._content_info = self._helpers.analyzeRequest( self._controller.getHttpService(), content) else: self._content_info = self._helpers.analyzeResponse(content) self._is_request = is_request self._request = None self._request_content_info = None if not is_request: self._request = self._controller.getRequest() self._request_content_info = self._helpers.analyzeRequest( self._controller.getHttpService(), self._request) message_hash = self.getMessageHash() # Try to find saved messsage type self.message_type = None if message_hash in self._extender.known_types: self.message_type = self._extender.known_types[message_hash] try: protobuf_data = None if 'get_protobuf_data' in dir(user_funcs): protobuf_data = user_funcs.get_protobuf_data( content, is_request, self._content_info, self._helpers, self._request, self._request_content_info) if protobuf_data is None: protobuf_data = content[self._content_info.getBodyOffset( ):].tostring() protobuf_data = self.decodePayload(protobuf_data) json_data, self.message_type = blackboxprotobuf.protobuf_to_json( protobuf_data, self.message_type) # Save the message type self._extender.known_types[message_hash] = self.message_type self._original_json = json_data self._text_editor.setText(json_data) success = True except Exception as exc: success = False self._callbacks.printError(traceback.format_exc()) # Bring out of exception handler to avoid nexting handlers if not success: if retry: # Clear existing type info and retry prev_type = self.message_type self.message_type = None if message_hash in self._extender.known_types: del self._extender.known_types[message_hash] try: self.setMessage(content, is_request, False) except Exception as exc: # If it still won't parse, restore the types self.message_type = prev_type self._extender.known_types[message_hash] = prev_type else: self._text_editor.setText("Error parsing protobuf") def decodePayload(self, payload): """Add support for decoding a few default methods. Including Base64 and GZIP""" if payload.startswith(bytearray([0x1f, 0x8b, 0x08])): gzip_decompress = zlib.decompressobj(-zlib.MAX_WBITS) self._encoder = 'gzip' return gzip_decompress.decompress(payload) # Try to base64 decode try: protobuf = base64.b64decode(payload, validate=True) self._encoder = 'base64' return protobuf except Exception as exc: pass #try: # protobuf = base64.urlsafe_b64decode(payload) # self._encoder = 'base64_url' # return protobuf #except Exception as exc: # pass self._encoder = None return payload def encodePayload(self, payload): """If we detected an encoding like gzip or base64 when decoding, redo that encoding step here """ if self._encoder == 'base64': return base64.b64encode(payload) elif self._encoder == 'base64_url': return base64.urlsafe_b64encode(payload) elif self._encoder == 'gzip': gzip_compress = zlib.compressobj(-1, zlib.DEFLATED, -zlib.MAX_WBITS) self._encoder = 'gzip' return gzip_compress.compress(payload) else: return payload def getSelectedData(self): """Get text currently selected in message""" return self._text_editor.getSelectedText() def getUiComponent(self): """Return Java AWT component for this tab""" return self._component def isEnabled(self, content, is_request): """Try to detect a protobuf in the message to enable the tab. Defaults to content-type header of 'x-protobuf'. User overridable """ # TODO implement some more default checks if is_request: info = self._helpers.analyzeRequest(content) else: info = self._helpers.analyzeResponse(content) if 'detect_protobuf' in dir(user_funcs): result = user_funcs.detect_protobuf(content, is_request, info, self._helpers) if result is not None: return result # Bail early if there is no body if info.getBodyOffset() == len(content): return False protobuf_content_types = ['x-protobuf', 'application/protobuf'] # Check all headers for x-protobuf for header in info.getHeaders(): if 'content-type' in header.lower(): for protobuf_content_type in protobuf_content_types: if protobuf_content_type in header.lower(): return True return False def isModified(self): """Return if the message was modified""" return self._text_editor.isTextModified() def createButtonPane(self): """Create a new button pane for the message editor tab""" self._button_listener = EditorButtonListener(self) panel = JPanel() panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS)) if self._editable: panel.add(self.createButton("Validate", "validate")) panel.add(self.createButton("Save Type", "save-type")) panel.add(self.createButton("Load Type", "load-type")) panel.add(self.createButton("Edit Type", "edit-type")) panel.add(self.createButton("Reset", "reset")) return panel def createButton(self, text, command): """Create a new button with the given text and command""" button = JButton(text) button.setAlignmentX(Component.CENTER_ALIGNMENT) button.setActionCommand(command) button.addActionListener(self._button_listener) return button def validateMessage(self): """Callback for validate button. Attempts to encode the message with the current type definition """ try: json_data = self._text_editor.getText().tostring() blackboxprotobuf.protobuf_from_json(json_data, self.message_type) # If it works, save the message self._original_json = json_data except Exception as exc: JOptionPane.showMessageDialog(self._component, str(exc)) self._callbacks.printError(traceback.format_exc()) def resetMessage(self): """Drop any changes and reset the message. Callback for "reset" button """ self._text_editor.setText(self._original_json) def getMessageHash(self): """Compute an "identifier" for the message which is used for sticky type definitions. User modifiable """ message_hash = None if 'hash_message' in dir(user_funcs): message_hash = user_funcs.hash_message( self._original_content, self._is_request, self._content_info, self._helpers, self._request, self._request_content_info) if message_hash is None: # Base it off just the URL and request/response content_info = self._content_info if self._is_request else self._request_content_info url = content_info.getUrl().getPath() message_hash = (url, self._is_request) return message_hash def saveTypeMenu(self): """Open an input dialog to save the current type definiton""" type_defs = blackboxprotobuf.known_messages.keys() type_defs.insert(0, "New...") selection = JOptionPane.showInputDialog(self._component, "Select name for type", "Type selection", JOptionPane.PLAIN_MESSAGE, None, type_defs, None) if selection is None: return elif selection == "New...": selection = JOptionPane.showInputDialog("Enter new name") blackboxprotobuf.known_messages[selection] = self.message_type self._extender.suite_tab.updateList() def loadTypeMenu(self): """Open an input menu dialog to load a type definition""" type_defs = blackboxprotobuf.known_messages.keys() selection = JOptionPane.showInputDialog(self._component, "Select type", "Type selection", JOptionPane.PLAIN_MESSAGE, None, type_defs, None) if selection is None: return message_type = blackboxprotobuf.known_messages[selection] try: self.applyType(message_type) except Exception as exc: self._callbacks.printError(traceback.format_exc()) JOptionPane.showMessageDialog(self._component, "Error applying type: " + str(exc)) def applyType(self, typedef): """Apply a new typedef to the message. Throws an exception if type is invalid.""" # Convert to protobuf as old type and re-interpret as new type old_message_type = self.message_type json_data = self._text_editor.getText().tostring() protobuf_data = blackboxprotobuf.protobuf_from_json( json_data, old_message_type) new_json, message_type = blackboxprotobuf.protobuf_to_json( str(protobuf_data), typedef) # Should exception out before now if there is an issue # Set the message type and reparse with the new type self.message_type = message_type self._text_editor.setText(str(new_json)) message_hash = self.getMessageHash() self._extender.known_types[message_hash] = message_type
class AtfAreaView(JPanel): ''' Initializes the ATF (edit/model) view and sets its layout. ''' def __init__(self, controller): ''' Creates default empty text area in a panel for ATF edition. It has syntax highlighting based on the ATF parser (pyoracc). It also highlights line numbers where there are validations errors returned by the ORACC server. ''' # Give reference to controller to delegate action response self.controller = controller # Make text area occupy all available space and resize with parent # window self.setLayout(BorderLayout()) # Short hand for edit area and line numbers area self.edit_area = self.controller.edit_area self.line_numbers_area = self.controller.line_numbers_area # Create secondary text area for split view self.secondary_area = self.controller.secondary_area self.secondary_line_numbers = self.controller.secondary_line_numbers # Set undo/redo manager to edit area self.undo_manager = UndoManager() self.undo_manager.limit = 3000 self.edit_listener = AtfUndoableEditListener(self.undo_manager) self.edit_area.getDocument().addUndoableEditListener( self.edit_listener) # Sort out layout by synch-ing line numbers and text area and putting # only the text area in a scroll pane as indicated in the # TextLineNumber tutorial. self.edit_area.setPreferredSize(Dimension(1, 500)) self.container = JScrollPane(self.edit_area) self.container.setRowHeaderView(self.line_numbers_area) self.add(self.container, BorderLayout.CENTER) self.vert_scroll = self.container.getVerticalScrollBar() self.vert_scroll.addAdjustmentListener(atfAreaAdjustmentListener(self)) # Key listener that triggers syntax highlighting, etc. upon key release self.edit_area.addKeyListener(AtfAreaKeyListener(self)) # Also needed in secondary area: self.secondary_area.addKeyListener(AtfAreaKeyListener(self)) # Add a document listener to track changes to files docListener = atfAreaDocumentListener(self) self.edit_area.getDocument().addDocumentListener(docListener) # instance variable to store a record of the text contents prior to the # most recent change. Needed so that the different listeners can access # this to handle error line updating. self.oldtext = '' def toggle_split(self, split_orientation=None): ''' Clear ATF edit area and repaint chosen layout (splitscreen/scrollpane). ''' # Remove all existent components in parent JPanel self.removeAll() # Check what editor view to toggle self.setup_edit_area(split_orientation) # Revalitate is needed in order to repaint the components self.revalidate() self.repaint() def setup_edit_area(self, split_orientation=None): ''' Check if the ATF text area is being displayed in a split editor. If so, resets to normal JScrollPane. If not, splits the screen. ''' if isinstance(self.container, JSplitPane): # If Nammu is already displaying a split pane, reset to original # setup self.container = JScrollPane(self.edit_area) self.container.setRowHeaderView(self.line_numbers_area) self.container.setVisible(True) self.add(self.container, BorderLayout.CENTER) else: # If there is not a split pane, create both panels and setup view main_editor = JScrollPane(self.edit_area) main_editor.setRowHeaderView(self.line_numbers_area) secondary_editor = JScrollPane(self.secondary_area) secondary_editor.setRowHeaderView(self.secondary_line_numbers) self.container = JSplitPane(split_orientation, main_editor, secondary_editor) self.container.setDividerSize(5) self.container.setVisible(True) self.container.setDividerLocation(0.5) self.container.setResizeWeight(0.5) self.add(self.container, BorderLayout.CENTER) def get_viewport_carets(self): ''' Get the top left and bottom left caret position of the current viewport ''' extent = self.container.getViewport().getExtentSize() top_left_position = self.container.getViewport().getViewPosition() top_left_char = self.edit_area.viewToModel(top_left_position) bottom_left_position = Point(top_left_position.x, top_left_position.y + extent.height) bottom_left_char = self.edit_area.viewToModel(bottom_left_position) # Something has gone wrong. Assume that top_left should be at the start # of the file if top_left_char >= bottom_left_char: top_left_char = 0 # Get the text in the full edit area text = self.controller.edit_area.getText() # Pad the top of the viewport to capture up to the nearest header and # the bottom by 2 lines top_ch = self.controller.pad_top_viewport_caret(top_left_char, text) bottom_ch = self.controller.pad_bottom_viewport_caret( bottom_left_char, text) return top_ch, bottom_ch def refresh(self): ''' Restyle edit area using user selected appearance settings. ''' config = self.controller.controller.config # Create a new font with the new size font = set_font(config['edit_area_style']['fontsize']['user']) # Update the sytnax highlighter font params, so our changes are not # superceded self.controller.syntax_highlighter.font = font self.controller.syntax_highlighter.setup_attribs() attrs = self.controller.edit_area.getInputAttributes() StyleConstants.setFontSize(attrs, font.getSize()) # Get the Styledoc so we can update it doc = self.controller.edit_area.getStyledDocument() # Apply the new fontsize to the whole document doc.setCharacterAttributes(0, doc.getLength() + 1, attrs, False)
class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory): def registerExtenderCallbacks(self, callbacks): # keep a reference to our callbacks object self._callbacks = callbacks # obtain an extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("Autorize") # create the log and a lock on which to synchronize when adding log entries self._log = ArrayList() self._lock = Lock() self.intercept = 0 self.initInterceptionFilters() self.initEnforcementDetector() self.initExport() self.initConfigurationTab() self.initTabs() self.initCallbacks() print "Thank you for installing Autorize v0.9 extension" print "by Barak Tawily" return def initExport(self): # ## init enforcement detector tab # exportLType = JLabel("File Type:") exportLType.setBounds(10, 10, 100, 30) exportLES = JLabel("Enforcement Statuses:") exportLES.setBounds(10, 50, 160, 30) exportFileTypes = ["HTML"] self.exportType = JComboBox(exportFileTypes) self.exportType.setBounds(100, 10, 200, 30) exportES = [ "All Statuses", "Authorization bypass!", "Authorization enforced??? (please configure enforcement detector)", "Authorization enforced!" ] self.exportES = JComboBox(exportES) self.exportES.setBounds(100, 50, 200, 30) exportLES = JLabel("Statuses:") exportLES.setBounds(10, 50, 100, 30) self.exportButton = JButton("Export", actionPerformed=self.exportToHTML) self.exportButton.setBounds(390, 25, 100, 30) self.exportPnl = JPanel() self.exportPnl.setLayout(None) self.exportPnl.setBounds(0, 0, 1000, 1000) self.exportPnl.add(exportLType) self.exportPnl.add(self.exportType) self.exportPnl.add(exportLES) self.exportPnl.add(self.exportES) self.exportPnl.add(self.exportButton) def initEnforcementDetector(self): # ## init enforcement detector tab # self.EDFP = ArrayList() self.EDCT = ArrayList() EDLType = JLabel("Type:") EDLType.setBounds(10, 10, 140, 30) EDLContent = JLabel("Content:") EDLContent.setBounds(10, 50, 140, 30) EDLabelList = JLabel("Filter List:") EDLabelList.setBounds(10, 165, 140, 30) EDStrings = [ "Finger Print: (enforced message body contains)", "Content-Length: (constant Content-Length number of enforced response)" ] self.EDType = JComboBox(EDStrings) self.EDType.setBounds(80, 10, 430, 30) self.EDText = JTextArea("", 5, 30) self.EDText.setBounds(80, 50, 300, 110) self.EDModel = DefaultListModel() self.EDList = JList(self.EDModel) self.EDList.setBounds(80, 175, 300, 110) self.EDList.setBorder(LineBorder(Color.BLACK)) self.EDAdd = JButton("Add filter", actionPerformed=self.addEDFilter) self.EDAdd.setBounds(390, 85, 120, 30) self.EDDel = JButton("Remove filter", actionPerformed=self.delEDFilter) self.EDDel.setBounds(390, 210, 120, 30) self.EDPnl = JPanel() self.EDPnl.setLayout(None) self.EDPnl.setBounds(0, 0, 1000, 1000) self.EDPnl.add(EDLType) self.EDPnl.add(self.EDType) self.EDPnl.add(EDLContent) self.EDPnl.add(self.EDText) self.EDPnl.add(self.EDAdd) self.EDPnl.add(self.EDDel) self.EDPnl.add(EDLabelList) self.EDPnl.add(self.EDList) def initInterceptionFilters(self): # ## init interception filters tab # IFStrings = [ "URL Contains: ", "Scope items only: (Content is not required)" ] self.IFType = JComboBox(IFStrings) self.IFType.setBounds(80, 10, 430, 30) self.IFModel = DefaultListModel() self.IFList = JList(self.IFModel) self.IFList.setBounds(80, 175, 300, 110) self.IFList.setBorder(LineBorder(Color.BLACK)) self.IFText = JTextArea("", 5, 30) self.IFText.setBounds(80, 50, 300, 110) IFLType = JLabel("Type:") IFLType.setBounds(10, 10, 140, 30) IFLContent = JLabel("Content:") IFLContent.setBounds(10, 50, 140, 30) IFLabelList = JLabel("Filter List:") IFLabelList.setBounds(10, 165, 140, 30) self.IFAdd = JButton("Add filter", actionPerformed=self.addIFFilter) self.IFAdd.setBounds(390, 85, 120, 30) self.IFDel = JButton("Remove filter", actionPerformed=self.delIFFilter) self.IFDel.setBounds(390, 210, 120, 30) self.filtersPnl = JPanel() self.filtersPnl.setLayout(None) self.filtersPnl.setBounds(0, 0, 1000, 1000) self.filtersPnl.add(IFLType) self.filtersPnl.add(self.IFType) self.filtersPnl.add(IFLContent) self.filtersPnl.add(self.IFText) self.filtersPnl.add(self.IFAdd) self.filtersPnl.add(self.IFDel) self.filtersPnl.add(IFLabelList) self.filtersPnl.add(self.IFList) def initConfigurationTab(self): # ## init configuration tab # self.prevent304 = JCheckBox("Prevent 304 Not Modified status code") self.prevent304.setBounds(290, 25, 300, 30) self.ignore304 = JCheckBox("Ignore 304/204 status code responses") self.ignore304.setBounds(290, 5, 300, 30) self.ignore304.setSelected(True) self.autoScroll = JCheckBox("Auto Scroll") self.autoScroll.setBounds(290, 45, 140, 30) startLabel = JLabel("Authorization checks:") startLabel.setBounds(10, 10, 140, 30) self.startButton = JButton("Autorize is off", actionPerformed=self.startOrStop) self.startButton.setBounds(160, 10, 120, 30) self.startButton.setBackground(Color(255, 100, 91, 255)) self.clearButton = JButton("Clear List", actionPerformed=self.clearList) self.clearButton.setBounds(10, 40, 100, 30) self.replaceString = JTextArea("Cookie: Insert=injected; header=here;", 5, 30) self.replaceString.setWrapStyleWord(True) self.replaceString.setLineWrap(True) self.replaceString.setBounds(10, 80, 470, 180) self.filtersTabs = JTabbedPane() self.filtersTabs.addTab("Enforcement Detector", self.EDPnl) self.filtersTabs.addTab("Interception Filters", self.filtersPnl) self.filtersTabs.addTab("Export", self.exportPnl) self.filtersTabs.setBounds(0, 280, 2000, 700) self.pnl = JPanel() self.pnl.setBounds(0, 0, 1000, 1000) self.pnl.setLayout(None) self.pnl.add(self.startButton) self.pnl.add(self.clearButton) self.pnl.add(self.replaceString) self.pnl.add(startLabel) self.pnl.add(self.autoScroll) self.pnl.add(self.ignore304) self.pnl.add(self.prevent304) self.pnl.add(self.filtersTabs) def initTabs(self): # ## init autorize tabs # self.logTable = Table(self) self._splitpane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._splitpane.setResizeWeight(1) self.scrollPane = JScrollPane(self.logTable) self._splitpane.setLeftComponent(self.scrollPane) self.scrollPane.getVerticalScrollBar().addAdjustmentListener( autoScrollListener(self)) copyURLitem = JMenuItem("Copy URL") copyURLitem.addActionListener(copySelectedURL(self)) self.menu = JPopupMenu("Popup") self.menu.add(copyURLitem) self.tabs = JTabbedPane() self._requestViewer = self._callbacks.createMessageEditor(self, False) self._responseViewer = self._callbacks.createMessageEditor(self, False) self._originalrequestViewer = self._callbacks.createMessageEditor( self, False) self._originalresponseViewer = self._callbacks.createMessageEditor( self, False) self.tabs.addTab("Modified Request", self._requestViewer.getComponent()) self.tabs.addTab("Modified Response", self._responseViewer.getComponent()) self.tabs.addTab("Original Request", self._originalrequestViewer.getComponent()) self.tabs.addTab("Original Response", self._originalresponseViewer.getComponent()) self.tabs.addTab("Configuration", self.pnl) self.tabs.setSelectedIndex(4) self._splitpane.setRightComponent(self.tabs) def initCallbacks(self): # ## init callbacks # # customize our UI components self._callbacks.customizeUiComponent(self._splitpane) self._callbacks.customizeUiComponent(self.logTable) self._callbacks.customizeUiComponent(self.scrollPane) self._callbacks.customizeUiComponent(self.tabs) self._callbacks.customizeUiComponent(self.filtersTabs) self._callbacks.registerContextMenuFactory(self) # add the custom tab to Burp's UI self._callbacks.addSuiteTab(self) # ## Events functions # def startOrStop(self, event): if self.startButton.getText() == "Autorize is off": self.startButton.setText("Autorize is on") self.startButton.setBackground(Color.GREEN) self.intercept = 1 self._callbacks.registerHttpListener(self) else: self.startButton.setText("Autorize is off") self.startButton.setBackground(Color(255, 100, 91, 255)) self.intercept = 0 self._callbacks.removeHttpListener(self) def addEDFilter(self, event): typeName = self.EDType.getSelectedItem().split(":")[0] self.EDModel.addElement(typeName + ": " + self.EDText.getText()) def delEDFilter(self, event): index = self.EDList.getSelectedIndex() if not index == -1: self.EDModel.remove(index) def addIFFilter(self, event): typeName = self.IFType.getSelectedItem().split(":")[0] self.IFModel.addElement(typeName + ": " + self.IFText.getText()) def delIFFilter(self, event): index = self.IFList.getSelectedIndex() if not index == -1: self.IFModel.remove(index) def clearList(self, event): self._lock.acquire() self._log = ArrayList() row = self._log.size() self.fireTableRowsInserted(row, row) self._lock.release() def exportToHTML(self, event): parentFrame = JFrame() fileChooser = JFileChooser() fileChooser.setSelectedFile(File("AutorizeReprort.html")) fileChooser.setDialogTitle("Save Autorize Report") userSelection = fileChooser.showSaveDialog(parentFrame) if userSelection == JFileChooser.APPROVE_OPTION: fileToSave = fileChooser.getSelectedFile() enforcementStatusFilter = self.exportES.getSelectedItem() htmlContent = """<html><title>Autorize Report by Barak Tawily</title> <style> .datagrid table { border-collapse: collapse; text-align: left; width: 100%; } .datagrid {font: normal 12px/150% Arial, Helvetica, sans-serif; background: #fff; overflow: hidden; border: 1px solid #006699; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .datagrid table td, .datagrid table th { padding: 3px 10px; } .datagrid table thead th {background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #006699), color-stop(1, #00557F) );background:-moz-linear-gradient( center top, #006699 5%, #00557F 100% );filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#006699', endColorstr='#00557F');background-color:#006699; color:#FFFFFF; font-size: 15px; font-weight: bold; border-left: 1px solid #0070A8; } .datagrid table thead th:first-child { border: none; }.datagrid table tbody td { color: #00496B; border-left: 1px solid #E1EEF4;font-size: 12px;font-weight: normal; }.datagrid table tbody .alt td { background: #E1EEF4; color: #00496B; }.datagrid table tbody td:first-child { border-left: none; }.datagrid table tbody tr:last-child td { border-bottom: none; }.datagrid table tfoot td div { border-top: 1px solid #006699;background: #E1EEF4;} .datagrid table tfoot td { padding: 0; font-size: 12px } .datagrid table tfoot td div{ padding: 2px; }.datagrid table tfoot td ul { margin: 0; padding:0; list-style: none; text-align: right; }.datagrid table tfoot li { display: inline; }.datagrid table tfoot li a { text-decoration: none; display: inline-block; padding: 2px 8px; margin: 1px;color: #FFFFFF;border: 1px solid #006699;-webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #006699), color-stop(1, #00557F) );background:-moz-linear-gradient( center top, #006699 5%, #00557F 100% );filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#006699', endColorstr='#00557F');background-color:#006699; }.datagrid table tfoot ul.active, .datagrid table tfoot ul a:hover { text-decoration: none;border-color: #006699; color: #FFFFFF; background: none; background-color:#00557F;}div.dhtmlx_window_active, div.dhx_modal_cover_dv { position: fixed !important; } table { width: 100%; table-layout: fixed; } td { border: 1px solid #35f; overflow: hidden; text-overflow: ellipsis; } td.a { width: 13%; white-space: nowrap; } td.b { width: 9%; word-wrap: break-word; } </style> <body> <h1>Autorize Report<h1> <div class="datagrid"><table> <thead><tr><th>URL</th><th>Authorization Enforcement Status</th></tr></thead> <tbody>""" for i in range(0, self._log.size()): color = "" if self._log.get( i )._enfocementStatus == "Authorization enforced??? (please configure enforcement detector)": color = "yellow" if self._log.get(i)._enfocementStatus == "Authorization bypass!": color = "red" if self._log.get(i)._enfocementStatus == "Authorization enforced!": color = "LawnGreen" if enforcementStatusFilter == "All Statuses": htmlContent += "<tr bgcolor=\"%s\"><td><a href=\"%s\">%s</a></td><td>%s</td></tr>" % ( color, self._log.get(i)._url, self._log.get(i)._url, self._log.get(i)._enfocementStatus) else: if enforcementStatusFilter == self._log.get( i)._enfocementStatus: htmlContent += "<tr bgcolor=\"%s\"><td><a href=\"%s\">%s</a></td><td>%s</td></tr>" % ( color, self._log.get(i)._url, self._log.get(i)._url, self._log.get(i)._enfocementStatus) htmlContent += "</tbody></table></div></body></html>" f = open(fileToSave.getAbsolutePath(), 'w') f.writelines(htmlContent) f.close() # # implement IContextMenuFactory # def createMenuItems(self, invocation): responses = invocation.getSelectedMessages() if responses > 0: ret = LinkedList() requestMenuItem = JMenuItem("Send request to Autorize") cookieMenuItem = JMenuItem("Send cookie to Autorize") requestMenuItem.addActionListener( handleMenuItems(self, responses[0], "request")) cookieMenuItem.addActionListener( handleMenuItems(self, responses[0], "cookie")) ret.add(requestMenuItem) ret.add(cookieMenuItem) return (ret) return null # # implement ITab # def getTabCaption(self): return "Autorize" def getUiComponent(self): return self._splitpane # # extend AbstractTableModel # def getRowCount(self): try: return self._log.size() except: return 0 def getColumnCount(self): return 2 def getColumnName(self, columnIndex): if columnIndex == 0: return "URL" if columnIndex == 1: return "Authorization Enforcement Status" return "" def getValueAt(self, rowIndex, columnIndex): logEntry = self._log.get(rowIndex) if columnIndex == 0: return logEntry._url.toString() if columnIndex == 1: return logEntry._enfocementStatus return "" # # implement IMessageEditorController # this allows our request/response viewers to obtain details about the messages being displayed # def getHttpService(self): return self._currentlyDisplayedItem.getHttpService() def getRequest(self): return self._currentlyDisplayedItem.getRequest() def getResponse(self): return self._currentlyDisplayedItem.getResponse() # # implement IHttpListener # def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): if self.intercept == 1: if self.prevent304.isSelected(): if messageIsRequest: requestHeaders = list( self._helpers.analyzeRequest(messageInfo).getHeaders()) newHeaders = list() found = 0 for header in requestHeaders: if not "If-None-Match:" in header and not "If-Modified-Since:" in header: newHeaders.append(header) found = 1 if found == 1: requestInfo = self._helpers.analyzeRequest(messageInfo) bodyBytes = messageInfo.getRequest()[requestInfo. getBodyOffset():] bodyStr = self._helpers.bytesToString(bodyBytes) messageInfo.setRequest( self._helpers.buildHttpMessage( newHeaders, bodyStr)) if not messageIsRequest: if not self.replaceString.getText( ) in self._helpers.analyzeRequest(messageInfo).getHeaders(): if self.ignore304.isSelected(): firstHeader = self._helpers.analyzeResponse( messageInfo.getResponse()).getHeaders()[0] if "304" in firstHeader or "204" in firstHeader: return if self.IFList.getModel().getSize() == 0: self.checkAuthorization( messageInfo, self._helpers.analyzeResponse( messageInfo.getResponse()).getHeaders()) else: urlString = str( self._helpers.analyzeRequest(messageInfo).getUrl()) for i in range(0, self.IFList.getModel().getSize()): if self.IFList.getModel().getElementAt(i).split( ":")[0] == "Scope items only": currentURL = URL(urlString) if self._callbacks.isInScope(currentURL): self.checkAuthorization( messageInfo, self._helpers.analyzeResponse( messageInfo.getResponse()). getHeaders()) if self.IFList.getModel().getElementAt(i).split( ":")[0] == "URL Contains": if self.IFList.getModel().getElementAt( i)[14:] in urlString: self.checkAuthorization( messageInfo, self._helpers.analyzeResponse( messageInfo.getResponse()). getHeaders()) return def makeRequest(self, messageInfo, message): requestURL = self._helpers.analyzeRequest(messageInfo).getUrl() return self._callbacks.makeHttpRequest( self._helpers.buildHttpService( str(requestURL.getHost()), int(requestURL.getPort()), requestURL.getProtocol() == "https"), message) def makeMessage(self, messageInfo, removeOrNot): requestInfo = self._helpers.analyzeRequest(messageInfo) headers = requestInfo.getHeaders() if removeOrNot: headers = list(headers) removeHeaders = ArrayList() removeHeaders.add(self.replaceString.getText() [0:self.replaceString.getText().index(":")]) for header in headers[:]: for removeHeader in removeHeaders: if removeHeader in header: headers.remove(header) headers.append(self.replaceString.getText()) msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] return self._helpers.buildHttpMessage(headers, msgBody) def checkAuthorization(self, messageInfo, originalHeaders): message = self.makeMessage(messageInfo, True) requestResponse = self.makeRequest(messageInfo, message) analyzedResponse = self._helpers.analyzeResponse( requestResponse.getResponse()) oldStatusCode = originalHeaders[0] newStatusCode = analyzedResponse.getHeaders()[0] oldContentLen = self.getContentLength(originalHeaders) newContentLen = self.getContentLength(analyzedResponse.getHeaders()) impression = "" EDFilters = self.EDModel.toArray() if oldStatusCode == newStatusCode: if oldContentLen == newContentLen: impression = "Authorization bypass!" else: impression = "Authorization enforced??? (please configure enforcement detector)" for filter in EDFilters: if str(filter).startswith("Content-Length: "): if newContentLen == filter: impression = "Authorization enforced!" if str(filter).startswith("Finger Print: "): if filter[14:] in self._helpers.bytesToString( requestResponse.getResponse() [analyzedResponse.getBodyOffset():]): impression = "Authorization enforced!" else: impression = "Authorization enforced!" self._lock.acquire() row = self._log.size() self._log.add( LogEntry(self._callbacks.saveBuffersToTempFiles(requestResponse), self._helpers.analyzeRequest(requestResponse).getUrl(), messageInfo, impression)) # same requests not include again. self.fireTableRowsInserted(row, row) self._lock.release() def getContentLength(self, analyzedResponseHeaders): for header in analyzedResponseHeaders: if "Content-Length:" in header: return header return "null" def getCookieFromMessage(self, messageInfo): headers = list( self._helpers.analyzeRequest( messageInfo.getRequest()).getHeaders()) for header in headers: if "Cookie:" in header: return header return None
class ProtoBufEditorTab(burp.IMessageEditorTab): """Tab in interceptor/repeater for editing protobuf message. Decodes them to JSON and back. The message type is attached to this object. """ def __init__(self, extension, controller, editable, callbacks): self._callbacks = callbacks self._extension = extension self._callbacks = extension.callbacks self._helpers = extension.helpers self._controller = controller self._text_editor = self._callbacks.createTextEditor() self._text_editor.setEditable(editable) self._editable = editable self._last_valid_type_index = None self._filtered_message_model = FilteredMessageModel( extension.known_message_model, self._callbacks ) self._type_list_component = JList(self._filtered_message_model) self._type_list_component.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) self._type_list_component.addListSelectionListener(TypeListListener(self)) self._new_type_field = JTextField() self._component = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._component.setLeftComponent(self._text_editor.getComponent()) self._component.setRightComponent(self.createButtonPane()) self._component.setResizeWeight(0.95) self.message_type = None self._is_request = None self._encoder = None self._original_json = None self._original_typedef = None self._last_set_json = "" self._content_info = None self._request_content_info = None self._request = None self._original_content = None def getTabCaption(self): """Return message tab caption""" return "Protobuf" def getMessage(self): """Transform the JSON format back to the binary protobuf message""" try: if self.message_type is None or not self.isModified(): return self._original_content json_data = self._text_editor.getText().tostring() protobuf_data = blackboxprotobuf.protobuf_from_json( json_data, self.message_type ) protobuf_data = self.encodePayload(protobuf_data) if "set_protobuf_data" in dir(user_funcs): result = user_funcs.set_protobuf_data( protobuf_data, self._original_content, self._is_request, self._content_info, self._helpers, self._request, self._request_content_info, ) if result is not None: return result headers = self._content_info.getHeaders() return self._helpers.buildHttpMessage(headers, str(protobuf_data)) except Exception as exc: self._callbacks.printError(traceback.format_exc()) JOptionPane.showMessageDialog( self._component, "Error encoding protobuf: " + str(exc) ) # Resets state return self._original_content def setMessage(self, content, is_request, retry=True): """Get the data from the request/response and parse into JSON. sets self.message_type """ # Save original content self._original_content = content if is_request: self._content_info = self._helpers.analyzeRequest( self._controller.getHttpService(), content ) else: self._content_info = self._helpers.analyzeResponse(content) self._is_request = is_request self._request = None self._request_content_info = None if not is_request: self._request = self._controller.getRequest() self._request_content_info = self._helpers.analyzeRequest( self._controller.getHttpService(), self._request ) # how we remember which message type correlates to which endpoint self._message_hash = self.getMessageHash() # Try to find saved messsage type self.message_type = None self.message_type_name = None if self._message_hash in self._extension.saved_types: typename = self._extension.saved_types[self._message_hash] if typename in default_config.known_types: self.message_type_name = typename self.message_type = default_config.known_types[typename] else: del self._extension.saved_types[self._message_hash] try: protobuf_data = None if "get_protobuf_data" in dir(user_funcs): protobuf_data = user_funcs.get_protobuf_data( content, is_request, self._content_info, self._helpers, self._request, self._request_content_info, ) if protobuf_data is None: protobuf_data = content[self._content_info.getBodyOffset() :].tostring() protobuf_data = self.decodePayload(protobuf_data) # source_typedef will be the original, updatable version of the dict # TODO fix this hack self._original_data = protobuf_data self._filtered_message_model.set_new_data(protobuf_data) self._source_typedef = self.message_type json_data, self.message_type = blackboxprotobuf.protobuf_to_json( protobuf_data, self.message_type ) self._original_json = json_data self._original_typedef = self.message_type self._last_set_json = str(json_data) self._text_editor.setText(json_data) success = True except Exception as exc: success = False self._callbacks.printError( "Got error decoding protobuf binary: " + traceback.format_exc() ) # Bring out of exception handler to avoid nexting handlers if not success: if self._message_hash in self._extension.saved_types: del self._extension.saved_types[self._message_hash] self.setMessage(content, is_request, False) self._text_editor.setText("Error decoding protobuf") if self.message_type_name: self.forceSelectType(self.message_type_name) def decodePayload(self, payload): """Add support for decoding a few default methods. Including Base64 and GZIP""" if payload.startswith(bytearray([0x1F, 0x8B, 0x08])): gzip_decompress = zlib.decompressobj(-zlib.MAX_WBITS) self._encoder = "gzip" return gzip_decompress.decompress(payload) # Try to base64 decode try: protobuf = base64.b64decode(payload, validate=True) self._encoder = "base64" return protobuf except Exception as exc: pass # try decoding as a gRPC payload: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md # we're naiively handling only uncompressed payloads if len(payload) > 1 + 4 and payload.startswith( bytearray([0x00]) ): # gRPC has 1 byte flag + 4 byte length (message_length,) = struct.unpack_from(">I", payload[1:]) if len(payload) == 1 + 4 + message_length: self._encoder = "gRPC" return payload[1 + 4 :] # try: # protobuf = base64.urlsafe_b64decode(payload) # self._encoder = 'base64_url' # return protobuf # except Exception as exc: # pass self._encoder = None return payload def encodePayload(self, payload): """If we detected an encoding like gzip or base64 when decoding, redo that encoding step here """ if self._encoder == "base64": return base64.b64encode(payload) elif self._encoder == "base64_url": return base64.urlsafe_b64encode(payload) elif self._encoder == "gzip": gzip_compress = zlib.compressobj(-1, zlib.DEFLATED, -zlib.MAX_WBITS) self._encoder = "gzip" return gzip_compress.compress(payload) elif self._encoder == "gRPC": message_length = struct.pack(">I", len(payload)) return bytearray([0x00]) + bytearray(message_length) + payload else: return payload def getSelectedData(self): """Get text currently selected in message""" return self._text_editor.getSelectedText() def getUiComponent(self): """Return Java AWT component for this tab""" return self._component def isEnabled(self, content, is_request): """Try to detect a protobuf in the message to enable the tab. Defaults to content-type header of 'x-protobuf'. User overridable """ # TODO implement some more default checks if is_request: info = self._helpers.analyzeRequest(content) else: info = self._helpers.analyzeResponse(content) if "detect_protobuf" in dir(user_funcs): result = user_funcs.detect_protobuf( content, is_request, info, self._helpers ) if result is not None: return result # Bail early if there is no body if info.getBodyOffset() == len(content): return False protobuf_content_types = [ "protobuf", "grpc", ] # Check all headers for x-protobuf for header in info.getHeaders(): if "content-type" in header.lower(): for protobuf_content_type in protobuf_content_types: if protobuf_content_type in header.lower(): return True return False def isModified(self): """Return if the message was modified""" return self._text_editor.isTextModified() def createButtonPane(self): """Create a new button pane for the message editor tab""" self._button_listener = EditorButtonListener(self) panel = JPanel() panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS)) panel.setBorder(EmptyBorder(5, 5, 5, 5)) panel.add(Box.createRigidArea(Dimension(0, 5))) type_scroll_pane = JScrollPane(self._type_list_component) type_scroll_pane.setMaximumSize(Dimension(200, 100)) type_scroll_pane.setMinimumSize(Dimension(150, 100)) panel.add(type_scroll_pane) panel.add(Box.createRigidArea(Dimension(0, 3))) new_type_panel = JPanel() new_type_panel.setLayout(BoxLayout(new_type_panel, BoxLayout.X_AXIS)) new_type_panel.add(self._new_type_field) new_type_panel.add(Box.createRigidArea(Dimension(3, 0))) new_type_panel.add( self.createButton( "New", "new-type", "Save this message's type under a new name" ) ) new_type_panel.setMaximumSize(Dimension(200, 20)) new_type_panel.setMinimumSize(Dimension(150, 20)) panel.add(new_type_panel) button_panel = JPanel() button_panel.setLayout(FlowLayout()) if self._editable: button_panel.add( self.createButton( "Validate", "validate", "Validate the message can be encoded." ) ) button_panel.add( self.createButton("Edit Type", "edit-type", "Edit the message type") ) button_panel.add( self.createButton( "Reset Message", "reset", "Reset the message and undo changes" ) ) button_panel.add( self.createButton( "Clear Type", "clear-type", "Reparse the message with an empty type" ) ) button_panel.setMinimumSize(Dimension(100, 200)) button_panel.setPreferredSize(Dimension(200, 1000)) panel.add(button_panel) return panel def createButton(self, text, command, tooltip): """Create a new button with the given text and command""" button = JButton(text) button.setAlignmentX(Component.CENTER_ALIGNMENT) button.setActionCommand(command) button.addActionListener(self._button_listener) button.setToolTipText(tooltip) return button def validateMessage(self): """Callback for validate button. Attempts to encode the message with the current type definition """ try: json_data = self._text_editor.getText().tostring() blackboxprotobuf.protobuf_from_json(json_data, self.message_type) # If it works, save the message self._original_json = json_data self._original_typedef = self.message_type except Exception as exc: JOptionPane.showMessageDialog(self._component, str(exc)) self._callbacks.printError(traceback.format_exc()) def resetMessage(self): """Drop any changes and reset the message. Callback for "reset" button """ self._last_set_json = str(self._original_json) self._text_editor.setText(self._original_json) self.message_type = self._original_typedef def getMessageHash(self): """Compute an "identifier" for the message which is used for sticky type definitions. User modifiable """ message_hash = None if "hash_message" in dir(user_funcs): message_hash = user_funcs.hash_message( self._original_content, self._is_request, self._content_info, self._helpers, self._request, self._request_content_info, ) if message_hash is None: # Base it off just the URL and request/response content_info = ( self._content_info if self._is_request else self._request_content_info ) url = content_info.getUrl().getPath() message_hash = ":".join([url, str(self._is_request)]) return message_hash def forceSelectType(self, typename): index = self._filtered_message_model.get_type_index(typename) if index is not None: self._last_valid_type_index = index self._type_list_component.setSelectedIndex(index) def updateTypeSelection(self): """Apply a new typedef based on the selected type in the type list""" # Check if something is selected if self._type_list_component.isSelectionEmpty(): self._last_valid_type_index = None del self._extension.saved_types[self._message_hash] return # TODO won't actually work right if we delete the type we're using a # new type is now in the index if self._last_valid_type_index == self._type_list_component.getSelectedIndex(): # hasn't actually changed since last time we tried # otherwise can trigger a second time when we call setSelectedIndex below on failure return type_name = self._type_list_component.getSelectedValue() # try to catch none here... if not type_name or type_name not in default_config.known_types: return try: self.applyType(default_config.known_types[type_name]) except BlackboxProtobufException as exc: self._callbacks.printError(traceback.format_exc()) if isinstance(exc, EncoderException): JOptionPane.showMessageDialog( self._component, "Error encoding protobuf with previous type: %s" % (exc), ) elif isinstance(exc, DecoderException): JOptionPane.showMessageDialog( self._component, "Error encoding protobuf with type %s: %s" % (type_name, exc), ) # decoder exception means it doesn't match the message that was sucessfully encoded by the prev type self._filtered_message_model.remove_type(type_name) if self._last_valid_type_index is not None: type_name = self._type_list_component.setSelectedIndex( self._last_valid_type_index ) else: self._type_list_component.clearSelection() return self._extension.saved_types[self._message_hash] = type_name self._last_valid_type_index = self._type_list_component.getSelectedIndex() def editType(self, typedef): """Apply the new typedef. Use dict.update to change the original dictionary, so we also update the anonymous cached definition and ones stored in known_messages""" # TODO this is kind of an ugly hack. Should redo how these are referenced # probably means rewriting a bunch of the editor old_source = self._source_typedef old_source.clear() old_source.update(typedef) self.applyType(old_source) def applyType(self, typedef): """Apply a new typedef to the message. Throws an exception if type is invalid.""" # store a reference for later mutation? self._source_typedef = typedef # Convert to protobuf as old type and re-interpret as new type old_message_type = self.message_type json_data = self._text_editor.getText().tostring() protobuf_data = blackboxprotobuf.protobuf_from_json(json_data, old_message_type) new_json, message_type = blackboxprotobuf.protobuf_to_json( str(protobuf_data), typedef ) # Should exception out before now if there is an issue self.message_type = message_type # if the json data was modified, then re-check our types if json_data != self._last_set_json: self._filtered_message_model.set_new_data(protobuf_data) self._last_set_json = str(new_json) self._text_editor.setText(str(new_json)) def saveAsNewType(self): """Copy the current type into known_messages""" name = self._new_type_field.getText().strip() if not NAME_REGEX.match(name): JOptionPane.showMessageDialog( self._component, "%s is not a valid " "message name. Message names should be alphanumeric." % name, ) return if name in default_config.known_types: JOptionPane.showMessageDialog( self._component, "Message name %s is " "already taken." % name ) return # Do a deep copy on the dictionary so we don't accidentally modify others default_config.known_types[name] = copy.deepcopy(self.message_type) # update the list of messages. This should trickle down to known message model self._extension.known_message_model.addElement(name) self._new_type_field.setText("") self._extension.saved_types[self._message_hash] = name # force select our new type self.forceSelectType(name) def clearType(self): self.applyType({}) self._type_list_component.clearSelection() self._new_type_field.setText("") def open_typedef_window(self): self._extension.open_typedef_editor(self.message_type, self.editType)
class BurpExtender(IBurpExtender, ITab, IContextMenuFactory): # # implement IBurpExtender # #global vars to access all the textboxes global btnDecode, btnGenerate global left_tb1, left_tb2, left_tb3, right_tb1, right_tb2, right_tb3 #global vars to set the endian value global tmp1, tmp2, tmp3, tmp4, tmp7 def registerExtenderCallbacks(self, callbacks): endianVal = 0 def generateTextBox(defaultTxt, editable): sample = JTextArea(5, 20) scrollPane1 = JScrollPane(sample) sample.setBounds(0, 0, 400, 400) sample.setEditable(editable) sample.setLineWrap(True) sample.setWrapStyleWord(True) sample.setBorder(BorderFactory.createLineBorder(Color.BLACK)) sample.setText(defaultTxt) return sample def decodeData(event): #Get Base64 token full_str = base64.b64decode(self._left_tb1.getText()) sha_mac = full_str[44:64].encode('hex') inflate_data = full_str[76:] data = zlib.decompress(inflate_data) user_length = data[20] loc = 21 tokenDetails = "User name :" + data[ loc:loc + int(user_length.encode('hex'), 16)].replace( "\x00", "") + "\n" loc = loc + int(user_length.encode('hex'), 16) lang_length = data[loc] loc = loc + 1 tokenDetails += "Lang Code :" + data[ loc:loc + int(lang_length.encode('hex'), 16)].replace( "\x00", "") + "\n" tmp1.setText(data[loc:loc + int(lang_length.encode('hex'), 16)].replace( "\x00", "")) loc = loc + int(lang_length.encode('hex'), 16) node_length = data[loc] loc = loc + 1 tokenDetails += "Node name :" + data[ loc:loc + int(node_length.encode('hex'), 16)].replace( "\x00", "") + "\n" tmp2.setText(data[loc:loc + int(node_length.encode('hex'), 16)].replace( "\x00", "")) loc = loc + int(node_length.encode('hex'), 16) time_length = data[loc] loc = loc + 1 tokenDetails += "Creation time :" + data[ loc:loc + int(time_length.encode('hex'), 16)].replace( "\x00", "") tmp = data[loc:loc + int(time_length.encode('hex'), 16)].replace( "\x00", "") datestamp = tmp[:len(tmp) - 7] tmp3.setText(datestamp) # Determine if it's little or big endian if (data[4:8].encode('hex') == '04030201'): endianVal = 0 else: endianVal = 1 tmp4.setText(str(endianVal)) hashcatFormat = sha_mac + ":" + data.encode("hex") left_tb2.setText(tokenDetails) left_tb3.setText(hashcatFormat) def make_field(part, size): part = chr(len(part) + size) + part return part def generateToken(event): newtoken = "" endianVal = int(tmp4.getText()) if endianVal == 1: username = right_tb2.getText().encode('utf_16_be') nodepw = right_tb1.getText().encode('utf_16_le') lang = tmp1.getText().encode('utf_16_be') nodename = tmp2.getText().encode('utf_16_be') datestamp = (tmp3.getText() + '.164266').encode('utf_16_be') token_ver = "8.10".encode('utf_16_be') unknown_field = "N".encode('utf_16_be') uncompressed_data = '\x01\x02\x03\x04\x00\x01\x00\x00\x00\x00\x02\xbc\x00\x00\x00\x00' + make_field( username, 0) + make_field(lang, 0) + make_field( nodename, 0) + make_field(datestamp, 0) + '\x00' uncompressed_field = '\x00\x00\x00' + make_field( uncompressed_data, 4) inflate_data = zlib.compress(uncompressed_field) sha1_mac = hashlib.sha1(uncompressed_field + nodepw).digest() uncompressed_length = chr(len(uncompressed_field)) static_headers1 = '\x01\x02\x03\x04\x00\x01\x00\x00\x00\x00\x02\xbc\x00\x00\x00\x00\x00\x00\x00\x2c\x00\x04\x53\x68\x64\x72\x02' + unknown_field + uncompressed_length + '\x08' + token_ver + '\x14' static_headers2 = '\x00\x05\x53\x64\x61\x74\x61' body = '\x00\x00\x00' + make_field( static_headers2 + make_field(inflate_data, 0), 4) token = '\x00\x00\x00' + make_field( static_headers1 + sha1_mac + body, 4) newtoken = base64.b64encode(token) elif endianVal == 0: username = right_tb2.getText().encode('utf_16_le') nodepw = right_tb1.getText().encode('utf_16_le') lang = tmp1.getText().encode('utf_16_le') nodename = tmp2.getText().encode('utf_16_le') datestamp = (tmp3.getText() + '.999543').encode('utf_16_le') token_ver = "8.10".encode('utf_16_le') unknown_field = "N".encode('utf_16_le') uncompressed_data = '\x00\x00\x00\x04\x03\x02\x01\x01\x00\x00\x00\xbc\x02\x00\x00\x00\x00\x00\x00' + make_field( username, 0) + make_field(lang, 0) + make_field( nodename, 0) + make_field(datestamp, 0) + '\x00' uncompressed_field = make_field(uncompressed_data, 1) inflate_data = zlib.compress(uncompressed_field) sha1_mac = hashlib.sha1(uncompressed_field + nodepw).digest() uncompressed_length = chr(len(uncompressed_field)) static_headers1 = '\x00\x00\x00\x04\x03\x02\x01\x01\x00\x00\x00\xbc\x02\x00\x00\x00\x00\x00\x00\x2c\x00\x00\x00\x04\x00\x53\x68\x64\x72\x02' + unknown_field + uncompressed_length + '\x08' + token_ver + '\x14' static_headers2 = '\x00\x00\x00\x05\x00\x53\x64\x61\x74\x61' body = make_field( static_headers2 + make_field(inflate_data, 0), 1) token = make_field(static_headers1 + sha1_mac + body, 1) newtoken = base64.b64encode(token) right_tb3.setText("PS_TOKEN=" + newtoken + ";") # keep a reference to our callbacks object self._callbacks = callbacks # obtain an extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("PeopleSoft PSToken Processor") # main split pane self._splitpane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._splitpane.setResizeWeight(0.5) c = GridBagConstraints() c.weightx = 1 c.weighty = 1 c.anchor = GridBagConstraints.NORTHWEST #Temp variables tmp1 = JLabel("tmp") tmp2 = JLabel("tmp") tmp3 = JLabel("tmp") tmp4 = JLabel("tmp") # add left panel panel1 = JPanel(GridBagLayout()) header1 = JLabel("EXTRACTION") header1.setFont(Font("Myriad Pro", Font.BOLD, 24)) left_t1 = JLabel("PS_TOKEN Cookie") left_t2 = JLabel("Token Details") left_t3 = JLabel("Token Hash + Salt (Hashcat Format)") left_t4 = JLabel( "Save this into a .hash file and run the following Hashcat command: hashcat -m 13500 <hashfile> <dictionary file>" ) self._left_tb1 = generateTextBox("Your PS_TOKEN value here", True) left_tb2 = generateTextBox("Token Details here", False) left_tb3 = generateTextBox("Hashcat format here", False) btnDecode = JButton("Decode", actionPerformed=decodeData) btnGenerate = JButton("Generate", actionPerformed=generateToken) #add right panel panel2 = JPanel(GridBagLayout()) header2 = JLabel("GENERATION") header2.setFont(Font("Myriad Pro", Font.BOLD, 24)) right_t1 = JLabel("Node password") right_t2 = JLabel("New username") right_t3 = JLabel("New Base64 PS_TOKEN") right_t4 = JLabel( "Match & Replace rule to modify PS_TOKEN (Type: Request Header, enable regex)" ) right_t5 = JLabel("Match rule: PS_TOKEN=[A-Za-z0-9\/\+]*;") right_t6 = JLabel("Replace: PS_TOKEN=<new generated PS_TOKEN>;") right_tb1 = generateTextBox("Password here", True) right_tb2 = generateTextBox("PSADMIN", True) right_tb3 = generateTextBox("Your new token here", False) panel1.add(header1, c) panel2.add(header2, c) c.gridx = 0 c.gridy = 1 panel1.add(left_t1, c) panel2.add(right_t1, c) c.gridy += 1 panel1.add(self._left_tb1, c) panel2.add(right_tb1, c) c.gridy += 1 panel1.add(left_t2, c) panel2.add(right_t2, c) c.gridy += 1 panel1.add(left_tb2, c) panel2.add(right_tb2, c) c.gridy += 1 panel1.add(left_t3, c) panel2.add(right_t3, c) c.gridy += 1 panel1.add(left_t4, c) panel2.add(right_t4, c) c.gridy += 1 panel1.add(left_tb3, c) panel2.add(right_t5, c) c.gridy += 1 panel2.add(right_t6, c) c.gridy += 1 panel2.add(right_tb3, c) c.gridy += 1 panel1.add(btnDecode, c) panel2.add(btnGenerate, c) self._splitpane.setLeftComponent(panel1) self._splitpane.setRightComponent(panel2) # customize our UI components callbacks.customizeUiComponent(self._splitpane) #callbacks.customizeUiComponent(panel1) #callbacks.customizeUiComponent(scrollPane) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) #add option to do right-click callbacks.registerContextMenuFactory(self) return # # implement ITab # def getTabCaption(self): return "PSTOKEN-JYTHON" def getUiComponent(self): return self._splitpane def createMenuItems(self, invocation): def sendValue(e): bd = invocation.getSelectionBounds() res = "" if invocation.getInvocationContext() == 2: data = invocation.getSelectedMessages()[0].getRequest() for i in (data[bd[0]:bd[1]]): res = res + chr(int(i)) elif invocation.getInvocationContext() == 3: data = invocation.getSelectedMessages()[0].getRequest() for i in (data[bd[0]:bd[1]]): res = res + chr(int(i)) self._left_tb1.setText(res) menuList = [] item = JMenuItem("Send to PSTOKEN", actionPerformed=sendValue) menuList.append(item) return menuList
def initGUI(self): # # Manual tab # tabPane = JTabbedPane(JTabbedPane.TOP) reqRestabPane = JTabbedPane(JTabbedPane.TOP) splitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) tabPane.addTab("Repeater", splitPane) splitPane2 = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) splitPane.setLeftComponent(splitPane2) panel1 = JPanel() panel2 = JPanel() splitPane2.setLeftComponent(panel1) splitPane2.setRightComponent(panel2) splitPane.setRightComponent(reqRestabPane) panel1.setLayout(BoxLayout(panel1,BoxLayout.Y_AXIS)) panel2.setLayout(BoxLayout(panel2,BoxLayout.Y_AXIS)) self.requestPanel = self._callbacks.createMessageEditor(None, False) self.responsePanel = self._callbacks.createMessageEditor(None, False) label1 = JLabel("files and folders") self.lblFilename = JLabel("File name") label3 = JLabel("Response") self.editField = self._callbacks.createMessageEditor(None, True) self.dirList = JList([], valueChanged = self.listSelect) listFileDirPane = JScrollPane(self.dirList) ## Set left align listFileDirPane.setAlignmentX(Component.LEFT_ALIGNMENT) btnPanel = JPanel() btnGo = JButton("Compress & Go", actionPerformed = self.btnGoClick) btnSave = JButton("Save", actionPerformed = self.btnSaveClick) btnClear = JButton("Clear", actionPerformed = self.btnClearClick) btnReset = JButton("Reset", actionPerformed = self.btnResetRepeaterClick) btnPanel.add(btnGo) btnPanel.add(btnSave) btnPanel.add(btnReset) btnPanel.add(btnClear) btnPanel.setLayout(BoxLayout(btnPanel,BoxLayout.X_AXIS)) panel1.add(label1) panel1.add(listFileDirPane) panel2.add(self.lblFilename) panel2.add(self.editField.getComponent()) panel2.add(btnPanel) reqRestabPane.addTab("Response",self.responsePanel.getComponent()) reqRestabPane.addTab("Request",self.requestPanel.getComponent()) splitPane.setResizeWeight(0.6) # # Scanner tab # scanSplitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) tabPane.addTab("Scanner", scanSplitPane) scanSplitPane2 = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) scanSplitPane.setLeftComponent(scanSplitPane2) scanPanel1 = JPanel() scanPanel2 = JPanel() scanPanel3 = JPanel() scanSplitPane2.setLeftComponent(scanPanel1) scanSplitPane2.setRightComponent(scanPanel2) scanSplitPane.setRightComponent(scanPanel3) scanPanel1.setLayout(BoxLayout(scanPanel1,BoxLayout.Y_AXIS)) scanPanel2.setLayout(BoxLayout(scanPanel2,BoxLayout.Y_AXIS)) scanPanel3.setLayout(BoxLayout(scanPanel3,BoxLayout.Y_AXIS)) scanLabel1 = JLabel("files and folders") self.scanLblFilename = JLabel("File name") scanLabel3 = JLabel("<html><h3>Config scanner</h3></html>") scanLabel4 = JLabel("<html><h3>Scanner status</h3></html>") scanLabel5 = JLabel("""<html> <div> <h3>Notice</h3> <ul> <li>Possible to run only a scan at time</li> <li>Work with .zip file only</li> <li>Cannot continue after exit Burp</li> </ul> </div> </html>""") self.scannerStatusLabel = JLabel("<html><i style='color:grey'> Not Running</i></html>") self.checkboxScanFilename = JCheckBox("Also scan zip filename (this may be upload several files to server)") self.scanEditField = self._callbacks.createMessageEditor(None, False) self.scanDirList = JList([], valueChanged = self.scanListSelect) scanListFileDirPane = JScrollPane(self.scanDirList) ## Set left align scanListFileDirPane.setAlignmentX(Component.LEFT_ALIGNMENT) scanBtnPanel = JPanel() self.scanBtnGo = JButton("Set insertion point", actionPerformed = self.btnSetInsertionPointClick) self.scanBtnSave = JButton("Send to scanner", actionPerformed = self.btnScanClick) self.scanBtnClearInsertionPoint = JButton("Clear insertion points", actionPerformed = self.scanBtnClearInsClick) self.scanBtnClear = JButton("Clear", actionPerformed = self.scanBtnClearClick) self.scanBtnCancel = JButton("Cancel", actionPerformed = self.cancelScan) scanBtnPanel.add(self.scanBtnGo) scanBtnPanel.add(self.scanBtnSave) scanBtnPanel.add(self.scanBtnClearInsertionPoint) scanBtnPanel.add(self.scanBtnClear) scanBtnPanel.setLayout(BoxLayout(scanBtnPanel,BoxLayout.X_AXIS)) scanPanel1.add(scanLabel1) scanPanel1.add(scanListFileDirPane) scanPanel2.add(self.scanLblFilename) scanPanel2.add(self.scanEditField.getComponent()) scanPanel2.add(scanBtnPanel) scanPanel3.add(scanLabel3) scanPanel3.add(self.checkboxScanFilename) scanPanel3.add(scanLabel4) scanPanel3.add(self.scannerStatusLabel) scanPanel3.add(self.scanBtnCancel) self.scanBtnCancel.setEnabled(False) scanPanel3.add(scanLabel5) scanSplitPane.setResizeWeight(0.6) self.tab = tabPane
def registerExtenderCallbacks(self, callbacks): # Set encoding to utf-8 to avoid some errors reload(sys) sys.setdefaultencoding('utf8') # Keep a reference to callback object and helper object self._callbacks = callbacks self._helpers = callbacks.getHelpers() # Set the extension name that shows in the burp extension menu callbacks.setExtensionName("InjectionScanner") # Create the log and a lock on which to synchronize when adding log entries self._log = ArrayList() self._logLock = Lock() self._httpLock = Lock() # The length of the basis used to fetch abnormal data, default to zero self._basisLen = 0 # 1: {POST. GET}; 2: {urlencoded, json, xml} self._postGet = 'NaN' self._dataType = 'NaN' # Scan list self._simpleList = [ '\'', '\"', '/', '/*', '#', ')', '(', ')\'', '(\'', 'and 1=1', 'and 1=2', 'and 1>2', 'and 12', '+', 'and+12', '/**/and/**/1' ] self._xmlList = ['a', 'b', 'c', 'd', 'e'] # Not setted # Response mutex: True = is blocking; False = free to go # self._mutexR = False # Other classes instance self._dataTable = Guis_DefaultTM() self._logTable = Guis_AbstractTM(self) self._xh = XMLHandler() listeners = Guis_Listeners(self, self._logTable) ''' Setting GUIs ''' # Divide the whole pane two: one upper and one lower pane self._mainSplitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) self._mainSplitpane.setResizeWeight(0.4) # Initizlize request table dataTable = JTable(self._dataTable) dataScrollPane = JScrollPane(dataTable) dataScrollPane.setPreferredSize(Dimension(0, 125)) self._dataTable.addTableModelListener(listeners) # Initialize log table logTable = Guis_LogTable(self._logTable) logScrollPane = JScrollPane(logTable) logScrollPane.setPreferredSize(Dimension(0, 125)) # Split the upper pane to two panes tableSplitpane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) tableSplitpane.setResizeWeight(0.5) # Set the data table to the left and log to the right tableSplitpane.setLeftComponent(dataScrollPane) tableSplitpane.setRightComponent(logScrollPane) # Tabs with request/response viewers tabs = JTabbedPane() self._requestViewer = callbacks.createMessageEditor(self, False) self._responseViewer = callbacks.createMessageEditor(self, False) tabs.addTab("Request", self._requestViewer.getComponent()) tabs.addTab("Response", self._responseViewer.getComponent()) # Create buttons that do operation with the test self._basisLabel = JLabel('Basis: ' + str(self._basisLen)) self._levelLabel = JLabel('Level:') self._setBasisButton = JButton('Set Basis') self._hitOnceButton = JButton('Hit Once') self._autoScanButton = JButton('Auto Scan') self._clearLogButton = JButton('Clear Log') self._cancelButton = JButton('Cancel') self._levelSelection = JComboBox() self._levelSelection.addItem('1') self._levelSelection.addItem('2') self._levelSelection.addItem('3') self._hitOnceButton.addActionListener(listeners) self._autoScanButton.addActionListener(listeners) self._clearLogButton.addActionListener(listeners) self._setBasisButton.addActionListener(listeners) self._cancelButton.addActionListener(listeners) self._basisLabel.setPreferredSize(Dimension(100, 20)) # Create bottom pane for holding the buttons buttonPane = JPanel() buttonPane.setLayout(BorderLayout()) centerPane = JPanel() leftPane = JPanel() rightPane = JPanel() leftPane.add(self._basisLabel) centerPane.add(self._setBasisButton) centerPane.add(self._hitOnceButton) centerPane.add(self._autoScanButton) centerPane.add(self._cancelButton) centerPane.add(self._clearLogButton) rightPane.add(self._levelLabel) rightPane.add(self._levelSelection) buttonPane.add(centerPane, BorderLayout.CENTER) buttonPane.add(leftPane, BorderLayout.WEST) buttonPane.add(rightPane, BorderLayout.EAST) # Create and set the bottom panel that holds viewers and buttons utilPane = JPanel() utilPane.setLayout(BorderLayout()) utilPane.add(tabs, BorderLayout.CENTER) utilPane.add(buttonPane, BorderLayout.SOUTH) self._mainSplitpane.setLeftComponent(tableSplitpane) self._mainSplitpane.setRightComponent(utilPane) # Customize UI components callbacks.customizeUiComponent(self._mainSplitpane) callbacks.customizeUiComponent(dataTable) callbacks.customizeUiComponent(dataScrollPane) callbacks.customizeUiComponent(logTable) callbacks.customizeUiComponent(logScrollPane) callbacks.customizeUiComponent(tabs) callbacks.customizeUiComponent(buttonPane) callbacks.customizeUiComponent(utilPane) callbacks.customizeUiComponent(self._basisLabel) callbacks.customizeUiComponent(self._setBasisButton) callbacks.customizeUiComponent(self._hitOnceButton) callbacks.customizeUiComponent(self._autoScanButton) callbacks.customizeUiComponent(self._clearLogButton) callbacks.customizeUiComponent(self._levelSelection) callbacks.customizeUiComponent(self._cancelButton) # Add the custom tab to Burp's UI callbacks.addSuiteTab(self) # Register the context menu and message editor for new tabs callbacks.registerContextMenuFactory(self) # Register as a HTTP listener callbacks.registerHttpListener(self) return
class BurpExtender(IBurpExtender, ITab, IHttpListener, IContextMenuFactory, IMessageEditorController, AbstractTableModel): # # implement IBurpExtender # def registerExtenderCallbacks(self, callbacks): print("[*] Loading Jaeles beta v0.1") # keep a reference to our callbacks object self._callbacks = callbacks # obtain an extension helpers object self._helpers = callbacks.getHelpers() # set our extension name callbacks.setExtensionName("Jaeles") # main split pane self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) # table of log entries # logTable = Table(self) # scrollPane = JScrollPane(logTable) # _toppane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) _mainpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) _mainpane.setResizeWeight(0.5) # _mainpane = JPanel() _toppane = JPanel() # top pane self.banner = JLabel("Jaeles - The Swiss Army knife for automated Web Application Testing. ") self.banner.setBounds(50, 30, 200, 400) self.banner2 = JLabel("Official Documentation: https://jaeles-project.github.io/") self.banner2.setBounds(100, 50, 200, 400) _toppane.add(self.banner) _toppane.add(self.banner2) # _botpane = JPanel() _botpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) # bot pane self.HostLabel = JLabel("Jaeles Endpoint: ") self.HostLabel.setBounds(100, 150, 200, 400) self.Jaeles_endpoint = 'http://127.0.0.1:5000/api/parse' self.jwt = 'Jaeles token_here' # just prevent plugin error when you doesn't have server running try: self.initial() jwt, endpoint = self.get_config() if endpoint: self.Jaeles_endpoint = endpoint if jwt: self.jwt = jwt except: pass endpoint_pane = JPanel() # end point to submit request self.EndpointText = JTextArea(self.Jaeles_endpoint, 3, 100) self.jwtLabel = JLabel("Jaeles JWT token: ") self.jwtLabel.setBounds(100, 300, 250, 450) self.jwtText = JTextArea(self.jwt, 3, 100, lineWrap=True) buttons = JPanel() self.buttonLabel = JLabel("Actions: ") self.buttonLabel.setBounds(150, 200, 200, 400) self._saveButton = JButton("Save", actionPerformed=self.saveToken) self._loadButton = JButton( "Test Connection", actionPerformed=self.butClick) self._reloadButton = JButton("Reload", actionPerformed=self.butClick) oob_control = JPanel() self.oobLabel = JLabel("OOB: ") self.oobLabel.setBounds(150, 200, 200, 400) self._saveoob = JButton("Save OOB", actionPerformed=self.saveToken) self._pollingBox = JCheckBox("Polling") self._pollingBox.setBounds(290, 25, 300, 30) oob_control.add(self.oobLabel) oob_control.add(self._saveoob) oob_control.add(self._pollingBox) # _botpane.add(self.banner) # endpoint_pane.add(self.blankLabel) endpoint_pane.add(self.HostLabel) endpoint_pane.add(self.EndpointText) endpoint_pane.add(self.jwtLabel) endpoint_pane.add(self.jwtText) buttons.add(self.buttonLabel) buttons.add(self._saveButton) buttons.add(self._loadButton) buttons.add(self._reloadButton) _botpane.setLeftComponent(oob_control) _botpane.setLeftComponent(endpoint_pane) _botpane.setRightComponent(buttons) _botpane.setResizeWeight(0.7) # set _mainpane.setLeftComponent(_toppane) _mainpane.setRightComponent(_botpane) self._splitpane.setLeftComponent(_mainpane) ########### # tabs with request/response viewers tabs = JTabbedPane() self.log_area = JTextArea("", 5, 30) # self._requestViewer = callbacks.createMessageEditor(self, False) tabs.addTab("Log", self.log_area) # tabs.addTab("Config", self._requestViewer.getComponent()) self._splitpane.setRightComponent(tabs) self._splitpane.setResizeWeight(0.5) callbacks.customizeUiComponent(self._splitpane) callbacks.customizeUiComponent(tabs) callbacks.registerContextMenuFactory(self) # add the custom tab to Burp's UI callbacks.addSuiteTab(self) # register ourselves as an HTTP listener # callbacks.registerHttpListener(self) self.print_log("[*] Jaeles Loaded ...") return # # implement ITab # ## def saveToken(self, e): token = self.jwtText.getText().strip() endpoint = self.EndpointText.getText().strip() self.Jaeles_endpoint = endpoint self.jwt = token self.set_config(token, endpoint) def butClick(self, e): button_name = e.getActionCommand() if button_name == 'Reload': # self.initial() username, password = self.get_cred() self.login(username, password) jwt, endpoint = self.get_config() self.Jaeles_endpoint = endpoint self.jwt = jwt self.print_log("[+] Reload Config") elif button_name == 'Test Connection': connection = self.test_connection() if connection: self.print_log("[+] Ready to send request to {0}".format(self.Jaeles_endpoint)) else: self.print_log("[-] Fail to authen with API server at {0}".format(self.Jaeles_endpoint)) def createMenuItems(self, invocation): responses = invocation.getSelectedMessages() if responses > 0: ret = LinkedList() requestMenuItem = JMenuItem("[*] Send request to Jaeles Endpoint") requestMenuItem.addActionListener( handleMenuItems(self, responses, "request")) ret.add(requestMenuItem) return ret return None def highlightTab(self): currentPane = self._splitpane previousPane = currentPane while currentPane and not isinstance(currentPane, JTabbedPane): previousPane = currentPane currentPane = currentPane.getParent() if currentPane: index = currentPane.indexOfComponent(previousPane) currentPane.setBackgroundAt(index, Color(0xff6633)) class setColorBackActionListener(ActionListener): def actionPerformed(self, e): currentPane.setBackgroundAt(index, Color.BLACK) timer = Timer(5000, setColorBackActionListener()) timer.setRepeats(False) timer.start() def getTabCaption(self): return "Jaeles" def getUiComponent(self): return self._splitpane # # implement Polling Collaborator # this allows our request/response viewers to obtain details about the messages being displayed # # def jaeles_collab(self, collab): # oob = collab.generatePayload(True) # # oob2 = collab.generatePayload(True) # # print(oob2) # self.print_log("[+] Gen oob host: {0}".format(oob)) # # print(oob) # # os.system('curl {0}'.format(oob)) # # implement IMessageEditorController # this allows our request/response viewers to obtain details about the messages being displayed # def sendRequestToJaeles(self, messageInfos): for messageInfo in messageInfos: data_json = self.req_parsing(messageInfo) if data_json: self.print_log("Import to external Jaeles ...") self.import_to_Jaeles(data_json) else: self.print_log("No response on selected request") self.print_log("-"*30) # start of my function def req_parsing(self, messageInfo): data_json = {} data_json['req_scheme'] = str(messageInfo.getProtocol()) # return http data_json['req_host'] = str(messageInfo.getHost()) data_json['req_port'] = str(messageInfo.getPort()) data_json['url'] = str(messageInfo.getUrl()) # full request full_req = self._helpers.bytesToString(messageInfo.getRequest()) data_json['req'] = self.just_base64(str(full_req)) if messageInfo.getResponse(): full_res = self._helpers.bytesToString(messageInfo.getResponse()) else: full_res = None if not full_res: data_json['res'] = "" return data_json data_json['res'] = self.just_base64(str(full_res.encode('utf-8'))) return data_json # send data to Jaeles API Endpoint def import_to_Jaeles(self, data_json): req = urllib2.Request(self.Jaeles_endpoint) req.add_header('Content-Type', 'application/json') req.add_header('Authorization', self.jwt) response = urllib2.urlopen(req, json.dumps(data_json)) if str(response.code) == "200": self.print_log("[+] Start scan {0}".format(data_json['url'])) else: self.print_log("[-] Fail Send request to {0}".format(self.Jaeles_endpoint)) # check if token is available or not def initial(self): connection = self.test_connection() if connection: return True username, password = self.get_cred() valid_cred = self.login(username, password) if valid_cred: return True return False # do login def login(self, username, password): req = urllib2.Request(self.Jaeles_endpoint.replace("/api/parse","/auth/login")) req.add_header('Content-Type', 'application/json') response = urllib2.urlopen(req, json.dumps({"username": username, "password": password})) if str(response.code) == "200": data = json.loads(response.read()) token = "Jaeles " + data.get("token") self.set_config(token, self.Jaeles_endpoint, username, password) print("[+] Authentication success on {0}".format(self.Jaeles_endpoint)) return True else: print("[-] Can't authen on {0}".format(self.Jaeles_endpoint)) return False # check connection def test_connection(self): req = urllib2.Request(self.Jaeles_endpoint.replace("/parse", "/ping")) req.add_header('Content-Type', 'application/json') req.add_header('Authorization', self.jwt) try: response = urllib2.urlopen(req) if str(response.code) == "200": return True except: pass return False # get default credentials def get_cred(self): config_path = self.get_config_path() if os.path.isfile(config_path): with open(config_path, 'r') as f: data = json.load(f) print('[+] Load credentials from {0}'.format(config_path)) return data.get('username', False), data.get('password', False) else: print('[-] No config file to load.') return False, False # get token and endpoint def get_config(self): config_path = self.get_config_path() if os.path.isfile(config_path): with open(config_path, 'r') as f: data = json.load(f) print('[+] Load JWT from {0}'.format(config_path)) return data.get('JWT', False), data.get('endpoint', False) else: print('[-] No config file to load.') return False, False # save jwt token and endpoint to ~/.jaeles/burp.json def set_config(self, token, endpoint, username='', password=''): data = { 'JWT': token, 'endpoint': endpoint, 'username': username, 'password': password, } config_path = self.get_config_path() jaeles_path = os.path.dirname(config_path) if jaeles_path and not os.path.exists(jaeles_path): os.makedirs(jaeles_path) with open(config_path, 'w+') as f: json.dump(data, f) print('[+] Store JWT in {0}'.format(config_path)) return True def just_base64(self, text): if not text: return "" return str(base64.b64encode(str(text))) def get_config_path(self): home = os.path.expanduser('~{0}'.format(getpass.getuser())) jaeles_path = os.path.join(home, '.jaeles') config_path = os.path.join(jaeles_path, 'burp.json') return config_path def print_log(self, text): if type(text) != str: text = str(text) self.log_area.append(text) self.log_area.append("\n") def getHttpService(self): return self._currentlyDisplayedItem.getHttpService() def getRequest(self): return self._currentlyDisplayedItem.getRequest() def getResponse(self): return self._currentlyDisplayedItem.getResponse()