├── 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 |
4 |
5 |
6 | Burp Extension for Automated Vulnerability Discovery
7 |
8 | 
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 | 
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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------