diff --git a/pom.xml b/pom.xml index f4b3566..c3861c0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,16 +5,15 @@ interactsh collaborator - 1.0.1-dev + 1.0.2-dev jar - net.portswigger.burp.extender - burp-extender-api - 2.2 - provided + net.portswigger.burp.extensions + montoya-api + LATEST org.json @@ -63,7 +62,7 @@ UTF-8 - 11 - 11 + 17 + 17 \ No newline at end of file diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java index aeab013..aaf4eb6 100644 --- a/src/burp/BurpExtender.java +++ b/src/burp/BurpExtender.java @@ -1,5 +1,7 @@ package burp; +import burp.api.montoya.ui.contextmenu.ContextMenuEvent; +import burp.api.montoya.ui.contextmenu.ContextMenuItemsProvider; import burp.listeners.InteractshListener; import burp.listeners.PollTimeListener; import interactsh.Client; @@ -16,227 +18,86 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; -public class BurpExtender extends AbstractTableModel implements IBurpExtender, IContextMenuFactory, ITab, IExtensionStateListener -{ +import burp.api.montoya.BurpExtension; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.extension.ExtensionUnloadingHandler; + + +public class BurpExtender implements BurpExtension, ContextMenuItemsProvider, ExtensionUnloadingHandler { + public static MontoyaApi api; public static int pollTime = 60; - private static IBurpExtenderCallbacks callbacks; - private static IExtensionHelpers helpers; - - private JTabbedPane mainPane; - private JSplitPane splitPane; - private JScrollPane scrollPane; - private JSplitPane tableSplitPane; - private JPanel resultsPanel; - private static JTextField pollField; - private static Table logTable; - public static JTextField serverText; - public static JTextField portText; - public static JTextField authText; - public static JCheckBox tlsBox; - private static List log = new ArrayList(); + public static InteractshTab tab; private static ArrayList clients = new ArrayList(); - private InteractshListener listener; - - // - // implement IBurpExtender - // + private InteractshListener listener = new InteractshListener(); @Override - public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) - { - // keep a reference to our callbacks object - this.callbacks = callbacks; + public void initialize(MontoyaApi api) { + this.api = api; + api.extension().setName("Interactsh Collaborator"); + api.userInterface().registerContextMenuItemsProvider(this); + api.extension().registerUnloadingHandler(this); - // obtain an extension helpers object - helpers = callbacks.getHelpers(); + api.logging().logToOutput("Starting Interactsh Collaborator!"); - // set our extension name - callbacks.setExtensionName("Interactsh Collaborator"); - callbacks.printOutput("Starting Interactsh Collaborator!"); - - // Save settings burp.gui.Config.generateConfig(); + tab = new InteractshTab(api, listener, clients); + burp.gui.Config.loadConfig(); - // Register this as a IExtensionStateListener - callbacks.registerExtensionStateListener(BurpExtender.this); - - // Register this as a IContextMenuFactory - callbacks.registerContextMenuFactory(BurpExtender.this); - - // create our UI - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - mainPane = new JTabbedPane(); - splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - mainPane.addTab("Logs", splitPane); - - resultsPanel = new JPanel(); - tableSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - logTable = new Table(BurpExtender.this); - scrollPane = new JScrollPane(logTable); - - tableSplitPane.setTopComponent(scrollPane); - tableSplitPane.setBottomComponent(resultsPanel); - splitPane.setBottomComponent(tableSplitPane); - - JPanel panel = new JPanel(); - JButton CollaboratorButton = new JButton("Generate Interactsh url"); - JLabel pollLabel = new JLabel("Poll Time: "); - pollField = new JTextField("60", 4); - pollField.getDocument().addDocumentListener(new PollTimeListener()); - - listener = new InteractshListener(); - CollaboratorButton.addActionListener(listener); - panel.add(CollaboratorButton); - panel.add(pollLabel); - panel.add(pollField); - splitPane.setTopComponent(panel); - - // Configuration pane - JPanel configPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - mainPane.addTab("Configuration", configPanel); - JPanel innerConfig = new JPanel(); - innerConfig.setSize(new Dimension(80, 150)); - innerConfig.setLayout(new SpringLayout()); - configPanel.add(innerConfig); - - serverText = new JTextField("interact.sh", 20); - portText = new JTextField("443", 20); - authText = new JTextField("", 20); - tlsBox = new JCheckBox("", true); - - JLabel server = new JLabel("Server: "); - innerConfig.add(server); - server.setLabelFor(serverText); - innerConfig.add(serverText); - - JLabel port = new JLabel("Port: "); - innerConfig.add(port); - port.setLabelFor(portText); - innerConfig.add(portText); - - JLabel auth = new JLabel("Authorization: "); - innerConfig.add(auth); - auth.setLabelFor(authText); - innerConfig.add(authText); - - JLabel tls = new JLabel("TLS: "); - innerConfig.add(tls); - tls.setLabelFor(tlsBox); - innerConfig.add(tlsBox); - - JButton updateConfigButton = new JButton("Update Settings"); - updateConfigButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - burp.gui.Config.updateConfig(); - } - }); - innerConfig.add(updateConfigButton); - - // Add a blank panel so that SpringUtilities can make a well shaped grid - innerConfig.add(new JPanel()); - - SpringUtilities.makeCompactGrid(innerConfig, - 5, 2, //rows, cols - 6, 6, //initX, initY - 6, 6); //xPad, yPad - - burp.gui.Config.loadConfig(); - - - // customize our UI components - callbacks.customizeUiComponent(mainPane); - callbacks.customizeUiComponent(resultsPanel); - callbacks.customizeUiComponent(tableSplitPane); - callbacks.customizeUiComponent(splitPane); - callbacks.customizeUiComponent(logTable); - callbacks.customizeUiComponent(scrollPane); - callbacks.customizeUiComponent(configPanel); - callbacks.customizeUiComponent(innerConfig); - callbacks.customizeUiComponent(CollaboratorButton); - - // add the custom tab to Burp's UI - callbacks.addSuiteTab(BurpExtender.this); - } - }); - } - - public static IBurpExtenderCallbacks getCallbacks() { - return callbacks; - } - - public static IExtensionHelpers getHelpers() { - return helpers; + api.userInterface().registerSuiteTab("Interactsh", tab); } @Override public void extensionUnloaded() { - // Get all threads and stop them. - listener.running = false; - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) {} - for (int i = 0; i < listener.pollers.size(); i++){ - listener.pollers.get(i).stop(); - } + if (listener != null) { + // Get all threads and stop them. + listener.running = false; + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + } + for (int i = 0; i < listener.pollers.size(); i++) { + listener.pollers.get(i).interrupt(); + } - // Tell all clients to deregister - for (int i = 0; i < clients.size(); i++){ - clients.get(i).deregister(); + // Tell all clients to deregister + for (int i = 0; i < clients.size(); i++) { + clients.get(i).deregister(); + } } - callbacks.printOutput("Thanks for collaborating!"); + api.logging().logToOutput("Thanks for collaborating!"); } - public static ArrayList getClients(){ + public ArrayList getClients() { return clients; } - public static void addClient(Client c){ + public static void addClient(Client c) { clients.add(c); } - public static int getPollTime(){ - try{ - return Integer.parseInt(pollField.getText()); - }catch (Exception ex) {} + public static int getPollTime() { + try { + return Integer.parseInt(tab.getPollField().getText()); + } catch (Exception ex) { + } return 60; } - public static void updatePollTime(int poll){ + public static void updatePollTime(int poll) { pollTime = poll; } - public static void addToTable(InteractEntry i){ - log.add(i); - logTable.revalidate(); - } - - // - // implement ITab - // - - @Override - public String getTabCaption() - { - return "Interactsh"; - } - - @Override - public Component getUiComponent() - { - return mainPane; + public static void addToTable(InteractEntry i) { + tab.addToTable(i); } // - // extend IContextMenuFactory + // implement ContextMenuItemsProvider // @Override - public List createMenuItems(IContextMenuInvocation invocation) { - List menuList = new ArrayList(); + public List provideMenuItems(ContextMenuEvent event) { + List menuList = new ArrayList(); JMenuItem item = new JMenuItem("Generate Interactsh url"); item.addActionListener(new InteractshListener()); menuList.add(item); @@ -244,94 +105,238 @@ public List createMenuItems(IContextMenuInvocation invocation) { return menuList; } - // - // extend AbstractTableModel - // + public class InteractshTab extends JComponent { + private JTabbedPane mainPane; + private JSplitPane splitPane; + private JScrollPane scrollPane; + private JSplitPane tableSplitPane; + private JPanel resultsPanel; + private JTextField pollField; + private Table logTable; + private static JTextField serverText; + private static JTextField portText; + private static JTextField authText; + private static JCheckBox tlsBox; + private List log = new ArrayList(); + private InteractshListener listener; + private ArrayList clients; + + public InteractshTab(MontoyaApi api, InteractshListener listener, ArrayList clients) { + this.listener = listener; + this.clients = clients; + + setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + + mainPane = new JTabbedPane(); + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + mainPane.addTab("Logs", splitPane); + + resultsPanel = new JPanel(); + tableSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + logTable = new Table(new LogTable()); + scrollPane = new JScrollPane(logTable); + + tableSplitPane.setTopComponent(scrollPane); + tableSplitPane.setBottomComponent(resultsPanel); + splitPane.setBottomComponent(tableSplitPane); + + JPanel panel = new JPanel(); + JButton CollaboratorButton = new JButton("Generate Interactsh url"); + JLabel pollLabel = new JLabel("Poll Time: "); + pollField = new JTextField("60", 4); + pollField.getDocument().addDocumentListener(new PollTimeListener()); + + CollaboratorButton.addActionListener(listener); + panel.add(CollaboratorButton); + panel.add(pollLabel); + panel.add(pollField); + splitPane.setTopComponent(panel); + + // Configuration pane + JPanel configPanel = new JPanel(); + configPanel.setLayout(new BoxLayout(configPanel, BoxLayout.Y_AXIS)); + JPanel subConfigPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + mainPane.addTab("Configuration", configPanel); + configPanel.add(subConfigPanel); + JPanel innerConfig = new JPanel(); + subConfigPanel.setMaximumSize(new Dimension(configPanel.getMaximumSize().width, 150)); + innerConfig.setLayout(new SpringLayout()); + subConfigPanel.add(innerConfig); + + serverText = new JTextField("oast.pro", 20); + portText = new JTextField("443", 20); + authText = new JTextField("", 20); + tlsBox = new JCheckBox("", true); + + JLabel server = new JLabel("Server: "); + innerConfig.add(server); + server.setLabelFor(serverText); + innerConfig.add(serverText); + + JLabel port = new JLabel("Port: "); + innerConfig.add(port); + port.setLabelFor(portText); + innerConfig.add(portText); + + JLabel auth = new JLabel("Authorization: "); + innerConfig.add(auth); + auth.setLabelFor(authText); + innerConfig.add(authText); + + JLabel tls = new JLabel("TLS: "); + innerConfig.add(tls); + tls.setLabelFor(tlsBox); + innerConfig.add(tlsBox); + + JButton updateConfigButton = new JButton("Update Settings"); + updateConfigButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + burp.gui.Config.updateConfig(); + } + }); + innerConfig.add(updateConfigButton); + + // Add a blank panel so that SpringUtilities can make a well shaped grid + innerConfig.add(new JPanel()); + + SpringUtilities.makeCompactGrid(innerConfig, + 5, 2, //rows, cols + 6, 6, //initX, initY + 6, 6); //xPad, yPad + + JPanel documentationPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + JLabel help = new JLabel("Check out https://github.com/projectdiscovery/interactsh for an up to date list of public Interactsh servers", SwingConstants.LEFT); + documentationPanel.setAlignmentY(Component.TOP_ALIGNMENT); + documentationPanel.add(help); + configPanel.add(documentationPanel); + + add(mainPane); + } - @Override - public int getRowCount() - { - return log.size(); - } + public static String getServerText() { + return serverText.getText(); + } - @Override - public int getColumnCount() - { - return 4; - } + public static void setServerText(String t) { + serverText.setText(t); + } - @Override - public String getColumnName(int columnIndex) - { - switch (columnIndex) - { - case 0: - return "Entry"; - case 1: - return "Type"; - case 2: - return "Address"; - case 3: - return "Time"; - default: - return ""; + public static String getPortText() { + return portText.getText(); } - } - @Override - public Class getColumnClass(int columnIndex) - { - return String.class; - } + public static void setPortText(String text) { + portText.setText(text); + } - @Override - public Object getValueAt(int rowIndex, int columnIndex) - { - InteractEntry ie = log.get(rowIndex); - - switch (columnIndex) - { - case 0: - return ie.uid; - case 1: - return ie.protocol; - case 2: - return ie.address; - case 3: - return ie.timestamp; - default: - return ""; + public static String getAuthText() { + return authText.getText(); } - } - // - // extend JTable to handle cell selection - // + public static void setAuthText(String text) { + authText.setText(text); + } + + public static String getTlsBox() { + return Boolean.toString(tlsBox.isSelected()); + } + + public static void setTlsBox(boolean value) { + tlsBox.setSelected(value); + } + + public JTextField getPollField() { + return pollField; + } - private class Table extends JTable - { - public TableModel tableModel; - public Table(TableModel tableModel) - { - super(tableModel); - this.tableModel = tableModel; + public void addToTable(InteractEntry i) { + log.add(i); + logTable.revalidate(); } - @Override - public void changeSelection(int row, int col, boolean toggle, boolean extend) - { - // show the log entry for the selected row - InteractEntry ie = log.get(row); + // + // extend JTable to handle cell selection + // + + private class Table extends JTable { + public TableModel tableModel; + + public Table(TableModel tableModel) { + super(tableModel); + this.tableModel = tableModel; + } + + @Override + public void changeSelection(int row, int col, boolean toggle, boolean extend) { + // show the log entry for the selected row + InteractEntry ie = log.get(row); - resultsPanel.removeAll(); // Refresh pane - resultsPanel.setLayout(new BorderLayout()); //give your JPanel a BorderLayout + resultsPanel.removeAll(); // Refresh pane + resultsPanel.setLayout(new BorderLayout()); //give your JPanel a BorderLayout - JTextArea text = new JTextArea(ie.details); - JScrollPane scroll = new JScrollPane(text); //place the JTextArea in a scroll pane - resultsPanel.add(scroll, BorderLayout.CENTER); //add the JScrollPane to the panel - tableSplitPane.revalidate(); + JTextArea text = new JTextArea(ie.details); + JScrollPane scroll = new JScrollPane(text); //place the JTextArea in a scroll pane + resultsPanel.add(scroll, BorderLayout.CENTER); //add the JScrollPane to the panel + tableSplitPane.revalidate(); - super.changeSelection(row, col, toggle, extend); + super.changeSelection(row, col, toggle, extend); + } + } + + // + // implement AbstractTableModel + // + + private class LogTable extends AbstractTableModel { + + @Override + public int getRowCount() { + return log.size(); + } + + @Override + public int getColumnCount() { + return 4; + } + + @Override + public String getColumnName(int columnIndex) { + switch (columnIndex) { + case 0: + return "Entry"; + case 1: + return "Type"; + case 2: + return "Address"; + case 3: + return "Time"; + default: + return ""; + } + } + + @Override + public Class getColumnClass(int columnIndex) { + return String.class; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + InteractEntry ie = log.get(rowIndex); + + switch (columnIndex) { + case 0: + return ie.uid; + case 1: + return ie.protocol; + case 2: + return ie.address; + case 3: + return ie.timestamp; + default: + return ""; + } + } } } } \ No newline at end of file diff --git a/src/burp/gui/Config.java b/src/burp/gui/Config.java index 4787301..7cb90c4 100644 --- a/src/burp/gui/Config.java +++ b/src/burp/gui/Config.java @@ -1,82 +1,68 @@ package burp.gui; -import burp.IBurpExtenderCallbacks; - -import javax.swing.*; +import burp.api.montoya.persistence.Preferences; public class Config { - public static void generateConfig(){ - IBurpExtenderCallbacks callbacks = burp.BurpExtender.getCallbacks(); + public static void generateConfig() { + Preferences preferences = burp.BurpExtender.api.persistence().preferences(); - String server = callbacks.loadExtensionSetting("interactsh-server"); - String port = callbacks.loadExtensionSetting("interactsh-port"); + String server = preferences.getString("interactsh-server"); + String port = preferences.getString("interactsh-port"); if ((server == null || server.isEmpty()) || - (port == null || port.isEmpty())){ - callbacks.saveExtensionSetting("interactsh-server", "interact.sh"); - callbacks.saveExtensionSetting("interactsh-port", "443"); - callbacks.saveExtensionSetting("interactsh-uses-tls", Boolean.toString(true)); + (port == null || port.isEmpty()) || + !preferences.stringKeys().contains("interactsh-authorization") || + !preferences.stringKeys().contains("interactsh-uses-tls")) { + preferences.setString("interactsh-server", "oast.pro"); + preferences.setString("interactsh-port", "443"); + preferences.setString("interactsh-authorization", ""); + preferences.setString("interactsh-uses-tls", Boolean.toString(true)); } } - public static void loadConfig(){ - IBurpExtenderCallbacks callbacks = burp.BurpExtender.getCallbacks(); - String server = callbacks.loadExtensionSetting("interactsh-server"); - String port = callbacks.loadExtensionSetting("interactsh-port"); - boolean tls = Boolean.parseBoolean(callbacks.loadExtensionSetting("interactsh-uses-tls")); - String authorization = callbacks.loadExtensionSetting("interactsh-authorization"); + public static void loadConfig() { + Preferences preferences = burp.BurpExtender.api.persistence().preferences(); + String server = preferences.getString("interactsh-server"); + String port = preferences.getString("interactsh-port"); + String tls = preferences.getString("interactsh-uses-tls"); + String authorization = preferences.getString("interactsh-authorization"); // Update each of the text boxes on the Configuration pane - burp.BurpExtender.serverText.setText(server); - burp.BurpExtender.portText.setText(port); - burp.BurpExtender.authText.setText(authorization); - burp.BurpExtender.tlsBox.setSelected(tls); + burp.BurpExtender.tab.setServerText(server); + burp.BurpExtender.tab.setPortText(port); + burp.BurpExtender.tab.setAuthText(authorization); + burp.BurpExtender.tab.setTlsBox(Boolean.parseBoolean(tls)); } - public static void updateConfig(){ - IBurpExtenderCallbacks callbacks = burp.BurpExtender.getCallbacks(); + public static void updateConfig() { + Preferences preferences = burp.BurpExtender.api.persistence().preferences(); // Read each of the text boxes on the Configuration pane - String server = burp.BurpExtender.serverText.getText(); - String port = burp.BurpExtender.portText.getText(); - String authorization = burp.BurpExtender.authText.getText(); - boolean tls = burp.BurpExtender.tlsBox.isSelected(); - - callbacks.saveExtensionSetting("interactsh-server", server); - callbacks.saveExtensionSetting("interactsh-port", port); - callbacks.saveExtensionSetting("interactsh-uses-tls", Boolean.toString(tls)); - callbacks.saveExtensionSetting("interactsh-authorization", authorization); - } - - public static String getHost(){ - return burp.BurpExtender.getCallbacks().loadExtensionSetting("interactsh-server"); + String server = burp.BurpExtender.tab.getServerText(); + String port = burp.BurpExtender.tab.getPortText(); + String authorization = burp.BurpExtender.tab.getAuthText(); + String tls = burp.BurpExtender.tab.getTlsBox(); + + preferences.setString("interactsh-server", server); + preferences.setString("interactsh-port", port); + preferences.setString("interactsh-uses-tls", tls); + preferences.setString("interactsh-authorization", authorization); } - public static String getPort(){ - return burp.BurpExtender.getCallbacks().loadExtensionSetting("interactsh-port"); + public static String getHost() { + return burp.BurpExtender.api.persistence().preferences().getString("interactsh-server"); } - public static boolean getScheme(){ - return Boolean.parseBoolean(burp.BurpExtender.getCallbacks().loadExtensionSetting("interactsh-uses-tls")); + public static String getPort() { + return burp.BurpExtender.api.persistence().preferences().getString("interactsh-port"); } - public static String getAuth(){ - return burp.BurpExtender.getCallbacks().loadExtensionSetting("interactsh-authorization"); + public static boolean getScheme() { + return Boolean.parseBoolean(burp.BurpExtender.api.persistence().preferences().getString("interactsh-uses-tls")); } - public static String getUrl(){ - String scheme = "https://"; - if(Boolean.parseBoolean(burp.BurpExtender.getCallbacks().loadExtensionSetting("interactsh-server")) == false){ - scheme = "http://"; - } - - String url = scheme + getHost(); - String port = getPort(); - if(!(port == "80" || port == "443")){ - url += ":" + port; - } - - return url; + public static String getAuth() { + return burp.BurpExtender.api.persistence().preferences().getString("interactsh-authorization"); } } diff --git a/src/burp/listeners/InteractshListener.java b/src/burp/listeners/InteractshListener.java index c35a5b9..ec710b1 100644 --- a/src/burp/listeners/InteractshListener.java +++ b/src/burp/listeners/InteractshListener.java @@ -13,44 +13,48 @@ public class InteractshListener implements ActionListener { public ArrayList pollers = new ArrayList(); public boolean running = true; - public InteractshListener() {} + + public InteractshListener() { + } + @Override public void actionPerformed(ActionEvent e) { - BurpExtender.getCallbacks().printOutput("Generating new Interactsh client"); - Client c = new Client(); - try { - c.generateKeys(); + burp.BurpExtender.api.logging().logToOutput("Generating new Interactsh client"); + Client c = new Client(); + try { + c.generateKeys(); - Thread polling = new Thread(new Runnable() { - public void run() { - try { - if (c.registerClient()) { - burp.BurpExtender.addClient(c); - while (running == true) { - if (!c.poll()){ - return; - } - TimeUnit.SECONDS.sleep(burp.BurpExtender.pollTime); - } - } else { - BurpExtender.getCallbacks().printOutput("Error registering client"); - } - } catch (Exception ex) { - // Nothing to do here. - } - } - }); - pollers.add(polling); - polling.start(); + Thread polling = new Thread(new Runnable() { + public void run() { + try { + if (c.registerClient()) { + burp.BurpExtender.addClient(c); + while (running == true) { + if (!c.poll()) { + return; + } + TimeUnit.SECONDS.sleep(burp.BurpExtender.getPollTime()); + } + } else { + burp.BurpExtender.api.logging().logToOutput("Error registering client"); + } + } catch (InterruptedException ie) { + } catch (Exception ex) { + burp.BurpExtender.api.logging().logToError(ex.getMessage()); + } + } + }); + pollers.add(polling); + polling.start(); - TimeUnit.SECONDS.sleep(1); - // Set clipboard with new interactsh domain - String domain = c.getInteractDomain(); - BurpExtender.getCallbacks().printOutput("New domain is: " + domain); - StringSelection stringSelection = new StringSelection(domain); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null); - } catch (Exception ex){ - BurpExtender.getCallbacks().printOutput(ex.getMessage()); - } + TimeUnit.SECONDS.sleep(1); + // Set clipboard with new interactsh domain + String domain = c.getInteractDomain(); + burp.BurpExtender.api.logging().logToOutput("New domain is: " + domain); + StringSelection stringSelection = new StringSelection(domain); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null); + } catch (Exception ex) { + burp.BurpExtender.api.logging().logToError(ex.getMessage()); + } } } \ No newline at end of file diff --git a/src/interactsh/Client.java b/src/interactsh/Client.java index 7cb1d63..82984db 100644 --- a/src/interactsh/Client.java +++ b/src/interactsh/Client.java @@ -1,13 +1,16 @@ package interactsh; -import burp.IBurpExtenderCallbacks; -import burp.BurpExtender; -import burp.IResponseInfo; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.http.HttpService; + import javax.crypto.*; import javax.crypto.spec.*; + import com.github.shamil.Xid; import org.json.JSONArray; import org.json.JSONObject; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.spec.MGF1ParameterSpec; @@ -17,26 +20,24 @@ public class Client { public PrivateKey privateKey; private PublicKey publicKey; - private IBurpExtenderCallbacks callbacks = BurpExtender.getCallbacks(); private Xid xid; private String secretKey; private String correlationId; // Defaults - private String server = "https://interact.sh"; - private String host = "interact.sh"; + private String host = "oast.pro"; private int port = 443; private boolean scheme = true; private String authorization = null; - public Client(){ - server = burp.gui.Config.getUrl(); + public Client() { host = burp.gui.Config.getHost(); scheme = burp.gui.Config.getScheme(); authorization = burp.gui.Config.getAuth(); try { port = Integer.parseInt(burp.gui.Config.getPort()); - } catch (NumberFormatException ne){ + + } catch (NumberFormatException ne) { port = 443; } } @@ -58,19 +59,20 @@ public boolean registerClient() throws Exception { + "User-Agent: Interact.sh Client\r\n" + "Content-Type: application/json\r\n" + "Content-Length: " + registerData.toString().length() + "\r\n"; - if(!(authorization == null || authorization.isEmpty())){ + if (!(authorization == null || authorization.isEmpty())) { request += "Authorization: " + authorization + "\r\n"; } request += "Connection: close\r\n\r\n" + registerData.toString(); - byte[] response = callbacks.makeHttpRequest(host, port, scheme, request.getBytes(StandardCharsets.UTF_8)); - IResponseInfo responseInfo = BurpExtender.getHelpers().analyzeResponse(response); - if (responseInfo.getStatusCode() == 200) { + HttpRequest httpRequest = HttpRequest.httpRequest(HttpService.httpService(host, port, scheme), request); + HttpResponse resp = burp.BurpExtender.api.http().sendRequest(httpRequest).response(); + + if (resp.statusCode() == 200) { return true; } - }catch (Exception ex){ - callbacks.printOutput(ex.getMessage()); + } catch (Exception ex) { + burp.BurpExtender.api.logging().logToError(ex.getMessage()); } return false; } @@ -79,67 +81,69 @@ public boolean poll() throws IOException, InterruptedException { String request = "GET /poll?id=" + correlationId + "&secret=" + secretKey + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: Interact.sh Client\r\n"; - if(!(authorization == null || authorization.isEmpty())){ + if (!(authorization == null || authorization.isEmpty())) { request += "Authorization: " + authorization + "\r\n"; } request += "Connection: close\r\n\r\n"; - byte[] response = callbacks.makeHttpRequest(host, port, scheme, request.getBytes(StandardCharsets.UTF_8)); - IResponseInfo responseInfo = BurpExtender.getHelpers().analyzeResponse(response); - if (responseInfo.getStatusCode() != 200) { - callbacks.printOutput("Poll for " + correlationId + " was unsuccessful: " + responseInfo.getStatusCode()); + HttpRequest httpRequest = HttpRequest.httpRequest(HttpService.httpService(host, port, scheme), request); + HttpResponse resp = burp.BurpExtender.api.http().sendRequest(httpRequest).response(); + if (resp.statusCode() != 200) { + burp.BurpExtender.api.logging().logToOutput("Poll for " + correlationId + " was unsuccessful: " + resp.statusCode()); return false; } - String responseStr = new String(response); - String responseBody = responseStr.split("\r\n\r\n")[1]; + String responseBody = resp.bodyToString(); try { JSONObject jsonObject = new JSONObject(responseBody); String aesKey = jsonObject.getString("aes_key"); String key = decryptAesKey(aesKey); - JSONArray data = jsonObject.getJSONArray("data"); - for (int i = 0; i < data.length(); i++){ - String d = data.getString(i); + if (!jsonObject.isNull("data")) { + JSONArray data = jsonObject.getJSONArray("data"); + for (int i = 0; i < data.length(); i++) { + String d = data.getString(i); - String decryptedData = decryptData(d, key); + String decryptedData = decryptData(d, key); - InteractEntry entry = new InteractEntry(decryptedData); - burp.BurpExtender.addToTable(entry); - callbacks.printOutput(entry.toString()); + InteractEntry entry = new InteractEntry(decryptedData); + burp.BurpExtender.addToTable(entry); + burp.BurpExtender.api.logging().logToOutput(entry.toString()); + } } - - } catch(Exception ex){ - callbacks.printOutput(ex.getMessage()); + } catch (Exception ex) { + burp.BurpExtender.api.logging().logToError(ex.getMessage()); } return true; } - public void deregister(){ - callbacks.printOutput("Deregistering " + correlationId); + public void deregister() { + burp.BurpExtender.api.logging().logToOutput("Deregistering " + correlationId); try { JSONObject deregisterData = new JSONObject(); deregisterData.put("correlation-id", correlationId); + deregisterData.put("secret-key", secretKey); String request = "POST /deregister HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: Interact.sh Client\r\n" + "Content-Type: application/json\r\n" + "Content-Length: " + deregisterData.toString().length() + "\r\n"; - if(!(authorization == null || authorization.isEmpty())){ + if (!(authorization == null || authorization.isEmpty())) { request += "Authorization: " + authorization + "\r\n"; } request += "Connection: close\r\n\r\n" + deregisterData.toString(); - callbacks.makeHttpRequest(host, port, scheme, request.getBytes(StandardCharsets.UTF_8)); - }catch (Exception ex){ - callbacks.printOutput(ex.getMessage()); + HttpRequest httpRequest = HttpRequest.httpRequest(HttpService.httpService(host, port, scheme), request); + burp.BurpExtender.api.http().sendRequest(httpRequest).response(); + } catch (Exception ex) { + burp.BurpExtender.api.logging().logToError(ex.getMessage()); } } - public String getInteractDomain(){ - if (correlationId == null || correlationId.isEmpty()){ + public String getInteractDomain() { + if (correlationId == null || correlationId.isEmpty()) { return ""; } else { String fullDomain = correlationId; @@ -147,7 +151,7 @@ public String getInteractDomain(){ // Fix the string up to 33 characters Random random = new Random(); while (fullDomain.length() < 33) { - fullDomain += (char)(random.nextInt(26) + 'a'); + fullDomain += (char) (random.nextInt(26) + 'a'); } fullDomain += "." + host; return fullDomain; @@ -163,18 +167,17 @@ public void generateKeys() throws NoSuchAlgorithmException { privateKey = kp.getPrivate(); } - private String getPublicKey(){ + private String getPublicKey() { String pubKey = "-----BEGIN PUBLIC KEY-----\n"; - String [] chunks = splitStringEveryN(Base64.getEncoder().encodeToString(publicKey.getEncoded()), 64); - for (String chunk: chunks) { + String[] chunks = splitStringEveryN(Base64.getEncoder().encodeToString(publicKey.getEncoded()), 64); + for (String chunk : chunks) { pubKey += chunk + "\n"; } pubKey += "-----END PUBLIC KEY-----\n"; return pubKey; } - private String decryptAesKey(String encrypted) throws Exception - { + private String decryptAesKey(String encrypted) throws Exception { byte[] cipherTextArray = Base64.getDecoder().decode(encrypted); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); @@ -198,10 +201,10 @@ private static String decryptData(String input, String key) throws Exception { byte[] decrypted = cipher.doFinal(cipherText); return new String(decrypted); - }; + } private String[] splitStringEveryN(String s, int interval) { - int arrayLength = (int) Math.ceil(((s.length() / (double)interval))); + int arrayLength = (int) Math.ceil(((s.length() / (double) interval))); String[] result = new String[arrayLength]; int j = 0; diff --git a/src/interactsh/InteractEntry.java b/src/interactsh/InteractEntry.java index 077125a..74aefea 100644 --- a/src/interactsh/InteractEntry.java +++ b/src/interactsh/InteractEntry.java @@ -1,4 +1,5 @@ package interactsh; + import org.json.*; // {"protocol":"dns","unique-id":"c4jup534f3acspvifdr0cru63feyyyyyn","full-id":"c4jup534f3acspvifdr0cru63feyyyyyn","q-type":"A","raw-request":";; opcode: QUERY, status: NOERROR, id: 52297\n;; flags: cd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0\n\n;; QUESTION SECTION:\n;c4jup534f3acspvifdr0cru63feyyyyyn.interact.sh.\tIN\t A\n","raw-response":";; opcode: QUERY, status: NOERROR, id: 52297\n;; flags: qr aa cd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2\n\n;; QUESTION SECTION:\n;c4jup534f3acspvifdr0cru63feyyyyyn.interact.sh.\tIN\t A\n\n;; ANSWER SECTION:\nc4jup534f3acspvifdr0cru63feyyyyyn.interact.sh.\t3600\tIN\tA\t46.101.25.250\n\n;; AUTHORITY SECTION:\nc4jup534f3acspvifdr0cru63feyyyyyn.interact.sh.\t3600\tIN\tNS\tns1.interact.sh.\nc4jup534f3acspvifdr0cru63feyyyyyn.interact.sh.\t3600\tIN\tNS\tns2.interact.sh.\n\n;; ADDITIONAL SECTION:\nns1.interact.sh.\t3600\tIN\tA\t46.101.25.250\nns2.interact.sh.\t3600\tIN\tA\t46.101.25.250\n","remote-address":"172.253.196.66","timestamp":"2021-08-26T19:35:24.221293174Z"} @@ -22,16 +23,28 @@ public InteractEntry(String event) throws JSONException { private String processDetails(String protocol, JSONObject obj) throws JSONException { String result; - switch(protocol){ + switch (protocol) { case "dns": result = "Query Type: " + obj.getString("q-type") + "\n\n"; result += "Request: \n" + obj.getString("raw-request") + "\n"; result += "Response: \n" + obj.getString("raw-response") + "\n"; break; + case "ftp": + result = "FTP From: " + obj.getString("remote-address") + "\n\n"; + result += "Request: \n" + obj.getString("raw-request") + "\n"; + break; case "http": result = "Request: \n" + obj.getString("raw-request") + "\n"; result += "Response: \n" + obj.getString("raw-response") + "\n"; break; + case "ldap": + result = "LDAP From: " + obj.getString("remote-address") + "\n\n"; + result += "Request: \n" + obj.getString("raw-request") + "\n"; + break; + case "responder": + case "smb": + result = "Request: \n" + obj.getString("raw-request") + "\n"; + break; case "smtp": result = "SMTP From: " + obj.getString("smtp-from") + "\n\n"; result += "Request: \n" + obj.getString("raw-request") + "\n"; @@ -42,7 +55,7 @@ private String processDetails(String protocol, JSONObject obj) throws JSONExcept return result; } - public String toString(){ + public String toString() { return "Protocol: " + protocol + "\n" + "UID: " + uid + "\n" + "Address: " + address + "\n"