├── LICENSE ├── MultiTap.py └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jakom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MultiTap.py: -------------------------------------------------------------------------------- 1 | from burp import IBurpExtender, IHttpListener, ITab, IContextMenuFactory 2 | from javax.swing import JMenuItem, JPopupMenu, JSplitPane, JTabbedPane, JColorChooser 3 | from java.util import ArrayList 4 | from java.awt import Color, Dimension 5 | from javax.swing import JPanel, JButton, JTextField, BoxLayout, JLabel, Box, JTextArea, JScrollPane, JTable 6 | from javax.swing.table import AbstractTableModel 7 | from java.awt import BorderLayout 8 | from java.text import SimpleDateFormat 9 | from java.util import Date 10 | import random 11 | import json 12 | import threading 13 | import os 14 | import traceback 15 | from javax.swing.table import DefaultTableCellRenderer 16 | from java.lang import Object 17 | 18 | class BurpExtender(IBurpExtender, IHttpListener, ITab, IContextMenuFactory): 19 | def registerExtenderCallbacks(self, callbacks): 20 | self.callbacks = callbacks 21 | self.helpers = callbacks.getHelpers() 22 | self.sessions = {} 23 | self.matched_requests = [] 24 | 25 | callbacks.setExtensionName("MultiTapBurp") 26 | callbacks.registerHttpListener(self) 27 | callbacks.registerContextMenuFactory(self) 28 | 29 | self.setupUI() 30 | self.setupRequestsTab() 31 | callbacks.addSuiteTab(self) 32 | callbacks.addSuiteTab(self.requestsTab) 33 | 34 | self.log("Extension initialized") 35 | 36 | def setupUI(self): 37 | self.panel = JPanel(BorderLayout()) 38 | 39 | topPanel = JPanel() 40 | topPanel.setLayout(BoxLayout(topPanel, BoxLayout.Y_AXIS)) 41 | 42 | creditsPanel = JPanel() 43 | creditsLabel = JLabel("Multi Taps by Aymen @J4k0m | LinkedIn: linkedin.com/in/jakom/") 44 | creditsPanel.add(creditsLabel) 45 | 46 | controlPanel = JPanel() 47 | self.sessionNameField = JTextField(15) 48 | self.patternField = JTextField(25) 49 | self.currentColor = self.generate_random_color() 50 | 51 | colorButton = JButton("Pick Color", actionPerformed=self.showColorPicker) 52 | self.colorPreview = JPanel() 53 | self.colorPreview.setBackground(self.currentColor) 54 | self.colorPreview.setPreferredSize(Dimension(20, 20)) 55 | self.colorPreview.setMaximumSize(Dimension(20, 20)) 56 | 57 | addButton = JButton("Add Session Pattern", actionPerformed=self.addBrowserSession) 58 | 59 | controlPanel.add(JLabel("Session Name:")) 60 | controlPanel.add(self.sessionNameField) 61 | controlPanel.add(Box.createHorizontalStrut(10)) 62 | controlPanel.add(JLabel("Pattern:")) 63 | controlPanel.add(self.patternField) 64 | controlPanel.add(Box.createHorizontalStrut(10)) 65 | controlPanel.add(colorButton) 66 | controlPanel.add(self.colorPreview) 67 | controlPanel.add(Box.createHorizontalStrut(10)) 68 | controlPanel.add(addButton) 69 | 70 | topPanel.add(creditsPanel) 71 | topPanel.add(Box.createVerticalStrut(10)) 72 | topPanel.add(controlPanel) 73 | 74 | centerPanel = JPanel() 75 | centerPanel.setLayout(BoxLayout(centerPanel, BoxLayout.Y_AXIS)) 76 | 77 | self.sessionsPanel = JPanel() 78 | self.sessionsPanel.setLayout(BoxLayout(self.sessionsPanel, BoxLayout.Y_AXIS)) 79 | sessionsScrollPane = JScrollPane(self.sessionsPanel) 80 | sessionsScrollPane.setPreferredSize(Dimension(800, 200)) 81 | 82 | centerPanel.add(JLabel("Active Sessions:")) 83 | centerPanel.add(Box.createVerticalStrut(5)) 84 | centerPanel.add(sessionsScrollPane) 85 | 86 | bottomPanel = JPanel(BorderLayout()) 87 | bottomPanel.add(JLabel("Logs:"), BorderLayout.NORTH) 88 | 89 | self.logArea = JTextArea() 90 | self.logArea.setEditable(False) 91 | self.logArea.setLineWrap(True) 92 | self.logArea.setWrapStyleWord(True) 93 | logScrollPane = JScrollPane(self.logArea) 94 | logScrollPane.setPreferredSize(Dimension(800, 200)) 95 | 96 | bottomPanel.add(logScrollPane, BorderLayout.CENTER) 97 | 98 | self.panel.add(topPanel, BorderLayout.NORTH) 99 | self.panel.add(centerPanel, BorderLayout.CENTER) 100 | self.panel.add(bottomPanel, BorderLayout.SOUTH) 101 | 102 | def setupRequestsTab(self): 103 | self.requestsTab = RequestsTab(self) 104 | 105 | def log(self, message): 106 | timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()) 107 | log_message = "[{}] {}\n".format(timestamp, message) 108 | self.logArea.append(log_message) 109 | self.logArea.setCaretPosition(self.logArea.getDocument().getLength()) 110 | 111 | def addBrowserSession(self, event): 112 | session_name = self.sessionNameField.getText() 113 | pattern = self.patternField.getText() 114 | 115 | if not session_name or not pattern: 116 | self.log("ERROR: Both session name and pattern are required") 117 | return 118 | 119 | session_id = str(random.randint(10000, 99999)) 120 | 121 | self.sessions[session_id] = { 122 | 'name': session_name, 123 | 'pattern': pattern, 124 | 'color': self.currentColor 125 | } 126 | 127 | self.currentColor = self.generate_random_color() 128 | self.colorPreview.setBackground(self.currentColor) 129 | 130 | self.updateSessionsList() 131 | self.log("Added new session: {} (ID: {}, Pattern: {})".format( 132 | session_name, session_id, pattern)) 133 | 134 | def generate_random_color(self): 135 | hue = random.random() 136 | saturation = 0.7 + random.random() * 0.3 137 | brightness = 0.7 + random.random() * 0.3 138 | 139 | color = Color.getHSBColor(hue, saturation, brightness) 140 | return color 141 | 142 | def updateSessionsList(self): 143 | self.sessionsPanel.removeAll() 144 | 145 | for session_id, session_info in self.sessions.items(): 146 | row = JPanel() 147 | row.setLayout(BoxLayout(row, BoxLayout.X_AXIS)) 148 | row.setMaximumSize(Dimension(32767, 30)) 149 | 150 | sessionLabel = JLabel("{} (Pattern: {})".format( 151 | session_info['name'], session_info['pattern'])) 152 | labelPanel = JPanel() 153 | labelPanel.add(sessionLabel) 154 | 155 | colorPanel = JPanel() 156 | colorPanel.setBackground(session_info['color']) 157 | colorPanel.setPreferredSize(Dimension(20, 20)) 158 | 159 | row.add(labelPanel) 160 | row.add(Box.createHorizontalGlue()) 161 | row.add(colorPanel) 162 | 163 | self.sessionsPanel.add(row) 164 | self.sessionsPanel.add(Box.createVerticalStrut(5)) 165 | 166 | self.sessionsPanel.revalidate() 167 | self.sessionsPanel.repaint() 168 | 169 | def showColorPicker(self, event): 170 | color = JColorChooser.showDialog( 171 | self.panel, 172 | "Choose Session Color", 173 | self.currentColor 174 | ) 175 | if color: 176 | self.currentColor = color 177 | self.colorPreview.setBackground(color) 178 | 179 | def getTabCaption(self): 180 | return "Multi Tap" 181 | 182 | def getUiComponent(self): 183 | return self.panel 184 | 185 | def createMenuItems(self, invocation): 186 | return None 187 | 188 | def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): 189 | if not messageIsRequest: 190 | try: 191 | request = messageInfo.getRequest() 192 | requestInfo = self.helpers.analyzeRequest(request) 193 | 194 | headers = requestInfo.getHeaders() 195 | body = request[requestInfo.getBodyOffset():].tostring() 196 | full_request = "\n".join(str(header) for header in headers) + "\n" + body 197 | 198 | for session_id, session_info in self.sessions.items(): 199 | pattern = session_info['pattern'] 200 | 201 | if pattern.lower() in full_request.lower(): 202 | color = session_info['color'] 203 | rgb_int = color.getRGB() & 0xFFFFFF 204 | messageInfo.setHighlight(str(rgb_int)) 205 | 206 | self.requestsTab.addRequest(messageInfo, session_info['name'], pattern) 207 | 208 | self.log("Request matched session {} (Pattern: {})".format( 209 | session_info['name'], pattern)) 210 | break 211 | 212 | except Exception as e: 213 | self.log("ERROR: Processing message failed - {}".format(str(e))) 214 | self.log("Stack trace: {}".format(traceback.format_exc())) 215 | 216 | class RequestsTab(ITab): 217 | def __init__(self, extender): 218 | self.extender = extender 219 | self.matched_requests = [] 220 | 221 | self.panel = JPanel(BorderLayout()) 222 | 223 | topPanel = JPanel(BorderLayout()) 224 | clearButton = JButton("Clear History", actionPerformed=self.clearHistory) 225 | topPanel.add(clearButton, BorderLayout.EAST) 226 | 227 | self.requestsTable = JTable(self.RequestsTableModel(self)) 228 | self.requestsTable.setAutoCreateRowSorter(True) 229 | self.requestsTable.setDefaultRenderer(Object, self.CellRenderer(self)) 230 | 231 | self.setupPopupMenu() 232 | 233 | scrollPane = JScrollPane(self.requestsTable) 234 | 235 | splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT) 236 | splitPane.setResizeWeight(0.5) 237 | 238 | self.requestViewer = self.extender.callbacks.createMessageEditor(None, False) 239 | self.responseViewer = self.extender.callbacks.createMessageEditor(None, False) 240 | 241 | reqResPanel = JPanel(BorderLayout()) 242 | tabs = JTabbedPane() 243 | tabs.addTab("Request", self.requestViewer.getComponent()) 244 | tabs.addTab("Response", self.responseViewer.getComponent()) 245 | reqResPanel.add(tabs, BorderLayout.CENTER) 246 | 247 | splitPane.setLeftComponent(scrollPane) 248 | splitPane.setRightComponent(reqResPanel) 249 | 250 | self.panel.add(topPanel, BorderLayout.NORTH) 251 | self.panel.add(splitPane, BorderLayout.CENTER) 252 | 253 | self.requestsTable.getSelectionModel().addListSelectionListener( 254 | lambda e: self.showReqRes(e) if not e.getValueIsAdjusting() else None 255 | ) 256 | 257 | def setupPopupMenu(self): 258 | self.menu = JPopupMenu() 259 | sendToRepeater = JMenuItem("Send to Repeater", 260 | actionPerformed=lambda x: self.sendToRepeater()) 261 | sendToIntruder = JMenuItem("Send to Intruder", 262 | actionPerformed=lambda x: self.sendToIntruder()) 263 | 264 | self.menu.add(sendToRepeater) 265 | self.menu.add(sendToIntruder) 266 | 267 | self.requestsTable.setComponentPopupMenu(self.menu) 268 | 269 | def sendToRepeater(self): 270 | row = self.requestsTable.getSelectedRow() 271 | if row != -1: 272 | row = self.requestsTable.convertRowIndexToModel(row) 273 | request = self.matched_requests[row] 274 | 275 | messageInfo = request['messageInfo'] 276 | url = self.extender.helpers.analyzeRequest(messageInfo).getUrl() 277 | 278 | self.extender.callbacks.sendToRepeater( 279 | url.getHost(), 280 | url.getPort(), 281 | url.getProtocol() == "https", 282 | messageInfo.getRequest(), 283 | request['session'] 284 | ) 285 | 286 | def sendToIntruder(self): 287 | row = self.requestsTable.getSelectedRow() 288 | if row != -1: 289 | row = self.requestsTable.convertRowIndexToModel(row) 290 | request = self.matched_requests[row] 291 | 292 | messageInfo = request['messageInfo'] 293 | url = self.extender.helpers.analyzeRequest(messageInfo).getUrl() 294 | 295 | self.extender.callbacks.sendToIntruder( 296 | url.getHost(), 297 | url.getPort(), 298 | url.getProtocol() == "https", 299 | messageInfo.getRequest() 300 | ) 301 | 302 | def getTabCaption(self): 303 | return "Session Requests" 304 | 305 | def getUiComponent(self): 306 | return self.panel 307 | 308 | def addRequest(self, messageInfo, session_name, pattern): 309 | request = messageInfo.getRequest() 310 | url = self.extender.helpers.analyzeRequest(messageInfo).getUrl() 311 | 312 | self.matched_requests.append({ 313 | 'messageInfo': messageInfo, 314 | 'session': session_name, 315 | 'pattern': pattern, 316 | 'url': str(url), 317 | 'method': self.extender.helpers.analyzeRequest(messageInfo).getMethod(), 318 | 'timestamp': SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()) 319 | }) 320 | 321 | self.requestsTable.getModel().fireTableDataChanged() 322 | 323 | def showReqRes(self, event): 324 | row = self.requestsTable.getSelectedRow() 325 | if row != -1: 326 | row = self.requestsTable.convertRowIndexToModel(row) 327 | messageInfo = self.matched_requests[row]['messageInfo'] 328 | 329 | self.requestViewer.setMessage(messageInfo.getRequest(), True) 330 | 331 | response = messageInfo.getResponse() 332 | if response: 333 | self.responseViewer.setMessage(response, False) 334 | else: 335 | self.responseViewer.setMessage(None, False) 336 | 337 | def clearHistory(self, event): 338 | self.matched_requests = [] 339 | self.requestsTable.getModel().fireTableDataChanged() 340 | self.requestViewer.setMessage(None, False) 341 | self.responseViewer.setMessage(None, False) 342 | self.extender.log("Request history cleared") 343 | 344 | class RequestsTableModel(AbstractTableModel): 345 | def __init__(self, tab): 346 | self.tab = tab 347 | self.columnNames = ["Time", "Session", "Method", "URL", "Pattern"] 348 | 349 | def getColumnCount(self): 350 | return len(self.columnNames) 351 | 352 | def getRowCount(self): 353 | return len(self.tab.matched_requests) 354 | 355 | def getColumnName(self, col): 356 | return self.columnNames[col] 357 | 358 | def getValueAt(self, row, col): 359 | request = self.tab.matched_requests[row] 360 | if col == 0: return request['timestamp'] 361 | elif col == 1: return request['session'] 362 | elif col == 2: return request['method'] 363 | elif col == 3: return request['url'] 364 | elif col == 4: return request['pattern'] 365 | return "" 366 | 367 | class CellRenderer(DefaultTableCellRenderer): 368 | def __init__(self, tab): 369 | self.tab = tab 370 | 371 | def getTableCellRendererComponent(self, table, value, isSelected, hasFocus, row, col): 372 | c = DefaultTableCellRenderer.getTableCellRendererComponent( 373 | self, table, value, isSelected, hasFocus, row, col) 374 | 375 | if not isSelected: 376 | row = table.convertRowIndexToModel(row) 377 | session_name = self.tab.matched_requests[row]['session'] 378 | 379 | for session in self.tab.extender.sessions.values(): 380 | if session['name'] == session_name: 381 | c.setBackground(session['color']) 382 | break 383 | 384 | return c 385 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultiTapBurp 2 | 3 | ![image](https://github.com/user-attachments/assets/dd49b92b-534b-48bc-bc6d-41daf8559ec8) 4 | 5 | 6 | MultiTapBurp is a Burp Suite extension that helps track and manage multiple sessions simultaneously by color-coding HTTP requests based on custom patterns. 7 | 8 | ## Features 9 | 10 | 1. **Session Management** 11 | - Create multiple sessions with unique names 12 | - Define custom patterns to identify requests 13 | - Assign custom colors to each session 14 | - Visual display of active sessions 15 | 16 | 2. **Request Tracking** 17 | - Automatically captures and color-codes requests matching session patterns 18 | - Real-time request monitoring 19 | - Displays timestamp, session name, HTTP method, URL, and matching pattern 20 | - Color-coded table rows for easy visual identification 21 | 22 | 3. **Request Analysis** 23 | - View full request and response details 24 | - Split view showing request list and request/response details 25 | - Tabbed interface for request and response inspection 26 | 27 | 4. **Integration with Burp Tools** 28 | - Send requests to Burp Repeater 29 | - Send requests to Burp Intruder 30 | - Right-click context menu for easy access 31 | 32 | 5. **Utility Features** 33 | - Clear history functionality 34 | - Custom color picker for sessions 35 | - Activity logging 36 | - Sortable request table 37 | 38 | ## Use Cases 39 | 40 | - Testing multiple user sessions simultaneously 41 | - Tracking requests from different authentication tokens 42 | - Monitoring specific request patterns 43 | - Organizing and categorizing HTTP traffic 44 | - Identifying session-specific behaviors 45 | 46 | ## Interface 47 | 48 | 1. **Main Tab** 49 | - Session creation controls 50 | - Active sessions display 51 | - Activity logs 52 | 53 | 2. **Session Requests Tab** 54 | - Color-coded request table 55 | - Request/Response viewer 56 | - Tool integration options 57 | 58 | ## Installation 59 | 60 | 1. Download the extension file 61 | 2. Open Burp Suite 62 | 3. Go to Extender tab 63 | 4. Click "Add" button 64 | 5. Select the extension file 65 | 6. The extension will appear as "MultiTapBurp" in the extensions list 66 | 67 | ## Usage 68 | 69 | 1. Create a new session: 70 | - Enter a session name 71 | - Define a pattern to match requests 72 | - Choose a color (or use random) 73 | - Click "Add Session Pattern" 74 | 75 | 2. Monitor requests: 76 | - Requests matching your patterns will be automatically captured 77 | - View requests in the Session Requests tab 78 | - Use the color coding to identify different sessions 79 | 80 | 3. Analyze requests: 81 | - Click on any request to view details 82 | - Use tabs to switch between request and response 83 | - Right-click to send to other Burp tools 84 | 85 | ## Author 86 | Multi Taps by Aymen @J4k0m | LinkedIn: linkedin.com/in/jakom/ 87 | --------------------------------------------------------------------------------