Description
Use the JaWT Scratchpad application. The admin user has the flag.
Solution
Walk me through it- Step 1Log in and capture the JWTRegister or log in as any user (e.g., 'guest'). After login, inspect your cookies in browser DevTools. You will find a JWT (JSON Web Token) cookie - it looks like three base64url segments separated by dots.
Learn more
A JWT has three parts:
header.payload.signature. Each part is base64url-encoded. The header specifies the algorithm (e.g., HS256), the payload contains claims like the username, and the signature verifies integrity.The payload is not encrypted - only signed. Anyone can decode and read the claims, but modifying them invalidates the signature (unless you can forge it).
- Step 2Decode the JWTPaste your JWT into jwt.io or use Python to base64-decode the payload. You will see something like {"user": "guest"}. Your goal is to change this to {"user": "admin"}.python
python3 -c " import base64, json token = 'YOUR.JWT.HERE' payload = token.split('.')[1] # Add padding if needed payload += '=' * (4 - len(payload) % 4) print(json.loads(base64.urlsafe_b64decode(payload))) "Learn more
Base64url is a URL-safe variant of base64 that replaces
+with-and/with_and omits padding=. You may need to add padding back when decoding manually. - Step 3Crack the HMAC secret or use 'none' algorithmTry two approaches: (1) Change the algorithm to 'none' and remove the signature. (2) Use hashcat or jwt_tool to crack the weak HMAC secret (often 'ilovepico' or 'secret'). Then forge a new token signed with the cracked secret.python
python3 -m pip install jwtpythonpython3 -c " import jwt # Attempt 1: none algorithm header = {'alg': 'none', 'typ': 'JWT'} payload = {'user': 'admin'} # Manually construct: base64(header).base64(payload). import base64, json h = base64.urlsafe_b64encode(json.dumps(header).encode()).rstrip(b'=').decode() p = base64.urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b'=').decode() print(f'{h}.{p}.') "bashhashcat -a 0 -m 16500 <jwt_token> /usr/share/wordlists/rockyou.txtLearn more
The 'none' algorithm attack works on JWT libraries that accept unsigned tokens without validation. The attacker sets
alg: nonein the header and omits the signature. Many older libraries trusted this.If the server validates the algorithm, the next attack is cracking the HMAC secret. HS256 JWTs use HMAC-SHA256 with a shared secret. If the secret is weak (a dictionary word), hashcat with
-m 16500(JWT mode) can recover it from a wordlist.Once you have the secret, use PyJWT:
jwt.encode({"user": "admin"}, secret, algorithm="HS256")to forge a valid admin token. - Step 4Set the forged JWT and access admin pageReplace your JWT cookie with the forged admin token using DevTools (Application > Cookies). Reload the page to see the flag.
Learn more
JWT vulnerabilities arise from weak secrets, accepting the 'none' algorithm, or algorithm confusion attacks (e.g., confusing RS256 with HS256). Always use strong random secrets and explicitly validate the expected algorithm on the server side.
Alternate Solution
Use the JWT Decoder tool on this site to decode and inspect the token's header and payload in one click. Once you confirm the payload claims, forge the modified token using jwt.io's built-in editor (paste your token, swap the algorithm to none, and edit the payload).
Flag
picoCTF{...}
Forge a JWT with user=admin by either cracking the weak HMAC secret (try 'ilovepico') or exploiting the 'none' algorithm.