Burp Python Scripter

A few weeks ago I was performing a security test on a mobile banking application. The application was using a framework that provided additional obfuscation and encryption on top of the TLS connection it used to communicate with the remote server. I used Frida to intercept and dump the plaintext requests/responses before the encryption took place. I wanted to modify intercepted API calls and see how the remote server responded, but this required me to modify the Frida script each time.

Browse other questions tagged python burp-suite http-proxy or ask your own question. The Overflow Blog Podcast 320: Covid vaccine websites are frustrating. PyScripter-er PyScripter-er (you can thank @kevcody for the name:-P) is a framework built on top of the Python Scripter Burp Suite extension. PyScripter-er is designed to make wielding the power of Python Scripter easier by providing interfaces to common functionality not already provided within the Burp.

Burp is really my tool of choice for (web) backend testing, and a mobile backend should not be that much different. I wanted to make Burp work together with Frida, and intercept/modify API calls in Burp. I did not go as far as creating a Burp plugin, but using a small script we can already get started with intercepting API calls. This spared me the trouble of modifying the frida-trace handler script each time I wanted to modify an API parameter.

Intercepting API requests with Burp does not require a lot of work:

  1. Set up a Burp listener (e.g. listen port 26080) redirecting traffic to an echo server (e.g. port 27080) in invisible proxy mode
  2. Have an echo server listening on port 27080 (which just echoes back the request)
  3. Use Frida to synchronously send an HTTP request to the Burp listener with as payload the API call data

Burp will receive the API request sent by Frida. The user can then modify the call in transit in Burp, which then forwards the data to the echo server. The echo server will simply reflect the (modified) request in the response, which is received back by the Frida code.

Set up Burp listener

Let’s start with setting up a Burp listener. Here, I’ve used listener port 26080 in invisible proxy mode:

Setting up a Burp listener redirecting to the echo server.

Python tracer code

I opted to extend (or rather monkey patch) the existing frida-trace code, in order to have the same flexibility of the tracing tool.

The following Python code extends the frida-trace code to work together with a server you forward the API call to. It does this by sending an HTTP request to the local Burp listener we created. You could also add metadata in the HTTP headers about the API call, or specify a URL path to distinguish between different API calls, which will be visible in Burp.

The current code might be version dependent: I don’t know what will change in future versions, though it currently works with Frida 8.2.2.

Burp Python Scripter Example

burp-tracer.py
2
4
6
8
10
12
14
16
18
20
22
24
26
28
importrequests
BURP_HOST='localhost'
handled=False
ifmessage['type']'input':
elifmessage['type']'send':
req=requests.request('FRIDA','http://%s:%d/'%(BURP_HOST,BURP_PORT),headers={'content-type':'text/plain'},data=stanza['payload'])
self._script.post({'type':'input','payload':req.content})
self.__process_message(message,data,ui)
tracer.Tracer.__process_message=tracer.Tracer._process_message
tracer.Tracer._process_message=frida_process_message
if__name__'__main__':
print('[x] To intercept in Burp, set up an invisible proxy listening on port %d, forwarding to the echo server.'%BURP_PORT)

Trace handler script

The following example could be used to intercept read() calls in the onLeave function. This should be easily adaptable for your own usecase.We only need to forward/intercept the arguments that we want to tamper with.

I’ve implemented this by using the Frida send() API. This call is received by the Python code, which then responds back to the handler script. The script waits synchronously for a response (while we are performing modifications in Burp), and then executes the callback function.

__handlers__/libc_2.19.so/read.js
2
4
6
8
10
12
14
onEnter:function(log,args,state){
log('read('+'fd='+args[0]+', buf='+args[1]+', count='+args[2]+')');
},
onLeave:function(log,retval,state){
send({from:'/http',payload:Memory.readUtf8String(state.buf)})
varop=recv('input',function(value){// callback function
Memory.writeUtf8String(state.buf,value.payload)
op.wait();
}

Echo server

The following script is a small implementation of an echo server that responds to “FRIDA” requests and echoes back the request payload. This could be extended as well with your own logic to modify requests on-the-fly (although you could just as well implement that directly in your Frida scripts).

echo.py
2
4
6
8
10
12
14
16
18
20
22
24
26
fromBaseHTTPServerimportHTTPServer,BaseHTTPRequestHandler
request_path=self.path
request_headers=self.headers
content_length=request_headers.getheaders('content-length')
length=int(content_length[0])ifcontent_length else0
self.send_response(200)
print('Listening on localhost:%d'%ECHO_PORT)
server.serve_forever()
if__name__'__main__':
print('[x] Starting echo server on port %d'%ECHO_PORT)

Demo

Here’s the code in action:

Frida tracing with Burp interception

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:

And a simple SQLMap command something like:

Free Scripter

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!

Burp Python Scripter

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.

Photo editor 10.7. VSCO Cam offers abundant effects, yet it is easy to understand. As for photo editing, you can put a filter on, brighten or darken it, adjust the contrast, change the shadows and highlights and many more. VSCO Cam provides a vibrant community, where you can look at people's collections of photos, go to the journal feature and read people's blog posts.

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.

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.

And running…

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

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

Using only the Command Line with Cookies

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

Using only the Command Line with GET/POST Parameters

Python Scripter Download

The following calls to SQLMap also work.

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

Python Scripter Burp Extension

Success!

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!

Comments are closed.