This is the second article in a three part series on JSON Web Tokens.
The first article outlined what a JWT is, what its components are and how you can read and edit its content (if you haven’t read this first article, I strongly suggest you take a moment to do it now before going any futher).
In this present article, let’s look at how you can fuzz a signed JWT to obtain its encryption password (in JWT lingo, this password is called a secret). With the token’s secret, you’ll be able to edit and resign an existing token. Forging your own rogue JWT will let you elevate your own privileges or interact with the target API using another user’s identity.
Hacking web tokens is a great way of breaking into an API. It’s also an easy way of finding and exploiting flaws in an API’s authentication mechanism. If you want to do any serious API hacking, I really recommend you take the time to learn and practice the art of abusing JWTs. Besides, it’s also fun…
Fuzzing a JSON Web Token
In most cases, the JWTs you will come across will be signed. This means that if you want to edit the content of the payload section of the token (as outlined in my previous article), you will need the secret in order to resign the token.
One way of finding out the secret is to fuzz the token. To do this, your tool of choice is the JSON Web Token Toolkit (install instructions here).
The JSON Web Token Toolkit is a command line tool written in Python that lets you read the content of a JWT, scan for know vulnerabilities and perform a series of actions including fuzzing with a wordlist of potential secrets.
To fuzz a token, the syntax of the command is:
python3 jwt_tool.py <full_JWT_token> -C -d <path to wordlist>
The -C
flag indicates we want to run a crack attack.
The -d
flag specifies the path to the wordlist we want to use
An example command could be:
python3 jwt_tool.py eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlZHdAemVyb2RheWhhY2tlci5jb20iLCJpYXQiOjE2NjU3NTM4NTYsImV4cCI6MTY2NTg0MDI1Nn0.SbZ3F1kmuntmjy8aJQFj5xUZL_RelJE4bCfWAHUVzbfxyWGIzQhq5zx-CF3voRz3xxHWfY5H1GxjbdedRjQdfw -C -d /usr/share/wordlists/rockyou.txt
One thing to note is that our fuzzing command is executed locally, on our host machine, with no interaction with the target app. This means we can fuzz freely without worrying about our network traffic triggering alerts server side. Also, without having to send requests and get responses from the target, the process is a lot faster and we can use larger wordlists.
In fact, the challenge of cracking a token’s secret is not so much performing the crack but coming up with a successful wordlist.
There are several avenue you need to explore.
Make your own wordlist
First go for the quick and obvious. A method that I often see recommended is to use your understanding of the target app and whatever information you obtained during recon to produce a list of 20 or 30 names that are relevant with the app’s area of focus or business.
Suppose you are dealing with a yoga or fitness app. You could try to add the name of some postures to your bespoke wordlist, as well as words that have something to do with effort, persistence, success or achieving goals.
You can also throw in the first name or last name of the app’s admin if you have the info.
Also add a date to the end of some of the word (ex. Jessie2022 or flower2022).
Oh, and the words ‘token’ and ‘secret’ should probably be the first in your list.
I know it’s probably a long shot but you may just get lucky.
Move on to pure bruteforcing
If that doesn’t work, go for something a bit more systematic. Use the crunch command line tool to generate sequential wordlists. crunch
comes preinstalled with Kali linux.
To generate a list of words of 1 to 6 characters, using all combinations of letters from the alphabet, type:
crunch 1 6 -o passwords01.txt
This generates a 2 Gb text file that you can use with the jwt_tool
command described earlier, by typing:
python3 jwt_tool.py eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlZHdAemVyb2RheWhhY2tlci5jb20iLCJpYXQiOjE2NjU3NTM4NTYsImV4cCI6MTY2NTg0MDI1Nn0.SbZ3F1kmuntmjy8aJQFj5xUZL_RelJE4bCfWAHUVzbfxyWGIzQhq5zx-CF3voRz3xxHWfY5H1GxjbdedRjQdfw -C -d passwords01.txt
If this isn’t enough, you can always try generating a list of 7 letter words with:
crunch 7 7 -o passwords02.txt
Be aware however than this will generates a 60 Gb file. Also, these words will only contain letters and no numbers.
A better option would be to generate a list of 1 to 6 character words with a mix of letters and numbers. To do this, you need to give crunch
the exact list of characters you want to use.
Download the charset.lst
file here and move it to the directory you are running your jwt_tool
commands from.
This text file lists a series of character combinations you can use with crunch
.
To generate a list of words of 1 to 6 characters, using letters and numbers, type:
crunch 1 6 -f charset.lst lalpha-numeric -o passwords03.txt
This produces a 14 Gb file.
If this seems too much, you can drop to 1 to 5 character words with:
crunch 1 5 -f charset.lst lalpha-numeric -o passwords04.txt
This generates a 354 Mb file.
With this wordlist, jwt_tool
will test out 44 million password combinations, as you can see below.
Now if you want to also add capital letters, type:
crunch 1 5 -f charset.lst mixalpha-numeric -o passwords05.txt
This generates a 5,3 Gb file with combination of lowercase letters, uppercase letters and numerals.
This time jwt_tool
goes through 780 million passwords…
Finally, if you want to add in a full set of special characters, type:
crunch 1 5 -f charset.lst mixalpha-numeric-all -o passwords06.txt
This generates a 42 Gb file that should let you bruteforce all possible combinations of up to 5 character passwords.
OK, so you get the idea. If you have the disk space and can afford to let jwt_tool
run overnight, nothing stops you from pulling the same stunt with longer words.
Use standard wordlists
If pure bruteforcing failed to crack a JWT’s secret, you can fall back to dictionary based fuzzing, with standard wordlists. If you haven’t done so already, install Daniel Miesler’s SecLists.
The wordlists I recommend in the Seclists collection are (adjust paths to your own install):
/usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt
/usr/share/seclists/Passwords/Common-Credentials/1900-2020.txt
/usr/share/seclists/Passwords/scraped-JWT-secrets.txt
Also don’t forget the classic default list:
/usr/share/wordlists/rockyou.txt
Resign the token
If your target is using a weak password to sign their tokens, you should have gotten through by now.
To resign your forged token using the secret you just obtained, go back to jwt_tool
and run the following command:
python3 jwt_tool.py <full_JWT_token> -S <algorithm> -p <secret>
The -S
flag specifies the algorithm you want the token to be signed with
The -p
flag specifies the secret to use
An example of this would be:
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gQWRhbXMiLCJpYXQiOjE1MTYyMzkwMjJ9.Qg3v3uHk-btWZAeOUjS4kC9ErBfG4KFheRN79Q_My_k -S hs256 -p mysupersecret
Alternatively, if you prefer a web interface, you can use jwt.io to edit the JWT’s payload and sign the token with the appropriate secret.
Hacking JSON Web Tokens doesn’t stop with cracking the secret however. In the final article in this three part series, I’m going through other ways of tampering with JWTs.
And if you want to take your bruteforcing game one step further, check this article by Dana Epp where he explains how to use the power of cloud computing to crack JWT secrets using a GPU-optimized Azure virtual machine. Definitely worth a read.
November 13, 2022
I really do not consider this method of attacK to be adequate.
No reasonable programmer would use 5 character secret or use the ‘theme’ of the API service as secret.
There are alphanumeric secrets with inclusion of special characters of 120characters long. This would deem impractical to attack or take years.
Won’t it?
November 13, 2022
You’re totally right. My point here is not to give a fool-proof method that will reliably crack all tokens around. The idea is more to give some insight into how to take advantage of weakly signed tokens you may come across. If some ‘unreasonable programmer’ uses their girlfriend’s name to sign their tokens, you definitely want to be the one to find out.
November 17, 2022
Typically using a secret for signing is a bad idea unless the secret is very strong.
Consider using cryptographic keys instead They allow higher protection and as a benefit anyone can proove the integrity with knowledge of the public key
November 21, 2022
Agreed. This is why coming across a weakly signed token when testing an app is usually a good catch.