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);