
Loopback-PoC für eine SAML-Broker-Schwachstelle in Keycloak 26.8.1: Ein prozentkodierter doppelter SAMLResponse-Name umgeht die Signaturprüfung bei Weiterleitungen und ermöglicht die Anmeldung als ein anderer Benutzer.
Jemand ohne Keycloak-Konto kann einen Unternehmens-SAML-Login als eine andere Person abschließen. Die Person meldet sich einmal beim vorgelagerten Identity Provider an, behält diese signierte Weiterleitung und hängt ein zweites SAML-Dokument unter einem prozentkodierten Namen davor. Keycloak prüft die echte Signatur und meldet die Person dann unter dem Namen aus dem gefälschten Dokument an. Dafür ist ein SAML-Broker erforderlich, der HTTP-Redirect-Antworten signiert, mit aktivierter Signaturvalidierung und deaktivierten Assertion-Signaturen, was der Metadaten-Import so konfiguriert. Es handelt sich nicht um die Semikolon-Aufteilung aus Issue #53354.
Klasse: OAuth-Überbleibsel Reichweite: Remote (unauthentifiziert gegenüber Keycloak; erfordert einen redirect-signierenden SAML-Broker)
Keycloak 26.8.1 (24ed1dad) - Red Hat
| ID | noch keine CVE |
| Klasse | OAuth-Überbleibsel |
| Reichweite | Remote (kein Keycloak-Konto; erfordert eine echte HTTP-Redirect-Signatur vom IdP) |
| CWE | CWE-287 / CWE-347 / CWE-116 |
| CVSS | Hoch: 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Produkt | Keycloak |
| Betroffen | 26.8.1 (24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b); Quarkus 3.40.1; Image quay.io/keycloak/keycloak:26.8.1 |
| Auth | keine bei Keycloak. Erfordert einen SAML-Identity-Provider-Broker mit validateSignature=true und wantAssertionsSigned nicht gesetzt oder false. |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1:18084 |
Erfordert die NameID des Opfers (üblicherweise eine E-Mail) und eine echte signierte HTTP-Redirect-Weiterleitung von diesem IdP. Kein Keycloak-Passwort erforderlich. Kein Postfach erforderlich. Ein reiner POST-IdP liefert diese Signatur nicht. Der GET-ACS bleibt eingebunden, selbst wenn der Metadaten-Import POST bevorzugt.
| Privileg | Methode / Sink | Recon / Integrität / Verfügbarkeit | Im Lab nachgewiesen oder abgeleitet |
|---|---|---|---|
| Ein gefälschtes SAML-Dokument einbinden, während die echte Signatur weiterhin verifiziert | GET-Broker-ACS mit SAML%52esponse=<forged> vor dem signierten SAMLResponse | Integrität (Authn) | Im Lab nachgewiesen. Kontroll-E-Mail [email protected]. Angriffs-E-Mail [email protected] |
| Den Opfer-Benutzer über den First-Broker-Login anlegen | callback.authenticated auf der gefälschten NameID | Integrität (Kontoerstellung) | Im Lab nachgewiesen. Opfer-Anzahl 0 vor der Split-Abfrage, 1 danach |
| Sitzung als ein bestehender gebrokerter Benutzer, einschließlich eines Administrators, der denselben IdP nutzt | Derselbe ACS, NameID dieses Benutzers | Integrität (Kontoübernahme) | Abgeleitet. Das Lab legte das Opfer beim ersten Login an |
| RelayState und die Auth-Sitzung beibehalten | RelayState bleibt der signierte Wert | Integrität | Quellcode. Die Signatur deckt RelayState ab |
| Negativkontrolle | Echte signierte Weiterleitung, kein zusätzlicher Parameter | n/a | Im Lab nachgewiesen. Die Kontrolle endet als die Angreifer-E-Mail |
Bleibt bei diesem Überbleibsel geschlossen
| Achse | Methode / Sink | Warum geschlossen |
|---|---|---|
| Integrität | Semikolon-Aufteilung SAMLResponse=...;SAMLResponse=... | Issue #53354. quarkus.http.use-semicolon-as-query-param-delimiter=false |
| Integrität | Unsignierte Assertion bei wantAssertionsSigned=true | handleLoginResponse verlangt dann eine Assertion-Signatur |
| Integrität | Kein SAML-Broker oder reiner POST-IdP ohne Redirect-Signatur | Der Angreifer erhält nie SigAlg + Signature bei einem GET |
| Integrität | Host-RCE, unauthentifizierte Admin-REST | Außerhalb der Klasse |
| Integrität | Standard-VERIFY_PROFILE-Namensformular | Im Lab als Abschlussschritt nachgewiesen, nicht als Widerlegung. Das Formular enthält bereits die gebrokerte E-Mail |
Ich habe Keycloak 26.8.1 gepinnt. RESTEasy Reactive übernimmt @QueryParam("SAMLResponse") von Vert.x, das Query-Namen prozent-dekodiert, sodass SAML%52esponse auf SAMLResponse zusammenfällt und der erste Wert gewinnt. verifyRedirectSignature rekonstruiert den signierten String aus getQueryParameters(false) auf der rohen Query, wo dieser Name ein anderer Schlüssel bleibt.
Bereits dokumentierte Fehlversuche: #53354 als diesen Rest zu behandeln (prozent-kodierter Name, kein Semikolon); ACS-302s im Lab-Client zu folgen und AUTH_SESSION_ID zu verwerfen; leere firstName/lastName bei VERIFY_PROFILE als Fehlschlag zu lesen (die E-Mail ist bereits die gebrokerte NameID); eine Reverse Shell. Theater.
Python 3.11+ mit cryptography. Docker erforderlich.
python3 keycloak-saml-redirect-param-split-Abraxas-Labs.py
oder:
cd lab
./run.sh
Ziel ist ausschließlich http://127.0.0.1:18084. Image quay.io/keycloak/keycloak:26.8.1. Der Launcher führt chdir in lab/ aus und startet run.sh per exec. Der Lab-IdP läuft im selben Prozess. Er kommuniziert niemals mit einem echten Identity Provider.
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
Verifiziere dasselbe Query-Dokument, das der Endpunkt parst. Prozent-dekodiere Parameternamen vor der Signatur-Rekonstruktion oder lehne eine Query ab, die nach dem Dekodieren mehr als eine SAMLResponse enthält. Aktiviere wantAssertionsSigned für Redirect-Broker, einschließlich des Metadata-Import-Pfads.
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. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.
Abraxas Labs [email protected]
Bitte schreibe die CVE Abraxas Labs zu.