├── LICENSE ├── README.md ├── Trishul_intercept_working_gif.gif ├── trishul.py ├── trishul_add_website_scope.png ├── trishul_main_picture.png ├── trishul_send_req.png └── trishul_usage.gif /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Gaurav Narwani 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | Trishul 4 |

5 | 6 |

Burp Extension for Automated Vulnerability Discovery

7 | 8 | ![alt text](https://raw.githubusercontent.com/gauravnarwani97/Trishul/master/trishul_main_picture.png "Trishul Main") 9 | 10 | # Trishul 11 | Trishul is an automated vulnerability finding Burp Extension. Built with Jython supports real-time vulnerability detection in multiple requests with user-friendly output. This tool was made to supplement testing where results have to be found in a limited amount of time. Currently, the tool supports finding of Cross-Site Scripting, SQL Injections and Server-Side Template Injections. More vulnerabilities would be added in the later versions. 12 | 13 | ## Installation 14 | 1. Download and Install Burp Suite: http://portswigger.net/burp/download.html 15 | 2. Download Jython standalone JAR: http://www.jython.org/download.html 16 | 3. Open Extender Tab in Burp. Go to Options. Under Python Environment, you have to update the location of Jython Standalone JAR you just downloaded. Click on Select File and Choose the downloaded Jython jar file. 17 | 4. Download the trishul.py file from this repository. 18 | 5. Open Extender Tab under Extender and Click on Add under Burp Extensions. Choose Extension Type as Python and give the location of trishul.py file. Click on Next. 19 | 6. Once the Extension is done installing, you will see a Tab added to your Burp with the Name “Trishul”. Click on the Tab and enjoy automatic vulnerability detection. 20 | 21 | Note: All demonstration shown on this tool has been done on the website http://testphp.vulnweb.com. 22 | 23 | ## Usage 24 | To detect vulnerabilities in requests, the requests are provided via the two ways: 25 | 1. Send each request manually to Trishul 26 | 2. Automatically test for all requests in a scope added Website 27 | Each of the following ways will be explained in more detail in the following paragraphs. 28 | 29 | ### Usage #1: 30 | While Installing Trishul, we add another item in our drop-down menu while using Right-Click on various requests. Once you Right-Click any request in Proxy/Target/Repeater, you will find an option “Send a request to Trishul”. With this option, you can send any request to Trishul that you want to test. 31 | ![alt text](https://raw.githubusercontent.com/gauravnarwani97/Trishul/master/trishul_send_req.png "send_req_to_trishul") 32 | 33 | ### Usage #2: 34 | Add the website to be tested in scope and Turn Intercept on in Trishul to test all requests flowing to the website in scope. 35 | ![alt text](https://raw.githubusercontent.com/gauravnarwani97/Trishul/master/trishul_add_website_scope.png "show_scope") 36 | 37 | Once the website is added to Scope. Head over to Trishul and Turn Intercept On to capture all the requests flowing to the inscoped Website. 38 | 39 | ![alt text](https://raw.githubusercontent.com/gauravnarwani97/Trishul/master/Trishul_intercept_working_gif.gif "intercept_video") 40 | 41 | ## Configurations 42 | There are a couple of configurations available for a user to use Trishul. To view these configurations, head over to Trishul and view the config tab in the bottom left of the pane. Here is the List of Options Available: 43 | 1. Intercept Button: With Intercept Button set to On, the tool will perform a test on all requests flowing to the website added in Scope. This button is restricted to scope as it is not feasible to test all the requests flowing to Burp from multiple domains. This would affect the performance. 44 | 2. Auto-Scroll: With Auto-Scroll checked, the tool will scroll automatically to the last tested request. This option is feasible when testing a huge domain with Intercept turned on such that scrolling shouldn’t be a tough job. 45 | 3. Detect XSS, SQLi, SSTI – These checkboxes are added if any user wants to only test for a specific vulnerability and want to omit other test cases. Used to obtain much faster results for a specific request. 46 | 4. Blind XSS: This textbox is added for users who want to append their Blind XSS Payload for every parameter in a request. To use this, enter your Blind XSS payload (singular) in the text box and click on the Blind XSS Checkbox. Now, for every request passing through Trishul, the value of all parameters in the request would be replaced with the Blind XSS payload. 47 | 48 | ## Interpreting Results 49 | For every result, Trishul displays one of the three options for each of the vulnerability tested: 50 | + Found: The vulnerability was successfully detected for the Request parameters. 51 | + Not Found: The vulnerability was not present in the Request parameters. 52 | + Possible! Check Manually: The vulnerability maybe present. The tester has to reconfirm the finding. 53 | 54 | The test for these vulnerabilities depends on the parameters in the request. If the request has no parameters, Trishul would not process this request and would show Not Found in all of the vulnerabilities. 55 | 56 | If any of the Found/Possible! Check Manually is been seen under the vulnerability class for the specific request, the user has to click the result to see the vulnerable parameter displayed under the Vulnerability class in Issues Tab in the bottom left. 57 | 58 | The user then has to select the parameter displayed under the Vulnerability class and the description for that parameter would be shown to him. The user can then view the Request and Response which was sent from Trishul to determine the vulnerability. 59 | 60 | On Clicking the Highlighted Response Tab, you will be shown the highlighted text for some of the vulnerability class. For Example: Payload reflection for Cross-Site Scripting or Error Based SQLi text shown in response. The Highlighted Response tab was added as there was no option in Burp API to highlight the response text in Burp’s MessageEditor Tab. 61 | 62 | ![alt text](https://raw.githubusercontent.com/gauravnarwani97/Trishul/master/trishul_usage.gif "Trishul Usage Video") 63 | 64 | 65 | ## Authors 66 | Gaurav Narwani @gauravnarwani97 67 | -------------------------------------------------------------------------------- /Trishul_intercept_working_gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravnarwani97/Trishul/1409a12db89fbe8ac9820099db411a2223921fa2/Trishul_intercept_working_gif.gif -------------------------------------------------------------------------------- /trishul.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from burp import ITab 5 | from burp import IBurpExtender 6 | from burp import IHttpListener 7 | from burp import IContextMenuFactory 8 | from burp import IMessageEditorController 9 | from burp import IHttpRequestResponse 10 | from burp import IHttpRequestResponseWithMarkers 11 | from burp import IHttpService 12 | from burp import ITextEditor 13 | from javax.swing import JList 14 | from javax.swing import JTable 15 | from javax.swing import JFrame 16 | from javax.swing import JLabel 17 | from javax.swing import JPanel 18 | from javax.swing import JToggleButton 19 | from javax.swing import JCheckBox 20 | from javax.swing import JMenuItem 21 | from javax.swing import JTextArea 22 | from javax.swing import JTree 23 | from javax.swing.tree import TreePath 24 | from javax.swing import JPopupMenu 25 | from javax.swing import JSplitPane 26 | from javax.swing import JEditorPane 27 | from javax.swing import JScrollPane 28 | from javax.swing import JTabbedPane 29 | from javax.swing import SwingUtilities 30 | from javax.swing.table import TableRowSorter 31 | from javax.swing.table import AbstractTableModel 32 | from javax.swing.tree import DefaultMutableTreeNode 33 | from javax.swing.tree import DefaultTreeCellRenderer 34 | from javax.swing.tree import DefaultTreeModel 35 | from javax.swing.text.html import HTMLEditorKit 36 | from threading import Lock 37 | from java.io import File 38 | from java.net import URL 39 | from java.net import URLEncoder 40 | from java.awt import Color 41 | from java.awt import Dimension 42 | from java.awt import BorderLayout 43 | from java.awt.event import MouseAdapter 44 | from java.awt.event import ActionListener 45 | from java.awt.event import AdjustmentListener 46 | from java.util import LinkedList 47 | from java.util import ArrayList 48 | from java.lang import Runnable 49 | from java.lang import Integer 50 | from java.lang import String 51 | from java.lang import Math 52 | from thread import start_new_thread 53 | from array import array 54 | import datetime 55 | import re 56 | 57 | # 58 | #Initialize BurpExtender API to use Extender features 59 | # 60 | 61 | class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory, IHttpRequestResponseWithMarkers, ITextEditor): 62 | def registerExtenderCallbacks(self, callbacks): 63 | self._callbacks = callbacks 64 | #Initialize callbacks to be used later 65 | 66 | self._helpers = callbacks.getHelpers() 67 | callbacks.setExtensionName("Trishul") 68 | 69 | self._log = ArrayList() 70 | #_log used to store our outputs for a URL, which is retrieved later by the tool 71 | 72 | self._lock = Lock() 73 | #Lock is used for locking threads while updating logs in order such that no multiple updates happen at once 74 | 75 | self.intercept = 0 76 | 77 | self.FOUND = "Found" 78 | self.CHECK = "Possible! Check Manually" 79 | self.NOT_FOUND = "Not Found" 80 | #Static Values for output 81 | 82 | 83 | #Initialize GUI 84 | self.issuesTab() 85 | 86 | self.advisoryReqResp() 87 | 88 | self.configTab() 89 | 90 | self.tabsInit() 91 | 92 | self.definecallbacks() 93 | 94 | 95 | print("Thank You for Installing Trishul") 96 | 97 | return 98 | 99 | # 100 | #Initialize Issues Tab displaying the JTree 101 | # 102 | 103 | def issuesTab(self): 104 | self.root = DefaultMutableTreeNode('Issues') 105 | 106 | frame = JFrame("Issues Tree") 107 | 108 | self.tree = JTree(self.root) 109 | self.rowSelected = '' 110 | self.tree.addMouseListener(mouseclick(self)) 111 | self.issuepanel = JScrollPane() 112 | self.issuepanel.setPreferredSize(Dimension(300,450)) 113 | self.issuepanel.getViewport().setView((self.tree)) 114 | frame.add(self.issuepanel,BorderLayout.CENTER) 115 | 116 | # 117 | #Adding Issues to Issues TreePath 118 | # 119 | def addIssues(self, branch, branchData=None): 120 | if branchData == None: 121 | branch.add(DefaultMutableTreeNode('No valid data')) 122 | else: 123 | for item in branchData: 124 | branch.add(DefaultMutableTreeNode(item)) 125 | 126 | # 127 | #Initialize the Config Tab to modify tool settings 128 | # 129 | def configTab(self): 130 | Config = JLabel("Config") 131 | self.startButton = JToggleButton("Intercept Off", actionPerformed=self.startOrStop) 132 | self.startButton.setBounds(40, 30, 200, 30) 133 | 134 | self.autoScroll = JCheckBox("Auto Scroll") 135 | self.autoScroll.setBounds(40, 80, 200, 30) 136 | 137 | self.xsscheck = JCheckBox("Detect XSS") 138 | self.xsscheck.setSelected(True) 139 | self.xsscheck.setBounds(40, 110, 200, 30) 140 | 141 | self.sqlicheck = JCheckBox("Detect SQLi") 142 | self.sqlicheck.setSelected(True) 143 | self.sqlicheck.setBounds(40, 140, 200, 30) 144 | 145 | self.ssticheck = JCheckBox("Detect SSTI") 146 | self.ssticheck.setSelected(True) 147 | self.ssticheck.setBounds(40, 170, 200, 30) 148 | 149 | self.blindxss = JCheckBox("Blind XSS") 150 | self.blindxss.setBounds(40, 200, 200, 30) 151 | 152 | self.BlindXSSText = JTextArea("", 5, 30) 153 | 154 | scrollbxssText = JScrollPane(self.BlindXSSText) 155 | scrollbxssText.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED) 156 | scrollbxssText.setBounds(40, 250, 400, 110) 157 | 158 | self.configtab = JPanel() 159 | self.configtab.setLayout(None) 160 | self.configtab.setBounds(0, 0, 300, 300) 161 | self.configtab.add(Config) 162 | self.configtab.add(self.startButton) 163 | self.configtab.add(self.autoScroll) 164 | self.configtab.add(self.xsscheck) 165 | self.configtab.add(self.sqlicheck) 166 | self.configtab.add(self.ssticheck) 167 | self.configtab.add(self.blindxss) 168 | self.configtab.add(scrollbxssText) 169 | 170 | # 171 | #Turn Intercept from Proxy on or off 172 | # 173 | def startOrStop(self, event): 174 | if self.startButton.getText() == "Intercept Off": 175 | self.startButton.setText("Intercept On") 176 | self.startButton.setSelected(True) 177 | self.intercept = 1 178 | else: 179 | self.startButton.setText("Intercept Off") 180 | self.startButton.setSelected(False) 181 | self.intercept = 0 182 | 183 | # 184 | #Intialize the Advisory, Request and Response Tabs 185 | # 186 | def advisoryReqResp(self): 187 | self.textfield = JEditorPane("text/html", "") 188 | self.kit = HTMLEditorKit() 189 | self.textfield.setEditorKit(self.kit) 190 | self.doc = self.textfield.getDocument() 191 | self.textfield.setEditable(0) 192 | self.advisorypanel = JScrollPane() 193 | self.advisorypanel.getVerticalScrollBar() 194 | self.advisorypanel.setPreferredSize(Dimension(300,450)) 195 | self.advisorypanel.getViewport().setView((self.textfield)) 196 | 197 | self.selectedreq = [] 198 | 199 | self._requestViewer = self._callbacks.createMessageEditor(self, False) 200 | self._responseViewer = self._callbacks.createMessageEditor(self, False) 201 | self._texteditor = self._callbacks.createTextEditor() 202 | self._texteditor.setEditable(False) 203 | 204 | # 205 | #Initialize Trishul Tabs 206 | # 207 | def tabsInit(self): 208 | self.logTable = Table(self) 209 | tableWidth = self.logTable.getPreferredSize().width 210 | self.logTable.getColumn("#").setPreferredWidth(Math.round(tableWidth / 50 * 0.1)) 211 | self.logTable.getColumn("Method").setPreferredWidth(Math.round(tableWidth / 50 * 3)) 212 | self.logTable.getColumn("URL").setPreferredWidth(Math.round(tableWidth / 50 * 40)) 213 | self.logTable.getColumn("Parameters").setPreferredWidth(Math.round(tableWidth / 50 * 1)) 214 | self.logTable.getColumn("XSS").setPreferredWidth(Math.round(tableWidth / 50 * 4)) 215 | self.logTable.getColumn("SQLi").setPreferredWidth(Math.round(tableWidth / 50 * 4)) 216 | self.logTable.getColumn("SSTI").setPreferredWidth(Math.round(tableWidth / 50 * 4)) 217 | self.logTable.getColumn("Request Time").setPreferredWidth(Math.round(tableWidth / 50 * 4)) 218 | 219 | self.tableSorter = TableRowSorter(self) 220 | self.logTable.setRowSorter(self.tableSorter) 221 | 222 | self._bottomsplit = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) 223 | self._bottomsplit.setDividerLocation(500) 224 | 225 | self.issuetab = JTabbedPane() 226 | self.issuetab.addTab("Config",self.configtab) 227 | self.issuetab.addTab("Issues",self.issuepanel) 228 | self._bottomsplit.setLeftComponent(self.issuetab) 229 | 230 | self.tabs = JTabbedPane() 231 | self.tabs.addTab("Advisory",self.advisorypanel) 232 | self.tabs.addTab("Request", self._requestViewer.getComponent()) 233 | self.tabs.addTab("Response", self._responseViewer.getComponent()) 234 | self.tabs.addTab("Highlighted Response", self._texteditor.getComponent()) 235 | self._bottomsplit.setRightComponent(self.tabs) 236 | 237 | self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) 238 | self._splitpane.setDividerLocation(450) 239 | self._splitpane.setResizeWeight(1) 240 | self.scrollPane = JScrollPane(self.logTable) 241 | self._splitpane.setLeftComponent(self.scrollPane) 242 | self.scrollPane.getVerticalScrollBar().addAdjustmentListener(autoScrollListener(self)) 243 | self._splitpane.setRightComponent(self._bottomsplit) 244 | 245 | # 246 | #Initialize burp callbacks 247 | # 248 | def definecallbacks(self): 249 | self._callbacks.registerHttpListener(self) 250 | self._callbacks.customizeUiComponent(self._splitpane) 251 | self._callbacks.customizeUiComponent(self.logTable) 252 | self._callbacks.customizeUiComponent(self.scrollPane) 253 | self._callbacks.customizeUiComponent(self._bottomsplit) 254 | self._callbacks.registerContextMenuFactory(self) 255 | self._callbacks.addSuiteTab(self) 256 | 257 | # 258 | #Menu Item to send Request to Trishul 259 | # 260 | def createMenuItems(self, invocation): 261 | responses = invocation.getSelectedMessages() 262 | if responses > 0: 263 | ret = LinkedList() 264 | requestMenuItem = JMenuItem("Send request to Trishul") 265 | 266 | for response in responses: 267 | requestMenuItem.addActionListener(handleMenuItems(self,response, "request")) 268 | ret.add(requestMenuItem) 269 | return ret 270 | return None 271 | 272 | # 273 | #Highlighting Response 274 | # 275 | def markHttpMessage( self, requestResponse, responseMarkString ): 276 | responseMarkers = None 277 | if responseMarkString: 278 | response = requestResponse.getResponse() 279 | responseMarkBytes = self._helpers.stringToBytes( responseMarkString ) 280 | start = self._helpers.indexOf( response, responseMarkBytes, False, 0, len( response ) ) 281 | if -1 < start: 282 | responseMarkers = [ array( 'i',[ start, start + len( responseMarkBytes ) ] ) ] 283 | 284 | requestHighlights = [array( 'i',[ 0, 5 ] )] 285 | return self._callbacks.applyMarkers( requestResponse, requestHighlights, responseMarkers ) 286 | 287 | def getTabCaption(self): 288 | return "Trishul" 289 | 290 | def getUiComponent(self): 291 | return self._splitpane 292 | 293 | # 294 | #Table Model to display URL's and results based on the log size 295 | # 296 | def getRowCount(self): 297 | try: 298 | return self._log.size() 299 | except: 300 | return 0 301 | 302 | def getColumnCount(self): 303 | return 8 304 | 305 | def getColumnName(self, columnIndex): 306 | data = ['#','Method', 'URL', 'Parameters', 'XSS', 'SQLi', "SSTI", "Request Time"] 307 | try: 308 | return data[columnIndex] 309 | except IndexError: 310 | return "" 311 | 312 | def getColumnClass(self, columnIndex): 313 | data = [Integer, String, String, Integer, String, String, String, String] 314 | try: 315 | return data[columnIndex] 316 | except IndexError: 317 | return "" 318 | 319 | #Get Data stored in log and display in the respective columns 320 | def getValueAt(self, rowIndex, columnIndex): 321 | logEntry = self._log.get(rowIndex) 322 | if columnIndex == 0: 323 | return rowIndex+1 324 | if columnIndex == 1: 325 | return logEntry._method 326 | if columnIndex == 2: 327 | return logEntry._url.toString() 328 | if columnIndex == 3: 329 | return len(logEntry._parameter) 330 | if columnIndex == 4: 331 | return logEntry._XSSStatus 332 | if columnIndex == 5: 333 | return logEntry._SQLiStatus 334 | if columnIndex == 6: 335 | return logEntry._SSTIStatus 336 | if columnIndex == 7: 337 | return logEntry._req_time 338 | return "" 339 | 340 | def getHttpService(self): 341 | return self._currentlyDisplayedItem.getHttpService() 342 | 343 | def getRequest(self): 344 | return self._currentlyDisplayedItem.getRequest() 345 | 346 | def getResponse(self): 347 | return self._currentlyDisplayedItem.getResponse() 348 | 349 | #For Intercepted requests perform tests in scope 350 | def processHttpMessage(self, toolFlag, messageIsRequest, messageInf): 351 | if self.intercept == 1: 352 | if toolFlag == self._callbacks.TOOL_PROXY: 353 | if not messageIsRequest: 354 | requestInfo = self._helpers.analyzeRequest(messageInf) 355 | requeststr = requestInfo.getUrl() 356 | parameters = requestInfo.getParameters() 357 | param_new = [p for p in parameters if p.getType() != 2] 358 | if len(param_new) != 0: 359 | if self._callbacks.isInScope(URL(str(requeststr))): 360 | start_new_thread(self.sendRequestToTrishul,(messageInf,)) 361 | return 362 | 363 | # 364 | #Main processing of Trishul 365 | # 366 | def sendRequestToTrishul(self,messageInfo): 367 | request = messageInfo.getRequest() 368 | req_time = datetime.datetime.today() 369 | requestURL = self._helpers.analyzeRequest(messageInfo).getUrl() 370 | messageInfo = self._callbacks.makeHttpRequest(self._helpers.buildHttpService(str(requestURL.getHost()), int(requestURL.getPort()), requestURL.getProtocol() == "https"), request) 371 | resp_time = datetime.datetime.today() 372 | time_taken = (resp_time - req_time).total_seconds() 373 | response = messageInfo.getResponse() 374 | #initialozations of default value 375 | SQLiimp = self.NOT_FOUND 376 | SSTIimp = self.NOT_FOUND 377 | XSSimp = self.NOT_FOUND 378 | Comp_req = messageInfo 379 | requestInfo = self._helpers.analyzeRequest(messageInfo) 380 | self.content_resp = self._helpers.analyzeResponse(response) 381 | requestURL = requestInfo.getUrl() 382 | parameters = requestInfo.getParameters() 383 | requeststring = self._helpers.bytesToString(request) 384 | headers = requestInfo.getHeaders() 385 | #Used to obtain GET, POST and JSON parameters from burp api 386 | param_new = [p for p in parameters if p.getType() == 0 or p.getType() == 1 or p.getType() == 6] 387 | i = 0 388 | xssflag=0 389 | sqliflag=0 390 | sstiflag=0 391 | resultxss = [] 392 | resultsqli = [] 393 | resultssti = [] 394 | xssreqresp = [] 395 | sqlireqresp = [] 396 | sstireqresp = [] 397 | ssti_description = [] 398 | sqli_description = [] 399 | xss_description = [] 400 | for i in range(len(param_new)): 401 | name = param_new[i].getName() 402 | ptype = param_new[i].getType() 403 | param_value = param_new[i].getValue() 404 | #check XSS if ticked 405 | if self.xsscheck.isSelected(): 406 | score = 0 407 | flag1 = 0 408 | XSSimp = self.NOT_FOUND 409 | payload_array = ["<", ">", "\\\\'asd", "\\\\\"asd", "\\", "'\""] 410 | json_payload_array = ["<", ">", "\\\\'asd", "\\\"asd", "\\", "\'\\\""] 411 | payload_all = "" 412 | json_payload = "" 413 | rand_str = "testtest" 414 | for payload in payload_array: 415 | payload_all = payload_all+rand_str+payload 416 | payload_all = URLEncoder.encode(payload_all, "UTF-8") 417 | for payload in json_payload_array: 418 | json_payload = json_payload+rand_str+payload 419 | json_payload = URLEncoder.encode(json_payload, "UTF-8") 420 | if ptype == 0 or ptype == 1: 421 | new_paramters_value = self._helpers.buildParameter(name, payload_all, ptype) 422 | updated_request = self._helpers.updateParameter(request, new_paramters_value) 423 | else: 424 | jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) 425 | new = jsonreq.split(name+"\":",1)[1] 426 | if new.startswith('\"'): 427 | newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+json_payload) 428 | else: 429 | newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+json_payload+"\"") 430 | updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) 431 | 432 | attack = self.makeRequest(Comp_req, updated_request) 433 | response = attack.getResponse() 434 | response_str = self._helpers.bytesToString(response) 435 | xssreqresp.append(attack) 436 | if_found_payload = "" 437 | non_encoded_symbols = "" 438 | for check_payload in payload_array: 439 | if_found_payload = rand_str+check_payload 440 | if if_found_payload in response_str: 441 | non_encoded_symbols = non_encoded_symbols+"
"+check_payload.replace('<', '<') 442 | score = score+1 443 | flag1 = 1 444 | if score > 2: XSSimp = self.CHECK 445 | if score > 3: XSSimp = self.FOUND 446 | xssflag = self.checkBetterScore(score,xssflag) 447 | if non_encoded_symbols == " \\\\'asd": 448 | XSSimp = self.NOT_FOUND 449 | 450 | if non_encoded_symbols != '': 451 | xss_description.append("The Payload " + payload_all.replace('<', '<') + " was passed in the request for the paramater " + self._helpers.urlDecode(name) + ". Some Tags were observed in the output unfiltered. A payload can be generated with the observed tags.
Symbols not encoded for parameter " + name + ": " + non_encoded_symbols) 452 | else: 453 | xss_description.append("") 454 | else: 455 | XSSimp = "Disabled" 456 | resultxss.append(XSSimp) 457 | 458 | if self.sqlicheck.isSelected(): 459 | SQLiimp = self.NOT_FOUND 460 | score = 0 461 | value = "%27and%28select%2afrom%28select%28sleep%285%29%29%29a%29--" 462 | orig_time = datetime.datetime.today() 463 | if ptype == 0 or ptype == 1: 464 | new_paramters_value = self._helpers.buildParameter(name, value, ptype) 465 | updated_request = self._helpers.updateParameter(request, new_paramters_value) 466 | else: 467 | jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) 468 | new = jsonreq.split(name+"\":",1)[1] 469 | if new.startswith('\"'): 470 | newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+value) 471 | else: 472 | newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+value+"\"") 473 | updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) 474 | attack1 = self.makeRequest(Comp_req, updated_request) 475 | response1 = attack1.getResponse() 476 | new_time = datetime.datetime.today() 477 | response_str1 = self._helpers.bytesToString(response1) 478 | sqlireqresp.append(attack1) 479 | diff = (new_time - orig_time).total_seconds() 480 | if (diff - time_taken) > 3: 481 | score = 4 482 | 483 | self.error_array = ["check the manual that corresponds to your", "You have an error", "syntax error", "SQL syntax", "SQL statement", "ERROR:", "Error:", "MySQL","Warning:","mysql_fetch_array()"] 484 | found_text = "" 485 | for error in self.error_array: 486 | if error in response_str1: 487 | found_text = found_text + error 488 | score = score + 1 489 | if score > 1: SQLiimp = self.CHECK 490 | if score > 2: SQLiimp = self.FOUND 491 | sqliflag = self.checkBetterScore(score,sqliflag) 492 | 493 | if found_text != '': 494 | sqli_description.append("The payload "+self._helpers.urlDecode(value)+" was passed in the request for parameter "+self._helpers.urlDecode(name)+". Some errors were generated in the response which confirms that there is an Error based SQLi. Please check the request and response for this parameter") 495 | elif (diff - time_taken) > 3: 496 | sqli_description.append("The payload "+self._helpers.urlDecode(value)+" was passed in the request for parameter "+self._helpers.urlDecode(name)+". The response was in a delay of "+str(diff)+" seconds as compared to original "+str(time_taken)+" seconds. This indicates that there is a time based SQLi. Please check the request and response for this parameter") 497 | else: 498 | sqli_description.append("") 499 | else: 500 | SQLiimp = "Disabled" 501 | 502 | resultsqli.append(SQLiimp) 503 | 504 | if self.ssticheck.isSelected(): 505 | score = 0 506 | SSTIimp = self.NOT_FOUND 507 | payload_array = ["${123*456}", "<%=123*567%>", "{{123*678}}"] 508 | json_payload_array = ["$\{123*456\}", "<%=123*567%>", "\{\{123*678\}\}"] 509 | payload_all = "" 510 | rand_str = "jjjjjjj" 511 | json_payload = "" 512 | for payload in payload_array: 513 | payload_all = payload_all+rand_str+payload 514 | for payload in json_payload_array: 515 | json_payload = json_payload+rand_str+payload 516 | payload_all = URLEncoder.encode(payload_all, "UTF-8") 517 | json_payload = URLEncoder.encode(json_payload, "UTF-8") 518 | if ptype == 0 or ptype == 1: 519 | new_paramters_value = self._helpers.buildParameter(name, payload_all, ptype) 520 | updated_request = self._helpers.updateParameter(request, new_paramters_value) 521 | else: 522 | jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) 523 | new = jsonreq.split(name+"\":",1)[1] 524 | if new.startswith('\"'): 525 | newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+json_payload) 526 | else: 527 | newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+json_payload+"\"") 528 | updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) 529 | 530 | attack = self.makeRequest(Comp_req, updated_request) 531 | response = attack.getResponse() 532 | response_str = self._helpers.bytesToString(response) 533 | self.expected_output = ["56088","69741","83394","3885","777777777777777"] 534 | for output in self.expected_output: 535 | if_found_payload = rand_str+output 536 | if if_found_payload in response_str: 537 | if output == self.expected_output[0]: 538 | sstireqresp.append(attack) 539 | ssti_description.append("Parameter " + self._helpers.urlDecode(name) + " is using Java Template
The value " + payload_new + " was passed which gave result as 56088") 540 | score = 2 541 | if output == self.expected_output[1]: 542 | sstireqresp.append(attack) 543 | ssti_description.append("Parameter " + self._helpers.urlDecode(name) + " is using Ruby Template
The value " + payload_new + " was passed which gave result as 69741") 544 | score = 2 545 | if output == self.expected_output[2]: 546 | payload_new = "{{5*'777'}}" 547 | json_payload_ssti = "\{\{5*'777'\}\}" 548 | payload = URLEncoder.encode("{{5*'777'}}", "UTF-8") 549 | json_ssti = URLEncoder.encode("\{\{5*'777'\}\}", "UTF-8") 550 | if ptype == 0 or ptype == 1: 551 | new_paramters = self._helpers.buildParameter(name, payload, ptype) 552 | ssti_updated_request = self._helpers.updateParameter(request, new_paramters) 553 | else: 554 | jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) 555 | new = jsonreq.split(name+"\":",1)[1] 556 | if new.startswith('\"'): 557 | newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+json_ssti) 558 | else: 559 | newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+json_ssti+"\"") 560 | ssti_updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) 561 | self.ssti_attack = self.makeRequest(Comp_req, ssti_updated_request) 562 | ssti_response = self.ssti_attack.getResponse() 563 | ssti_response_str = self._helpers.bytesToString(ssti_response) 564 | if self.expected_output[3] in ssti_response_str: 565 | sstireqresp.append(self.ssti_attack) 566 | ssti_description.append("Parameter " + self._helpers.urlDecode(name) + " is using Twig Template
The value " + payload_new + " was passed which gave result as 3885") 567 | score = 2 568 | elif self.expected_output[4] in ssti_response_str: 569 | sstireqresp.append(self.ssti_attack) 570 | self.responseMarkString = "777777777777777" 571 | ssti_description.append("Parameter " + self._helpers.urlDecode(name) + " is using Jinja2 Template
The value " + payload_new + " was passed which gave result as 777777777777777") 572 | score = 2 573 | if score > 0: SSTIimp = self.CHECK 574 | if score > 1: SSTIimp = self.FOUND 575 | sstiflag = self.checkBetterScore(score,sstiflag) 576 | else: 577 | SSTIimp = "Disabled" 578 | 579 | resultssti.append(SSTIimp) 580 | 581 | if self.blindxss.isSelected(): 582 | blindxss_value = self.BlindXSSText.getText() 583 | if ptype == 0 or ptype == 1: 584 | new_paramters_value = self._helpers.buildParameter(name, blindxss_value, ptype) 585 | updated_request = self._helpers.updateParameter(request, new_paramters_value) 586 | else: 587 | jsonreq = re.search(r"\s([{\[].*?[}\]])$", requeststring).group(1) 588 | new = jsonreq.split(name+"\":",1)[1] 589 | if new.startswith('\"'): 590 | newjsonreq = jsonreq.replace(name+"\":\""+param_value,name+"\":\""+blindxss_value) 591 | else: 592 | newjsonreq = jsonreq.replace(name+"\":"+param_value,name+"\":\""+blindxss_value+"\"") 593 | updated_request = self._helpers.buildHttpMessage(headers, newjsonreq) 594 | attack = self.makeRequest(Comp_req, updated_request) 595 | 596 | if XSSimp != "Disabled": 597 | if xssflag > 3: XSSimp = self.FOUND 598 | elif xssflag > 2: XSSimp = self.CHECK 599 | else: XSSimp = self.NOT_FOUND 600 | 601 | if SSTIimp != "Disabled": 602 | if sstiflag > 1: SSTIimp = self.FOUND 603 | elif sstiflag > 0: SSTIimp = self.CHECK 604 | else: SSTIimp = self.NOT_FOUND 605 | 606 | if SQLiimp != "Disabled": 607 | if sqliflag > 3: SQLiimp = self.FOUND 608 | elif sqliflag > 2: SQLiimp = self.CHECK 609 | else: SQLiimp = self.NOT_FOUND 610 | 611 | self.addToLog(messageInfo, XSSimp, SQLiimp, SSTIimp, param_new, resultxss, resultsqli, resultssti, xssreqresp, sqlireqresp, sstireqresp , xss_description, sqli_description, ssti_description, req_time.strftime('%H:%M:%S %m/%d/%y')) 612 | 613 | 614 | # 615 | #Function used to check if the score originally and mentioned is better 616 | # 617 | def checkBetterScore(self, score, ogscore): 618 | if score > ogscore: 619 | ogscore = score 620 | return ogscore 621 | 622 | 623 | def makeRequest(self, messageInfo, message): 624 | request = messageInfo.getRequest() 625 | requestURL = self._helpers.analyzeRequest(messageInfo).getUrl() 626 | return self._callbacks.makeHttpRequest(self._helpers.buildHttpService(str(requestURL.getHost()), int(requestURL.getPort()), requestURL.getProtocol() == "https"), message) 627 | 628 | 629 | def addToLog(self, messageInfo, XSSimp, SQLiimp, SSTIimp, parameters, resultxss, resultsqli, resultssti, xssreqresp, sqlireqresp, sstireqresp, xss_description, sqli_description, ssti_description, req_time): 630 | requestInfo = self._helpers.analyzeRequest(messageInfo) 631 | method = requestInfo.getMethod() 632 | self._lock.acquire() 633 | row = self._log.size() 634 | self._log.add(LogEntry(self._callbacks.saveBuffersToTempFiles(messageInfo), requestInfo.getUrl(),method,XSSimp,SQLiimp,SSTIimp,req_time, parameters,resultxss, resultsqli, resultssti, xssreqresp, sqlireqresp, sstireqresp, xss_description, sqli_description, ssti_description)) # same requests not include again. 635 | SwingUtilities.invokeLater(UpdateTableEDT(self,"insert",row,row)) 636 | self._lock.release() 637 | 638 | # 639 | # extend JTable to handle cell selection 640 | # 641 | class Table(JTable): 642 | 643 | def __init__(self, extender): 644 | self._extender = extender 645 | self.setModel(extender) 646 | self.addMouseListener(mouseclick(self._extender)) 647 | self.getColumnModel().getColumn(0).setPreferredWidth(0) 648 | self.setRowSelectionAllowed(True) 649 | LogEntry = [] 650 | return 651 | 652 | #Set color for cells in tables 653 | def prepareRenderer(self, renderer, row, col): 654 | comp = JTable.prepareRenderer(self, renderer, row, col) 655 | value = self._extender.getValueAt(self._extender.logTable.convertRowIndexToModel(row), col) 656 | 657 | if col == 4 or col == 5 or col == 6: 658 | if value == self._extender.FOUND: 659 | comp.setBackground(Color(179, 0, 0)) 660 | comp.setForeground(Color.WHITE) 661 | elif value == self._extender.CHECK: 662 | comp.setBackground(Color(255, 153, 51)) 663 | comp.setForeground(Color.BLACK) 664 | elif value == self._extender.NOT_FOUND: 665 | comp.setBackground(Color.LIGHT_GRAY) 666 | comp.setForeground(Color.BLACK) 667 | elif value == "Disabled": 668 | comp.setBackground(Color.LIGHT_GRAY) 669 | comp.setForeground(Color.BLACK) 670 | else: 671 | comp.setForeground(Color.BLACK) 672 | comp.setBackground(Color.LIGHT_GRAY) 673 | 674 | selectedRow = self._extender.logTable.getSelectedRow() 675 | if selectedRow == row: 676 | comp.setBackground(Color.WHITE) 677 | comp.setForeground(Color.BLACK) 678 | return comp 679 | 680 | 681 | #open Issue tab to display vulnerable parameters 682 | def changeSelection(self, row, col, toggle, extend): 683 | 684 | if col >= 0: 685 | self.performAction(row) 686 | self._extender.issuetab.setSelectedIndex(1) 687 | self._extender.tree.expandRow(0) 688 | 689 | if col == 4: 690 | self._extender.tree.collapseRow(2) 691 | self._extender.tree.collapseRow(3) 692 | self._extender.tree.expandRow(1) 693 | 694 | if col == 5: 695 | self._extender.tree.collapseRow(1) 696 | self._extender.tree.collapseRow(3) 697 | self._extender.tree.expandRow(2) 698 | 699 | if col == 6: 700 | self._extender.tree.collapseRow(1) 701 | self._extender.tree.collapseRow(2) 702 | self._extender.tree.expandRow(3) 703 | 704 | JTable.changeSelection(self, row, col, toggle, extend) 705 | return 706 | 707 | #Add parameters to array for every issue found for a particular request 708 | def performAction(self, row): 709 | model = self._extender.tree.getModel() 710 | root = model.getRoot() 711 | root.removeAllChildren() 712 | model.reload() 713 | self.xssroot = DefaultMutableTreeNode('Cross-Site-Scripting') 714 | root.add(self.xssroot) 715 | 716 | 717 | self.sqliroot = DefaultMutableTreeNode('SQL Injection') 718 | root.add(self.sqliroot) 719 | 720 | self.sstiroot = DefaultMutableTreeNode('Server Side Template Injection') 721 | root.add(self.sstiroot) 722 | resultxss = [] 723 | resultsqli = [] 724 | resultssti = [] 725 | logEntry = self._extender._log.get(self._extender.logTable.convertRowIndexToModel(row)) 726 | 727 | resultxss = logEntry._resultxss 728 | resultsqli = logEntry._resultsqli 729 | resultssti = logEntry._resultssti 730 | parameter = logEntry._parameter 731 | 732 | for i in range(len(parameter)): 733 | if resultxss[i] == self._extender.CHECK or resultxss[i] == self._extender.FOUND: 734 | array = [] 735 | array.append(parameter[i].getName()) 736 | self._extender.addIssues(self.xssroot, array) 737 | if resultsqli[i] == self._extender.CHECK or resultsqli[i] == self._extender.FOUND: 738 | array = [] 739 | array.append(parameter[i].getName()) 740 | self._extender.addIssues(self.sqliroot, array) 741 | if resultssti[i] == self._extender.CHECK or resultssti[i] == self._extender.FOUND: 742 | array = [] 743 | array.append(parameter[i].getName()) 744 | self._extender.addIssues(self.sstiroot, array) 745 | 746 | self._extender.rowSelected = row 747 | 748 | return 749 | 750 | # 751 | #Log to Store Data of Requests 752 | # 753 | class LogEntry: 754 | 755 | def __init__(self, requestResponse, url, method, XSSimp, SQLiimp, SSTIimp, req_time, parameter, resultxss, resultsqli, resultssti, xssreqresp, sqlireqresp, sstireqresp, xss_description, sqli_description, ssti_description): 756 | self._requestResponse = requestResponse 757 | self._url = url 758 | self._method = method 759 | self._XSSStatus = XSSimp 760 | self._SQLiStatus = SQLiimp 761 | self._SSTIStatus = SSTIimp 762 | self._req_time = req_time 763 | self._parameter = parameter 764 | self._resultxss = resultxss 765 | self._resultsqli = resultsqli 766 | self._resultssti = resultssti 767 | self._xssreqresp = xssreqresp 768 | self._sqlireqresp = sqlireqresp 769 | self._sstireqresp = sstireqresp 770 | self._ssti_description = ssti_description 771 | self._xss_description = xss_description 772 | self._sqli_description = sqli_description 773 | return 774 | 775 | # 776 | #Mouse Adapter to click on Table and Tree to display data 777 | # 778 | class mouseclick(MouseAdapter): 779 | 780 | def __init__(self, extender): 781 | self._extender = extender 782 | 783 | def mouseReleased(self, evt): 784 | if evt.button == 3: 785 | self._extender.menu.show(evt.getComponent(), evt.getX(), evt.getY()) 786 | self.path = self._extender.tree.getLastSelectedPathComponent() 787 | if self.path != None: 788 | row = self._extender.rowSelected 789 | logEntry = self._extender._log.get(self._extender.logTable.convertRowIndexToModel(row)) 790 | parameter = logEntry._parameter 791 | xssreqresp = logEntry._xssreqresp 792 | sqlireqresp = logEntry._sqlireqresp 793 | sstireqresp = logEntry._sstireqresp 794 | xss_description = logEntry._xss_description 795 | sqli_description = logEntry._sqli_description 796 | ssti_description = logEntry._ssti_description 797 | url = logEntry._url.toString() 798 | for i in range(len(parameter)): 799 | if str(self.path.getParent()) == "Server Side Template Injection": 800 | if str(self.path) == parameter[i].getName(): 801 | self._extender.textfield.setText("") 802 | response = sstireqresp[i].getResponse() 803 | confidence = self.checkConfidence(logEntry._resultssti[i]) 804 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

"+str(self.path.getParent())+"

", 0, 0, None) 805 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "
Issue: "+str(self.path.getParent())+"
Severity: High
Confidence: "+confidence+"
URL: "+url+"
", 0, 0, None) 806 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

Description

", 0, 0, None) 807 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "The Parameter " + self._extender._helpers.urlDecode(str(self.path)) + " is Vulnerable to " + str(self.path.getParent()) + "", 0, 0, None) 808 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), ssti_description[i], 0, 0, None) 809 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "Usually Mass Emailers use common templated like Smarty, Mako, Twig, and Jinja2 to send emails because it makes it easy to replace values when sending multiple mails. Also, Web applications commonly use these template engines to present dynamic data on web pages and emails. Examples include wikis, blogs, content management systems, and marketing applications. This feature allows embedding user input into the web application, and if not sanitized properly, could make it vulnerable to Server-Side Template Injection and potentially give remote code execution (RCE) capability to intruders.", 0, 0, None) 810 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

Remediation

", 0, 0, None) 811 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "Remediations are different for different types of Templates, but the following concepts could help remediate all template engines.
1.Sanitization: Always pass user inputs into templates as parameters. Always Sanitize the input before passing it into the template removing various malicious characters before parsing the data. Thus without specific characters inserted on the page, the malicious code will not execute.
2.Sandboxing: If your business restricts sanitizing characters, it is advisable to put the template in a sandboxeed environment like a docker container. So with the help of docker security you can craft a secure environment for these malicious activities.", 0, 0, None) 812 | self._extender.textfield.setCaretPosition(0) 813 | self._extender.selectedreq = sstireqresp[i] 814 | self._extender._requestViewer.setMessage(sstireqresp[i].getRequest(), True) 815 | self._extender._responseViewer.setMessage(sstireqresp[i].getResponse(), False) 816 | self._extender._currentlyDisplayedItem = sstireqresp[i] 817 | self._extender._texteditor.setText(sstireqresp[i].getResponse()) 818 | response_str = self._extender._helpers.bytesToString(response) 819 | for ssti_out in self._extender.expected_output: 820 | if ssti_out in response_str: 821 | self._extender._texteditor.setSearchExpression(ssti_out) 822 | break 823 | 824 | elif str(self.path.getParent()) == "Cross-Site-Scripting": 825 | if str(self.path) == parameter[i].getName(): 826 | self._extender.textfield.setText("") 827 | response = xssreqresp[i].getResponse() 828 | content_resp = self._extender._helpers.analyzeResponse(response) 829 | if content_resp.getStatedMimeType() == "HTML": 830 | confidence = self.checkConfidence(logEntry._resultxss[i]) 831 | else: 832 | confidence = "Tentative (Non HTML Output)" 833 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

"+str(self.path.getParent())+"

", 0, 0, None) 834 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "
Issue: "+str(self.path.getParent())+"
Severity: High
Confidence: "+confidence+"
URL: "+url+"
", 0, 0, None) 835 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

Description

", 0, 0, None) 836 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "The Parameter " + self._extender._helpers.urlDecode(str(self.path)) + " is Vulnerable to " + str(self.path.getParent()) + "", 0, 0, None) 837 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), xss_description[i], 0, 0, None) 838 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "Cross-site scripting vulnerabilities arise when data is copied from a request and echoed into the application's response in an unsafe way. An attacker can use the vulnerability to construct a request that, if issued by another application user, will cause JavaScript code supplied by the attacker to execute within the user's browser in the context of that user's session with the application.

The attacker-supplied code can perform a wide variety of actions, such as stealing the victim's session token or login credentials, performing arbitrary actions on the victim's behalf, and logging their keystrokes.

Users can be induced to issue the attacker's crafted request in various ways. For example, the attacker can send a victim a link containing a malicious URL in an email or instant message. They can submit the link to popular web sites that allow content authoring, for example in blog comments. And they can create an innocuous looking web site that causes anyone viewing it to make arbitrary cross-domain requests to the vulnerable application (using either the GET or the POST method).

The security impact of cross-site scripting vulnerabilities is dependent upon the nature of the vulnerable application, the kinds of data and functionality that it contains, and the other applications that belong to the same domain and organization. If the application is used only to display non-sensitive public content, with no authentication or access control functionality, then a cross-site scripting flaw may be considered low risk. However, if the same application resides on a domain that can access cookies for other more security-critical applications, then the vulnerability could be used to attack those other applications, and so may be considered high risk. Similarly, if the organization that owns the application is a likely target for phishing attacks, then the vulnerability could be leveraged to lend credibility to such attacks, by injecting Trojan functionality into the vulnerable application and exploiting users' trust in the organization in order to capture credentials for other applications that it owns. In many kinds of application, such as those providing online banking functionality, cross-site scripting should always be considered high risk.", 0, 0, None) 839 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

Remediation

", 0, 0, None) 840 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "In most situations where user-controllable data is copied into application responses, cross-site scripting attacks can be prevented using two layers of defenses:

1. Input should be validated as strictly as possible on arrival, given the kind of content that it is expected to contain. For example, personal names should consist of alphabetical and a small range of typographical characters, and be relatively short; a year of birth should consist of exactly four numerals; email addresses should match a well-defined regular expression. Input which fails the validation should be rejected, not sanitized.
2. User input should be HTML-encoded at any point where it is copied into application responses. All HTML metacharacters, including < > \" \' and =, should be replaced with the corresponding HTML entities (< > etc).

In cases where the application\'s functionality allows users to author content using a restricted subset of HTML tags and attributes (for example, blog comments which allow limited formatting and linking), it is necessary to parse the supplied HTML to validate that it does not use any dangerous syntax; this is a non-trivial task.", 0, 0, None) 841 | self._extender.textfield.setCaretPosition(0) 842 | self._extender.selectedreq = xssreqresp[i] 843 | self._extender.markHttpMessage(xssreqresp[i], "testtest") 844 | self._extender._requestViewer.setMessage(xssreqresp[i].getRequest(), True) 845 | self._extender._responseViewer.setMessage(xssreqresp[i].getResponse(), False) 846 | self._extender._currentlyDisplayedItem = xssreqresp[i] 847 | self._extender._texteditor.setText(xssreqresp[i].getResponse()) 848 | self._extender._texteditor.setSearchExpression("testtest") 849 | elif str(self.path.getParent()) == "SQL Injection": 850 | if str(self.path) == parameter[i].getName(): 851 | self._extender.textfield.setText("") 852 | confidence = self.checkConfidence(logEntry._resultsqli[i]) 853 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

"+str(self.path.getParent())+"

", 0, 0, None) 854 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "
Issue: "+str(self.path.getParent())+"
Severity: High
Confidence: "+confidence+"
URL: "+url+"
", 0, 0, None) 855 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

Description

", 0, 0, None) 856 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "The Parameter " + self._extender._helpers.urlDecode(str(self.path)) + " is Vulnerable to " + str(self.path.getParent()) + "", 0, 0, None) 857 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), sqli_description[i], 0, 0, None) 858 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "SQL injection vulnerabilities arise when user-controllable data is incorporated into database SQL queries in an unsafe manner. An attacker can supply crafted input to break out of the data context in which their input appears and interfere with the structure of the surrounding query.
A wide range of damaging attacks can often be delivered via SQL injection, including reading or modifying critical application data, interfering with application logic, escalating privileges within the database and taking control of the database server.", 0, 0, None) 859 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "

Remediation

", 0, 0, None) 860 | self._extender.kit.insertHTML(self._extender.doc, self._extender.doc.getLength(), "The most effective way to prevent SQL injection attacks is to use parameterized queries (also known as prepared statements) for all database access. This method uses two steps to incorporate potentially tainted data into SQL queries: first, the application specifies the structure of the query, leaving placeholders for each item of user input; second, the application specifies the contents of each placeholder. Because the structure of the query has already been defined in the first step, it is not possible for malformed data in the second step to interfere with the query structure. You should review the documentation for your database and application platform to determine the appropriate APIs which you can use to perform parameterized queries. It is strongly recommended that you parameterize every variable data item that is incorporated into database queries, even if it is not obviously tainted, to prevent oversights occurring and avoid vulnerabilities being introduced by changes elsewhere within the code base of the application.
You should be aware that some commonly employed and recommended mitigations for SQL injection vulnerabilities are not always effective:

1. One common defense is to double up any single quotation marks appearing within user input before incorporating that input into a SQL query. This defense is designed to prevent malformed data from terminating the string into which it is inserted. However, if the data being incorporated into queries is numeric, then the defense may fail, because numeric data may not be encapsulated within quotes, in which case only a space is required to break out of the data context and interfere with the query. Further, in second-order SQL injection attacks, data that has been safely escaped when initially inserted into the database is subsequently read from the database and then passed back to it again. Quotation marks that have been doubled up initially will return to their original form when the data is reused, allowing the defense to be bypassed.
2. Another often cited defense is to use stored procedures for database access. While stored procedures can provide security benefits, they are not guaranteed to prevent SQL injection attacks. The same kinds of vulnerabilities that arise within standard dynamic SQL queries can arise if any SQL is dynamically constructed within stored procedures. Further, even if the procedure is sound, SQL injection can arise if the procedure is invoked in an unsafe manner using user-controllable data.", 0, 0, None) 861 | self._extender.textfield.setCaretPosition(0) 862 | self._extender.selectedreq = sqlireqresp[i] 863 | response = sqlireqresp[i].getResponse() 864 | self._extender._requestViewer.setMessage(sqlireqresp[i].getRequest(), True) 865 | self._extender._responseViewer.setMessage(sqlireqresp[i].getResponse(), False) 866 | self._extender._currentlyDisplayedItem = sqlireqresp[i] 867 | self._extender._texteditor.setText(response) 868 | response_str = self._extender._helpers.bytesToString(response) 869 | for error in self._extender.error_array: 870 | if error in response_str: 871 | self._extender._texteditor.setSearchExpression(error) 872 | break 873 | else: 874 | pass 875 | 876 | #Color of Confidence in Description 877 | def checkConfidence(self, value): 878 | if value == self._extender.FOUND: 879 | return "Firm" 880 | elif value == self._extender.CHECK: 881 | return "Tentative" 882 | 883 | # 884 | #Autoscroll enabling feature 885 | # 886 | class autoScrollListener(AdjustmentListener): 887 | def __init__(self, extender): 888 | self._extender = extender 889 | 890 | def adjustmentValueChanged(self, e): 891 | if self._extender.autoScroll.isSelected() is True: 892 | e.getAdjustable().setValue(e.getAdjustable().getMaximum()) 893 | 894 | # 895 | #Menu Iten Added on Right Click which sends request to Trishul 896 | # 897 | class handleMenuItems(ActionListener): 898 | def __init__(self, extender, messageInfo, menuName): 899 | self._extender = extender 900 | self._menuName = menuName 901 | self._messageInfo = messageInfo 902 | 903 | def actionPerformed(self, e): 904 | start_new_thread(self._extender.sendRequestToTrishul,(self._messageInfo,)) 905 | 906 | # 907 | #Function to insert request details into the table 908 | # 909 | class UpdateTableEDT(Runnable): 910 | def __init__(self,extender,action,firstRow,lastRow): 911 | self._extender=extender 912 | self._action=action 913 | self._firstRow=firstRow 914 | self._lastRow=lastRow 915 | 916 | def run(self): 917 | if self._action == "insert": 918 | self._extender.fireTableRowsInserted(self._firstRow, self._lastRow) 919 | elif self._action == "update": 920 | self._extender.fireTableRowsUpdated(self._firstRow, self._lastRow) 921 | elif self._action == "delete": 922 | self._extender.fireTableRowsDeleted(self._firstRow, self._lastRow) 923 | else: 924 | print("Invalid action in UpdateTableEDT") 925 | -------------------------------------------------------------------------------- /trishul_add_website_scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravnarwani97/Trishul/1409a12db89fbe8ac9820099db411a2223921fa2/trishul_add_website_scope.png -------------------------------------------------------------------------------- /trishul_main_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravnarwani97/Trishul/1409a12db89fbe8ac9820099db411a2223921fa2/trishul_main_picture.png -------------------------------------------------------------------------------- /trishul_send_req.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravnarwani97/Trishul/1409a12db89fbe8ac9820099db411a2223921fa2/trishul_send_req.png -------------------------------------------------------------------------------- /trishul_usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravnarwani97/Trishul/1409a12db89fbe8ac9820099db411a2223921fa2/trishul_usage.gif --------------------------------------------------------------------------------