Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/ivanesk315/cve-2026-18963
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebPruebas de PenetraciónGestión de Identidad y Acceso (IAM)AutenticaciónAprendizaje y EducaciónLabs y Práctica
GitHubivanesk315/cve-2026-18963

CVE-2026-18963

Laboratorio basado en Docker y exploit en Python para CVE-2026-18963, un bypass del flujo de restablecimiento de credenciales de Keycloak que permite la toma de control de cuentas mediante la elusión de la verificación por correo electrónico.

Ver Repositorio
hace 0 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-18963 — Laboratorio de bypass del flujo de restablecimiento de credenciales de Keycloak

Descripción general

Laboratorio que simula la vulnerabilidad CVE-2026-18963 (CVSS 9.1) en Keycloak, que permite a un atacante tomar el control de cualquier cuenta mediante el bypass de la verificación de correo electrónico en el flujo de restablecimiento de contraseña.

Solo para fines de investigación de seguridad y educación.

Requisitos

  • Docker & Docker Compose
  • Python 3.8+
  • pip

Guía de uso

1. Iniciar Keycloak vulnerable

root@kitploit:~
docker-compose up -d

Espere a que Keycloak se inicie (~30-60 segundos).

2. Configurar el laboratorio

root@kitploit:~
pip install -r requirements.txt
python setup-lab.py

El script creará:

  • Realm vuln-lab con reset-password habilitado
  • Configuración SMTP (MailHog) para enviar correos
  • Usuario victim ([email protected] / VictimPass123!)

3. Ejecutar el exploit

root@kitploit:~
python exploit.py -u http://127.0.0.1:8080 -r vuln-lab -t victim -p Pwned123!

Opciones:

  • -u / --url: URL de Keycloak (default: http://127.0.0.1:8080)
  • -r / --realm: Nombre del realm (default: vuln-lab)
  • -t / --target: Nombre de usuario objetivo (default: victim)
  • -p / --password: Nueva contraseña (default: Pwned123!)
  • -v / --verbose: Activar salida de depuración

4. Ver correos (opcional)

MailHog UI: http://127.0.0.1:8025 — ver los correos de reset-password enviados durante el exploit.

5. Limpieza

root@kitploit:~
docker-compose down -v

Detalles técnicos

Causa raíz

Dos fallos combinados en Keycloak forman la cadena de ataque:

Fallo 1 — Corrupción del estado del selector (DefaultAuthenticationFlow.java): Cuando el usuario hace clic en "Try Another Way", la nota de autenticación AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED se guarda como "true" (cadena booleana) en lugar del ID del modelo de ejecución. El valor "true" no está vinculado a ninguna ejecución específica, por lo que persiste a lo largo de los pasos del flujo, haciendo que el selector se muestre en un contexto incorrecto.

Fallo 2 — Éxito de acción incondicional (ResetCredentialEmail.java): El método action() de ResetCredentialEmail llama a context.success() de forma incondicional sin verificar el token de acción. Normalmente, action() solo se llama cuando el usuario hace clic en el enlace del correo (que tiene token de acción). Pero cuando el selector está corrupto, el atacante puede activar action() directamente a través del procesamiento del flujo.

Flujo de ataque detallado

root@kitploit:~
Attacker                              Keycloak
   │                                     │
   │─── GET /auth (OIDC + PKCE) ────────>│  1. Inicializar sesión de autenticación
   │<── Login page + cookies ────────────│
   │                                     │
   │─── GET /reset-credentials ─────────>│  2. Pasar al flujo de reset
   │<── Username form ──────────────────│
   │                                     │
   │─── POST tryAnotherWay=on ─────────>│  3. Corromper el estado del selector
   │<── Authenticator selector ─────────│     SELECTOR_DISPLAYED = "true"
   │                                     │
   │─── POST username=victim ──────────>│  4. Enviar username a través del selector
   │<── "Check your email" page ────────│     Correo enviado, CURRENT_EXEC = email_id
   │                                     │
   │─── GET /reset-credentials ────────>│  5. Reingresar al flujo de reset
   │<── Corrupted selector (!!!) ───────│     processFlow() ve SELECTOR="true"
   │                                     │     → muestra el selector para el paso de email
   │                                     │
   │─── POST {} (empty body) ──────────>│  6. BYPASS: activar action()
   │<── 302 → UPDATE_PASSWORD ─────────│     processAction() no encuentra
   │                                     │     authenticationExecution en el formulario
   │─── GET /required-action ──────────>│     → cae en la rama action()
   │<── Password update form ──────────│     ResetCredentialEmail.action()
   │                                     │     → context.success() (¡incondicional!)
   │                                     │     → el flujo pasa a ResetPassword
   │                                     │
   │─── POST password-new=Pwned! ──────>│  7. Establecer nueva contraseña
   │<── 302 → /account/ ──────────────│     Account takeover completado
   │                                     │
   └── Iniciar sesión con la nueva contraseña ──────┘

¿Por qué funciona el paso 6?

En DefaultAuthenticationFlow.processAction(), al recibir el POST:

  1. Verifica tryAnotherWay en el formulario → NO (formulario vacío)
  2. Verifica authenticationExecution en el formulario → NO (formulario vacío)
  3. Cae en la rama final: llama a authenticator.action(result) sobre el modelo de la URL

Como la URL contiene execution=<email_exec_id> (del action del formulario del selector), se llama a ResetCredentialEmail.action() → devuelve context.success() → el flujo pasa a ResetPassword → muestra el formulario de establecimiento de contraseña.

Versiones afectadas

ProductoAfectadoParcheado
Keycloak (upstream)< 26.7.226.7.2+
RHBK 26.4.x< 26.4.1526.4.15+
RHBK 26.6.x< 26.6.626.6.6+

Parche (PR #51844)

DefaultAuthenticationFlow.java:

  • setAuthNote(SELECTOR_DISPLAYED, "true") → setAuthNote(SELECTOR_DISPLAYED, model.getId())
  • processFlow() verifica selector.equals(lastExecutionId) en lugar de Boolean.parseBoolean()
  • Si no coincide → removeAuthNote(SELECTOR_DISPLAYED)

ResetCredentialEmail.java:

  • action() verifica ACTION_TOKEN_USER_ID antes de llamar a context.success()
  • Si no hay un token de acción válido → context.failure(INVALID_USER)

Referencias

  • NVD - CVE-2026-18963
  • Red Hat CVE Page
  • Keycloak Issue #51833
  • Fix PR #51844
  • Kudelski Security Research
  • The Hacker News
Descargar herramienta