Using Burp Suite’s Cookie Jar for JSON Web Tokens
I was going over an application I wrote prepping for my upcoming class and realized the shortcoming’s of Burp’s default session handling mechanisms. Not to knock Burp, but working with a Single Page Application (SPA) that makes calls to several APIs using a JSON Web Token (JWT) doesn’t mix well with Burp’s macros and Cookie Jar. Granted, this isn’t a problem if your JWT’s expiration date is sufficiently far out in the future and none of the APIs you’re working with de-authenticate your “session” (revoke or blacklist your token). JWTs aren’t necessarily meant to be linked to any real session state. They grant access to things and expire at a certain date.
Before I get into everything I worked through to solve this problem, I need to issue a quick, shameless plug. I am going to be teaching a beginner-to-intermediate level web application training course at both the Lascon (Austin — Oct 22–23) and Pacific Hackers (Santa Clara — Nov 8th) security conferences. The main goal of my course is to illustrate how web application testers approach comprehensively assessing an application’s security posture during a time-compressed engagement. If interested, come hang out with me and we’ll work on developing an efficiency-focused mindset along with a framework of techniques that will help you rapidly work through your next application assessment.
Sorry, had to be done! Now on to the good stuff…
In wanting to show students how applications often pose problems when faced with malicious request activity, I made it such that my application features a de-authentication mechanism that blacklists JWTs when malicious activity is witnessed. As such, I had to work up a way (in Burp Suite) to check my session and re-authenticate once a token has been blacklisted. Before jumping into how I solved this I should probably explain how my application works. Again, it’s a SPA that reaches out to various APIs and a content distribution “network” (CDN). The origins at play here are:
• https://books2.store.com (hosts the SPA — HTML and Javascript)
• https://api.store.com (does all the things for the “store”)
• https://auth.foobar.com (provides authentication/autherization for everything)
• https://cdn1.foo.com (serves up various files — no token required)
Pretty straight forward. JWTs get handed out by auth.foobar.com to books2.store.com and ultimately get passed back and forth between all hosts but cdn1.foo.com. The JWT is stored as a cookie by books2.store.com and sent in a header when ajax requests are made to auth.foobar.com and api.store.com (from the books2.store.com origin).
The following figure details a login request made to auth.foobar.com.
This figure details the response handed back by auth.foobar.com with a JWT.
This figure details a request to api.store.com which includes the JWT placed in a header.
This figure details the response received from api.store.com.
Again, the JWT is stored as a cookie by the user agent and is associated with books2.store.com.
To add a little more context, make sure to understand the maintenance of an authenticated state is necessary when automating attacks (Intruder) and performing vulnerability scanning (Scanner).
Without any de-authentication or blacklisting you could just set a token via the “Add Custom Header” extension and go about your business given that your token has a far away expiration date.
When faced with de-authentication and blacklisting mechanisms you can create a macro that fires off a login request, grabs the token using the “Add Custom Header” extension, and include it in the current request. Granted, this will happen for EVERY request. This may be completely acceptable given that the authentication API isn’t a production asset and your client doesn’t necessarily mind you beating up their infrastructure. Fire away and go about your business! But if this isn’t the case, or you just want to be more efficient and less cumbersome, you’ll need to come up with a more elegant solution than acquiring a new token for EVERY request.
This is where Burp Extensions come in. I created a way to mimic the Cookie Jar for JWTs (or header values, in general). In the end, two small extensions were necessary to pull this off.
1) “Store & Set” — Grab a JWT from a login macro when the current request is deemed “invalid”, store the value in the cookie jar, and then insert it into a request header.
2) “Set” — Grab the stored token value from the cookie jar and insert it into a request header.
Both extensions contained logic that only inserts the token value (taken from the cookie jar) into a header if the specified header was already included in the request.
Only a single session handling rule with two associated actions is required for this endeavor.
The first action will use the “Set” extension to update the current request’s JWT with the one that’s been stored in the Cookie Jar. This action behaves very much like Burp’s builtin “Use cookies from the session handling cookie jar” action.
The second will be a “check session is valid” action that will issue the current request, test its validity, if invalid, run a login macro, followed by our “Store & Set” extension which will parse the new JWT from the macro’s response, store the JWT in the cookie jar, and then update the current request’s JWT with the newly acquired one.
The following figure details the settings for the session check portion of the session handling rule. We know that when a JWT is revoked or has expired the string “Expired Token” will be present in responses. We will use this to invalidate the session and trigger a macro.
The following figure details the settings used where a macro is triggered due to an invalid session which is then followed by our “Store & Set” extension being called.
This is all very similar to how one would utilize Burp session handling rules and macros to provide state maintenance when an application makes use of traditional session cookies. The difference being that we’re not using cookies and, instead, are relegated to request headers.
The following code comprises the “Store & Set” extension. Take note of the “Cookie” class and its associated functions. These allow you to work with Burp’s Cookie Jar.
The following code comprises the “Set” extension. It’s almost identical to the “Store & Set” extension’s code aside from checking for an initial cookie (JWT) value and leaving out the macro response parsing.
The only difference between the two scripts are contained in the “performAction” function and the extension naming portions of the code. For the sake of brevity, I will only post that function to illustrate the differences. Swap one version out for the other.
Putting all of this into play and verifying the results proves successful. The following figure details where the application revoked the current token causing the session handling rules detailed above to re-authenticate and update subsequent tokens.
So altogether, nothing too mind-bending here but definitely useful and a fun exercise to work through. Hopefully this post will provide you with some insight into using Burp Extensions to interact with the Cookie Jar.
One bit of criticism I have for this exercise is that the act of setting the current request to the updated value seems to be pretty resource intensive. Running a scan with 40 threads making use of this pair of extensions seems to be pretty slow. I am going to revisit this code in Java and see if I can improve the performance. Stay tuned…