class PropertyEditor(WindowAdapter): """ Edits Tabular Properties of a given WindowAdapter """ instances = {} last_location = None locations = {} last_size = None sizes = {} NEW_WINDOW_OFFSET = 32 offset = NEW_WINDOW_OFFSET @staticmethod def get_instance(text="Property Editor", columns=None, data=None, empty=None, add_actions=True, actions=None): """ Singleton Method based on the text property. It tries to generate only one property configuration page per text. :param text: getinstance key :param columns: proparty columns it should be an array alike :param data: it contains the current property rows :param empty: empty row property when adding a new one :param add_actions: include or not new actions :param actions: default set of actions to be appended to Add and Delete Rows :return: a new instance of PropertyEditor or a reused one. """ if not actions: actions = [] if not columns: columns = [] if data == None: data = [] if not empty: empty = [] try: PropertyEditor.instances[text] except KeyError: PropertyEditor.instances[text] = \ PropertyEditor().__private_init__(text, columns, data, empty, add_actions, actions) try: PropertyEditor.instances[text].this.setLocation( PropertyEditor.locations[text]) except KeyError: if PropertyEditor.last_location: PropertyEditor.instances[text].this.setLocation( PropertyEditor.last_location.x + PropertyEditor.offset, PropertyEditor.last_location.y + PropertyEditor.offset) PropertyEditor.offset = PropertyEditor.NEW_WINDOW_OFFSET try: PropertyEditor.instances[text].this.setSize( PropertyEditor.sizes[text]) except KeyError: if PropertyEditor.last_size: PropertyEditor.instances[text].this.setSize( PropertyEditor.last_size) PropertyEditor.last_location = PropertyEditor.instances[ text].this.getLocation() PropertyEditor.last_size = PropertyEditor.instances[ text].this.getSize() ## Hack ON: Bring on Front PropertyEditor.instances[text].this.setAlwaysOnTop(True) PropertyEditor.instances[text].this.setAlwaysOnTop(False) ## Hack OFF return PropertyEditor.instances[text] def __private_init__(self, text="Property Editor", columns=None, data=None, empty=None, add_actions=True, actions=None): if not actions: actions = [] if not columns: columns = [] if data == None: data = [] if not empty: empty = [] self._text = text self.this = JFrame(text) self._table = JTable() self._dtm = DefaultTableModel(0, 0) self._dtm.setColumnIdentifiers(columns) self._table.setModel(self._dtm) self._data = data for d in data: self._dtm.addRow(d) self._pane = JScrollPane(self._table) self.this.add(self._pane) self._empty = empty self.this.addWindowListener(self) self._dtm.addTableModelListener(lambda _: self._update_model()) self.this.setLocation(PropertyEditor.NEW_WINDOW_OFFSET, PropertyEditor.NEW_WINDOW_OFFSET) if add_actions: self._popup = JPopupMenu() self._pane.setComponentPopupMenu(self._popup) inherits_popup_menu(self._pane) self._actions = actions self._actions.append( ExecutorAction('Remove Selected Rows', action=lambda e: self._remove_row())) self._actions.append( ExecutorAction('Add New Row', action=lambda e: self._add_row())) for action in self._actions: self._popup.add(action.menuitem) self.this.setForeground(Color.black) self.this.setBackground(Color.lightGray) self.this.pack() self.this.setVisible(True) self.this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE) return self def _add_row(self): """ Add a new row the selection :return: None """ self._dtm.addRow(self._empty) def _remove_row(self): """ Remove all the selected rows from the selection :return: """ rows = self._table.getSelectedRows() for i in range(0, len(rows)): self._dtm.removeRow(rows[i] - i) def windowClosing(self, evt): """ Overrides WindowAdapter method :param evt: unused :return: None """ PropertyEditor.locations[self._text] = self.this.getLocation() PropertyEditor.sizes[self._text] = self.this.getSize() PropertyEditor.last_location = self.this.getLocation() PropertyEditor.last_size = self.this.getSize() PropertyEditor.offset = 0 self.this.setVisible(False) self.this.dispose() del PropertyEditor.instances[self._text] def _update_model(self): """ Update the data content with the updated rows :return: None """ del self._data[:] nRow = self._dtm.getRowCount() nCol = self._dtm.getColumnCount() for i in range(0, nRow): self._data.append([None] * nCol) for j in range(0, nCol): d = str(self._dtm.getValueAt(i, j)).lower() if d == 'none' or d == '': self._data[i][j] = None elif d == 'true' or d == 't': self._data[i][j] = True elif d == 'false' or d == 'f': self._data[i][j] = False else: try: self._data[i][j] = int(self._dtm.getValueAt(i, j)) except ValueError: self._data[i][j] = self._dtm.getValueAt(i, j)
class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory, IScannerCheck): name = "Femida XSS" conf_path = "./config.py" _jTabbedPane = JTabbedPane() _jPanel = JPanel() _jAboutPanel = JPanel() _jPanelConstraints = GridBagConstraints() _jLabelParameters = None _jTextFieldParameters = None _jLabelTechniques = None _jTextFieldURL = None _jLabelFuzzFactor = None _jTextFieldFuzzFactor = None _jLabelAdditionalCmdLine = None _jTextFieldAdditionalCmdLine = None _jButtonSetCommandLine = None _jLabelAbout = None _overwriteHeader = False _overwriteParam = False _forkRequestParam = False def doActiveScan(self, baseRequestResponse, insertionPoint): scan_issues = [] try: requestString = str(baseRequestResponse.getRequest().tostring()) newRequestString = self.prepareRequest(requestString) vulnerable, verifyingRequestResponse = self.quickCheckScan( newRequestString, baseRequestResponse) except Exception as msg: print(msg) return [] def quickCheckScan(self, preparedRequest, requestResponse): check = self._callbacks.makeHttpRequest( requestResponse.getHttpService(), self._helpers.stringToBytes(preparedRequest)) vulner = self._helpers.analyzeResponse( check.getResponse()).getStatusCode() == 200 return vulner, check # # implement IBurpExtender # def registerExtenderCallbacks(self, callbacks): self._callbacks = callbacks self._helpers = callbacks.getHelpers() self._callbacks.setExtensionName(self.name) self._callbacks.registerScannerCheck(self) self._dictPayloads = {} self._dictHeaders = {} self._dictParams = {} self.status_flag = False self.jfc = JFileChooser("./") self.jfc.setDialogTitle("Upload Payloads") self.jfc.setFileFilter(FileNameExtensionFilter("TXT file", ["txt"])) self._layout = GridBagLayout() self._jPanel.setLayout(self._layout) self._jLabelTechniques = JLabel("Press to start:") self.createAnyView(self._jLabelTechniques, 0, 0, 3, 1, Insets(0, 0, 10, 0)) self.submitSearchButton = swing.JButton( 'Run proxy', actionPerformed=self.active_flag) self.submitSearchButton.setBackground(Color.WHITE) self.createAnyView(self.submitSearchButton, 3, 0, 6, 1, Insets(0, 0, 10, 0)) self._jPanel.setBounds(0, 0, 1000, 1000) self._jLabelTechniques = JLabel("Your URL (my.burpcollaborator.net):") self.createAnyView(self._jLabelTechniques, 0, 1, 3, 1, Insets(0, 0, 10, 0)) self._jTextFieldURL = JTextField("", 30) self._jTextFieldURL.addActionListener(self.setCallbackUrl) self.createAnyView(self._jTextFieldURL, 3, 1, 5, 1, Insets(0, 0, 10, 0)) self._forkRequestButton = swing.JButton( 'Parallel Request', actionPerformed=self.forkRequest) self._forkRequestButton.setBackground(Color.WHITE) self.createAnyView(self._forkRequestButton, 8, 1, 1, 1, Insets(0, 0, 10, 0)) self._tableModelPayloads = DefaultTableModel() self._tableModelPayloads.addColumn("Payload") self._tableModelPayloads.addColumn("Active") self._tableModelHeaders = DefaultTableModel() self._tableModelHeaders.addColumn("Header") self._tableModelHeaders.addColumn("Active") self._tableModelParams = DefaultTableModel() self._tableModelParams.addColumn("Parameter") self._tableModelParams.addColumn("Active") self._payloadTable = self.createAnyTable(self._tableModelPayloads, 1, Dimension(300, 200)) self.createAnyView(self._payloadTable, 0, 2, 3, 1, Insets(0, 0, 0, 10)) self._headerTable = self.createAnyTable(self._tableModelHeaders, 2, Dimension(300, 200)) self.createAnyView(self._headerTable, 3, 2, 3, 1, Insets(0, 0, 0, 10)) self._paramTable = self.createAnyTable(self._tableModelParams, 3, Dimension(300, 200)) self.createAnyView(self._paramTable, 6, 2, 3, 1, Insets(0, 0, 0, 0)) deletePayloadButton = swing.JButton( 'Delete', actionPerformed=self.deleteToPayload) deletePayloadButton.setBackground(Color.WHITE) self.createAnyView(deletePayloadButton, 0, 3, 1, 1, Insets(3, 0, 0, 0)) deletePayloadButton = swing.JButton( 'Upload', actionPerformed=self.uploadToPayload) deletePayloadButton.setBackground(Color.WHITE) self.createAnyView(deletePayloadButton, 1, 3, 1, 1, Insets(3, 0, 0, 0)) addPayloadButton = swing.JButton('Add', actionPerformed=self.addToPayload) addPayloadButton.setBackground(Color.WHITE) self.createAnyView(addPayloadButton, 2, 3, 1, 1, Insets(3, 0, 0, 10)) deleteHeaderButton = swing.JButton('Delete', actionPerformed=self.deleteToHeader) deleteHeaderButton.setBackground(Color.WHITE) self.createAnyView(deleteHeaderButton, 3, 3, 1, 1, Insets(3, 0, 0, 0)) self._overwriteHeaderButton = swing.JButton( 'Overwrite', actionPerformed=self.overwriteHeader) self._overwriteHeaderButton.setBackground(Color.WHITE) self.createAnyView(self._overwriteHeaderButton, 4, 3, 1, 1, Insets(3, 0, 0, 0)) addHeaderButton = swing.JButton('Add', actionPerformed=self.addToHeader) addHeaderButton.setBackground(Color.WHITE) self.createAnyView(addHeaderButton, 5, 3, 1, 1, Insets(3, 0, 0, 10)) deleteParamsButton = swing.JButton('Delete', actionPerformed=self.deleteToParams) deleteParamsButton.setBackground(Color.WHITE) self.createAnyView(deleteParamsButton, 6, 3, 1, 1, Insets(3, 0, 0, 0)) self._overwriteParamButton = swing.JButton( 'Overwrite', actionPerformed=self.overwriteParam) self._overwriteParamButton.setBackground(Color.WHITE) self.createAnyView(self._overwriteParamButton, 7, 3, 1, 1, Insets(3, 0, 0, 0)) addParamsButton = swing.JButton('Add', actionPerformed=self.addToParams) addParamsButton.setBackground(Color.WHITE) self.createAnyView(addParamsButton, 8, 3, 1, 1, Insets(3, 0, 0, 0)) self._resultsTextArea = swing.JTextArea() resultsOutput = swing.JScrollPane(self._resultsTextArea) resultsOutput.setMinimumSize(Dimension(800, 200)) self.createAnyView(resultsOutput, 0, 4, 9, 1, Insets(10, 0, 0, 0)) self.clearSearchButton = swing.JButton( 'Clear Search Output', actionPerformed=self.clearOutput) self.createAnyView(self.clearSearchButton, 3, 6, 3, 1, Insets(3, 0, 0, 0)) self._callbacks.customizeUiComponent(self._jPanel) self._callbacks.addSuiteTab(self) self.starterPack() self._callbacks.registerHttpListener(self) self._callbacks.registerContextMenuFactory(self) return def createAnyTable(self, table_model, table_number, min_size): _table = JTable(table_model) _table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS) for i in range(2): column = _table.getColumnModel().getColumn(i) if i == 0: column.setPreferredWidth(250) else: column.setPreferredWidth(50) _scrolltable = JScrollPane(_table) _scrolltable.setMinimumSize(min_size) return _scrolltable def insertAnyTable(self, table, data): def detectTable(table): name = table.getColumnName(0) if name == 'Payloads': return 0 elif name == 'Headers': return 1 elif name == 'Parameters': return 2 tableNum = detectTable(table) new_data = [str(x) for x in data] table.insertRow(table.getRowCount(), new_data) return table.getRowCount() def replaceLine(self, file_path, new_line): from tempfile import mkstemp from shutil import move from os import fdopen, remove #Create temp file fh, abs_path = mkstemp() with fdopen(fh, 'w') as new_file: with open(file_path) as old_file: for line in old_file: a = re.findall('^Callback_url[ =]+(.+)$', line) if a: for k in a: temp = k.replace("\'", "").replace("\"", "") new_file.write(line.replace(temp, new_line)) else: new_file.write(line) #Remove original file remove(file_path) #Move new file move(abs_path, file_path) def createAnyView(self, _component, gridx, gridy, gridwidth, gridheight, insets): self._jPanelConstraints.fill = GridBagConstraints.HORIZONTAL self._jPanelConstraints.gridx = gridx self._jPanelConstraints.gridy = gridy self._jPanelConstraints.gridwidth = gridwidth self._jPanelConstraints.gridheight = gridheight self._jPanelConstraints.insets = insets self._jPanel.add(_component, self._jPanelConstraints) def createMenuItems(self, contextMenuInvocation): context = contextMenuInvocation.getInvocationContext() filterMenu = JMenu("Femida XSS") self._contextMenuData = contextMenuInvocation if (context == 0 or context == 1 or context == 2 or context == 3 or context == 8 or context == 9): filterMenu.add( JMenuItem("Add to Headers", actionPerformed=self.addToHeadersItem)) filterMenu.add( JMenuItem("Add to Parameters", actionPerformed=self.addToParametersItem)) return Arrays.asList(filterMenu) return Arrays.asList([]) def addToHeadersItem(self, event): start, end = self._contextMenuData.getSelectionBounds() message = self._contextMenuData.getSelectedMessages()[0] ctx = self._contextMenuData.getInvocationContext() if ctx == 0 or ctx == 2: message = message.getRequest() elif ctx == 1 or ctx == 3: message = message.getResponse() else: print(ctx) return try: selected_text = self._helpers.bytesToString(message)[start:end] self.insertAnyTable(self._tableModelHeaders, [str(selected_text), '1']) except Exception: pass def addToParametersItem(self, event): start, end = self._contextMenuData.getSelectionBounds() message = self._contextMenuData.getSelectedMessages()[0] ctx = self._contextMenuData.getInvocationContext() if ctx == 0 or ctx == 2: message = message.getRequest() elif ctx == 1 or ctx == 3: message = message.getResponse() else: print(ctx) return try: selected_text = self._helpers.bytesToString(message)[start:end] self.insertAnyTable(self._tableModelParams, [str(selected_text), '1']) except Exception: pass def starterPack(self): self.addFromFileAsync(config.Payloads, self._tableModelPayloads) self.addFromFileAsync(config.Headers, self._tableModelHeaders) self.addFromFileAsync(config.Parameters, self._tableModelParams) self._jTextFieldURL.setText(config.Callback_url) self._tableModelPayloads.addTableModelListener( MyTableModelListener(self._tableModelPayloads, self, self._dictPayloads, config.Payloads)) self._tableModelHeaders.addTableModelListener( MyTableModelListener(self._tableModelHeaders, self, self._dictHeaders, config.Headers)) self._tableModelParams.addTableModelListener( MyTableModelListener(self._tableModelParams, self, self._dictParams, config.Parameters)) def setCallbackUrl(self, event): self.replaceLine(self.conf_path, self._jTextFieldURL.getText()) self.appendToResults('New url={} saved.'.format( self._jTextFieldURL.getText())) def addToPayload(self, button): self.insertAnyTable(self._tableModelPayloads, ['', '1']) def addToHeader(self, button): self.insertAnyTable(self._tableModelHeaders, ['', '1']) def addToParams(self, button): self.insertAnyTable(self._tableModelParams, ['', '1']) def uploadToPayload(self, button): self._returnFileChooser = self.jfc.showDialog(None, "Open") if (self._returnFileChooser == JFileChooser.APPROVE_OPTION): selectedFile = self.jfc.getSelectedFile() self.fileUpload(selectedFile, self._tableModelPayloads) def deleteToPayload(self, button): try: val = self._tableModelPayloads.getValueAt( self._tableModelPayloads.getRowCount() - 1, 0) self._tableModelPayloads.removeRow( self._tableModelPayloads.getRowCount() - 1) self._dictPayloads.pop(val) self.saveToFileAsync(config.Payloads, self._dictPayloads) except Exception as msg: # print(msg) pass def deleteToHeader(self, button): try: val = self._tableModelHeaders.getValueAt( self._tableModelHeaders.getRowCount() - 1, 0) self._tableModelHeaders.removeRow( self._tableModelHeaders.getRowCount() - 1) self._dictHeaders.pop(val) self.saveToFileAsync(config.Headers, self._dictHeaders) except Exception as msg: # print(msg) pass def deleteToParams(self, button): try: val = self._tableModelParams.getValueAt( self._tableModelParams.getRowCount() - 1, 0) self._tableModelParams.removeRow( self._tableModelParams.getRowCount() - 1) self._dictParams.pop(val) self.saveToFileAsync(config.Parameters, self._dictParams) except Exception as msg: # print(msg) pass def clearOutput(self, button): self._resultsTextArea.setText("") def fileUpload(self, path, table): with open(str(path), "r") as f: for line in f: self.insertAnyTable(table, [str(line), '1']) def active_flag(self, button): if not self.status_flag: self.status_flag = True self.submitSearchButton.setBackground(Color.GRAY) self.appendToResults("Proxy start...\n") else: self.status_flag = False self.submitSearchButton.setBackground(Color.WHITE) self.appendToResults("Proxy stop...\n") def overwriteHeader(self, button): if not self._overwriteHeader: self._overwriteHeader = True self._overwriteHeaderButton.setBackground(Color.GRAY) else: self._overwriteHeader = False self._overwriteHeaderButton.setBackground(Color.WHITE) def overwriteParam(self, button): if not self._overwriteParam: self._overwriteParam = True self._overwriteParamButton.setBackground(Color.GRAY) else: self._overwriteParam = False self._overwriteParamButton.setBackground(Color.WHITE) def forkRequest(self, button): if not self._forkRequestParam: self._forkRequestParam = True self._forkRequestButton.setBackground(Color.GRAY) else: self._forkRequestParam = False self._forkRequestButton.setBackground(Color.WHITE) def prepareRequest(self, requestString, messageInfo=None): requestString = str(requestString) listHeader = re.findall('([\w-]+):\s?(.*)', requestString) dictRealHeaders = {x[0].lower(): x[1] for x in listHeader} selectedPayloads = {} for ind, k in enumerate(self._dictPayloads): if self._dictPayloads[k] == '1': selectedPayloads[k] = '1' else: continue for index, key in enumerate(self._dictHeaders): if key.lower() in dictRealHeaders.keys( ) and self._dictHeaders[key] == '1': if len(self._dictPayloads.keys()) == 0: pass elif self._overwriteHeader: payload = random.choice(selectedPayloads.keys()) payload = payload.replace(r"{URL}", self._jTextFieldURL.getText(), 1) requestString = requestString.replace( dictRealHeaders.get(key.lower()), payload, 1) elif not self._overwriteHeader: payload = random.choice(selectedPayloads.keys()) payload = payload.replace(r"{URL}", self._jTextFieldURL.getText(), 1) payload = dictRealHeaders.get(key.lower()) + payload requestString = requestString.replace( dictRealHeaders.get(key.lower()), payload, 1) else: pass for index, key in enumerate(self._dictParams): analyzed = self._helpers.analyzeRequest(requestString.encode()) param = analyzed.getParameters() dictRealParams = { x.getName().lower(): [x.getValue(), x.getValueStart(), x.getValueEnd()] for x in param } if key.lower() in dictRealParams.keys( ) and self._dictParams[key] == '1': if len(self._dictPayloads.keys()) == 0: pass elif self._overwriteParam: payload = random.choice(selectedPayloads.keys()) payload = payload.replace(r"{URL}", self._jTextFieldURL.getText(), 1) start_word = dictRealParams[key.lower()][1] end_word = dictRealParams[key.lower()][2] requestString = requestString[: start_word] + payload + requestString[ end_word:] elif not self._overwriteParam: payload = random.choice(selectedPayloads.keys()) payload = payload.replace(r"{URL}", self._jTextFieldURL.getText(), 1) payload = dictRealParams[key.lower()][0] + payload start_word = dictRealParams[key.lower()][1] end_word = dictRealParams[key.lower()][2] requestString = requestString[: start_word] + payload + requestString[ end_word:] else: pass return requestString def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): if not self.status_flag: return # only process requests if not messageIsRequest: return if self._forkRequestParam: requestString = messageInfo.getRequest().tostring() # SOOOO HARD FIX! It should be better if requestString[0] == '@': messageInfo.setRequest( self._helpers.stringToBytes(requestString[1:])) else: newRequestString = self.prepareRequest(requestString, messageInfo) self.appendToResults('Parallel Request:') self.appendToResults(newRequestString.encode()) newRequestString = '@' + newRequestString func = self._callbacks.makeHttpRequest thread = Thread( target=func, args=(messageInfo.getHttpService(), self._helpers.stringToBytes(newRequestString))) thread.start() else: requestString = messageInfo.getRequest().tostring() newRequestString = self.prepareRequest(requestString, messageInfo) self.appendToResults(newRequestString.encode()) messageInfo.setRequest( self._helpers.stringToBytes(newRequestString)) # Fnction to provide output to GUI def appendToResults(self, s): def appendToResults_run(s): self._resultsTextArea.append(s) self._resultsTextArea.append('\n') swing.SwingUtilities.invokeLater( PyRunnable(appendToResults_run, str(s))) def addFromFileAsync(self, file, table): def addFromFile_run(file, table): if os.path.exists(file): with open(file, 'r') as f: for row in f.readlines(): if row != '': temp = row[:-1] if row[-1] == '\n' else row self.insertAnyTable(table, [str(temp), '1']) swing.SwingUtilities.invokeLater( PyRunnable(addFromFile_run, file, table)) def saveToFileAsync(self, file, data, isAppend=False): def saveToFile_run(file, data, isAppend): isAppend = 'w' with open(file, isAppend) as f: for i, k in enumerate(data): f.write("{}\n".format(k)) f.seek(-1, os.SEEK_END) f.truncate() swing.SwingUtilities.invokeLater( PyRunnable(saveToFile_run, file, data, isAppend)) def getTabCaption(self): return self.name def getUiComponent(self): return self._jPanel