Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-18963-Exploit — Keycloak CVE-2026-18963용 익스플로잇으로, reset-credentials 우회를 통해 인증되지 않은 계정 탈취를 가능하게 합니다. 안전한 탐지, 비파괴적 증명, 전체 계정 탈취, 사용자 이름 열거, 취약한 버전과 패치된 버전을 포함한 랩이 포함되어 있습니다. | Kitploit
도구/GitHubGitHub/snizi/cve-2026-18963-exploit
Authentication & AuthorizationVulnerability AnalysisExploitationWeb Application ExploitationPenetration Testing
GitHubsnizi/cve-2026-18963-exploit

CVE-2026-18963-Exploit

Keycloak CVE-2026-18963용 익스플로잇으로, reset-credentials 우회를 통해 인증되지 않은 계정 탈취를 가능하게 합니다. 안전한 탐지, 비파괴적 증명, 전체 계정 탈취, 사용자 이름 열거, 취약한 버전과 패치된 버전을 포함한 랩이 포함되어 있습니다.

저장소 보기
714일 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-18963 — Keycloak reset-credentials 우회 → 인증되지 않은 계정 탈취

CVE Affected Python Dependencies

오직 사용자 이름 또는 이메일 주소만 알면, 인증되지 않은 공격자는 임의의 Keycloak 계정에 임의의 비밀번호를 설정할 수 있습니다. 비밀번호 재설정 이메일은 실제 피해자에게 전달되며 절대 필요하지 않습니다 — 공격자는 사서함을 읽지도, 링크를 클릭하지도 않으며, 사전 자격 증명이나 세션을 보유하지 않습니다.

영향받는 버전: Keycloak 26.0.0 – 26.7.1. 26.7.2에서 수정됨.


취약한가요?

명령 하나면 충분합니다. 유효한 사용자 이름이 필요 없고 부작용도 없습니다 — 이메일을 보내지 않고, 계정에 기록하지 않으며, 악용 가능한 단계 전에 중지됩니다.```bash git clone https://github.com/Snizi/CVE-2026-18963-Exploit cd CVE-2026-18963-Exploit

python3 cve_2026_18963_poc.py
--base https://sso.example.com --realm YOUR_REALM
--client-id account
--redirect-uri https://sso.example.com/realms/YOUR_REALM/account/
--safe-check

root@kitploit:~
Python 3.9+, 표준 라이브러리만 사용합니다. 설치할 것이 없습니다.

| 종료 | 판정 | 의미 |
|:---:|---|---|
| `0` | 🔴 **취약** | 대기 상태의 이메일 게이트가 응답됨 — 버그 자체입니다 |
| `2` | 🟢 **패치됨** | 흐름이 로그인으로 분기되어 그대로 유지됨 (fix #51844 존재) |
| `2` | 🟡 **완화됨** | reset-credentials에 도달할 수 없음 — *비밀번호 찾기*가 꺼져 있음. **패치가 아닙니다.** |
| `3` | ⚪ **판정 불가** | 인식할 수 없는 응답 — **통과로 읽지 마세요** |

realm별로 실행하세요 — *비밀번호 찾기*는 realm별 설정이며 `master`도 포함됩니다.
세부 사항과 검사에 사용자가 필요하지 않고 아무것도 건드리지 않는 이유는
[§4a](#4a-safe-detection---safe-check--start-here)에 있습니다.

**이미 노출된 사실을 알고 있나요?** [시정 조치](#8-remediation) 및
[탐지 / 위협 헌팅](#9-detection)으로 이동하세요.

### 대상 없이 시도해 보기

이 저장소는 동일한 realm을 대상으로 취약한 **26.7.1**과 패치된 **26.7.2**를
나란히 구동하는 랩을 제공하며, 비밀번호 재설정 이메일이 도착하고 계정이 탈취되는
동안 읽히지 않은 채 남아 있는지 지켜볼 수 있는 사서함도 함께 제공합니다.```bash
cd lab && docker compose up -d

python3 ../cve_2026_18963_poc.py --base http://localhost:8080 \
  --realm poc --client-id poc-app --safe-check   # VULNERABLE
python3 ../cve_2026_18963_poc.py --base http://localhost:8100 \
  --realm poc --client-id poc-app --safe-check   # PATCHED

⚠️ 승인된 테스트만 허용됩니다

이 저장소는 방어자, 사고 대응자 및 승인된 침투 테스터를 위해 존재합니다. 소유한 시스템 또는 서면 허가를 받은 시스템에서만 실행하십시오. 모든 항목에는 자체 포함된 취약 실습 환경 (lab/)이 함께 제공되므로, 버그 작동 방식을 배우기 위해 외부를 건드릴 필요가 없습니다. 승인 없이 제3자 인프라를 대상으로 지정하는 것은 대부분의 관할권에서 불법이며, 이 프로젝트가 지원하는 행위가 아닙니다.

참고 자료: keycloak#51833 · GHSA-4gv3-mc9p-5wqc · 수정 keycloak#51844


목차

  • 1. 근본 원인
  • 2. 영향을 받는 버전(레거시 라인 포함)
  • 3. 실습 환경
  • 4. 사용법
    • 4a. 안전한 탐지 (--safe-check) — 여기서 시작
    • 4b. 비파괴적 증명 (--check)
    • 4c. 전체 장악
    • 4d. 사용자 이름 열거 (--enum)
  • 5. 사용자 정의 로그인 테마
  • 6. 알려진 한계 — PKCE
  • 7. 수행된 검증
  • 8. 해결 방법
  • 9. 탐지
  • 작성자

1. 근본 원인

두 가지 결함이 연결되어 있습니다. 어느 것도 단독으로는 악용할 수 없습니다.

결함 1 — 범위가 지정되지 않은 스티키 플래그

services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java

processAction() — tryAnotherWay라는 폼 키를 포함한 모든 POST:```java processor.getAuthenticationSession().setAuthNote( AuthenticationProcessor.AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED, "true"); return createSelectAuthenticatorsScreen(model);

root@kitploit:~
그 메모는 **어떤 실행 세트가 설정했는지 기록하지 않는 단순 불리언(boolean)**입니다. 이 플래그는 제출된 `authenticationExecution` 매개변수를 처리하는 분기에서만 해제됩니다. 해당 매개변수를 생략하면 — 이 PoC가 전체적으로 그렇게 하듯이 — 인증 세션 수명 동안 플래그는 계속 설정된 상태로 유지됩니다.

`processFlow()` — 플래그가 truthy인 동안에는 정상적인 흐름 평가가 건너뛰어집니다:```java
if (Boolean.parseBoolean(authSession.getAuthNote(AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED))) {
    String lastExecutionId = authSession.getAuthNote(CURRENT_AUTHENTICATION_EXECUTION);
    if (lastExecutionId != null) {
        AuthenticationExecutionModel executionModel =
            realm.getAuthenticationExecutionById(lastExecutionId);
        if (executionModel != null)
            return createSelectAuthenticatorsScreen(executionModel);   // <-- attacker-usable form
    }
}

현재 대기 중인 실행을 대상으로 하는 제출 가능한 양식을 렌더링하며, 세션을 *"이메일을 기다리는 중"*에 고정하는 대신입니다.

핵심은 processResult() case FORK:입니다 — 재설정 이메일 보내기가 실행되면 CURRENT_AUTHENTICATION_EXECUTION = <reset-credential-email execution id>를 기록하고 브라우저를 로그인 페이지로 포크(fork)합니다. 대기 중인 실행이 바로 이메일 게이트입니다.

결함 2 — 이메일 게이트는 액션 토큰을 전혀 확인하지 않습니다

`services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java````java @Override public void action(AuthenticationFlowContext context) { context.getUser().setEmailVerified(true); context.success(); }

root@kitploit:~
무조건적이다. 흐름이 유효한 액션 토큰에 의해 재개되었는지 검증하는 것이 없으므로,
`action()`에 *도달하는 것*은 사서함 제어를 증명하는 것과 동등하게 취급된다.

### 체인```
tryAnotherWay POST            → sticky AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED="true"
submit victim identifier      → mail sent to victim, e-mail execution parked (FORK)
re-enter reset-credentials    → sticky flag serves a form targeting the parked e-mail execution
POST that form                → ResetCredentialEmail.action() → success() → gate bypassed
                              → flow advances to UPDATE_PASSWORD → attacker sets the password

HTTP 요청 6개, 인증 없음, authenticationExecution 매개변수는 어느 시점에도 없습니다.

수정 사항 (PR #51844)

  • 이제 노트는 model.getId()를 저장하며, processFlow()는 오직 그 값이 CURRENT_AUTHENTICATION_EXECUTION과 같을 때만 이를 인정하고, 그렇지 않으면 제거합니다. 공격에서는 두 값이 서로 다릅니다 (choose-user id vs. e-mail-gate id) — 패치가 감지하는 것이 정확히 이것이고, --safe-check가 기준으로 삼는 신호도 정확히 이것입니다.
  • ResetCredentialEmail.action()은 이제 다음을 요구합니다: context.getUser().getId().equals(authNote(ACTION_TOKEN_USER_ID)) 그렇지 않으면 INVALID_USER로 실패합니다.

2. 영향을 받는 버전 (레거시 라인 포함)

이 CVE에서 "레거시"가 의미하는 것

  • 레거시 릴리스는 자동으로 안전하지 않습니다 — 특정한 이유 때문에 안전합니다. 스티키 불리언 노트는 커밋 6a9e60bb에 의해 26.0.0에서 도입되었으며, "다른 방법 시도" 인증자 선택 화면을 재설정 흐름에 추가했습니다. 그보다 오래된 것은 단순히 해당 코드 경로가 없습니다. 여기에는 오래된 WildFly 기반 배포판과 RH-SSO 7.x가 포함되며, 이들은 이 버그의 영향을 받지 않지만 수명 종료 상태로 남아 다른 많은 취약점에 노출되어 있습니다. 레거시 빌드에 머무르는 것은 해결책이 아닙니다.
  • 레거시 26.x 라인이 실제 문제입니다. 26.7.2는 유일하게 수정된 릴리스로 커뮤니티 트레인에 배포되었습니다. 배포가 26.0 – 26.6에 머물러 있다면, 해당 라인에는 패치 릴리스가 없습니다 — 수정에는 포인트 릴리스가 아닌 마이너 버전 업그레이드가 필요합니다. 26.4.15 / 26.6.6 태그는 벤더 백포트이며 커뮤니티 이미지와 서로 바꿔 쓸 수 없습니다.
  • 많은 장기 운영 배포가 호환성 때문에 이전 26.x에 고정되어 있기 때문에, "우리 라인은 완전히 패치되어 있다"는 말이 여기서는 흔하면서도 잘못된 가정입니다. 실행 중인 빌드를 확인하세요. 업데이트 정책이 아니라요.

전제 조건: realm에서 비밀번호 찾기가 활성화되어 있어야 하며 그리고 연결된 자격 증명 재설정 흐름은 기본 제공되는 reset-credential-email 인증자를 사용해야 합니다.


3. 실습 환경

이 저장소는 취약한 Keycloak과 패치된 Keycloak을 모두 포함하며, 동일한 realm을 가져오고, 재설정 메일을 캡처하기 위해 Mailpit도 포함합니다 — 계정이 탈취되는 동안 메일이 도착하고 읽히지 않은 채로 남아 있는 것을 지켜볼 수 있습니다.```bash cd lab docker compose up -d

root@kitploit:~
| 서비스 | URL | 버전 |
|---|---|---|
| `kc-vuln` | http://localhost:8080 | 26.7.1 — **취약** |
| `kc-patched` | http://localhost:8100 | 26.7.2 — **패치된 대조군** |
| `kc-mailpit` | http://localhost:8025 | 피해자의 사서함 |

Realm `poc`, 퍼블릭 클라이언트 `poc-app`, 사용자 `victim` / `OriginalPassw0rd!`, Keycloak
관리자 `admin` / `admin`.

`KC_VULN_VERSION` / `KC_PATCHED_VERSION`으로 서로 다른 빌드를 고정하세요:```bash
KC_VULN_VERSION=26.5.7 docker compose up -d keycloak-vuln

실행 사이에 lab/reset-victim.sh가 피해자의 비밀번호를 복원합니다 (KC=http://localhost:8100 lab/reset-victim.sh는 패치된 인스턴스를 대상으로 합니다).

lab/legit_reset.py는 Mailpit에서 action-token 링크를 꺼내 클릭하여 진짜 재설정을 수행합니다. 이것은 §9의 탐지 작업을 위한 대조 샘플입니다. 같은 realm에 대해 이 스크립트와 익스플로잇을 실행한 다음, 트레이스를 diff하세요.

정리: docker compose down -v.


4. 사용법

Python 3.9+, 표준 라이브러리만 사용 — 의존성이 없어서 어떤 점프 박스에든 바로 투입할 수 있습니다.``` --base Keycloak base URL (e.g. https://sso.example.com) --realm realm name --client-id any enabled public client with the standard flow --redirect-uri a URI permitted by that client (default http://localhost:9999/callback) --insecure skip TLS verification --verbose log every HTTP request --dump FILE write the response body of a failing step to FILE

root@kitploit:~
`--client-id`는 표준 흐름을 지원하는 활성화된 공용 클라이언트라면 무엇이든 될 수 있습니다. 내장된
`account` 클라이언트는 모든 realm에 존재하며 신뢰할 수 있는 선택이지만 리다이렉트
URI를 제한하므로 `--redirect-uri`는 **반드시**
`<base>/realms/<realm>/account/`여야 합니다 — 기본값은 거부되며 1단계가 실패합니다.

### 4a. 안전한 탐지 (`--safe-check`) — 여기서 시작

**유효한 사용자 이름이 필요 없고** **부작용이 없습니다**. 이는 대상을 방해하면
안 되는 경우에 사용해야 하는 프로브입니다.```bash
python3 cve_2026_18963_poc.py \
  --base https://sso.example.com --realm corp \
  --client-id account \
  --redirect-uri https://sso.example.com/realms/corp/account/ \
  --safe-check

사용자가 필요 없고 메일을 보내지 않는 이유. ResetCredentialEmail.authenticate() 는 알 수 없는 사용자에 대해서도 분기합니다:```java if (user == null) { context.forkWithSuccessMessage(EMAIL_SENT); return; }

root@kitploit:~
`processResult()` `case FORK:` 따라서 `CURRENT_AUTHENTICATION_EXECUTION`을 이메일 실행에 대기시킨다 **아무도 발견되지 않았는데도** — 그리고 메일을 보낼 대상이 없으므로 메일도 전송되지 않는다. 프로브는 판별기(discriminator)에서 멈추고 게이트를 POST하지 않으므로 `action()`은 결코 실행되지 않는다: 대상에 NPE가 발생하지 않고, `emailVerified` 쓰기도 없고, 메일도 없고, 계정도 건드리지 않는다.

**오직 긍정 신호에 대해서만 단언한다.** VULNERABLE ⟺ 5단계가 `login-actions/reset-credentials` 안에 남아 있는 폼을 반환하는데, 그 `execution`이 사용자 선택 실행(choose-user execution)과 다르다. 그것이 바로 버그다: 대기 중인 이메일 게이트에 제공된 오래된 `AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED` 노트. 두 부분 모두 중요하다 — 경로는 여전히 리셋 흐름에 있음을 증명하고, 서로 다른 execution id는 그것이 재렌더링이 아니라 이메일 게이트임을 증명한다.

그 외의 것은 **조용히** 패치된 것으로 간주되지 않는다. PATCHED에는 그 자체의 증거가 필요하다(`login-actions/authenticate`로 분기됨 *그리고* 비밀번호 입력이 존재함). 나머지는 모두 INCONCLUSIVE이며 사람의 판단이 필요하다. 이전 설계는 "게이트 폼이 아님"을 패치된 것으로 처리했는데, 이는 모든 사용자 정의 테마, 오류 페이지, WAF 차단, 인터스티셜을 조용히 거짓 건강 진단서로 만들어 버린다.

### 4b. 비파괴적 증명(`--check`)

전체 체인을 구동하지만 Update Password 폼에서 멈춘다. 액션 토큰 없이 그 폼에 도달하는 것은 결정적이다.```bash
python3 cve_2026_18963_poc.py \
  --base https://sso.example.com --realm corp \
  --client-id account \
  --redirect-uri https://sso.example.com/realms/corp/account/ \
  --victim [email protected] --check

두 가지 부작용은 피할 수 없습니다, 그 이유는 비밀번호 양식의 *업스트림(upstream)*에서 발생하기 때문입니다 — 이를 작업 범위(engagement scope)에 명시하십시오:

  • 실제 피해자에게 비밀번호 재설정 이메일이 전달되며 (4단계는 진짜 재설정 요청입니다), 그리고
  • 취약한 action()이 계정에 **emailVerified = true**를 설정합니다.

자격 증명(credential)은 수정되지 않습니다. 전용 테스트 계정을 사용하는 것이 좋습니다.

4c. 전체 탈취

실습 환경 또는 명시적으로 승인된 데모에서만 수행하십시오.```bash python3 cve_2026_18963_poc.py
--base http://localhost:8080 --realm poc --client-id poc-app
--victim victim --new-password 'PoCPassw0rd!1'

root@kitploit:~
종료 코드 `0`은 취약 · `2`는 악용 불가 · `1`은 비밀번호가 변경되었지만 확인 grant는 실패 (`--verify-client-id`를 Direct Access Grants가 있는 클라이언트로 지정).

플로우를 완료하면 **피해자의 OIDC 인증 코드**도 반환되므로 즉시 계정을 탈취할 수 있습니다. 새 비밀번호로 다시 로그인할 필요가 없습니다.

### 4d. 사용자 이름 열거 (`--enum`)

동일한 결함은 사용자 이름 오라클이며, Keycloak이 일반적으로 허용하는 것보다 더 강력합니다. `ResetCredentialEmail.authenticate()`는 실제 사용자와 알 수 없는 사용자에게 동일한 *"곧 이메일을 받으실 것입니다"* 메시지를 의도적으로 반환하므로 재설정 폼 자체로는 열거할 수 없습니다 — 이 방어는 4단계에서 여전히 유효합니다. 그러나 **6단계**에서 `action()`이 사용자를 무조건 역참조하므로(`context.getUser().setEmailVerified(true)`) 이 방어는 깨집니다.

| 식별자 | 6단계 | 판정 |
|---|---|---|
| 실제 사용자 | `200`, 비밀번호 변경 폼에 도달 | 유효 |
| 알 수 없는 사용자 | `400` (NPE 오류 페이지) | 무효 |```bash
python3 cve_2026_18963_poc.py \
  --base http://localhost:8080 --realm poc --client-id poc-app \
  --enum candidates.example.txt

Never changes a password. Exit 0 if any identifier resolved, 2 otherwise.

Cost per probe — read before running. Reaching the oracle requires completing step 4, so every probe against a real account sends that person a genuine password-reset e-mail and sets emailVerified = true on their record. It is not a quiet check: it is visible to the account holder and it mutates their data. A 5,000-name wordlist is 5,000 e-mails to real people and 5,000 mutated accounts.

Use it to demonstrate that the oracle exists on a handful of identifiers for the report — not to harvest a directory. The guards are deliberately conservative:

  • --enum-max N refuses lists longer than N (default 25)
  • --enum-delay SEC pauses between probes (default 2.0)

Raising either should be a conscious decision recorded in the engagement notes.

Reporting angle: this defeats an anti-enumeration control Keycloak implemented on purpose. Worth writing up as its own finding alongside the takeover, and it kills "our usernames aren't guessable" as a mitigating factor.


5. Custom login themes

Any serious deployment ships a custom login theme, and custom themes rename or drop the stock element ids (kc-form-login, kc-reset-password-form, kc-select-credential-form, kc-passwd-update-form). A tool that keys on those ids reports a false negative on exactly the deployments that matter most — this one did, before it was rewritten. Themes seen in the wild use ids like id="login-form" and ship a forgot password anchor with an empty href.

This PoC therefore keys on nothing that a theme controls:

  • Form action URLs only. Every decision is made from the action= of the forms on the page — login-actions/reset-credentials, login-actions/authenticate, login-actions/required-action — and from the execution query parameter inside them. Those paths are produced by Keycloak's own LoginActionsService, not by the theme.
  • No element ids. grep the source: there is not one kc-* id in it.
  • No message text. Response strings are localised — a German realm answers "Reset Credential nicht erlaubt", and matching on "You should receive an email" breaks on every non-English realm.
  • It never follows a themed "Forgot password" link. The link may be absent, empty, JavaScript-driven, or point somewhere outside Keycloak entirely — none of which says anything about whether the endpoint is reachable. The tool constructs /realms/<realm>/login-actions/reset-credentials?client_id=…&tab_id=… and probes it directly, taking tab_id from whatever form the login page does expose.

If a target still returns INCONCLUSIVE, run with --verbose --dump out.html and read the response — the tool deliberately refuses to guess.


6. Known gap — PKCE

A client that enforces PKCE rejects step 1 with Missing parameter: code_challenge_method. This is reported as INCONCLUSIVE (exit 3), never as a pass. Until PKCE support lands, a realm whose only usable public client mandates PKCE cannot be checked with this tool — try the built-in account client, which normally does not enforce it.


7. Validation performed

Every run below is against the lab in this repo, using the code as published.

Two findings worth flagging beyond the advisory text:

  1. 이메일 주소가 없는 계정도 공격 가능합니다. ResetCredentialEmail.authenticate()는 user.getEmail()이 null이면 forkWithSuccessMessage 경로를 따르며, 그 경로도 case FORK:를 통해 실행을 계속 대기시킵니다. SMTP 전송 실패도 마찬가지입니다 — 메일 서버가 고장났거나 없어도 완화 수단이 되지 않습니다. 이는 계정에 메일 속성이 없는 경우가 잦은 AD/LDAP 연동 렐름과 직접 관련됩니다.
  2. 흐름을 완료하면 공격자가 피해자로 로그인됩니다. 최종 리디렉션에는 유효한 OIDC 인가 코드가 포함되므로, 비밀번호 양식을 제출하는 순간 계정이 손상됩니다.

MFA는 완화 수단이 아닙니다. 기본 reset-credentials 흐름에는 OTP 단계가 없으며, 일단 통과하면 공격자는 피해자가 등록한 인증 요소를 제거할 수 있습니다.


8. Remediation

해결 방법: 업그레이드. 커뮤니티 빌드는 26.7.2, 또는 구독에 해당하는 벤더 백포트 태그를 사용하십시오. 아래의 모든 방법은 임시 방편입니다.

임시 완화 조치, 효과 좋은 순서:

  1. 렐름별로 *비밀번호 찾기(Forgot password)*를 비활성화합니다 (Realm settings → Login). 효과가 확인되었습니다 — 흐름이 HTTP 400을 반환하며 진입할 수 없습니다. master를 포함한 모든 렐름을 확인하십시오.
  2. 바인딩된 reset-credentials 흐름에서 Reset Password 실행을 비활성화합니다. 효과는 있지만 로그인 페이지에 여전히 링크가 표시되므로 UX가 좋지 않습니다. 커스텀 테마가 렐름 스위치를 무시하는 경우 유용합니다.
  3. 재설정 흐름의 이메일 단계 다음에 필수 인증기(OTP/WebAuthn)를 추가합니다. 이는 우회를 차단하지 않습니다 — 해당 요소를 실제로 등록한 계정으로만 전체 탈취를 제한할 뿐입니다.

바인딩된 재설정 흐름이 완전히 커스텀이고 reset-credential-email을 호출하지 않는 렐름은 이 경로로 공격할 수 없습니다.


9. Detection

Keycloak은 "action token skipped" 이벤트를 내보내지 않으므로 탐지는 휴리스틱입니다. 익스플로잇과 함께 lab/legit_reset.py를 실행하여 두 트레이스를 생성하고 비교하십시오.

  • 리버스 프록시 / 인그레스 로그 — 가장 강력한 신호. 정상적인 재설정은 비밀번호 변경 전에 GET /login-actions/action-token?...(피해자가 메일을 클릭)을 보여줍니다. 우회에는 그런 GET이 없습니다. 대신 본문에 tryAnotherWay가 포함된 POST가 login-actions/reset-credentials로 전송되고, 이어서 같은 경로로 빈 본문의 두 번째 POST가 전송된 다음 비밀번호 양식이 나타납니다. 재설정 흐름 내에서 tryAnotherWay POST가 발생하는 것은 기본 UI가 정상 사용 중에는 생성하지 않는 것입니다.
  • 관리자 이벤트: SEND_RESET_PASSWORD 직후 몇 초 내에 동일한 code_id를 공유하는 UPDATE_PASSWORD가 이어집니다 — 랩에서는 1초 미만이었습니다. 메일을 이미 열어둔 사용자도 빠르게 보일 수 있으므로 프록시 로그와 함께 교차 확인하십시오.
  • 대응하는 VERIFY_EMAIL 이벤트 없이 emailVerified가 true로 바뀐 계정은 유용한 보조 지표이며, 공격자가 남기지 않을 수 없는 흔적입니다.

이벤트 로깅이나 보존 기간이 꺼져 있었다면 이벤트가 없다고 해서 아무것도 증명되지 않습니다. 배포 환경이 공격받지 않았다고 결론 내리기 전에 보존 기간을 확인하십시오.


Author

Snizi — github.com/Snizi — [email protected]

MIT License 하에 배포됩니다. 이슈와 PR을 환영합니다 — 특히 PKCE 지원과 추가적인 실제 테마 특이사항.

도구 다운로드
라인취약 버전커뮤니티 수정
레거시 (WildFly 기반 Keycloak, ≤ 17)영향 없음—
Quarkus 17 – 25.x영향 없음—
26.026.0.0 – 26.0.17없음
26.126.1.0 – 26.1.5없음
26.226.2.0 – 26.2.16없음
26.326.3.0 – 26.3.5없음
26.426.4.0 – 26.4.1426.4.15 (벤더 백포트 태그)
26.526.5.0 – 26.5.7없음
26.626.6.0 – 26.6.526.6.6 (벤더 백포트 태그)
26.726.7.0 – 26.7.126.7.2
ExitVerdictMeaning
0VULNERABLE대기 중인 이메일 게이트가 제공됨 — 바로 그 취약점
2PATCHED흐름이 로그인으로 분기되어 그 상태에 머무름 (fix #51844 존재)
2MITIGATED재설정 자격 증명에 도달 불가 — 비밀번호 찾기가 꺼져 있음. 패치가 아님.
3INCONCLUSIVE인식할 수 없는 응답 — 통과로 해석하지 말 것
테스트대상결과
--safe-check26.7.1VULNERABLE, exit 0 — 이메일 게이트 제공됨 (execution ≠ choose-user)
--safe-check26.7.2PATCHED, exit 2 — 로그인으로 분기 후 유지됨
--safe-check, Forgot password off26.7.2MITIGATED, exit 2 — HTTP 400, 흐름 도달 불가
전체 탈취26.7.1exit 0 — 비밀번호 설정됨, OIDC 코드 발급, password grant 확인됨
전체 탈취26.7.2exit 2 — 5단계에서 차단, 계정 변경 없음
--check26.7.1UPDATE_PASSWORD에 도달; 이후 비밀번호 변경되지 않음 확인됨
--enum26.7.1victim 및 [email protected] VALID, does-not-exist INVALID
탈취 후 자격 증명 상태26.7.1새 비밀번호 → 200, 이전 비밀번호 → 400
차단된 실행 후 자격 증명 상태26.7.2이전 비밀번호 → 200, 공격자 비밀번호 → 400
피해자 사서함Mailpit재설정 메일이 전달되고 읽지 않은 상태; action-token 링크는 절대 가져오지 않음