I love attending the sessions put on by Black Hills Information Security when I can. Last week, the session was on JWT token attacks which I found very interesting. I wanted to see if I could mimic part of the demonstrated attack, reproduce and then leverage that attack into elevated access on a site. The BHIS session for JWT attacks on the day of 6/18/2020 can be found here: https://www.youtube.com/watch?v=muYmiEtPL8U&t=2490s
For this lab, I downloaded Juice Shop which is intentionally vulnerable to many of the top OWASP attacks. Once I had the app up and running, I explored the app some to enumerate users. In the session we didn’t get to see where the admin user was exposed – turns out this was super easy to find. After poking around in the site I decided to try and attack a password change for the admin account to see if I could muster a complete account takeover.
Step 1: Install Juice-Shop
I already had an Ubuntu 18.04 LTS machine running in the lab, so I just wanted to add the app here. I tried the NodeJS and NPM route first, but I ran into some snags and I did not want to invest a ton of time troubleshooting. I decided to go the Docker route and I was able to get this working on the first try.
Docker installation directions: https://docs.docker.com/engine/install/ubuntu/
I followed the documented steps verbatim:
And I was able to browse the site:
Step 2: Recon
There is a ton to explore on this site. For the purposes of this post, the only necessary recon is to open the Apple Juice product, look at the product review and note the username of the person that left the review:
firstname.lastname@example.org seems like a “juicy” target (pun intended).
Ok, now onto trying to exploit a JWT token vulnerability…
Step 3: Identify Where/What to Attack
I need an account. If I have an account, I can look at how the JWT tokens are constructed and then I can use that to try and craft a new token as my victim user. I went to login – created a new account named email@example.com w/ a password of P@ssw0rd!
I then logged in with the newly created credentials:
I took a look at all of the traffic in the Burp proxy log and notice calls to the /rest/user/whoami endpoint with my JWT token:
- The tokens are the same in the Authorization header as well as in the cookie. I chose the bottom /rest/user/whoami GET and Send to Repeater
Now I need to learn if I need to attack the Authorization Bearer token, the token in the cookie or both
- I added a letter to auth header (basically break it) – no change on send
- I added a letter to the cookie and it breaks – this is the one that matters
Step 4: Craft the JWT Attack Token
This is a signed JWT token – I can tell because it’s <base64URLencoded-header>.< base64URLencoded -payload>.< base64URLencoded -signature> – 3 base64urlencoded strings separated by periods:
I copied the header into Decoder, decode as Base64, change “RS265” to “None” and then encode as base64. The new string is my new header. I paste this into a document off to the side for later use
** NOTE – the “=” sign will break the header. These need to dropped when copying and pasting!
Next, I grabbed the payload and dropped that into Decoder, modified the email address and then reencoded as base64:
And change it to this and then encode it as base64
I copied the new payload and then pasted the new header and payload into repeater. Drop the signature (since we now have “none” in the header) and hit send:
No luck – this is where I had to play with the headers a bit to get this to consistently work. In order to get the return I wanted I ended up removing headers until I got the result.
Modified request (missing some headers – most notably the bearer token):
Step 5: JWT Attack
I now have a JWT token that is accept by the /rest/user/whoami API. With that, I need to see if other parts of the application will accept the token and chose to attack the password change functionality. I went into password reset on my account to change the password:
And change the password to “password1”:
Looking at this traffic in Burp:
I simply take and replace the Authorization token with the newly crafted JWT and replace the entire cookie in this new request from the previous Repeater request:
I get a 200 back and the password is shown there within the returned payload. Turns out this is the MD5 of the password I did in fact just set. Now, can I login with the creds?
I was able to change the password for the firstname.lastname@example.org account and log into the app with the new credentials!
Base64url encoding for the headers
- Drop the trailing “=” or the header will break
- Play with the headers until you get a result you like