package burp; import java.awt.Component; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import com.spec.extender.CONST; import com.spec.extender.exception.ExtenderException; import com.spec.extender.updater.UpdaterFactory; import com.spec.extender.updater.UpdaterPayload; import com.spec.extender.util.StringUtil; import com.spec.extender.util._debug; public class BurpExtender extends AbstractTableModel implements IBurpExtender, ITab, IHttpListener, IMessageEditorController { /** * */ private static final long serialVersionUID = 1L; private IBurpExtenderCallbacks callbacks; private IExtensionHelpers helpers; private JSplitPane splitPane; private IMessageEditor requestViewer; private IMessageEditor responseViewer; private IHttpRequestResponse currentlyDisplayedItem; private final List<LogEntry> log = new ArrayList<LogEntry>(); private UpdaterFactory headerUpdaterFactory = new UpdaterFactory(); // // implement IBurpExtender // @Override public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) { // keep a reference to our callbacks object this.callbacks = callbacks; // obtain an extension helpers object helpers = callbacks.getHelpers(); // set our extension name callbacks.setExtensionName("SOS Logger"); // create our UI SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // main split pane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); // table of log entries Table logTable = new Table(BurpExtender.this); JScrollPane scrollPane = new JScrollPane(logTable); splitPane.setLeftComponent(scrollPane); // tabs with request/response viewers JTabbedPane tabs = new JTabbedPane(); requestViewer = callbacks.createMessageEditor(BurpExtender.this, false); responseViewer = callbacks.createMessageEditor(BurpExtender.this, false); tabs.addTab("Request", requestViewer.getComponent()); tabs.addTab("Response", responseViewer.getComponent()); splitPane.setRightComponent(tabs); // customize our UI components callbacks.customizeUiComponent(splitPane); callbacks.customizeUiComponent(logTable); callbacks.customizeUiComponent(scrollPane); callbacks.customizeUiComponent(tabs); // add the custom tab to Burp's UI callbacks.addSuiteTab(BurpExtender.this); // register ourselves as an HTTP listener callbacks.registerHttpListener(BurpExtender.this); } }); } // // implement ITab // @Override public String getTabCaption() { return "Logger"; } @Override public Component getUiComponent() { return splitPane; } // // implement IHttpListener // @Override public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { //process intruder events if (messageIsRequest && (toolFlag == IBurpExtenderCallbacks.TOOL_INTRUDER || toolFlag == IBurpExtenderCallbacks.TOOL_REPEATER) ) { IExtensionHelpers helpers = callbacks.getHelpers(); _debug.println("\n===============on proxy===============\n"); IRequestInfo requestInfo = helpers.analyzeRequest(messageInfo); String burpRequestURL = requestInfo.getUrl().toString(); String requestURL = burpRequestURL.substring(0, burpRequestURL.length() -1); _debug.println("--------burp request url--------\n" + burpRequestURL); List<String> headers = requestInfo.getHeaders(); _debug.println("--------original request headers to be updated-------\n" + StringUtil.listToStringLines(headers)); byte[] request = messageInfo.getRequest(); String requestBody = new String(request); requestBody = requestBody.substring(helpers.analyzeRequest(request).getBodyOffset()); _debug.println("--------original request body-------\n" + requestBody); UpdaterPayload payload = new UpdaterPayload(headers, requestBody); UpdaterPayload updatedPayload = headerUpdaterFactory.create(requestURL).doUpdate(payload); List<String> updatedHeaders = updatedPayload.getHeaders(); String updatedBody = updatedPayload.getRequestBody(); byte[] updatedRequest = helpers.buildHttpMessage(updatedHeaders, helpers.stringToBytes(updatedBody)); _debug.println("\n----[updated full request]\n" + helpers.bytesToString(updatedRequest)); /** make the updated request */ IHttpService httpService = messageInfo.getHttpService(); callbacks.makeHttpRequest(httpService, updatedRequest); } // only process responses if (!messageIsRequest) { // create a new log entry with the message details synchronized(log) { int row = log.size(); _debug.println("log row: " + row); log.add(new LogEntry(row, toolFlag, callbacks.saveBuffersToTempFiles(messageInfo), helpers.analyzeRequest(messageInfo).getUrl())); fireTableRowsInserted(row, row); } } } // // extend AbstractTableModel // @Override public int getRowCount() { return log.size(); } @Override public int getColumnCount() { return com.spec.extender.CONST.NUM_COL; } @Override public String getColumnName(int colIndex) { return com.spec.extender.CONST.COL_NAME[colIndex]; } @Override public Class<?> getColumnClass(int columnIndex) { return String.class; } @Override public Object getValueAt(int rowIndex, int columnIndex) { LogEntry logEntry = log.get(rowIndex); byte[] response = logEntry.requestResponse.getResponse(); IResponseInfo resInfo = helpers.analyzeResponse(response); List<String> resHeaders = resInfo.getHeaders(); // Util.debug("\nResHeader:\n" + Util.listToString(resHeaders)); switch (columnIndex) { case 0: return logEntry.index; case 1: return callbacks.getToolName(logEntry.tool); case 2: return logEntry.url.toString(); case 3: return resInfo.getStatusCode(); case 4: return response.length; case 5:{ return getAResponseHeader(resHeaders, CONST.HEADER_DATE); } default: return ""; } } private String getAResponseHeader(List<String> resHeaders, String headerName){ for (String h: resHeaders){ if (h.startsWith(headerName)) return h; } throw new ExtenderException("No header found the in response having the name: " + headerName); } // // implement IMessageEditorController // this allows our request/response viewers to obtain details about the messages being displayed // @Override public byte[] getRequest() { return currentlyDisplayedItem.getRequest(); } @Override public byte[] getResponse() { return currentlyDisplayedItem.getResponse(); } @Override public IHttpService getHttpService() { return currentlyDisplayedItem.getHttpService(); } // // extend JTable to handle cell selection // private class Table extends JTable { public Table(TableModel tableModel) { super(tableModel); } @Override public void changeSelection(int row, int col, boolean toggle, boolean extend) { // show the log entry for the selected row LogEntry logEntry = log.get(row); requestViewer.setMessage(logEntry.requestResponse.getRequest(), true); responseViewer.setMessage(logEntry.requestResponse.getResponse(), false); currentlyDisplayedItem = logEntry.requestResponse; super.changeSelection(row, col, toggle, extend); } } // // class to hold details of each log entry // private static class LogEntry { final int index; final int tool; final IHttpRequestResponsePersisted requestResponse; final URL url; LogEntry(int index, int tool, IHttpRequestResponsePersisted requestResponse, URL url) { this.index = index; this.tool = tool; this.requestResponse = requestResponse; this.url = url; } } }