
PoC de loopback para uma falha no broker SAML do Keycloak 26.8.1: um nome SAMLResponse duplicado com codificação percentual contorna a verificação de assinatura de redirecionamento, permitindo login como outro usuário.
Alguém sem conta no Keycloak consegue concluir um login SAML corporativo como outra pessoa. Ele faz login uma vez no provedor de identidade upstream, mantém esse redirecionamento assinado e coloca um segundo documento SAML na frente dele sob um nome codificado em percentual. O Keycloak verifica a assinatura legítima e então o autentica com o nome do documento forjado. Isso exige um broker SAML que assine respostas HTTP-Redirect, com validação de assinatura ativada e assinaturas de assertion desativadas, que é o que a importação de metadados configura. Não é a divisão por ponto e vírgula da issue #53354.
Classe: resíduo de OAuth Alcance: Remoto (não autenticado no Keycloak; requer um broker SAML que assine redirecionamentos)
Keycloak 26.8.1 (24ed1dad) - Red Hat
| ID | ainda sem CVE |
| Classe | resíduo de OAuth |
| Alcance | Remoto (sem conta no Keycloak; requer uma assinatura HTTP-Redirect genuína do IdP) |
| CWE | CWE-287 / CWE-347 / CWE-116 |
| CVSS | Alto: 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Produto | Keycloak |
| Afetado | 26.8.1 (24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b); Quarkus 3.40.1; imagem quay.io/keycloak/keycloak:26.8.1 |
| Autenticação | nenhuma no Keycloak. Requer um broker de provedor de identidade SAML com validateSignature=true e wantAssertionsSigned ausente ou falso. |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1:18084 |
Requer o NameID da vítima (geralmente um e-mail) e um HTTP-Redirect assinado real desse IdP. Não requer senha do Keycloak. Não requer caixa de e-mail. Um IdP somente POST não fornece essa assinatura. O ACS GET permanece montado mesmo quando a importação de metadados prefere POST.
| Privilégio | Método / sink | Recon / integridade / disponibilidade | Comprovado em lab ou inferido |
|---|---|---|---|
| Vincular um documento SAML forjado enquanto a assinatura legítima ainda é verificada | ACS do broker via GET com SAML%52esponse=<forged> antes do SAMLResponse assinado | Integridade (autenticação) | Comprovado em lab. E-mail de controle [email protected]. E-mail de ataque [email protected] |
| Criar o usuário vítima por meio do first-broker-login | callback.authenticated no NameID forjado | Integridade (criação de conta) | Comprovado em lab. Contagem de vítimas 0 antes da consulta dividida, 1 depois |
| Sessão como um usuário brokered existente, incluindo um admin que usa o mesmo IdP | Mesmo ACS, NameID desse usuário | Integridade (tomada de conta) | Inferido. O lab criou a vítima no primeiro login |
| Manter o RelayState e a sessão de autenticação | RelayState permanece o valor assinado | Integridade | Código-fonte. A assinatura cobre o RelayState |
| Controle negativo | Redirecionamento assinado legítimo, sem parâmetro extra | n/a | Comprovado em lab. O controle conclui como o e-mail do atacante |
Permanece fechado neste resíduo
| Eixo | Método / sink | Por que está fechado |
|---|---|---|
| Integridade | Divisão por ponto e vírgula SAMLResponse=...;SAMLResponse=... | issue #53354. quarkus.http.use-semicolon-as-query-param-delimiter=false |
| Integridade | Assertion não assinada quando wantAssertionsSigned=true | handleLoginResponse então exige uma assinatura de assertion |
| Integridade | Sem broker SAML, ou IdP somente POST sem assinatura de redirecionamento | O atacante nunca obtém SigAlg + Signature em um GET |
| Integridade | RCE no host, Admin REST não autenticado | Fora da classe |
| Integridade | Formulário de nome padrão VERIFY_PROFILE | Comprovado em lab como etapa de conclusão, não como refutação. O formulário já contém o e-mail brokered |
Fixe o Keycloak 26.8.1. O RESTEasy Reactive obtém @QueryParam("SAMLResponse") do Vert.x, que faz percent-decode dos nomes de query, então SAML%52esponse colapsa em SAMLResponse e o primeiro valor vence. verifyRedirectSignature reconstrói a string assinada a partir de getQueryParameters(false) na query bruta, onde esse nome permanece como uma chave diferente.
Caminhos errados já registrados: tratar #53354 como este resíduo (nome percent-encoded, não ponto e vírgula); seguir os 302 do ACS no cliente de laboratório e descartar AUTH_SESSION_ID; ler firstName/lastName vazios em VERIFY_PROFILE como falha (o email já é o NameID intermediado); um reverse shell. Teatro.
Python 3.11+ com cryptography. Docker necessário.
python3 keycloak-saml-redirect-param-split-Abraxas-Labs.py
ou:
cd lab
./run.sh
Aponte apenas para http://127.0.0.1:18084. Imagem quay.io/keycloak/keycloak:26.8.1. O launcher faz chdir para lab/ e executa run.sh. O IdP do laboratório é in-process. Ele nunca se comunica com um provedor de identidade real.
IOC image=quay.io/keycloak/keycloak:26.8.1 label-version=26.8.1
IOC serverinfo-version=26.8.1
IOC validate-signature=true want-assertions-signed=absent
IOC review-profile=DISABLED
IOC [email protected]
IOC victim-before=0
IOC [email protected]
IOC victim-after=1
SUCCESS KC-H04-SAML-REDIRECT kcs04b8n1
Verifique o mesmo documento de consulta que o endpoint analisa. Faça a decodificação percentual dos nomes dos parâmetros antes da reconstrução da assinatura, ou rejeite uma consulta que contenha mais de um SAMLResponse após a decodificação. Ative wantAssertionsSigned para brokers de redirecionamento, incluindo o caminho de importação de metadados.
24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b)SAMLEndpoint.redirectBinding (SAMLEndpoint.java:182-183, :779-813, :896-914)SamlProtocolUtils.verifyRedirectSignature (SamlProtocolUtils.java:216-249)KeycloakUriInfo.getQueryParameters (KeycloakUriInfo.java:147-163)GNU Affero GPL v3.0. Consulte LICENSE. Apenas laboratório de loopback. Sem garantia.
Abraxas Labs [email protected]
Por favor, credite o CVE ao Abraxas Labs.