
PoC en boucle locale pour une faille du broker SAML de Keycloak 26.8.1 : un nom SAMLResponse dupliqué et encodé en pourcentage contourne la vérification de signature de redirection, permettant de se connecter en tant qu'autre utilisateur.
Une personne sans compte Keycloak peut terminer une connexion SAML d'entreprise en se faisant passer pour quelqu'un d'autre. Elle se connecte une fois auprès du fournisseur d'identité amont, conserve cette redirection signée et place un second document SAML devant celle-ci sous un nom encodé en pourcentage. Keycloak vérifie la signature authentique puis la connecte sous le nom figurant dans le document falsifié. Cela nécessite un broker SAML qui signe les réponses HTTP-Redirect, avec la validation de signature activée et les signatures d'assertion désactivées, ce que configure l'import de métadonnées. Il ne s'agit pas du découpage par point-virgule de l'issue #53354.
Classe : résidu OAuth Portée : distante (non authentifié auprès de Keycloak ; nécessite un broker SAML signant les redirections)
Keycloak 26.8.1 (24ed1dad) - Red Hat
| ID | pas encore de CVE |
| Classe | résidu OAuth |
| Portée | distante (aucun compte Keycloak ; nécessite une véritable signature HTTP-Redirect de l'IdP) |
| CWE | CWE-287 / CWE-347 / CWE-116 |
| CVSS | Élevé : 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Produit | Keycloak |
| Affecté | 26.8.1 (24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b) ; Quarkus 3.40.1 ; image quay.io/keycloak/keycloak:26.8.1 |
| Auth | aucune sur Keycloak. Nécessite un broker de fournisseur d'identité SAML avec validateSignature=true et wantAssertionsSigned absent ou à false. |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:18084 uniquement |
Nécessite le NameID de la victime (généralement un e-mail) et une véritable redirection HTTP-Redirect signée provenant de cet IdP. Ne nécessite pas de mot de passe Keycloak. Ne nécessite pas de boîte mail. Un IdP en POST uniquement ne fournit pas cette signature. L'ACS GET reste monté même lorsque l'import de métadonnées préfère POST.
| Privilège | Méthode / cible | Reconnaissance / intégrité / disponibilité | Prouvé en lab ou déduit |
|---|---|---|---|
| Lier un document SAML falsifié tandis que la signature authentique est toujours vérifiée | ACS du broker en GET avec SAML%52esponse=<falsifié> avant le SAMLResponse signé | Intégrité (authn) | Prouvé en lab. E-mail de contrôle [email protected]. E-mail d'attaque [email protected] |
| Créer l'utilisateur victime via le first-broker-login | callback.authenticated sur le NameID falsifié | Intégrité (création de compte) | Prouvé en lab. Nombre de victimes 0 avant la requête découpée, 1 après |
| Session en tant qu'utilisateur brokerisé existant, y compris un administrateur qui utilise le même IdP | Même ACS, NameID de cet utilisateur | Intégrité (prise de contrôle de compte) | Déduit. Le lab a créé la victime à la première connexion |
| Conserver RelayState et la session d'authentification | RelayState reste la valeur signée | Intégrité | Source. La signature couvre RelayState |
| Contrôle négatif | Redirection signée authentique, sans paramètre supplémentaire | s.o. | Prouvé en lab. Le contrôle se termine avec l'e-mail de l'attaquant |
Restent fermés sur ce résidu
| Axe | Méthode / cible | Pourquoi c'est fermé |
|---|---|---|
| Intégrité | Découpage par point-virgule SAMLResponse=...;SAMLResponse=... | issue #53354. quarkus.http.use-semicolon-as-query-param-delimiter=false |
| Intégrité | Assertion non signée lorsque wantAssertionsSigned=true | handleLoginResponse exige alors une signature d'assertion |
| Intégrité | Pas de broker SAML, ou IdP en POST uniquement sans signature de redirection | L'attaquant n'obtient jamais SigAlg + Signature sur un GET |
| Intégrité | RCE sur l'hôte, Admin REST non authentifié | Hors classe |
| Intégrité | Formulaire de nom VERIFY_PROFILE par défaut | Prouvé en lab comme étape d'achèvement, non comme réfutation. Le formulaire contient déjà l'e-mail brokerisé |
J'ai épinglé Keycloak 26.8.1. RESTEasy Reactive récupère @QueryParam("SAMLResponse") depuis Vert.x, qui décode en pourcentage les noms de paramètres de requête, de sorte que SAML%52esponse se réduit à SAMLResponse et la première valeur l'emporte. verifyRedirectSignature reconstruit la chaîne signée à partir de getQueryParameters(false) sur la requête brute, où ce nom reste une clé différente.
Fausses pistes déjà consignées : traiter #53354 comme ce résidu (nom encodé en pourcentage, pas point-virgule) ; suivre les 302 de l'ACS dans le client de labo et perdre AUTH_SESSION_ID ; lire des firstName/lastName vides sur VERIFY_PROFILE comme un échec (l'email est déjà le NameID fédéré) ; un reverse shell. Du théâtre.
Python 3.11+ avec cryptography. Docker requis.
python3 keycloak-saml-redirect-param-split-Abraxas-Labs.py
ou :
cd lab
./run.sh
Ciblez uniquement http://127.0.0.1:18084. Image quay.io/keycloak/keycloak:26.8.1. Le lanceur fait un chdir dans lab/ et exécute run.sh. L'IdP du lab est en cours de processus. Il ne communique jamais avec un véritable fournisseur d'identité.
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
Vérifiez que le document de requête analysé par le point de terminaison est bien le même. Décodez les noms de paramètres en pourcentage avant la reconstruction de la signature, ou rejetez une requête qui contient plus d'un SAMLResponse après décodage. Activez wantAssertionsSigned pour les brokers de redirection, y compris le chemin d'importation des métadonnées.
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. Voir LICENSE. Laboratoire en boucle locale uniquement. Aucune garantie.
Abraxas Labs [email protected]
Veuillez créditer la CVE à Abraxas Labs.