diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..8c3042a --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,35 @@ +--- +name: "release" + +on: + push: + branches: + - "master" + +jobs: + release: + name: "Release" + runs-on: "ubuntu-latest" + + steps: + - name: Checkout + uses: actions/checkout@v4.1.1 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + - name: Build + run: | + java -version && + mvn package + + - name: Upload Release + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest" + title: "Latest Build" + files: | + README.md + target/*jar-with-dependencies.jar diff --git a/.gitignore b/.gitignore index 2f7896d..64ee209 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target/ +.vscode/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index c3861c0..ce0cd19 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ net.portswigger.burp.extensions montoya-api - LATEST + 2023.12.1 org.json diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java index aaf4eb6..7fa7432 100644 --- a/src/burp/BurpExtender.java +++ b/src/burp/BurpExtender.java @@ -1,38 +1,28 @@ 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; -import interactsh.InteractEntry; -import layout.SpringUtilities; - -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.Component; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; -import javax.swing.*; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableModel; + +import javax.swing.JMenuItem; import burp.api.montoya.BurpExtension; import burp.api.montoya.MontoyaApi; import burp.api.montoya.extension.ExtensionUnloadingHandler; - +import burp.api.montoya.ui.contextmenu.ContextMenuEvent; +import burp.api.montoya.ui.contextmenu.ContextMenuItemsProvider; +import burp.gui.InteractshTab; +import interactsh.InteractEntry; public class BurpExtender implements BurpExtension, ContextMenuItemsProvider, ExtensionUnloadingHandler { public static MontoyaApi api; public static int pollTime = 60; public static InteractshTab tab; - private static ArrayList clients = new ArrayList(); - private InteractshListener listener = new InteractshListener(); @Override public void initialize(MontoyaApi api) { - this.api = api; + BurpExtender.api = api; + api.extension().setName("Interactsh Collaborator"); api.userInterface().registerContextMenuItemsProvider(this); api.extension().registerUnloadingHandler(this); @@ -40,7 +30,7 @@ public void initialize(MontoyaApi api) { api.logging().logToOutput("Starting Interactsh Collaborator!"); burp.gui.Config.generateConfig(); - tab = new InteractshTab(api, listener, clients); + BurpExtender.tab = new InteractshTab(api); burp.gui.Config.loadConfig(); api.userInterface().registerSuiteTab("Interactsh", tab); @@ -48,36 +38,13 @@ public void initialize(MontoyaApi api) { @Override public void extensionUnloaded() { - 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(); - } - } - api.logging().logToOutput("Thanks for collaborating!"); - } - - public ArrayList getClients() { - return clients; - } - - public static void addClient(Client c) { - clients.add(c); + BurpExtender.tab.cleanup(); + BurpExtender.api.logging().logToOutput("Thanks for collaborating!"); } public static int getPollTime() { try { - return Integer.parseInt(tab.getPollField().getText()); + return Integer.parseInt(BurpExtender.tab.getPollField().getText()); } catch (Exception ex) { } return 60; @@ -88,7 +55,7 @@ public static void updatePollTime(int poll) { } public static void addToTable(InteractEntry i) { - tab.addToTable(i); + BurpExtender.tab.addToTable(i); } // @@ -99,244 +66,9 @@ public static void addToTable(InteractEntry i) { public List provideMenuItems(ContextMenuEvent event) { List menuList = new ArrayList(); JMenuItem item = new JMenuItem("Generate Interactsh url"); - item.addActionListener(new InteractshListener()); + item.addActionListener(e -> BurpExtender.tab.getListener().generateCollaborator()); menuList.add(item); return menuList; } - - 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); - } - - public static String getServerText() { - return serverText.getText(); - } - - public static void setServerText(String t) { - serverText.setText(t); - } - - public static String getPortText() { - return portText.getText(); - } - - public static void setPortText(String text) { - portText.setText(text); - } - - public static String getAuthText() { - return authText.getText(); - } - - 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; - } - - public void addToTable(InteractEntry i) { - log.add(i); - logTable.revalidate(); - } - - // - // 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 - - 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); - } - } - - // - // 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 7cb90c4..ddfc72d 100644 --- a/src/burp/gui/Config.java +++ b/src/burp/gui/Config.java @@ -16,6 +16,7 @@ public static void generateConfig() { preferences.setString("interactsh-server", "oast.pro"); preferences.setString("interactsh-port", "443"); preferences.setString("interactsh-authorization", ""); + preferences.setString("interactsh-poll-time", "60"); preferences.setString("interactsh-uses-tls", Boolean.toString(true)); } } @@ -26,26 +27,30 @@ public static void loadConfig() { String port = preferences.getString("interactsh-port"); String tls = preferences.getString("interactsh-uses-tls"); String authorization = preferences.getString("interactsh-authorization"); + String pollinterval = preferences.getString("interactsh-poll-time"); // Update each of the text boxes on the Configuration pane - burp.BurpExtender.tab.setServerText(server); - burp.BurpExtender.tab.setPortText(port); - burp.BurpExtender.tab.setAuthText(authorization); - burp.BurpExtender.tab.setTlsBox(Boolean.parseBoolean(tls)); + InteractshTab.setServerText(server); + InteractshTab.setPortText(port); + InteractshTab.setAuthText(authorization); + InteractshTab.setPollText(pollinterval); + InteractshTab.setTlsBox(Boolean.parseBoolean(tls)); } 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.tab.getServerText(); - String port = burp.BurpExtender.tab.getPortText(); - String authorization = burp.BurpExtender.tab.getAuthText(); - String tls = burp.BurpExtender.tab.getTlsBox(); + String server = InteractshTab.getServerText(); + String port = InteractshTab.getPortText(); + String authorization = InteractshTab.getAuthText(); + String pollinterval = InteractshTab.getPollText(); + String tls = InteractshTab.getTlsBox(); preferences.setString("interactsh-server", server); preferences.setString("interactsh-port", port); preferences.setString("interactsh-uses-tls", tls); + preferences.setString("interactsh-poll-time", pollinterval); preferences.setString("interactsh-authorization", authorization); } @@ -64,6 +69,10 @@ public static boolean getScheme() { public static String getAuth() { return burp.BurpExtender.api.persistence().preferences().getString("interactsh-authorization"); } + + public static String getPollInterval() { + return burp.BurpExtender.api.persistence().preferences().getString("interactsh-poll-time"); + } } diff --git a/src/burp/gui/InteractshTab.java b/src/burp/gui/InteractshTab.java new file mode 100644 index 0000000..f4f5164 --- /dev/null +++ b/src/burp/gui/InteractshTab.java @@ -0,0 +1,278 @@ +package burp.gui; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.util.ArrayList; + +import javax.swing.*; +import javax.swing.table.*; + +import burp.api.montoya.MontoyaApi; +import burp.listeners.InteractshListener; +import burp.listeners.PollTimeListener; +import layout.SpringUtilities; +import interactsh.InteractEntry; + +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 JTextField pollText; + private static JCheckBox tlsBox; + private ArrayList log = new ArrayList(); + private InteractshListener listener; + + public InteractshTab(MontoyaApi api) { + this.listener = new InteractshListener(); + + 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()); + logTable.setRowSelectionAllowed(true); + logTable.setColumnSelectionAllowed(true); + scrollPane = new JScrollPane(logTable); + + tableSplitPane.setTopComponent(scrollPane); + tableSplitPane.setBottomComponent(resultsPanel); + splitPane.setBottomComponent(tableSplitPane); + + JPanel panel = new JPanel(); + JButton CollaboratorButton = new JButton("Generate Interactsh url"); + JButton RefreshButton = new JButton("Refresh"); + + JLabel pollLabel = new JLabel("Poll Time: "); + pollField = new JTextField(Config.getPollInterval(), 4); + pollField.getDocument().addDocumentListener(new PollTimeListener()); + + CollaboratorButton.addActionListener(e -> this.listener.generateCollaborator()); + RefreshButton.addActionListener(e -> this.listener.pollNowAll()); + panel.add(CollaboratorButton); + panel.add(pollLabel); + panel.add(pollField); + panel.add(RefreshButton); + 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, 250)); + innerConfig.setLayout(new SpringLayout()); + subConfigPanel.add(innerConfig); + + serverText = new JTextField("oast.pro", 20); + portText = new JTextField("443", 20); + authText = new JTextField("", 20); + pollText = new JTextField("60", 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 poll = new JLabel("Poll Interval (sec): "); + innerConfig.add(poll); + poll.setLabelFor(pollText); + innerConfig.add(pollText); + + JLabel tls = new JLabel("TLS: "); + innerConfig.add(tls); + tls.setLabelFor(tlsBox); + innerConfig.add(tlsBox); + + JButton updateConfigButton = new JButton("Update Settings"); + updateConfigButton.addActionListener(e -> { + burp.gui.Config.updateConfig(); + // Re generate client listener and register again + listener.close(); + this.listener = new InteractshListener(); + }); + innerConfig.add(updateConfigButton); + + // Add a blank panel so that SpringUtilities can make a well shaped grid + innerConfig.add(new JPanel()); + + SpringUtilities.makeCompactGrid(innerConfig, + 6, 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); + } + + public InteractshListener getListener() { + return this.listener; + } + + public static String getServerText() { + return serverText.getText(); + } + + public static void setServerText(String t) { + serverText.setText(t); + } + + public static String getPortText() { + return portText.getText(); + } + + public static void setPortText(String text) { + portText.setText(text); + } + + public static String getAuthText() { + return authText.getText(); + } + + public static String getPollText() { + return pollText.getText(); + } + + public static void setAuthText(String text) { + authText.setText(text); + } + + public static void setPollText(String text) { + pollText.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; + } + + public void addToTable(InteractEntry i) { + log.add(i); + logTable.revalidate(); + } + + // + // 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 + InteractEntry ie = log.get(row); + + 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(); + + 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 ""; + } + } + } + + public void cleanup() { + listener.close(); + } +} diff --git a/src/burp/listeners/InteractshListener.java b/src/burp/listeners/InteractshListener.java index ec710b1..7476098 100644 --- a/src/burp/listeners/InteractshListener.java +++ b/src/burp/listeners/InteractshListener.java @@ -1,60 +1,66 @@ package burp.listeners; -import java.awt.*; +import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; import java.util.concurrent.TimeUnit; -import burp.BurpExtender; import interactsh.Client; -public class InteractshListener implements ActionListener { - public ArrayList pollers = new ArrayList(); - public boolean running = true; +public class InteractshListener { + private Thread poller; + private boolean running = true; + private Client client; public InteractshListener() { - } - - @Override - public void actionPerformed(ActionEvent e) { - 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; - } + this.poller = new Thread(new Runnable() { + public void run() { + client = new Client(); + try { + if(client.register()){ + while (running == true) { + client.poll(); + + try { TimeUnit.SECONDS.sleep(burp.BurpExtender.getPollTime()); + } catch (InterruptedException ie) { + // Ignore interrupt (re evaluate running and polling) } - } else { - burp.BurpExtender.api.logging().logToOutput("Error registering client"); } - } catch (InterruptedException ie) { - } catch (Exception ex) { - burp.BurpExtender.api.logging().logToError(ex.getMessage()); + } else { + burp.BurpExtender.api.logging().logToError("Unable to register interactsh client"); } + + + } catch (Exception ex) { + burp.BurpExtender.api.logging().logToError(ex.getMessage()); + } + + if (client.isRegistered()){ + client.deregister(); } - }); - pollers.add(polling); - polling.start(); - - 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) { + } + }); + this.poller.start(); + } + + public void close() { + this.running = false; + this.poller.interrupt(); + try { + this.poller.join(); + } catch (InterruptedException ex) { burp.BurpExtender.api.logging().logToError(ex.getMessage()); } } + + public void pollNowAll() { + this.poller.interrupt(); + } + + public void generateCollaborator() { + String interactDomain = client.getInteractDomain(); + burp.BurpExtender.api.logging().logToOutput("New domain is: " + interactDomain); + StringSelection stringSelection = new StringSelection(interactDomain); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null); + } } \ No newline at end of file diff --git a/src/interactsh/Client.java b/src/interactsh/Client.java index 82984db..ed295e6 100644 --- a/src/interactsh/Client.java +++ b/src/interactsh/Client.java @@ -1,53 +1,69 @@ package interactsh; -import burp.api.montoya.http.message.requests.HttpRequest; -import burp.api.montoya.http.message.responses.HttpResponse; -import burp.api.montoya.http.HttpService; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.MGF1ParameterSpec; +import java.util.Arrays; +import java.util.Base64; +import java.util.Random; +import java.util.UUID; -import javax.crypto.*; -import javax.crypto.spec.*; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; +import javax.crypto.spec.SecretKeySpec; -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; -import java.security.*; -import java.util.*; +import com.github.shamil.Xid; + +import burp.api.montoya.http.HttpService; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; public class Client { public PrivateKey privateKey; private PublicKey publicKey; - private Xid xid; private String secretKey; - private String correlationId; // Defaults private String host = "oast.pro"; private int port = 443; private boolean scheme = true; + private boolean is_registered = false; private String authorization = null; + private String pubKey; + private String correlationId; public Client() { + this.generateKeys(); + + this.correlationId = Xid.get().toString(); + host = burp.gui.Config.getHost(); scheme = burp.gui.Config.getScheme(); authorization = burp.gui.Config.getAuth(); + secretKey = UUID.randomUUID().toString(); + pubKey = Base64.getEncoder().encodeToString(getPublicKey().getBytes(StandardCharsets.UTF_8)); try { port = Integer.parseInt(burp.gui.Config.getPort()); - } catch (NumberFormatException ne) { port = 443; } } - public boolean registerClient() throws Exception { - String pubKey = Base64.getEncoder().encodeToString(getPublicKey().getBytes(StandardCharsets.UTF_8)); - secretKey = UUID.randomUUID().toString(); - xid = Xid.get(); - correlationId = xid.toString(); + public boolean isRegistered(){ + return this.is_registered; + } + public boolean register() { + burp.BurpExtender.api.logging().logToOutput("Registering " + correlationId); try { JSONObject registerData = new JSONObject(); registerData.put("public-key", pubKey); @@ -69,15 +85,19 @@ public boolean registerClient() throws Exception { HttpResponse resp = burp.BurpExtender.api.http().sendRequest(httpRequest).response(); if (resp.statusCode() == 200) { + this.is_registered = true; return true; + } else { + burp.BurpExtender.api.logging().logToError("Register correlation " + correlationId + + " was unsuccessful. Status Code: " + resp.statusCode()); } } catch (Exception ex) { - burp.BurpExtender.api.logging().logToError(ex.getMessage()); + burp.BurpExtender.api.logging().logToError(ex); } return false; } - public boolean poll() throws IOException, InterruptedException { + public boolean poll() { String request = "GET /poll?id=" + correlationId + "&secret=" + secretKey + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: Interact.sh Client\r\n"; @@ -89,7 +109,8 @@ public boolean poll() throws IOException, InterruptedException { 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()); + burp.BurpExtender.api.logging() + .logToOutput("Poll for " + correlationId + " was unsuccessful: " + resp.statusCode()); return false; } @@ -97,7 +118,7 @@ public boolean poll() throws IOException, InterruptedException { try { JSONObject jsonObject = new JSONObject(responseBody); String aesKey = jsonObject.getString("aes_key"); - String key = decryptAesKey(aesKey); + String key = this.decryptAesKey(aesKey); if (!jsonObject.isNull("data")) { JSONArray data = jsonObject.getJSONArray("data"); @@ -153,18 +174,25 @@ public String getInteractDomain() { while (fullDomain.length() < 33) { fullDomain += (char) (random.nextInt(26) + 'a'); } + fullDomain += "." + host; return fullDomain; } } - public void generateKeys() throws NoSuchAlgorithmException { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); - kpg.initialize(2048); - KeyPair kp = kpg.generateKeyPair(); - - publicKey = kp.getPublic(); - privateKey = kp.getPrivate(); + public void generateKeys(){ + KeyPairGenerator kpg; + try { + kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + KeyPair kp = kpg.generateKeyPair(); + + publicKey = kp.getPublic(); + privateKey = kp.getPrivate(); + } catch (NoSuchAlgorithmException e) { + burp.BurpExtender.api.logging().logToError("Unable to generate client key pair"); + burp.BurpExtender.api.logging().logToError(e); + } } private String getPublicKey() { @@ -181,7 +209,8 @@ private String decryptAesKey(String encrypted) throws Exception { byte[] cipherTextArray = Base64.getDecoder().decode(encrypted); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); - OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT); + OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), + PSource.PSpecified.DEFAULT); cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams); byte[] decrypted = cipher.doFinal(cipherTextArray);