
Prueba de concepto (PoC) para una vulnerabilidad de omisión de autenticación que afecta a aplicaciones que utilizan pac4j-jwt con JWE (Cifrado JSON Web).
Aviso legal: Este exploit fue desarrollado únicamente para la máquina retirada "Principal" de HackTheBox. Solo con fines educativos. No lo utilices contra sistemas que no poseas o para los que no tengas permiso explícito de prueba.
| Campo | Detalles |
|---|
| Nombre | Principal |
| Plataforma | HackTheBox |
| Dificultad | — |
| Estado | Retirada |
Biblioteca afectada: pac4j-jwt < 4.5.9 / 5.7.9 / 6.3.3
Gravedad: Crítica
Tipo: Bypass de autenticación
Una falla lógica en JwtAuthenticator permite a un atacante eludir la autenticación
envolviendo un PlainJWT sin firmar (alg=none) dentro de un JWE (JWT cifrado).
Cuando el servidor descifra el JWE, intenta analizar el token interno como un
SignedJWT. Debido a que el token interno es un PlainJWT, el objeto SignedJWT es
nulo y la verificación de firma se omite por completo. El servidor entonces construye
un perfil de usuario a partir de las claims no verificadas, lo que permite la suplantación
total de cualquier usuario, incluidos los administradores.
Crear claims maliciosas (sub=admin, role=ROLE_ADMIN)
↓
Construir PlainJWT sin firmar (alg=none)
↓
Envolver PlainJWT dentro de un JWE usando la clave pública RSA del servidor
↓
Enviar el token JWE al servidor
↓
El servidor descifra el JWE → encuentra el PlainJWT → omite la verificación de firma → concede acceso
Consulta exploit.py
Actualiza pac4j-jwt a una versión parcheada:
4.5.9 o posterior5.7.9 o posterior6.3.3 o posteriorO rechaza explícitamente los tokens internos PlainJWT después del descifrado del JWE:
if (jwt instanceof PlainJWT) {
throw new CredentialsException("PlainJWT not allowed inside JWE");
}
/api/auth/jwks
alg: noneEnvolver dentro de un JWE usando la clave pública
Enviar como:
Authorization: Bearer <token>
pip install jwcrypto requests
python3 poc.py \
--jwks-url http://target/api/auth/jwks \
--target http://target/api/dashboard
[+] Forged Token:
eyJhbGciOiJSU0EtT0FFUC0yNTYi...
[+] Browser Injection:
sessionStorage.setItem("auth_token", "eyJhbGciOiJSU0EtT0FFUC0yNTYi...")
[+] Status: 200
{"user":{"username":"admin","role":"ROLE_ADMIN"}}
Abre DevTools → Consola:
sessionStorage.setItem("auth_token", "<PASTE_TOKEN_HERE>")
Luego navega a:
/dashboard
alg: none