├── README.md └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # Burp Scope Monitor Extension 2 | A Burp Suite Extension to monitor and keep track of tested endpoints. 3 | 4 | ![](http://g.recordit.co/MwAyLS1VkZ.gif) 5 | 6 | ## Main Features 7 | 8 | - Simple, easy way to keep track of unique endpoints when testing an application 9 | - Mark individual endpoints as analyzed or not 10 | - Instantly understand when a new endpoint, not tested is requested 11 | - Accessible from Proxy tab (right click, mark request as analyzed/not) 12 | - Send to Repeater 13 | - Enforcement of Burp's in scope rules 14 | - Import/Export state file directly to a CSV file for 15 | - Autosave option 16 | 17 | ## Installation 18 | 19 | 1. Make sure you have Jython configured under Extender -> Options -> Python Environment. For further instructions, check PortSwigger official instructions at their [support page](https://support.portswigger.net/customer/portal/articles/1965930-how-to-install-an-extension-in-burp-suite). 20 | 2. `git clone git@github.com:Regala/burp-scope-monitor.git` 21 | 3. Import [main.py](main.py) in Extender - Extender -> Extensions -> Add -> Select Python -> Select [main.py](main.py) 22 | 23 | ## Documentation 24 | 25 | Most of the options available in General or Import tabs are auto-explanatory. 26 | 27 | - *"Repeater request automatically marks as analyzed"* - when issuing a request to an endpoint from repeater, it marks this request as analyzed automatically. 28 | - *"Color request in Proxy tab"* - this essentially applies the behavior of the extension in the Proxy tab, if you combine these options with "Show only highlighted items" in Proxy. However, it's not as pleasant to the eyes as the color pallete is limited. 29 | - *"Autosave periodically"* - backups the state file every 10 minutes. When activating this option, consider disabling *"Autostart Scope Monitor"*. This is in order to maintain a different state file per Burp project. However, you can easily maintain only one, master state file. 30 | - *"Import/Export"* is dedicated to handle the saved state files. It's preferred to open your Burp project file associated with the Scope Monitor. It will still work if the Burp project is different, but when loading the saved entries, you won't be able to send them to Repeater or view the request itself in the Request/Response viewer (this is due to the fact that we are not storing the actually requests - just the endpoint, it's analyzed status and a couple of other more. This makes it a little bit more efficient). 31 | 32 | ## Future Development 33 | 34 | - Keep track of parameters observed in all requests 35 | - Highlight when a new parameter was used in an already observed/analyzed endpoint 36 | - Export to spreadsheet / Google Sheets 37 | - Adding notes to the endpoint 38 | 39 | ## Implementation 40 | 41 | The code is not yet performant, optimized or anything similar. KISS and it works. Performance will be increased depending on demand and how the extension performs when handling large Burp projects. 42 | 43 | To circumvent some of Burp's Extender API limitations, some small hacks were implemented. One of those is automatically setting a comment on the requests that flow in the Proxy tab. You can still add comments on the items, as you'd normally would, but just make sure to keep the placeholder string (`scope-monitor-placeholder`) there. Hopefully in the future each requestResponse from Burp will have a unique identifier, which would make the import state / load from file much cleaner and fast. With large state files, this might hang a bit when loading. 44 | 45 | ## Contributing 46 | 47 | I welcome contributions from the public, from bug reports, feature suggestions and pull requests. 48 | 49 | ### Using the issue tracker 💡 50 | 51 | The issue tracker is the preferred channel for bug reports and features requests. 52 | 53 | ### Issues and labels 🏷 54 | 55 | The bug tracker utilizes several labels to help organize and identify issues. 56 | 57 | ### Guidelines for bug reports 🐛 58 | 59 | Use the GitHub issue search — check if the issue has already been reported. 60 | 61 | ### Known bugs: 62 | 63 | - Sometimes when switching from "Show All" to "Show New Only" Burp hangs/crashes. If you encounter this behavior please let me know how you reproduce it. 64 | - Manually marking requests as analyzed from the main extension UI tab doesn't apply colors in the proxy 65 | - The import/export function often makes Burp freeze (it unfreezes after a while) so this needs a review, probably has something to do with the locks 66 | 67 | ## Special Thanks 68 | 69 | - BlazeIt team 70 | - BBAC 71 | - HackerOne 72 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from burp import IBurpExtender 3 | from burp import ITab 4 | from burp import IHttpListener 5 | from burp import IMessageEditorController 6 | from burp import IContextMenuFactory 7 | from burp import IExtensionStateListener 8 | from burp import IScannerCheck 9 | from java.net import URL 10 | from java.awt import Component; 11 | from java.io import PrintWriter; 12 | from java.util import ArrayList; 13 | from java.util import LinkedList; 14 | from java.util import List; 15 | from javax.swing import JScrollPane; 16 | from javax.swing import JSplitPane; 17 | from javax.swing import JTabbedPane; 18 | from javax.swing import JTable; 19 | from javax.swing import JFrame; 20 | from javax.swing import JFileChooser; 21 | from javax.swing import SwingUtilities; 22 | from javax.swing.table import AbstractTableModel; 23 | from javax.swing.table import DefaultTableCellRenderer; 24 | from javax.swing.table import DefaultTableModel; 25 | from javax.swing.table import TableColumn; 26 | from javax.swing.table import TableColumnModel; 27 | from threading import Lock 28 | 29 | ### 30 | from java.awt import Color 31 | from java.awt.event import MouseAdapter 32 | from javax.swing import JMenuItem 33 | from javax.swing import JPopupMenu 34 | from javax.swing import ListSelectionModel 35 | from java.awt.event import ActionListener 36 | #from java.awt.event import ListSelectionListener 37 | from java.awt import BorderLayout 38 | from java.awt import GridLayout 39 | from javax.swing import JTextArea 40 | from javax.swing import ButtonGroup 41 | from javax.swing import JRadioButton 42 | from javax.swing import JLabel 43 | from javax.swing import JButton 44 | from javax.swing import JComboBox 45 | from javax.swing import JCheckBox 46 | from javax.swing import JPanel 47 | from javax.swing import SortOrder 48 | from java.lang import Runnable 49 | from javax.swing import RowFilter 50 | from java.awt.event import ItemListener 51 | from javax.swing.table import TableRowSorter 52 | 53 | 54 | from java.awt import EventQueue 55 | 56 | from urlparse import * 57 | import datetime 58 | import time 59 | import sched 60 | 61 | RED_COLOR = Color(255,135,135) 62 | GREEN_COLOR = Color(107,255,127) 63 | 64 | SHOW_ALL_BUTTON_LABEL = "Show All" 65 | SHOW_NEW_BUTTON_LABEL = "Show New Only" 66 | SHOW_TEST_BUTTON_LABEL = "Show Tested Only" 67 | 68 | MONITOR_ON_LABEL = "Monitor is ON" 69 | MONITOR_OFF_LABEL = "Monitor is OFF" 70 | 71 | SCOPE_MONITOR_COMMENT = "scope-monitor-placeholder" 72 | 73 | class Run(Runnable): 74 | def __init__(self, runner): 75 | self.runner = runner 76 | 77 | def run(self): 78 | self.runner() 79 | 80 | class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory, IExtensionStateListener): 81 | 82 | # 83 | # implement IBurpExtender 84 | # 85 | 86 | def registerExtenderCallbacks(self, callbacks): 87 | # keep a reference to our callbacks object 88 | self._callbacks = callbacks 89 | 90 | # obtain an extension helpers object 91 | self._helpers = callbacks.getHelpers() 92 | 93 | # set our extension name 94 | callbacks.setExtensionName("Burp Scope Monitor Experimental") 95 | 96 | self.GLOBAL_HANDLER_ANALYZED = False 97 | self.GLOBAL_HANDLER = False 98 | self.STATUS = False 99 | self.AUTOSAVE_REQUESTS = 10 100 | self.AUTOSAVE_TIMEOUT = 600 # 10 minutes should be fine 101 | self.CONFIG_INSCOPE = True 102 | 103 | self.BAD_EXTENSIONS_DEFAULT = ['.gif', '.png', '.js', '.woff', '.woff2', '.jpeg', '.jpg', '.css', '.ico', '.m3u8', '.ts', '.svg'] 104 | self.BAD_MIMES_DEFAULT = ['gif', 'script', 'jpeg', 'jpg', 'png', 'video', 'mp2t'] 105 | 106 | self.BAD_EXTENSIONS = self.BAD_EXTENSIONS_DEFAULT 107 | self.BAD_MIMES = self.BAD_MIMES_DEFAULT 108 | 109 | # create the log and a lock on which to synchronize when adding log entries 110 | 111 | self._currentlyDisplayedItem = None 112 | 113 | self.SELECTED_MODEL_ROW = 0 114 | self.SELECTED_VIEW_ROW = 0 115 | 116 | self._log = ArrayList() 117 | self._fullLog = ArrayList() 118 | self._lock = Lock() 119 | self._lockFile = Lock() 120 | 121 | # main split pane 122 | self._parentPane = JTabbedPane() 123 | 124 | self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) 125 | 126 | ##### config pane 127 | self._config = JTabbedPane() 128 | 129 | config = JPanel() 130 | iexport = JPanel() 131 | 132 | #config.setLayout(BorderLayout()) 133 | config.setLayout(None) 134 | iexport.setLayout(None) 135 | 136 | # config radio button 137 | X_BASE = 40 138 | Y_OFFSET = 5 139 | Y_OPTION = 200 140 | Y_OPTION_SPACING = 20 141 | Y_CHECKMARK_SPACING = 20 142 | 143 | 144 | self.showAllButton = JRadioButton(SHOW_ALL_BUTTON_LABEL, True) 145 | self.showNewButton = JRadioButton(SHOW_NEW_BUTTON_LABEL, False) 146 | self.showTestedButton = JRadioButton(SHOW_TEST_BUTTON_LABEL, False) 147 | 148 | self.showAllButton.setBounds(40, 60 + Y_OFFSET, 400, 30) 149 | self.showNewButton.setBounds(40, 80 + Y_OFFSET, 400, 30) 150 | self.showTestedButton.setBounds(40, 100 + Y_OFFSET, 400, 30) 151 | #self.showNewButton = JRadioButton(SHOW_NEW_BUTTON_LABEL, False) 152 | #self.showTestedButton = JRadioButton(SHOW_TEST_BUTTON_LABEL, False) 153 | 154 | self.showAllButton.addActionListener(self.handleRadioConfig) 155 | self.showNewButton.addActionListener(self.handleRadioConfig) 156 | self.showTestedButton.addActionListener(self.handleRadioConfig) 157 | 158 | self.clearButton = JButton("Clear") 159 | self.clearButton.addActionListener(self.handleClearButton) 160 | self.clearButton.setBounds(40, 20, 100, 30) 161 | 162 | self.startButton = JButton(MONITOR_ON_LABEL) 163 | self.startButton.addActionListener(self.handleStartButton) 164 | self.startButton.setBounds(150, 20, 200, 30) 165 | 166 | self.badExtensionsLabel = JLabel("Ignore extensions:") 167 | self.badExtensionsLabel.setBounds(X_BASE, 150, 200, 30) 168 | 169 | self.badExtensionsText = JTextArea("") 170 | self.loadBadExtensions() 171 | self.badExtensionsText.setBounds(X_BASE, 175, 310, 30) 172 | 173 | self.badExtensionsButton = JButton("Save") 174 | self.badExtensionsButton.addActionListener(self.handleBadExtensionsButton) 175 | self.badExtensionsButton.setBounds(355, 175, 70, 30) 176 | 177 | self.badExtensionsDefaultButton = JButton("Load Defaults") 178 | self.badExtensionsDefaultButton.addActionListener(self.handleBadExtensionsDefaultButton) 179 | self.badExtensionsDefaultButton.setBounds(430, 175, 120, 30) 180 | 181 | self.badMimesLabel = JLabel("Ignore mime types:") 182 | self.badMimesLabel.setBounds(X_BASE, 220, 200, 30) 183 | 184 | self.badMimesText = JTextArea("") 185 | self.loadBadMimes() 186 | self.badMimesText.setBounds(X_BASE, 245, 310, 30) 187 | 188 | self.badMimesButton = JButton("Save") 189 | self.badMimesButton.addActionListener(self.handleBadMimesButton) 190 | self.badMimesButton.setBounds(355, 245, 70, 30) 191 | 192 | self.badMimesDefaultButton = JButton("Load Defaults") 193 | self.badMimesDefaultButton.addActionListener(self.handleBadMimesDefaultButton) 194 | self.badMimesDefaultButton.setBounds(430, 245, 120, 30) 195 | 196 | 197 | self.otherLabel = JLabel("Other:") 198 | self.otherLabel.setBounds(40, 300, 120, 30) 199 | 200 | self.otherLabel2 = JLabel("Other:") 201 | self.otherLabel2.setBounds(X_BASE, Y_OPTION, 120, 30) 202 | 203 | self.autoSaveOption = JCheckBox("Auto save periodically") 204 | self.autoSaveOption.setSelected(True) 205 | self.autoSaveOption.addActionListener(self.handleAutoSaveOption) 206 | self.autoSaveOption.setBounds(X_BASE, Y_OPTION + Y_CHECKMARK_SPACING, 420, 30) 207 | 208 | self.repeaterOptionButton = JCheckBox("Repeater request automatically marks as analyzed") 209 | self.repeaterOptionButton.setSelected(True) 210 | self.repeaterOptionButton.addActionListener(self.handleRepeaterOptionButton) 211 | self.repeaterOptionButton.setBounds(50, 330, 420, 30) 212 | 213 | self.scopeOptionButton = JCheckBox("Follow Burp Target In Scope rules") 214 | self.scopeOptionButton.setSelected(True) 215 | self.scopeOptionButton.addActionListener(self.handleScopeOptionButton) 216 | self.scopeOptionButton.setBounds(50, 350, 420, 30) 217 | 218 | self.startOptionButton = JCheckBox("Autostart Scope Monitor") 219 | self.startOptionButton.setSelected(True) 220 | self.startOptionButton.addActionListener(self.handleStartOption) 221 | self.startOptionButton.setBounds(50, 350 + Y_OPTION_SPACING, 420, 30) 222 | 223 | self.markTestedRequestsProxy = JCheckBox("Color request in Proxy tab if analyzed") 224 | self.markTestedRequestsProxy.setSelected(True) 225 | self.markTestedRequestsProxy.addActionListener(self.handleTestedRequestsProxy) 226 | self.markTestedRequestsProxy.setBounds(50, 350 + Y_OPTION_SPACING*2, 420, 30) 227 | 228 | self.markNotTestedRequestsProxy = JCheckBox("Color request in Proxy tab if NOT analyzed") 229 | self.markNotTestedRequestsProxy.setSelected(True) 230 | self.markNotTestedRequestsProxy.addActionListener(self.handleNotTestedRequestsProxy) 231 | self.markNotTestedRequestsProxy.setBounds(50, 350 + Y_OPTION_SPACING*3, 420, 30) 232 | 233 | 234 | self.saveButton = JButton("Save now") 235 | self.saveButton.addActionListener(self.handleSaveButton) 236 | self.saveButton.setBounds(X_BASE + 320, 95, 90, 30) 237 | 238 | self.loadButton = JButton("Load now") 239 | self.loadButton.addActionListener(self.handleLoadButton) 240 | self.loadButton.setBounds(X_BASE + 420, 95, 90, 30) 241 | 242 | self.selectPath = JButton("Select path") 243 | self.selectPath.addActionListener(self.selectExportFile) 244 | self.selectPath.setBounds(X_BASE + 530, 60, 120, 30) 245 | 246 | self.selectPathText = JTextArea("") 247 | self.selectPathText.setBounds(X_BASE, 60, 510, 30) 248 | 249 | self.selectPathLabel = JLabel("State file:") 250 | self.selectPathLabel.setBounds(X_BASE, 30, 200, 30) 251 | 252 | bGroup = ButtonGroup() 253 | 254 | bGroup.add(self.showAllButton) 255 | bGroup.add(self.showNewButton) 256 | bGroup.add(self.showTestedButton) 257 | 258 | config.add(self.clearButton) 259 | config.add(self.startButton) 260 | config.add(self.startOptionButton) 261 | config.add(self.showAllButton) 262 | config.add(self.showNewButton) 263 | config.add(self.showTestedButton) 264 | 265 | config.add(self.badExtensionsButton) 266 | config.add(self.badExtensionsText) 267 | config.add(self.badExtensionsLabel) 268 | 269 | config.add(self.badMimesButton) 270 | config.add(self.badMimesText) 271 | config.add(self.badMimesLabel) 272 | 273 | config.add(self.badExtensionsDefaultButton) 274 | config.add(self.badMimesDefaultButton) 275 | 276 | config.add(self.otherLabel) 277 | config.add(self.repeaterOptionButton) 278 | config.add(self.scopeOptionButton) 279 | config.add(self.markTestedRequestsProxy) 280 | config.add(self.markNotTestedRequestsProxy) 281 | 282 | iexport.add(self.saveButton) 283 | iexport.add(self.loadButton) 284 | iexport.add(self.selectPath) 285 | iexport.add(self.selectPathText) 286 | iexport.add(self.selectPathLabel) 287 | iexport.add(self.otherLabel2) 288 | iexport.add(self.autoSaveOption) 289 | 290 | 291 | self._config.addTab("General", config) 292 | self._config.addTab("Import/Export", iexport) 293 | 294 | ##### end config pane 295 | 296 | 297 | self._parentPane.addTab("Monitor", self._splitpane) 298 | self._parentPane.addTab("Config", self._config) 299 | 300 | # table of log entries 301 | self.logTable = Table(self) 302 | 303 | #self.logTable.setDefaultRenderer(self.logTable.getColumnClass(0), ColoredTableCellRenderer(self)) 304 | 305 | self.logTable.setAutoCreateRowSorter(True) 306 | self.logTable.setRowSelectionAllowed(True) 307 | 308 | renderer = ColoredTableCellRenderer(self) 309 | #column = TableColumn(0, 190, renderer, None) 310 | 311 | print 'Initiating... ' 312 | 313 | # this could be improved by fetching initial dimensions 314 | self.logTable.getColumn("URL").setPreferredWidth(720) # noscope 315 | self.logTable.getColumn("URL").setResizable(True) 316 | 317 | self.logTable.getColumn("Checked").setCellRenderer(renderer) 318 | self.logTable.getColumn("Checked").setPreferredWidth(80) 319 | self.logTable.getColumn("Checked").setMaxWidth(80) 320 | 321 | self.logTable.getColumn("Method").setPreferredWidth(120) 322 | #self.logTable.getColumn("Method").setMaxWidth(120) 323 | self.logTable.getColumn("Method").setResizable(True) 324 | 325 | self.logTable.getColumn("Time").setPreferredWidth(120) # noscope 326 | self.logTable.getColumn("Time").setResizable(True) 327 | 328 | 329 | scrollPane = JScrollPane(self.logTable) 330 | self._splitpane.setLeftComponent(scrollPane) 331 | 332 | # tabs with request/response viewers 333 | tabs = JTabbedPane() 334 | self._requestViewer = callbacks.createMessageEditor(self, False) 335 | self._responseViewer = callbacks.createMessageEditor(self, False) 336 | tabs.addTab("Request", self._requestViewer.getComponent()) 337 | tabs.addTab("Response", self._responseViewer.getComponent()) 338 | self._splitpane.setRightComponent(tabs) 339 | 340 | ## Row sorter shit 341 | 342 | #self._tableRowSorterAutoProxyAutoAction = CustomTableRowSorter(self.logTable.getModel()) 343 | #self.logTable.setRowSorter(self._tableRowSorterAutoProxyAutoAction) 344 | 345 | 346 | markAnalyzedButton = JMenuItem("Mark Requests as Analyzed") 347 | markAnalyzedButton.addActionListener(markRequestsHandler(self, True)) 348 | 349 | markNotAnalyzedButton = JMenuItem("Mark Requests as NOT Analyzed") 350 | markNotAnalyzedButton.addActionListener(markRequestsHandler(self, False)) 351 | 352 | sendRequestMenu = JMenuItem("Send Request to Repeater") 353 | sendRequestMenu.addActionListener(sendRequestRepeater(self)) 354 | 355 | deleteRequestMenu = JMenuItem("Delete request") 356 | deleteRequestMenu.addActionListener(deleteRequestHandler(self)) 357 | 358 | self.menu = JPopupMenu("Popup") 359 | self.menu.add(markAnalyzedButton) 360 | self.menu.add(markNotAnalyzedButton) 361 | self.menu.add(sendRequestMenu) 362 | self.menu.add(deleteRequestMenu) 363 | 364 | # customize our UI components 365 | callbacks.customizeUiComponent(self._parentPane) 366 | callbacks.customizeUiComponent(self._splitpane) 367 | callbacks.customizeUiComponent(self._config) 368 | callbacks.customizeUiComponent(config) 369 | callbacks.customizeUiComponent(self.logTable) 370 | callbacks.customizeUiComponent(scrollPane) 371 | callbacks.customizeUiComponent(tabs) 372 | 373 | callbacks.registerContextMenuFactory(self) 374 | callbacks.registerExtensionStateListener(self) 375 | callbacks.registerScannerCheck(passiveScanner(self)) 376 | 377 | # add the custom tab to Burp's UI 378 | callbacks.addSuiteTab(self) 379 | 380 | # register ourselves as an HTTP listener 381 | callbacks.registerHttpListener(self) 382 | 383 | self.loadConfigs() 384 | 385 | print "Loaded!" 386 | 387 | print "Experimental import state.. " 388 | self.importState("") 389 | 390 | self.SC = sched.scheduler(time.time, time.sleep) 391 | self.SCC = self.SC.enter(10, 1, self.autoSave, (self.SC,)) 392 | self.SC.run() 393 | 394 | return 395 | 396 | ##### CUSTOM CODE ##### 397 | def loadConfigs(self): 398 | 399 | if self._callbacks.loadExtensionSetting("CONFIG_AUTOSTART") == "False": 400 | self.startOptionButton.setSelected(False) 401 | self.startOrStop(None, False) 402 | else: 403 | self.startOptionButton.setSelected(True) 404 | self.startOrStop(None, True) 405 | 406 | if self._callbacks.loadExtensionSetting("exportFile") != "": 407 | self.selectPathText.setText(self._callbacks.loadExtensionSetting("exportFile")) 408 | 409 | if self._callbacks.loadExtensionSetting("CONFIG_REPEATER") == "True": 410 | self.repeaterOptionButton.setSelected(True) 411 | else: 412 | self.repeaterOptionButton.setSelected(False) 413 | 414 | if self._callbacks.loadExtensionSetting("CONFIG_INSCOPE") == "True": 415 | self.scopeOptionButton.setSelected(True) 416 | else: 417 | self.scopeOptionButton.setSelected(False) 418 | 419 | if self._callbacks.loadExtensionSetting("CONFIG_AUTOSAVE") == "True": 420 | self.autoSaveOption.setSelected(True) 421 | else: 422 | self.autoSaveOption.setSelected(False) 423 | 424 | if self._callbacks.loadExtensionSetting("CONFIG_HIGHLIGHT_TESTED") == "True": 425 | self.markTestedRequestsProxy.setSelected(True) 426 | else: 427 | self.markTestedRequestsProxy.setSelected(False) 428 | 429 | if self._callbacks.loadExtensionSetting("CONFIG_HIGHLIGHT_NOT_TESTED") == "True": 430 | self.markNotTestedRequestsProxy.setSelected(True) 431 | else: 432 | self.markNotTestedRequestsProxy.setSelected(False) 433 | 434 | 435 | 436 | 437 | 438 | return 439 | 440 | def selectExportFile(self, event): 441 | parentFrame = JFrame() 442 | fileChooser = JFileChooser() 443 | fileChooser.setDialogTitle("Specify file to save state") 444 | fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY) 445 | 446 | userSelection = fileChooser.showOpenDialog(parentFrame) 447 | 448 | if (userSelection == JFileChooser.APPROVE_OPTION): 449 | fileLoad = fileChooser.getSelectedFile() 450 | filename = fileLoad.getAbsolutePath() 451 | 452 | self.selectPathText.setText(filename) 453 | print 'Filename selected:' + filename 454 | self._callbacks.saveExtensionSetting("exportFile", filename) 455 | 456 | return 457 | 458 | 459 | def extensionUnloaded(self): 460 | print 'extension unloading.. ' 461 | 462 | print 'canceling scheduler.. ' 463 | map(self.SC.cancel, self.SC.queue) 464 | return 465 | 466 | def loadBadExtensions(self): 467 | bad = self._callbacks.loadExtensionSetting("badExtensions") 468 | if bad: 469 | self.badExtensionsText.setText(bad) 470 | # transform text to array 471 | bad = bad.replace(" ", "") 472 | self.BAD_EXTENSIONS = bad.split(",") 473 | else: 474 | print 'no bad extension saved, reverting' 475 | self.badExtensionsText.setText(", ".join(self.BAD_EXTENSIONS)) 476 | 477 | def loadBadMimes(self): 478 | bad = self._callbacks.loadExtensionSetting("badMimes") 479 | if bad: 480 | self.badMimesText.setText(bad) 481 | 482 | bad = bad.replace(" ", "") 483 | self.BAD_MIMES = bad.split(",") 484 | else: 485 | print 'no bad mimes saved, reverting' 486 | self.badMimesText.setText(", ".join(self.BAD_MIMES)) 487 | 488 | 489 | 490 | 491 | 492 | ## GLOBAL CONTEXT CODE ## 493 | 494 | def createMenuItems(self, invocation): 495 | responses = invocation.getSelectedMessages() 496 | if responses > 0: 497 | ret = LinkedList() 498 | analyzedMenuItem = JMenuItem("Mark as analyzed") 499 | notAnalyzedMenuItem = JMenuItem("Mark as NOT analyzed") 500 | 501 | for response in responses: 502 | analyzedMenuItem.addActionListener(handleMenuItems(self,response, "analyzed")) 503 | notAnalyzedMenuItem.addActionListener(handleMenuItems(self, response, "not")) 504 | ret.add(analyzedMenuItem) 505 | ret.add(notAnalyzedMenuItem) 506 | return ret 507 | 508 | 509 | def getEndpoint(self, requestResponse): 510 | url_ = str(self._helpers.analyzeRequest(requestResponse).getUrl()) 511 | o = urlparse(url_) 512 | 513 | url = o.scheme+"://"+o.netloc+o.path 514 | #print "Url3: " + url 515 | return url 516 | 517 | 518 | def getMethod(self, requestResponse): 519 | return self._helpers.analyzeRequest(requestResponse).getMethod() 520 | 521 | 522 | ##### CUSTOM CODE ##### 523 | def handleTestedRequestsProxy(self, event): 524 | self._callbacks.saveExtensionSetting("CONFIG_HIGHLIGHT_TESTED", str(self.markTestedRequestsProxy.isSelected())) 525 | return 526 | 527 | def handleNotTestedRequestsProxy(self, event): 528 | self._callbacks.saveExtensionSetting("CONFIG_HIGHLIGHT_NOT_TESTED", str(self.markNotTestedRequestsProxy.isSelected())) 529 | return 530 | 531 | 532 | 533 | def handleStartOption(self, event): 534 | self._callbacks.saveExtensionSetting("CONFIG_AUTOSTART", str(self.startOptionButton.isSelected())) 535 | #print 'saving autostart: ' + str(self.startOptionButton.isSelected()) 536 | return 537 | 538 | def startOrStop(self, event, autoStart): 539 | if (self.startButton.getText() == MONITOR_OFF_LABEL) or autoStart: 540 | self.startButton.setText(MONITOR_ON_LABEL) 541 | self.startButton.setBackground(GREEN_COLOR) 542 | self.STATUS = True 543 | else: 544 | self.startButton.setText(MONITOR_OFF_LABEL) 545 | self.startButton.setBackground(RED_COLOR) 546 | self.STATUS = False 547 | 548 | def handleStartButton(self, event): 549 | self.startOrStop(event, False) 550 | 551 | def handleAutoSaveOption(self, event): 552 | self._callbacks.saveExtensionSetting("CONFIG_AUTOSAVE", str(self.autoSaveOption.isSelected())) 553 | return 554 | 555 | def handleSaveButton(self, event): 556 | self.exportState("") 557 | 558 | def handleLoadButton(self, event): 559 | self.importState("") 560 | 561 | def handleRepeaterOptionButton(self, event): 562 | self._callbacks.saveExtensionSetting("CONFIG_REPEATER", str(self.repeaterOptionButton.isSelected())) 563 | return 564 | 565 | def handleScopeOptionButton(self, event): 566 | self.CONFIG_INSCOPE = self.scopeOptionButton.isSelected() 567 | self._callbacks.saveExtensionSetting("CONFIG_INSCOPE", str(self.CONFIG_INSCOPE)) 568 | return 569 | 570 | def handleBadExtensionsButton(self, event): 571 | #print "before BAD array: " 572 | print self.BAD_EXTENSIONS 573 | 574 | extensions = self.badExtensionsText.getText() 575 | self._callbacks.saveExtensionSetting("badExtensions", extensions) 576 | print 'New extensions blocked: ' + extensions 577 | bad = extensions.replace(" ", "") 578 | self.BAD_EXTENSIONS = bad.split(",") 579 | #print "BAD array: " 580 | #print self.BAD_EXTENSIONS 581 | 582 | def handleBadExtensionsDefaultButton(self, event): 583 | self.BAD_EXTENSIONS = self.BAD_EXTENSIONS_DEFAULT 584 | self.badExtensionsText.setText(", ".join(self.BAD_EXTENSIONS)) 585 | self._callbacks.saveExtensionSetting("badExtensions", ", ".join(self.BAD_EXTENSIONS)) 586 | return 587 | 588 | def handleBadMimesDefaultButton(self, event): 589 | self.BAD_MIMES = self.BAD_MIMES_DEFAULT 590 | self.badMimesText.setText(", ".join(self.BAD_MIMES)) 591 | self._callbacks.saveExtensionSetting("badExtensions", ", ".join(self.BAD_MIMES)) 592 | return 593 | 594 | def handleBadMimesButton(self, event): 595 | mimes = self.badMimesText.getText() 596 | self._callbacks.saveExtensionSetting("badMimes", mimes) 597 | print 'New mimes blocked: ' + mimes 598 | bad = mimes.replace(" ", "") 599 | self.BAD_MIMES = bad.split(",") 600 | 601 | def handleClearButton(self, event): 602 | print 'Clearing table' 603 | self._lock.acquire() 604 | self._log = ArrayList() 605 | self._fullLog = ArrayList() 606 | self._lock.release() 607 | return 608 | 609 | def handleRadioConfig(self, event): 610 | #print ' radio button clicked ' 611 | #print event.getActionCommand() 612 | self._lock.acquire() 613 | 614 | if event.getActionCommand() == SHOW_ALL_BUTTON_LABEL: 615 | print "Showing all" 616 | self._log = self._fullLog 617 | elif event.getActionCommand() == SHOW_NEW_BUTTON_LABEL: 618 | print "Showing new scope only" 619 | tmpLog = ArrayList() 620 | for item in self._fullLog: 621 | if not(item._analyzed): 622 | tmpLog.add(item) 623 | self._log = tmpLog 624 | elif event.getActionCommand() == SHOW_TEST_BUTTON_LABEL: 625 | print "Showing tested scope only" 626 | tmpLog = ArrayList() 627 | for item in self._fullLog: 628 | if item._analyzed: 629 | tmpLog.add(item) 630 | self._log = tmpLog 631 | else: 632 | print "unrecognized radio label" 633 | 634 | 635 | self.fireTableDataChanged() 636 | #self._tableRowSorterAutoProxyAutoAction.toggleSortOrder(1) 637 | #self.toggleSortOrder(2) 638 | 639 | #self.logTable.toggleSortOrder(2) 640 | 641 | # refresh table? 642 | 643 | self._lock.release() 644 | 645 | 646 | # 647 | # implement ITab 648 | # 649 | 650 | def getTabCaption(self): 651 | return "Scope Monitor" 652 | 653 | def getUiComponent(self): 654 | return self._parentPane 655 | 656 | # 657 | # implement IHttpListener 658 | # 659 | 660 | def markAnalyzed(self, messageIsRequest, state): 661 | #print "markAnalyzed..." 662 | self._lock.acquire() 663 | 664 | url = self.getEndpoint(messageIsRequest) 665 | for item in self._log: 666 | if url == item._url: 667 | item._analyzed = state 668 | self._lock.release() 669 | return 670 | self._lock.release() 671 | return 672 | 673 | def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): 674 | # only process requests 675 | 676 | #print "processing httpMessage.." 677 | #print messageIsRequest 678 | 679 | print "processHttpMessage toolFlag: " + str(toolFlag) 680 | #print " -- " + str(self._callbacks.getToolName(toolFlag)) + " -- " 681 | 682 | if not(self.STATUS): 683 | return 684 | 685 | #print "global handler status: (true): " + str(self.GLOBAL_HANDLER) 686 | #print "(processHTTP) messageIsRequest" 687 | #print messageIsRequest 688 | 689 | isFromPassiveScan = False 690 | if toolFlag == 1234: 691 | print "1 processHttpMessage: processing passiveScan item" 692 | isFromPassiveScan = True 693 | 694 | if toolFlag != 1234: 695 | if messageIsRequest and not(self.GLOBAL_HANDLER): 696 | print "1.5 processHttpMessage droping message" 697 | return 698 | 699 | if self.scopeOptionButton.isSelected(): 700 | url = self._helpers.analyzeRequest(messageInfo).getUrl() 701 | if not self._callbacks.isInScope(url): 702 | #print 'Url not in scope, skipping.. ' 703 | return 704 | 705 | #print "still processing httpMessage.., request came from: " + self._callbacks.getToolName(toolFlag) 706 | if toolFlag == 1234: 707 | print "2 processHttpMessage: processing passiveScan item; setting toolFlag to proxy (4)" 708 | toolFlag = 4 709 | 710 | #toolFlag = 4 711 | if ((self._callbacks.getToolName(toolFlag) != "Repeater") and (self._callbacks.getToolName(toolFlag) != "Proxy") and (self._callbacks.getToolName(toolFlag) != "Target")): 712 | #print 'Aborting processHTTP, request came from: ' + str(self._callbacks.getToolName(toolFlag)) 713 | print "Droping request from " + str(self._callbacks.getToolName(toolFlag)) 714 | return 715 | 716 | #print "---> still processing from tool: " + str(self._callbacks.getToolName(toolFlag)) 717 | 718 | url = self.getEndpoint(messageInfo) 719 | method = self.getMethod(messageInfo) 720 | 721 | #print "(processHTTP) before extensions check: " + url 722 | 723 | for extension in self.BAD_EXTENSIONS: 724 | if url.endswith(extension): 725 | return 726 | 727 | if messageInfo.getResponse(): 728 | mime = self._helpers.analyzeResponse(messageInfo.getResponse()).getStatedMimeType() 729 | #print 'Declared mime:' + mime 730 | mime = mime.lower() 731 | if mime in self.BAD_MIMES: 732 | #print 'Bad mime:' + mime 733 | return 734 | 735 | #print "[httpMessage] before lock" 736 | # create a new log entry with the message details 737 | self._lock.acquire() 738 | row = self._log.size() 739 | 740 | 741 | for item in self._log: 742 | if url == item._url: 743 | if method == self._helpers.analyzeRequest(item._requestResponse).getMethod(): 744 | #print 'duplicate URL+method, skipping.. ' 745 | self._lock.release() 746 | 747 | # has it been analyzed? 748 | analyzed = False 749 | if self._callbacks.getToolName(toolFlag) == "Repeater": 750 | if self.repeaterOptionButton.isSelected(): 751 | analyzed = True 752 | #print "[httpMessage] setting analyzed as true" 753 | if self.GLOBAL_HANDLER_ANALYZED: 754 | analyzed = True 755 | 756 | item._analyzed = analyzed 757 | self.paintItems(messageInfo, item) 758 | 759 | return 760 | 761 | #print "[httpMessage] before setComment" 762 | if not (isFromPassiveScan): 763 | messageInfo.setComment(SCOPE_MONITOR_COMMENT) 764 | # reached here, must be new entry 765 | analyzed = False 766 | if self._callbacks.getToolName(toolFlag) == "Repeater": 767 | if self.repeaterOptionButton.isSelected(): 768 | analyzed = True 769 | #print "[httpMessage] setting analyzed as true" 770 | if self.GLOBAL_HANDLER_ANALYZED: 771 | analyzed = True 772 | 773 | #print "[httpMessage] after comment" 774 | #print 'in httpmessage, response:' 775 | #print self._helpers.analyzeResponse(messageInfo.getResponse()) 776 | 777 | date = datetime.datetime.fromtimestamp(time.time()).strftime('%H:%M:%S %d %b %Y') 778 | entry = LogEntry(toolFlag, self._callbacks.saveBuffersToTempFiles(messageInfo), url, analyzed, date, method) 779 | #print "toolFlag: " + str(toolFlag) 780 | 781 | #print "(processHTTP) Adding URL: " + url 782 | self._log.add(entry) 783 | self._fullLog.add(entry) 784 | self.fireTableRowsInserted(row, row) 785 | 786 | self.paintItems(messageInfo, entry) 787 | 788 | self._lock.release() 789 | 790 | #print "columnCoun:" + str(self.logTable.getColumnCount()) 791 | 792 | # 793 | # extend AbstractTableModel 794 | # 795 | 796 | def paintItems(self, messageInfo, item): 797 | ''' 798 | print "in paint Items" 799 | print "mark color is: (true)" + str(self.markTestedRequestsProxy.isSelected()) 800 | print "global handler analyzed: :" + str(self.GLOBAL_HANDLER_ANALYZED) 801 | print "item analyzed should be the same ^^:" + str(item._analyzed) 802 | ''' 803 | if (self.markTestedRequestsProxy.isSelected()) and (item._analyzed and self.GLOBAL_HANDLER_ANALYZED): 804 | messageInfo.setHighlight("green") 805 | return 806 | 807 | if self.markNotTestedRequestsProxy.isSelected() and not(item._analyzed): 808 | messageInfo.setHighlight("red") 809 | 810 | 811 | def getRowCount(self): 812 | try: 813 | return self._log.size() 814 | except: 815 | return 0 816 | 817 | def getColumnCount(self): 818 | return 4 819 | 820 | def getColumnName(self, columnIndex): 821 | if columnIndex == 0: 822 | return "Checked" 823 | if columnIndex == 1: 824 | return "URL" 825 | if columnIndex == 2: 826 | return "Method" 827 | if columnIndex == 3: 828 | return "Time" 829 | 830 | def getValueAt(self, rowIndex, columnIndex): 831 | logEntry = self._log.get(rowIndex) 832 | 833 | #self.setBackground(Color.GREEN) 834 | return self.returnEntry(rowIndex, columnIndex, logEntry) 835 | 836 | if self.showNewButton.isSelected() and not(logEntry._analyzed): 837 | return self.returnEntry(rowIndex, columnIndex, logEntry) 838 | elif self.showTestedButton.isSelected() and logEntry._analyzed: 839 | return self.returnEntry(rowIndex, columnIndex, logEntry) 840 | elif self.showAllButton.isSelected(): 841 | return self.returnEntry(rowIndex, columnIndex, logEntry) 842 | 843 | 844 | def returnEntry(self, rowIndex, columnIndex, entry): 845 | logEntry = self._log.get(rowIndex) 846 | if columnIndex == 0: 847 | if logEntry._analyzed: 848 | return "True" 849 | else: 850 | return "False" 851 | if columnIndex == 1: 852 | return self._helpers.urlDecode(logEntry._url) 853 | if columnIndex == 2: 854 | return logEntry._method 855 | if columnIndex == 3: 856 | return logEntry._date 857 | # return date 858 | return "" 859 | 860 | 861 | # 862 | # implement IMessageEditorController 863 | # this allows our request/response viewers to obtain details about the messages being displayed 864 | # 865 | 866 | 867 | def getHttpService(self): 868 | return self._currentlyDisplayedItem.getHttpService() 869 | 870 | def getRequest(self): 871 | #print 'getRequest called' 872 | return self._currentlyDisplayedItem.getRequest() 873 | 874 | def getResponse(self): 875 | #print 'getResponse called: ' 876 | print self._currentlyDisplayedItem.getResponse() 877 | return self._currentlyDisplayedItem.getResponse() 878 | 879 | 880 | def exportRequest(self, entity, filename): 881 | 882 | line = str(entity._analyzed) + "," 883 | line = line + self._helpers.urlEncode(entity._url).replace(",", "%2c") + "," # URL is encoded so we should be good 884 | line = line + entity._method + "," 885 | line = line + entity._date 886 | line = line + '\n' 887 | 888 | #print 'Exporting: "' + line + '"' 889 | return line 890 | 891 | def exportUrlEncode(self, url): 892 | return self._helpers.urlEncode(url).replace(",", "%2c") 893 | 894 | def exportState(self, filename): 895 | filename = self.selectPathText.getText() 896 | 897 | if filename == "": 898 | filename = self._callbacks.loadExtensionSetting("exportFile") 899 | print 'Empty filename, skipping export' 900 | return 901 | else: 902 | self._callbacks.saveExtensionSetting("exportFile", filename) 903 | 904 | print 'saving state to: ' + filename 905 | 906 | savedUrls = [] 907 | 908 | self._lockFile.acquire() 909 | try: 910 | with open(filename, 'r') as fr: 911 | savedEntries = fr.read().splitlines() 912 | savedUrls = [] 913 | for savedEntry in savedEntries: 914 | savedUrls.append(savedEntry.split(",")[1]) 915 | #print "savedUrls len: " + str(len(savedUrls)) 916 | #print "savedUrls:" 917 | #print savedUrls 918 | fr.close() 919 | except IOError: 920 | print "Autosaving skipped as file doesn't exist yet" 921 | 922 | with open(filename, 'a+') as f: 923 | 924 | for item in self._log: 925 | if self.exportUrlEncode(item._url) not in savedUrls: 926 | line = self.exportRequest(item, "xx") 927 | f.write(line) 928 | f.close() 929 | self._lockFile.release() 930 | 931 | return 932 | 933 | def importState(self, filename): 934 | filename = self.selectPathText.getText() 935 | 936 | if filename == "": 937 | filename = self._callbacks.loadExtensionSetting("exportFile") 938 | print 'Empty filename, skipping import' 939 | return 940 | else: 941 | self._callbacks.saveExtensionSetting("exportFile", filename) 942 | 943 | print 'loading state from: ' + filename 944 | 945 | self.STATUS = False 946 | 947 | self._lockFile.acquire() 948 | with open(filename, 'r') as f: 949 | 950 | proxy = self._callbacks.getProxyHistory() 951 | 952 | proxyItems = [] 953 | for item in proxy: 954 | if item.getComment(): 955 | if SCOPE_MONITOR_COMMENT in item.getComment(): 956 | proxyItems.append(item) 957 | 958 | 959 | print 'proxyItems has: ' + str(len(proxyItems)) 960 | # TODO - if no proxy items, sraight to import 961 | 962 | lines = f.read().splitlines() 963 | for line in lines: 964 | data = line.split(",") 965 | url = data[1] 966 | url = self._helpers.urlDecode(url) 967 | 968 | #print 'Saving: ' + url 969 | if not self._callbacks.isInScope(URL(url)): 970 | print '-- imported url not in scope, skipping.. ' 971 | continue 972 | 973 | 974 | analyzed = False 975 | if data[0] == "True": 976 | analyzed = True 977 | 978 | 979 | #print '.. simulating url search.. ' 980 | requestResponse = None 981 | for request in proxyItems: 982 | if url == self.getEndpoint(request): 983 | #print 'Match found when importing for url: ' + url 984 | requestResponse = request 985 | break 986 | 987 | self._log.add(LogEntry("", requestResponse, url, analyzed, data[3], data[2]) ) 988 | 989 | 990 | self._lockFile.release() 991 | print 'finished loading.. ' 992 | #print 'size: ' + str(self._log.size()) 993 | self.fireTableDataChanged() 994 | 995 | if self.startButton.getText() == MONITOR_ON_LABEL: 996 | self.STATUS = True 997 | 998 | return 999 | 1000 | def autoSave(self, sc): 1001 | #print 'autosaving.. lol what' 1002 | if self.autoSaveOption.isSelected(): 1003 | print "[" + self.getTime() + "] autosaving to " + self._callbacks.loadExtensionSetting("exportFile") 1004 | self.exportState("") 1005 | 1006 | self.SC.enter(self.AUTOSAVE_TIMEOUT, 1, self.autoSave, (self.SC,)) 1007 | return 1008 | 1009 | def getTime(self): 1010 | date = datetime.datetime.fromtimestamp(time.time()).strftime('%H:%M:%S') 1011 | return date 1012 | 1013 | # 1014 | # extend JTable to handle cell selection 1015 | # 1016 | 1017 | #def getRequest(self,): 1018 | 1019 | class Table(JTable): 1020 | def __init__(self, extender): 1021 | self._extender = extender 1022 | self.setModel(extender) 1023 | self.addMouseListener(mouseclick(self._extender)) 1024 | self.setRowSelectionAllowed(True) 1025 | self.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) 1026 | 1027 | def changeSelection(self, row, col, toggle, extend): 1028 | 1029 | # shows "entries" matching 1030 | 1031 | # show the log entry for the selected row 1032 | #print 'Selecting entry ' + str(row) + ' in changeSelection: ' 1033 | 1034 | JTable.changeSelection(self, row, col, toggle, extend) 1035 | 1036 | modelRow = self.convertRowIndexToModel(row) 1037 | #print 'converted: ' + str() 1038 | 1039 | logEntry = self._extender._log.get(modelRow) 1040 | 1041 | #print str(self._extender._helpers.analyzeRequest(logEntry._requestResponse).getUrl()) 1042 | 1043 | self._extender.SELECTED_MODEL_ROW = modelRow 1044 | self._extender.SELECTED_VIEW_ROW = row 1045 | 1046 | self._extender._currentlyDisplayedItem = logEntry._requestResponse 1047 | self._extender._requestViewer.setMessage(logEntry._requestResponse.getRequest(), True) 1048 | self._extender._responseViewer.setMessage(logEntry._requestResponse.getResponse(), False) 1049 | 1050 | 1051 | #JTable.changeSelection(self, row, col, toggle, extend) 1052 | return 1053 | 1054 | 1055 | class mouseclick(MouseAdapter): 1056 | def __init__(self, extender): 1057 | self._extender = extender 1058 | 1059 | def mouseReleased(self, evt): 1060 | if evt.button == 3: 1061 | self._extender.menu.show(evt.getComponent(), evt.getX(), evt.getY()) 1062 | 1063 | 1064 | class ColoredTableCellRenderer(DefaultTableCellRenderer): 1065 | def __init__(self, extender): 1066 | self._extender = extender 1067 | 1068 | def setValue(self, value): 1069 | if value == "False": 1070 | self.setBackground(Color(255,135,135)) 1071 | #self.setForeground(Color.RED) 1072 | elif value == "True": 1073 | self.setBackground(Color(107,255,127)) 1074 | #self.setForeground(Color.GREEN) 1075 | #return value 1076 | self.super__setValue(value) 1077 | 1078 | 1079 | # 1080 | # class to hold details of each log entry 1081 | # 1082 | 1083 | class LogEntry: 1084 | def __init__(self, tool, requestResponse, url, analyzed, date, method): 1085 | self._tool = tool 1086 | self._requestResponse = requestResponse 1087 | self._url = url 1088 | self._displayed = False 1089 | self._analyzed = analyzed 1090 | self._date = date 1091 | self._method = method 1092 | 1093 | class CustomTableRowSorter(TableRowSorter): 1094 | 1095 | # override toggleSortOrder method 1096 | def toggleSortOrder(self, column): 1097 | 1098 | # check if valid column 1099 | if column >= 0: 1100 | 1101 | # get the sort keys 1102 | keys = self.getSortKeys() 1103 | 1104 | # check if the sort keys are not empty 1105 | if keys.isEmpty() == False: 1106 | 1107 | # get the sort key 1108 | sortKey = keys.get(0) 1109 | 1110 | # check if the column clicked is sorted in descending order 1111 | if sortKey.getColumn() == column and sortKey.getSortOrder() == SortOrder.DESCENDING: 1112 | 1113 | # clear sorting 1114 | self.setSortKeys(None) 1115 | 1116 | # do not continue 1117 | return 1118 | 1119 | # try to toggle default toggleSortOrder 1120 | try: 1121 | # toggle default toggleSortOrder 1122 | TableRowSorter.toggleSortOrder(self, column) 1123 | 1124 | # catch if table is being sorted by processProxyMessage and user 1125 | except: 1126 | pass 1127 | 1128 | class deleteRequestHandler(ActionListener): 1129 | def __init__(self, extender): 1130 | self._extender = extender 1131 | 1132 | def actionPerformed(self, e): 1133 | #print "COPY SELECTED URL HANDLER ******" 1134 | 1135 | rows = self._extender.logTable.getSelectedRows() 1136 | to_delete = [] 1137 | 1138 | for row in rows: 1139 | 1140 | model_row = self._extender.logTable.convertRowIndexToModel(row) 1141 | 1142 | self._extender._log.remove(self._extender._log.get(model_row)) 1143 | 1144 | self._extender.fireTableDataChanged() 1145 | #print 'refreshing view ..... *****' 1146 | 1147 | return 1148 | 1149 | class sendRequestRepeater(ActionListener): 1150 | def __init__(self, extender): 1151 | self._extender = extender 1152 | 1153 | def actionPerformed(self, e): 1154 | #print "COPY SELECTED URL HANDLER ******" 1155 | 1156 | rows = self._extender.logTable.getSelectedRows() 1157 | for row in rows: 1158 | 1159 | model_row = self._extender.logTable.convertRowIndexToModel(row) 1160 | 1161 | request = self._extender._log.get(model_row)._requestResponse 1162 | url = self._extender._log.get(model_row)._url 1163 | 1164 | host = request.getHttpService().getHost() 1165 | port = request.getHttpService().getPort() 1166 | proto = request.getHttpService().getProtocol() 1167 | 1168 | secure = True if proto == 'https' else False 1169 | 1170 | self._extender._callbacks.sendToRepeater(host, port, secure, request.getRequest(), None); 1171 | 1172 | return 1173 | 1174 | ### LOCAL CONTEXT 1175 | class markRequestsHandler(ActionListener): 1176 | def __init__(self, extender, state): 1177 | self._extender = extender 1178 | self._state = state 1179 | 1180 | def actionPerformed(self, e): 1181 | #print "COPY SELECTED URL HANDLER ******" 1182 | #print "Status is: " + str(self._state) 1183 | 1184 | rows = self._extender.logTable.getSelectedRows() 1185 | for row in rows: 1186 | 1187 | model_row = self._extender.logTable.convertRowIndexToModel(row) 1188 | url = self._extender._log.get(model_row)._url 1189 | 1190 | #print "Changing url: " + url 1191 | 1192 | ### TODO REPLACE FOR MARK_AS_ANALYZED 1193 | self._extender._lock.acquire() 1194 | 1195 | for item in self._extender._log: 1196 | if url == item._url: 1197 | item._analyzed = self._state 1198 | self._extender.paintItems(item._requestResponse, item) 1199 | #break 1200 | self._extender._lock.release() 1201 | 1202 | 1203 | self._extender.fireTableDataChanged() 1204 | #print 'refreshing view ..... *****' 1205 | 1206 | #self._extender.changeSelection(self._extender.SELECTED_VIEW_ROW, 1, True, True) 1207 | #self._extender.changeSelection(self._extender.SELECTED_VIEW_ROW, 1, False, False) 1208 | #self._extender.changeSelection(self._extender.SELECTED_VIEW_ROW, 1, True, True) 1209 | 1210 | return 1211 | 1212 | 1213 | ### GLOBAL CONTEXT #### 1214 | class handleMenuItems(ActionListener): 1215 | def __init__(self, extender, messageInfo, menuName): 1216 | self._extender = extender 1217 | self._menuName = menuName 1218 | self._messageInfo = messageInfo 1219 | 1220 | def actionPerformed(self, e): 1221 | self._extender.GLOBAL_HANDLER = True 1222 | 1223 | if self._menuName == "analyzed": 1224 | self._extender.GLOBAL_HANDLER_ANALYZED = True 1225 | self._extender.processHttpMessage(4, self._messageInfo, self._messageInfo) 1226 | self._extender.markAnalyzed(self._messageInfo, True) 1227 | #start_new_thread(self._extender.sendRequestToAutorizeWork,(self._messageInfo,)) 1228 | 1229 | if self._menuName == "not": 1230 | self._extender.GLOBAL_HANDLER_ANALYZED = False 1231 | self._extender.processHttpMessage(4, self._messageInfo, self._messageInfo) 1232 | self._extender.markAnalyzed(self._messageInfo, False) 1233 | 1234 | self._extender.GLOBAL_HANDLER_ANALYZED = False 1235 | self._extender.GLOBAL_HANDLER = False 1236 | #self._extender.replaceString.setText(self._extender.getCookieFromMessage(self._messageInfo)) 1237 | 1238 | class passiveScanner(IScannerCheck): 1239 | def __init__(self, extender): 1240 | self._extender = extender 1241 | 1242 | def doPassiveScan(self, messageInfo): 1243 | print "--> passiveScan:" 1244 | #print messageInfo 1245 | # 4 = "Proxy" 1246 | self._extender.processHttpMessage(1234, messageInfo, messageInfo) 1247 | 1248 | if __name__ in ('__main__', 'main'): 1249 | EventQueue.invokeLater(Run(BurpExtender)) 1250 | --------------------------------------------------------------------------------