From c371162dd683c7edf952adc172ce9be9de7435ea Mon Sep 17 00:00:00 2001 From: Hannah Law Date: Mon, 27 Nov 2023 17:20:16 +0000 Subject: [PATCH 1/5] #2 Ability to view and edit upgrade request for WebSocket message. --- build.gradle | 2 +- src/main/java/attack/AttackHandler.java | 18 ++++-------- src/main/java/connection/Connection.java | 4 +++ .../java/connection/ConnectionFactory.java | 6 ++-- .../java/connection/WebSocketConnection.java | 19 +++++++++---- src/main/java/ui/WebSocketFrame.java | 4 +-- .../java/ui/attack/WebSocketAttackPanel.java | 15 ++++++++-- .../attack/table/WebSocketMessageTable.java | 7 +++-- .../java/ui/editor/WebSocketEditorPanel.java | 28 +++++++++++++++---- src/main/resources/examples/BasicExample.py | 4 +-- .../examples/BasicExampleWithComments.py | 4 +-- .../resources/examples/MultipleConnections.py | 6 ++-- ...ipleConnectionsDifferentUpgradeRequests.py | 12 ++++++++ .../resources/examples/OneDirectionExample.py | 4 +-- src/main/resources/examples/Recursive.py | 4 +-- src/main/resources/examples/SleepExample.py | 4 +-- .../examples/WebSecurityAcademyLabExample.py | 13 +++++++++ 17 files changed, 107 insertions(+), 47 deletions(-) create mode 100644 src/main/resources/examples/MultipleConnectionsDifferentUpgradeRequests.py create mode 100644 src/main/resources/examples/WebSecurityAcademyLabExample.py diff --git a/build.gradle b/build.gradle index 492240a..9ec3862 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'net.portswigger' -version = '1.0.1' +version = '1.1.0' repositories { mavenLocal() diff --git a/src/main/java/attack/AttackHandler.java b/src/main/java/attack/AttackHandler.java index 507d2af..d217c47 100644 --- a/src/main/java/attack/AttackHandler.java +++ b/src/main/java/attack/AttackHandler.java @@ -1,6 +1,6 @@ package attack; -import burp.api.montoya.ui.contextmenu.WebSocketMessage; +import burp.api.montoya.http.message.requests.HttpRequest; import burp.api.montoya.websocket.Direction; import burp.api.montoya.websocket.WebSockets; import connection.Connection; @@ -27,7 +27,6 @@ public class AttackHandler private final BlockingQueue sendMessageQueue; private final BlockingQueue tableBlockingQueue; private final WebSocketMessageTableModel webSocketMessageTableModel; - private final WebSocketMessage baseWebSocketMessage; private final AtomicBoolean isAttackRunning; private final PythonInterpreter interpreter; private ExecutorService sendMessageExecutorService; @@ -39,7 +38,6 @@ public AttackHandler( BlockingQueue sendMessageQueue, BlockingQueue tableBlockingQueue, WebSocketMessageTableModel webSocketMessageTableModel, - WebSocketMessage baseWebSocketMessage, AtomicBoolean isAttackRunning ) { @@ -47,24 +45,23 @@ public AttackHandler( this.sendMessageQueue = sendMessageQueue; this.tableBlockingQueue = tableBlockingQueue; this.webSocketMessageTableModel = webSocketMessageTableModel; - this.baseWebSocketMessage = baseWebSocketMessage; this.isAttackRunning = isAttackRunning; interpreter = new PythonInterpreter(); interpreter.setOut(logger.outputStream()); interpreter.setErr(logger.errorStream()); - interpreter.set("base_websocket", baseWebSocketMessage); - interpreter.set("websocket_connection", new ConnectionFactory(logger, webSockets, sendMessageQueue, isAttackRunning)); interpreter.set("results_table", new TableBlockingQueueProducer(logger, tableBlockingQueue)); } - public void executeJython(String payload, String editorCodeString) + public void executeJython(String payload, HttpRequest upgradeRequest, String editorCodeString) { interpreter.set("payload", payload); + interpreter.set("upgrade_request", upgradeRequest); interpreter.exec(editorCodeString); - interpreter.exec("queue_websockets(base_websocket, payload)"); + interpreter.exec("queue_websockets(upgrade_request, payload)"); + logger.logOutput(LoggerLevel.DEFAULT, "request: " + upgradeRequest.toString()); } public void executeCallback(WebSocketConnectionMessage webSocketConnectionMessage) @@ -94,11 +91,6 @@ public WebSocketMessageTableModel getWebSocketMessageTableModel() return webSocketMessageTableModel; } - public WebSocketMessage getBaseWebSocketMessage() - { - return baseWebSocketMessage; - } - public AtomicBoolean getIsAttackRunning() { return isAttackRunning; diff --git a/src/main/java/connection/Connection.java b/src/main/java/connection/Connection.java index cf7482b..c21786a 100644 --- a/src/main/java/connection/Connection.java +++ b/src/main/java/connection/Connection.java @@ -1,8 +1,12 @@ package connection; +import burp.api.montoya.http.message.requests.HttpRequest; + public interface Connection { void queue(String payload); void queue(String payload, String comment); + + HttpRequest upgradeRequest(); } diff --git a/src/main/java/connection/ConnectionFactory.java b/src/main/java/connection/ConnectionFactory.java index c1cd152..fae8252 100644 --- a/src/main/java/connection/ConnectionFactory.java +++ b/src/main/java/connection/ConnectionFactory.java @@ -1,6 +1,6 @@ package connection; -import burp.api.montoya.ui.contextmenu.WebSocketMessage; +import burp.api.montoya.http.message.requests.HttpRequest; import burp.api.montoya.websocket.WebSockets; import data.WebSocketConnectionMessage; import logger.Logger; @@ -28,8 +28,8 @@ public ConnectionFactory( this.isAttackRunning = isAttackRunning; } - public Connection create(WebSocketMessage baseWebSocketMessage) + public Connection create(HttpRequest upgradeRequest) { - return new WebSocketConnection(logger, webSockets, sendMessageQueue, baseWebSocketMessage, isAttackRunning); + return new WebSocketConnection(logger, webSockets, sendMessageQueue, upgradeRequest, isAttackRunning); } } diff --git a/src/main/java/connection/WebSocketConnection.java b/src/main/java/connection/WebSocketConnection.java index c9bc0c9..d9b6139 100644 --- a/src/main/java/connection/WebSocketConnection.java +++ b/src/main/java/connection/WebSocketConnection.java @@ -1,7 +1,7 @@ package connection; import burp.WebSocketExtensionWebSocketMessageHandler; -import burp.api.montoya.ui.contextmenu.WebSocketMessage; +import burp.api.montoya.http.message.requests.HttpRequest; import burp.api.montoya.websocket.Direction; import burp.api.montoya.websocket.WebSockets; import burp.api.montoya.websocket.extension.ExtensionWebSocket; @@ -19,6 +19,7 @@ public class WebSocketConnection implements Connection private final Logger logger; private final WebSockets webSockets; private final BlockingQueue sendMessageQueue; + private final HttpRequest upgradeRequest; private final AtomicBoolean isAttackRunning; private final ExtensionWebSocket extensionWebSocket; @@ -26,16 +27,17 @@ public class WebSocketConnection implements Connection Logger logger, WebSockets webSockets, BlockingQueue sendMessageQueue, - WebSocketMessage baseWebSocketMessage, + HttpRequest upgradeRequest, AtomicBoolean isAttackRunning ) { this.logger = logger; this.webSockets = webSockets; this.sendMessageQueue = sendMessageQueue; + this.upgradeRequest = upgradeRequest; this.isAttackRunning = isAttackRunning; - extensionWebSocket = createExtensionWebSocket(baseWebSocketMessage); + extensionWebSocket = createExtensionWebSocket(upgradeRequest); } @Override @@ -68,16 +70,23 @@ public void queue(String payload, String comment) } } + @Override + public HttpRequest upgradeRequest() + { + return upgradeRequest; + } + public void sendMessage(String payload) { extensionWebSocket.sendTextMessage(payload); } - private ExtensionWebSocket createExtensionWebSocket(WebSocketMessage baseWebSocketMessage) + private ExtensionWebSocket createExtensionWebSocket(HttpRequest upgradeRequest) { ExtensionWebSocket extensionWebSocket; - ExtensionWebSocketCreation extensionWebSocketCreation = webSockets.createWebSocket(baseWebSocketMessage.upgradeRequest()); + ExtensionWebSocketCreation extensionWebSocketCreation = webSockets.createWebSocket(upgradeRequest); + logger.logOutput(LoggerLevel.DEBUG, "WebSocketConnection Upgrade request: " + upgradeRequest.toString()); if (extensionWebSocketCreation.webSocket().isPresent()) { diff --git a/src/main/java/ui/WebSocketFrame.java b/src/main/java/ui/WebSocketFrame.java index 1089cff..6d1b812 100644 --- a/src/main/java/ui/WebSocketFrame.java +++ b/src/main/java/ui/WebSocketFrame.java @@ -75,9 +75,9 @@ private void initComponents() WebSocketMessageTableModel webSocketMessageTableModel = new WebSocketMessageTableModel(); - attackHandler = new AttackHandler(logger, webSockets, sendMessageQueue, tableBlockingQueue, webSocketMessageTableModel, webSocketMessage, isAttackRunning); + attackHandler = new AttackHandler(logger, webSockets, sendMessageQueue, tableBlockingQueue, webSocketMessageTableModel, isAttackRunning); - cardDeck.add(new WebSocketEditorPanel(logger, userInterface, persistence, cardLayout, cardDeck, attackHandler), "editorPanel"); + cardDeck.add(new WebSocketEditorPanel(logger, userInterface, persistence, cardLayout, cardDeck, attackHandler, webSocketMessage), "editorPanel"); cardDeck.add(new WebSocketAttackPanel(userInterface, cardLayout, cardDeck, attackHandler), "attackPanel"); this.getContentPane().add(cardDeck); diff --git a/src/main/java/ui/attack/WebSocketAttackPanel.java b/src/main/java/ui/attack/WebSocketAttackPanel.java index 15f7b41..c8a0217 100644 --- a/src/main/java/ui/attack/WebSocketAttackPanel.java +++ b/src/main/java/ui/attack/WebSocketAttackPanel.java @@ -3,6 +3,7 @@ import attack.AttackHandler; import burp.api.montoya.ui.UserInterface; import burp.api.montoya.ui.editor.EditorOptions; +import burp.api.montoya.ui.editor.HttpRequestEditor; import burp.api.montoya.ui.editor.WebSocketMessageEditor; import ui.attack.table.WebSocketMessageTable; @@ -46,12 +47,15 @@ private void initComponents() private Component getWebSocketMessageDisplay() { WebSocketMessageEditor webSocketMessageEditor = getWebSocketMessageEditor(); - return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, getWebSocketMessageTable(webSocketMessageEditor), webSocketMessageEditor.uiComponent()); + HttpRequestEditor upgradeRequestEditor = getUpgradeRequestEditor(); + + JSplitPane webSocketInformationDisplay = new JSplitPane(JSplitPane.VERTICAL_SPLIT, webSocketMessageEditor.uiComponent(), upgradeRequestEditor.uiComponent()); + return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, getWebSocketMessageTable(webSocketMessageEditor, upgradeRequestEditor), webSocketInformationDisplay); } - private Component getWebSocketMessageTable(WebSocketMessageEditor webSocketMessageEditor) + private Component getWebSocketMessageTable(WebSocketMessageEditor webSocketMessageEditor, HttpRequestEditor upgradeRequestEditor) { - return new WebSocketMessageTable(attackHandler.getWebSocketMessageTableModel(), webSocketMessageEditor); + return new WebSocketMessageTable(attackHandler.getWebSocketMessageTableModel(), webSocketMessageEditor, upgradeRequestEditor); } private WebSocketMessageEditor getWebSocketMessageEditor() @@ -59,6 +63,11 @@ private WebSocketMessageEditor getWebSocketMessageEditor() return userInterface.createWebSocketMessageEditor(EditorOptions.READ_ONLY); } + private HttpRequestEditor getUpgradeRequestEditor() + { + return userInterface.createHttpRequestEditor(EditorOptions.READ_ONLY); + } + private Component getHaltConfigureButton() { JButton haltConfigureButton = new JButton("Halt"); diff --git a/src/main/java/ui/attack/table/WebSocketMessageTable.java b/src/main/java/ui/attack/table/WebSocketMessageTable.java index 8c2cd86..da05bb2 100644 --- a/src/main/java/ui/attack/table/WebSocketMessageTable.java +++ b/src/main/java/ui/attack/table/WebSocketMessageTable.java @@ -1,6 +1,7 @@ package ui.attack.table; import burp.api.montoya.core.ByteArray; +import burp.api.montoya.ui.editor.HttpRequestEditor; import burp.api.montoya.ui.editor.WebSocketMessageEditor; import data.ConnectionMessage; @@ -11,8 +12,8 @@ public class WebSocketMessageTable extends JPanel { public WebSocketMessageTable( WebSocketMessageTableModel webSocketMessageTableModel, - WebSocketMessageEditor webSocketMessageEditor - ) + WebSocketMessageEditor webSocketMessageEditor, + HttpRequestEditor upgradeRequestEditor) { super(new BorderLayout()); @@ -25,6 +26,8 @@ public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boole webSocketMessageEditor.setContents(ByteArray.byteArray(webSocketConnectionMessage.getPayload())); + upgradeRequestEditor.setRequest(webSocketConnectionMessage.getConnection().upgradeRequest()); + super.changeSelection(rowIndex, columnIndex, toggle, extend); } }; diff --git a/src/main/java/ui/editor/WebSocketEditorPanel.java b/src/main/java/ui/editor/WebSocketEditorPanel.java index 4d0cd49..6312507 100644 --- a/src/main/java/ui/editor/WebSocketEditorPanel.java +++ b/src/main/java/ui/editor/WebSocketEditorPanel.java @@ -2,9 +2,12 @@ import attack.AttackHandler; import burp.WebSocketFuzzer; +import burp.api.montoya.http.message.requests.HttpRequest; import burp.api.montoya.persistence.Persistence; import burp.api.montoya.ui.Theme; import burp.api.montoya.ui.UserInterface; +import burp.api.montoya.ui.contextmenu.WebSocketMessage; +import burp.api.montoya.ui.editor.HttpRequestEditor; import burp.api.montoya.ui.editor.WebSocketMessageEditor; import logger.Logger; import logger.LoggerLevel; @@ -35,8 +38,10 @@ public class WebSocketEditorPanel extends JPanel private final CardLayout cardLayout; private final JPanel cardDeck; private final AttackHandler attackHandler; + private final WebSocketMessage originalWebSocketMessage; private JComboBox scriptComboBox; private WebSocketMessageEditor webSocketsMessageEditor; + private HttpRequestEditor upgradeHttpMessageEditor; private JSpinner numberOfThreadsSpinner; public WebSocketEditorPanel( @@ -45,8 +50,8 @@ public WebSocketEditorPanel( Persistence persistence, CardLayout cardLayout, JPanel cardDeck, - AttackHandler attackHandler - ) + AttackHandler attackHandler, + WebSocketMessage originalWebSocketMessage) { this.logger = logger; this.userInterface = userInterface; @@ -54,6 +59,7 @@ public WebSocketEditorPanel( this.cardLayout = cardLayout; this.cardDeck = cardDeck; this.attackHandler = attackHandler; + this.originalWebSocketMessage = originalWebSocketMessage; this.setLayout(new BorderLayout()); @@ -62,7 +68,10 @@ public WebSocketEditorPanel( private void initComponents() { - JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, getWebSocketMessageEditor(), getPythonCodeEditor()); + JSplitPane editableEditors = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, getWebSocketMessageEditor(), getUpgradeHttpMessageEditor()); + editableEditors.setResizeWeight(0.5); + + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, editableEditors, getPythonCodeEditor()); splitPane.setResizeWeight(0.3); this.add(splitPane, BorderLayout.CENTER); @@ -71,11 +80,19 @@ private void initComponents() private Component getWebSocketMessageEditor() { webSocketsMessageEditor = userInterface.createWebSocketMessageEditor(); - webSocketsMessageEditor.setContents(attackHandler.getBaseWebSocketMessage().payload()); + webSocketsMessageEditor.setContents(originalWebSocketMessage.payload()); return webSocketsMessageEditor.uiComponent(); } + private Component getUpgradeHttpMessageEditor() + { + upgradeHttpMessageEditor = userInterface.createHttpRequestEditor(); + upgradeHttpMessageEditor.setRequest(originalWebSocketMessage.upgradeRequest()); + + return upgradeHttpMessageEditor.uiComponent(); + } + private Component getPythonCodeEditor() { JPanel panel = new JPanel(new BorderLayout()); @@ -279,13 +296,14 @@ private JButton getAttackButton(RSyntaxTextArea rSyntaxTextArea) JButton attackButton = new JButton("Attack"); attackButton.addActionListener(l -> { String payload = webSocketsMessageEditor.getContents().toString(); + HttpRequest upgradeRequest = upgradeHttpMessageEditor.getRequest(); String jythonCode = rSyntaxTextArea.getText(); new Thread(() -> { try { - attackHandler.executeJython(payload, jythonCode); + attackHandler.executeJython(payload, upgradeRequest, jythonCode); } catch (Exception e) { diff --git a/src/main/resources/examples/BasicExample.py b/src/main/resources/examples/BasicExample.py index aeb0be9..1fc59d4 100644 --- a/src/main/resources/examples/BasicExample.py +++ b/src/main/resources/examples/BasicExample.py @@ -1,5 +1,5 @@ -def queue_websockets(base_websocket, payload): - connection1 = websocket_connection.create(base_websocket) +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) for i in range(10): connection1.queue(payload) diff --git a/src/main/resources/examples/BasicExampleWithComments.py b/src/main/resources/examples/BasicExampleWithComments.py index 2646183..132accb 100644 --- a/src/main/resources/examples/BasicExampleWithComments.py +++ b/src/main/resources/examples/BasicExampleWithComments.py @@ -1,5 +1,5 @@ -def queue_websockets(base_websocket, payload): - connection1 = websocket_connection.create(base_websocket) +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) for i in range(10): connection1.queue(payload, "foo") diff --git a/src/main/resources/examples/MultipleConnections.py b/src/main/resources/examples/MultipleConnections.py index 0cd6009..d4174b5 100644 --- a/src/main/resources/examples/MultipleConnections.py +++ b/src/main/resources/examples/MultipleConnections.py @@ -1,6 +1,6 @@ -def queue_websockets(base_websocket, payload): - connection1 = websocket_connection.create(base_websocket) - connection2 = websocket_connection.create(base_websocket) +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) + connection2 = websocket_connection.create(upgrade_request) for i in range(10): connection1.queue(payload) diff --git a/src/main/resources/examples/MultipleConnectionsDifferentUpgradeRequests.py b/src/main/resources/examples/MultipleConnectionsDifferentUpgradeRequests.py new file mode 100644 index 0000000..e9bbbe0 --- /dev/null +++ b/src/main/resources/examples/MultipleConnectionsDifferentUpgradeRequests.py @@ -0,0 +1,12 @@ +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) + connection2 = websocket_connection.create(upgrade_request.withHeader("Cookie", "session=foo")) + + connection1.queue("READY") + connection2.queue("foo") + +def handle_outgoing_message(websocket_message): + results_table.add(websocket_message) + +def handle_incoming_message(websocket_message): + results_table.add(websocket_message) \ No newline at end of file diff --git a/src/main/resources/examples/OneDirectionExample.py b/src/main/resources/examples/OneDirectionExample.py index d0fa539..544355e 100644 --- a/src/main/resources/examples/OneDirectionExample.py +++ b/src/main/resources/examples/OneDirectionExample.py @@ -1,5 +1,5 @@ -def queue_websockets(base_websocket, payload): - connection1 = websocket_connection.create(base_websocket) +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) for i in range(10): connection1.queue(payload) diff --git a/src/main/resources/examples/Recursive.py b/src/main/resources/examples/Recursive.py index 87bd5d4..3f68564 100644 --- a/src/main/resources/examples/Recursive.py +++ b/src/main/resources/examples/Recursive.py @@ -1,5 +1,5 @@ -def queue_websockets(base_websocket, payload): - connection1 = websocket_connection.create(base_websocket) +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) for i in range(10): connection1.queue(payload) diff --git a/src/main/resources/examples/SleepExample.py b/src/main/resources/examples/SleepExample.py index e075a54..d558647 100644 --- a/src/main/resources/examples/SleepExample.py +++ b/src/main/resources/examples/SleepExample.py @@ -1,7 +1,7 @@ import time -def queue_websockets(base_websocket, payload): - connection1 = websocket_connection.create(base_websocket) +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) for i in range(10): connection1.queue(payload) diff --git a/src/main/resources/examples/WebSecurityAcademyLabExample.py b/src/main/resources/examples/WebSecurityAcademyLabExample.py new file mode 100644 index 0000000..04dd780 --- /dev/null +++ b/src/main/resources/examples/WebSecurityAcademyLabExample.py @@ -0,0 +1,13 @@ +def queue_websockets(upgrade_request, payload): + connection1 = websocket_connection.create(upgrade_request) + + connection1.queue("READY") + +def handle_outgoing_message(websocket_message): + results_table.add(websocket_message) + +def handle_incoming_message(websocket_message): + # Warning: will continue sending messages until attack paused. + if "Hal Pline" in websocket_message.getPayload(): + websocket_message.getConnection().queue(payload) + results_table.add(websocket_message) \ No newline at end of file From 8673622dafb084609444e865a8085b1f9c87ad76 Mon Sep 17 00:00:00 2001 From: Hannah Law Date: Mon, 27 Nov 2023 20:25:50 +0000 Subject: [PATCH 2/5] Set version of extension in build.gradle. Refactor WebSocketFuzzer and minor refactor. --- build.gradle | 3 ++ src/main/java/attack/AttackHandler.java | 5 +- .../WebSocketContextMenuItemsProvider.java | 3 +- src/main/java/burp/WebSocketFuzzer.java | 51 +++---------------- .../java/connection/WebSocketConnection.java | 3 +- src/main/java/utils/Utilities.java | 39 ++++++++++++++ 6 files changed, 57 insertions(+), 47 deletions(-) diff --git a/build.gradle b/build.gradle index 9ec3862..b782dbf 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,9 @@ dependencies { } tasks.register('fatJar', Jar) { + manifest { + attributes 'implementation-version': "${version}" + } duplicatesStrategy = DuplicatesStrategy.EXCLUDE from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } } with jar diff --git a/src/main/java/attack/AttackHandler.java b/src/main/java/attack/AttackHandler.java index d217c47..dcc5590 100644 --- a/src/main/java/attack/AttackHandler.java +++ b/src/main/java/attack/AttackHandler.java @@ -46,12 +46,13 @@ public AttackHandler( this.tableBlockingQueue = tableBlockingQueue; this.webSocketMessageTableModel = webSocketMessageTableModel; this.isAttackRunning = isAttackRunning; + interpreter = new PythonInterpreter(); + interpreter.setOut(logger.outputStream()); interpreter.setErr(logger.errorStream()); interpreter.set("websocket_connection", new ConnectionFactory(logger, webSockets, sendMessageQueue, isAttackRunning)); - interpreter.set("results_table", new TableBlockingQueueProducer(logger, tableBlockingQueue)); } @@ -61,7 +62,7 @@ public void executeJython(String payload, HttpRequest upgradeRequest, String edi interpreter.set("upgrade_request", upgradeRequest); interpreter.exec(editorCodeString); interpreter.exec("queue_websockets(upgrade_request, payload)"); - logger.logOutput(LoggerLevel.DEFAULT, "request: " + upgradeRequest.toString()); + logger.logOutput(LoggerLevel.DEBUG, "request: " + upgradeRequest.toString()); } public void executeCallback(WebSocketConnectionMessage webSocketConnectionMessage) diff --git a/src/main/java/burp/WebSocketContextMenuItemsProvider.java b/src/main/java/burp/WebSocketContextMenuItemsProvider.java index d2a6263..8289359 100644 --- a/src/main/java/burp/WebSocketContextMenuItemsProvider.java +++ b/src/main/java/burp/WebSocketContextMenuItemsProvider.java @@ -15,11 +15,11 @@ public class WebSocketContextMenuItemsProvider implements ContextMenuItemsProvider { - private final List frameList; private final Logger logger; private final UserInterface userInterface; private final Persistence persistence; private final WebSockets webSockets; + private final List frameList; public WebSocketContextMenuItemsProvider( Logger logger, @@ -36,6 +36,7 @@ public WebSocketContextMenuItemsProvider( this.frameList = frameList; } + @Override public List provideMenuItems(WebSocketContextMenuEvent event) { JMenuItem sendToContextMenuItem = new JMenuItem("Send to " + WebSocketFuzzer.EXTENSION_NAME); diff --git a/src/main/java/burp/WebSocketFuzzer.java b/src/main/java/burp/WebSocketFuzzer.java index 0034d68..106924a 100644 --- a/src/main/java/burp/WebSocketFuzzer.java +++ b/src/main/java/burp/WebSocketFuzzer.java @@ -8,13 +8,12 @@ import burp.api.montoya.websocket.WebSockets; import logger.Logger; import logger.LoggerLevel; +import utils.Utilities; import javax.swing.*; import java.util.ArrayList; import java.util.List; -import static utils.Utilities.closeAllFrames; - public class WebSocketFuzzer implements BurpExtension { public static final String EXTENSION_NAME = "WebSocket Turbo Intruder"; @@ -26,56 +25,22 @@ public void initialize(MontoyaApi api) Extension extension = api.extension(); Persistence persistence = api.persistence(); UserInterface userInterface = api.userInterface(); - Logger logger = new Logger(api.logging()); WebSockets websockets = api.websockets(); - List frameList = new ArrayList<>(); + Logger logger = new Logger(api.logging()); - extension.setName(EXTENSION_NAME); + Utilities.initializeDefaultDirectory(logger, persistence); - initializeDefaultDirectory(logger, persistence); + List frameList = new ArrayList<>(); - JMenu menu = generateMenu(logger, persistence, frameList); + JMenu menu = Utilities.generateMenu(logger, persistence, frameList); userInterface.menuBar().registerMenu(menu); userInterface.registerContextMenuItemsProvider(new WebSocketContextMenuItemsProvider(logger, userInterface, persistence, websockets, frameList)); - extension.registerUnloadingHandler(new WebSocketExtensionUnloadingHandler(frameList)); - logger.logOutput(LoggerLevel.DEFAULT, EXTENSION_NAME + " - Loaded"); - } - - private void initializeDefaultDirectory(Logger logger, Persistence persistence) - { - if (persistence.preferences().getString("websocketsScriptsPath") == null) - { - persistence.preferences().setString("websocketsScriptsPath", DEFAULT_SCRIPT_DIRECTORY); - logger.logOutput(LoggerLevel.DEBUG, "Default script directory initialized."); - } - } - - private JMenu generateMenu(Logger logger, Persistence persistence, List frameList) - { - JMenuItem resetDefaultScriptsMenuItem = new JMenuItem("Reset scripts directory to default."); - resetDefaultScriptsMenuItem.addActionListener(l -> { - persistence.preferences().setString("websocketsScriptsPath", DEFAULT_SCRIPT_DIRECTORY); - logger.logOutput(LoggerLevel.DEBUG, "Scripts directory reset to " + DEFAULT_SCRIPT_DIRECTORY); - }); - - JMenuItem closeAllFramesMenuItem = new JMenuItem("Close all " + EXTENSION_NAME + " windows."); - closeAllFramesMenuItem.addActionListener(l -> { - closeAllFrames(frameList); - logger.logOutput(LoggerLevel.DEBUG, "All " + EXTENSION_NAME + " windows closed."); - }); - - JCheckBoxMenuItem loggingLevelDebug = new JCheckBoxMenuItem("Debug mode", logger.isDebugLogLevel()); - loggingLevelDebug.addActionListener(l -> logger.setDebugLogLevel(loggingLevelDebug.getState())); - - JMenu menu = new JMenu(EXTENSION_NAME); - menu.add(resetDefaultScriptsMenuItem); - menu.add(closeAllFramesMenuItem); - menu.add(loggingLevelDebug); - - return menu; + extension.setName(EXTENSION_NAME); + String extensionVersion = WebSocketFuzzer.class.getPackage().getImplementationVersion(); + logger.logOutput(LoggerLevel.DEFAULT, EXTENSION_NAME + " v" + extensionVersion); } } \ No newline at end of file diff --git a/src/main/java/connection/WebSocketConnection.java b/src/main/java/connection/WebSocketConnection.java index d9b6139..c6ff25d 100644 --- a/src/main/java/connection/WebSocketConnection.java +++ b/src/main/java/connection/WebSocketConnection.java @@ -59,7 +59,8 @@ public void queue(String payload) public void queue(String payload, String comment) { if (isAttackRunning.get()) - {try + { + try { sendMessageQueue.put(new WebSocketConnectionMessage(payload, Direction.CLIENT_TO_SERVER, LocalDateTime.now(), comment, this)); } diff --git a/src/main/java/utils/Utilities.java b/src/main/java/utils/Utilities.java index 7bae17d..c505181 100644 --- a/src/main/java/utils/Utilities.java +++ b/src/main/java/utils/Utilities.java @@ -1,5 +1,10 @@ package utils; +import burp.WebSocketFuzzer; +import burp.api.montoya.persistence.Persistence; +import logger.Logger; +import logger.LoggerLevel; + import javax.swing.*; import java.util.List; @@ -12,4 +17,38 @@ public static void closeAllFrames(List frameList) frame.dispose(); } } + + public static void initializeDefaultDirectory(Logger logger, Persistence persistence) + { + if (persistence.preferences().getString("websocketsScriptsPath") == null) + { + persistence.preferences().setString("websocketsScriptsPath", WebSocketFuzzer.DEFAULT_SCRIPT_DIRECTORY); + logger.logOutput(LoggerLevel.DEBUG, "Default script directory initialized."); + } + } + + public static JMenu generateMenu(Logger logger, Persistence persistence, List frameList) + { + JMenuItem resetDefaultScriptsMenuItem = new JMenuItem("Reset scripts directory to default."); + resetDefaultScriptsMenuItem.addActionListener(l -> { + persistence.preferences().setString("websocketsScriptsPath", WebSocketFuzzer.DEFAULT_SCRIPT_DIRECTORY); + logger.logOutput(LoggerLevel.DEBUG, "Scripts directory reset to " + WebSocketFuzzer.DEFAULT_SCRIPT_DIRECTORY); + }); + + JMenuItem closeAllFramesMenuItem = new JMenuItem("Close all " + WebSocketFuzzer.EXTENSION_NAME + " windows."); + closeAllFramesMenuItem.addActionListener(l -> { + closeAllFrames(frameList); + logger.logOutput(LoggerLevel.DEBUG, "All " + WebSocketFuzzer.EXTENSION_NAME + " windows closed."); + }); + + JCheckBoxMenuItem loggingLevelDebug = new JCheckBoxMenuItem("Debug mode", logger.isDebugLogLevel()); + loggingLevelDebug.addActionListener(l -> logger.setDebugLogLevel(loggingLevelDebug.getState())); + + JMenu menu = new JMenu(WebSocketFuzzer.EXTENSION_NAME); + menu.add(resetDefaultScriptsMenuItem); + menu.add(closeAllFramesMenuItem); + menu.add(loggingLevelDebug); + + return menu; + } } From c0a30b961dd39dc4493c496abc9464257323a5af Mon Sep 17 00:00:00 2001 From: Hannah Law Date: Mon, 27 Nov 2023 20:28:40 +0000 Subject: [PATCH 3/5] Set resize weight on attack results panel. --- src/main/java/ui/attack/WebSocketAttackPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ui/attack/WebSocketAttackPanel.java b/src/main/java/ui/attack/WebSocketAttackPanel.java index c8a0217..055eeeb 100644 --- a/src/main/java/ui/attack/WebSocketAttackPanel.java +++ b/src/main/java/ui/attack/WebSocketAttackPanel.java @@ -50,6 +50,7 @@ private Component getWebSocketMessageDisplay() HttpRequestEditor upgradeRequestEditor = getUpgradeRequestEditor(); JSplitPane webSocketInformationDisplay = new JSplitPane(JSplitPane.VERTICAL_SPLIT, webSocketMessageEditor.uiComponent(), upgradeRequestEditor.uiComponent()); + webSocketInformationDisplay.setResizeWeight(0.5); return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, getWebSocketMessageTable(webSocketMessageEditor, upgradeRequestEditor), webSocketInformationDisplay); } From de337a57bf46c3ab2e8725140b547fcead81ebe7 Mon Sep 17 00:00:00 2001 From: Hannah Law Date: Mon, 27 Nov 2023 20:38:19 +0000 Subject: [PATCH 4/5] Remove unconfigurable LoggerLevel.ERROR. --- src/main/java/attack/AttackHandler.java | 1 - ...bSocketExtensionWebSocketMessageHandler.java | 7 +++---- .../java/connection/WebSocketConnection.java | 6 +++--- src/main/java/logger/Logger.java | 17 +++-------------- src/main/java/logger/LoggerLevel.java | 3 +-- .../java/queue/SendMessageQueueConsumer.java | 3 +-- .../java/queue/TableBlockingQueueConsumer.java | 3 +-- .../java/queue/TableBlockingQueueProducer.java | 3 +-- .../java/ui/editor/WebSocketEditorPanel.java | 5 ++--- 9 files changed, 15 insertions(+), 33 deletions(-) diff --git a/src/main/java/attack/AttackHandler.java b/src/main/java/attack/AttackHandler.java index dcc5590..ffb08bf 100644 --- a/src/main/java/attack/AttackHandler.java +++ b/src/main/java/attack/AttackHandler.java @@ -62,7 +62,6 @@ public void executeJython(String payload, HttpRequest upgradeRequest, String edi interpreter.set("upgrade_request", upgradeRequest); interpreter.exec(editorCodeString); interpreter.exec("queue_websockets(upgrade_request, payload)"); - logger.logOutput(LoggerLevel.DEBUG, "request: " + upgradeRequest.toString()); } public void executeCallback(WebSocketConnectionMessage webSocketConnectionMessage) diff --git a/src/main/java/burp/WebSocketExtensionWebSocketMessageHandler.java b/src/main/java/burp/WebSocketExtensionWebSocketMessageHandler.java index 590f610..cceb1e9 100644 --- a/src/main/java/burp/WebSocketExtensionWebSocketMessageHandler.java +++ b/src/main/java/burp/WebSocketExtensionWebSocketMessageHandler.java @@ -6,7 +6,6 @@ import connection.WebSocketConnection; import data.WebSocketConnectionMessage; import logger.Logger; -import logger.LoggerLevel; import java.time.LocalDateTime; import java.util.concurrent.BlockingQueue; @@ -37,13 +36,13 @@ public void textMessageReceived(TextMessage textMessage) } catch (InterruptedException e) { - logger.logError(LoggerLevel.ERROR, "Failed to put message on queue."); + logger.logError("Failed to put message on queue."); } } @Override public void binaryMessageReceived(BinaryMessage binaryMessage) { - logger.logOutput(LoggerLevel.ERROR, "Unhandled binary message received"); + logger.logError("Unhandled binary message received"); } -} +} \ No newline at end of file diff --git a/src/main/java/connection/WebSocketConnection.java b/src/main/java/connection/WebSocketConnection.java index c6ff25d..af9d90c 100644 --- a/src/main/java/connection/WebSocketConnection.java +++ b/src/main/java/connection/WebSocketConnection.java @@ -50,7 +50,7 @@ public void queue(String payload) } catch (InterruptedException e) { - logger.logError(LoggerLevel.ERROR, "Failed to put message on sendMessageQueue"); + logger.logError("Failed to put message on sendMessageQueue"); } } } @@ -66,7 +66,7 @@ public void queue(String payload, String comment) } catch (InterruptedException e) { - logger.logError(LoggerLevel.ERROR, "Failed to put message on sendMessageQueue"); + logger.logError("Failed to put message on sendMessageQueue"); } } } @@ -97,7 +97,7 @@ private ExtensionWebSocket createExtensionWebSocket(HttpRequest upgradeRequest) } else { - logger.logError(LoggerLevel.DEFAULT, "Failed to create websocket connection"); + logger.logError("Failed to create websocket connection"); extensionWebSocket = null; } diff --git a/src/main/java/logger/Logger.java b/src/main/java/logger/Logger.java index 5a3a88d..d9b33ad 100644 --- a/src/main/java/logger/Logger.java +++ b/src/main/java/logger/Logger.java @@ -5,20 +5,16 @@ import java.io.OutputStream; import static logger.LoggerLevel.DEBUG; -import static logger.LoggerLevel.ERROR; public class Logger { private final Logging logging; private boolean debugLogLevel; - private final boolean errorLogLevel; public Logger(Logging logging) { this.logging = logging; - debugLogLevel = false; - errorLogLevel = true; } public boolean isDebugLogLevel() @@ -39,12 +35,9 @@ public void logOutput(LoggerLevel loggerLevel, String output) } } - public void logError(LoggerLevel loggerLevel, String output) + public void logError(String output) { - if (isValid(loggerLevel)) - { - logging.logToError(output); - } + logging.logToError(output); } public OutputStream outputStream() @@ -59,10 +52,6 @@ public OutputStream errorStream() private boolean isValid(LoggerLevel outputLogLevel) { - if (outputLogLevel.equals(DEBUG) && !debugLogLevel) - { - return false; - } - else return !outputLogLevel.equals(ERROR) || errorLogLevel; + return !outputLogLevel.equals(DEBUG) || debugLogLevel; } } diff --git a/src/main/java/logger/LoggerLevel.java b/src/main/java/logger/LoggerLevel.java index 5695e85..612484e 100644 --- a/src/main/java/logger/LoggerLevel.java +++ b/src/main/java/logger/LoggerLevel.java @@ -3,6 +3,5 @@ public enum LoggerLevel { DEFAULT, - DEBUG, - ERROR + DEBUG } diff --git a/src/main/java/queue/SendMessageQueueConsumer.java b/src/main/java/queue/SendMessageQueueConsumer.java index ebeb313..5399fdf 100644 --- a/src/main/java/queue/SendMessageQueueConsumer.java +++ b/src/main/java/queue/SendMessageQueueConsumer.java @@ -4,7 +4,6 @@ import burp.api.montoya.websocket.Direction; import data.WebSocketConnectionMessage; import logger.Logger; -import logger.LoggerLevel; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; @@ -47,7 +46,7 @@ public void run() attackHandler.executeCallback(webSocketConnectionMessage); } catch (InterruptedException e) { - logger.logError(LoggerLevel.ERROR, "Failed to take message from sendMessageQueue"); + logger.logError("Failed to take message from sendMessageQueue"); } } } diff --git a/src/main/java/queue/TableBlockingQueueConsumer.java b/src/main/java/queue/TableBlockingQueueConsumer.java index c4b3971..2aa9106 100644 --- a/src/main/java/queue/TableBlockingQueueConsumer.java +++ b/src/main/java/queue/TableBlockingQueueConsumer.java @@ -2,7 +2,6 @@ import data.ConnectionMessage; import logger.Logger; -import logger.LoggerLevel; import ui.attack.table.WebSocketMessageTableModel; import java.awt.*; @@ -40,7 +39,7 @@ public void run() EventQueue.invokeLater(() -> tableModel.add(connectionMessage)); } catch (InterruptedException e) { - logger.logError(LoggerLevel.ERROR, "Error taking from tableBlockingQueue."); + logger.logError("Error taking from tableBlockingQueue."); } } } diff --git a/src/main/java/queue/TableBlockingQueueProducer.java b/src/main/java/queue/TableBlockingQueueProducer.java index f356f0d..7706e49 100644 --- a/src/main/java/queue/TableBlockingQueueProducer.java +++ b/src/main/java/queue/TableBlockingQueueProducer.java @@ -2,7 +2,6 @@ import data.ConnectionMessage; import logger.Logger; -import logger.LoggerLevel; import org.python.core.PyObject; import java.util.concurrent.BlockingQueue; @@ -29,7 +28,7 @@ public void add(ConnectionMessage connectionMessage) } catch (InterruptedException e) { - logger.logError(LoggerLevel.ERROR, "Failed to insert Server to Client message into tableBlockingQueue."); + logger.logError("Failed to insert Server to Client message into tableBlockingQueue."); } } } diff --git a/src/main/java/ui/editor/WebSocketEditorPanel.java b/src/main/java/ui/editor/WebSocketEditorPanel.java index 6312507..bde69f6 100644 --- a/src/main/java/ui/editor/WebSocketEditorPanel.java +++ b/src/main/java/ui/editor/WebSocketEditorPanel.java @@ -10,7 +10,6 @@ import burp.api.montoya.ui.editor.HttpRequestEditor; import burp.api.montoya.ui.editor.WebSocketMessageEditor; import logger.Logger; -import logger.LoggerLevel; import org.apache.commons.io.IOUtils; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; @@ -284,7 +283,7 @@ private RSyntaxTextArea getRSyntaxTextArea() } catch (IOException e) { - logger.logError(LoggerLevel.ERROR, "Unable to apply dark theme."); + logger.logError("Unable to apply dark theme."); } } @@ -308,7 +307,7 @@ private JButton getAttackButton(RSyntaxTextArea rSyntaxTextArea) catch (Exception e) { JOptionPane.showMessageDialog(this, "Jython code error. Please review.\r\n" + e, "Error", JOptionPane.ERROR_MESSAGE); - logger.logError(LoggerLevel.DEBUG, "Jython code error. Please review.\r\n" + e); + logger.logError("Jython code error. Please review.\r\n" + e); } }).start(); From 59643775605fe0f65adaac849d9494e094fdab10 Mon Sep 17 00:00:00 2001 From: Hannah Law Date: Mon, 27 Nov 2023 20:45:47 +0000 Subject: [PATCH 5/5] Remove dependency on commons-io. --- build.gradle | 3 +-- src/main/java/ui/editor/WebSocketEditorPanel.java | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index b782dbf..fdcb011 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,7 @@ dependencies { implementation ( 'org.python:jython-standalone:2.7.3', - 'com.fifesoft:rsyntaxtextarea:3.3.4', - 'commons-io:commons-io:2.13.0' + 'com.fifesoft:rsyntaxtextarea:3.3.4' ) } diff --git a/src/main/java/ui/editor/WebSocketEditorPanel.java b/src/main/java/ui/editor/WebSocketEditorPanel.java index bde69f6..3ec6463 100644 --- a/src/main/java/ui/editor/WebSocketEditorPanel.java +++ b/src/main/java/ui/editor/WebSocketEditorPanel.java @@ -10,7 +10,6 @@ import burp.api.montoya.ui.editor.HttpRequestEditor; import burp.api.montoya.ui.editor.WebSocketMessageEditor; import logger.Logger; -import org.apache.commons.io.IOUtils; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rtextarea.RTextScrollPane; @@ -23,6 +22,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.ArrayList; import java.util.HashMap; @@ -116,12 +116,13 @@ else if (path.toString().startsWith(WebSocketFuzzer.DEFAULT_SCRIPT_DIRECTORY)) { if (stream != null) { - data = IOUtils.toString(stream); + data = new String(stream.readAllBytes(), StandardCharsets.UTF_8); } } catch (IOException e) { throw new RuntimeException(e); } + rSyntaxTextArea.setText(data); } else