
Keycloak 26.8.1のSAMLブローカー脆弱性に対するループバックPoC:パーセントエンコードされた重複したSAMLResponse名がリダイレクト署名検証をバイパスし、別のユーザーとしてログインできるようにします。
Keycloak アカウントを持たない人物が、他人として企業の SAML ログインを完了できてしまう。上流の ID プロバイダーで一度サインインし、その署名済みリダイレクトを保持したまま、パーセントエンコードされた名前の下に 2 つ目の SAML ドキュメントを前置する。Keycloak は正規の署名を検証したうえで、偽造ドキュメント内の名前としてログインさせてしまう。これには HTTP-Redirect レスポンスに署名する SAML ブローカーが必要で、署名検証が有効かつアサーション署名が無効になっている構成、すなわちメタデータインポートが設定する構成が該当する。これは issue #53354 のセミコロン分割とは異なる。
クラス: OAuth の取りこぼし 到達性: リモート(Keycloak に対しては未認証。リダイレクト署名を行う SAML ブローカーが必要)
Keycloak 26.8.1 (24ed1dad) - Red Hat
| ID | CVE 未採番 |
| クラス | OAuth の取りこぼし |
| 到達性 | リモート(Keycloak アカウント不要。IdP からの正規の HTTP-Redirect 署名が 1 つ必要) |
| CWE | CWE-287 / CWE-347 / CWE-116 |
| CVSS | High: 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| 製品 | Keycloak |
| 影響範囲 | 26.8.1 (24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b); Quarkus 3.40.1; イメージ quay.io/keycloak/keycloak:26.8.1 |
| 認証 | Keycloak 側は不要。validateSignature=true かつ wantAssertionsSigned が未設定または false の SAML アイデンティティプロバイダーブローカーが必要。 |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1:18084 のみ |
被害者の NameID(通常はメールアドレス)と、その IdP からの正規の署名済み HTTP-Redirect が 1 つ必要。Keycloak のパスワードは不要。メールボックスも不要。POST 専用の IdP ではその署名が渡されない。メタデータインポートが POST を優先する場合でも、GET の ACS はマウントされたままになる。
| 権限 | メソッド / シンク | 偵察 / 完全性 / 可用性 | ラボ実証または推測 |
|---|---|---|---|
| 正規の署名が検証されるまま偽造 SAML ドキュメントをバインド | 署名済み SAMLResponse の前に SAML%52esponse=<forged> を付けて GET でブローカー ACS を呼び出す | 完全性(認証) | ラボ実証済み。対照用メール [email protected]。攻撃用メール [email protected] |
| 初回ブローカーログイン経由で被害者ユーザーを作成 | 偽造 NameID に対する callback.authenticated | 完全性(アカウント作成) | ラボ実証済み。分割クエリ前は被害者数 0、後は 1 |
| 同じ IdP を使う管理者を含む、既存のブローカーユーザーとしてのセッション | 同じ ACS、そのユーザーの NameID | 完全性(アカウント乗っ取り) | 推測。ラボでは初回ログイン時に被害者を作成した |
| RelayState と認証セッションを保持 | RelayState は署名済みの値のまま | 完全性 | ソース。署名は RelayState を対象とする |
| ネガティブコントロール | 余分なパラメーターのない正規の署名済みリダイレクト | 該当なし | ラボ実証済み。対照は攻撃者メールとして完了する |
この取りこぼしで塞がれたままのもの
| 軸 | メソッド / シンク | 塞がれている理由 |
|---|---|---|
| 完全性 | セミコロン分割 SAMLResponse=...;SAMLResponse=... | issue #53354。quarkus.http.use-semicolon-as-query-param-delimiter=false |
| 完全性 | wantAssertionsSigned=true 時の未署名アサーション | その場合 handleLoginResponse はアサーション署名を要求する |
| 完全性 | SAML ブローカーなし、またはリダイレクト署名のない POST 専用 IdP | 攻撃者は GET 上で SigAlg + Signature を取得できない |
| 完全性 | ホストの RCE、未認証の Admin REST | クラス外 |
| 完全性 | デフォルトの VERIFY_PROFILE 名前フォーム | 反証ではなく完了ステップとしてラボ実証済み。フォームにはすでにブローカー経由のメールが入っている |
Keycloak 26.8.1 を固定しました。RESTEasy Reactive は Vert.x から @QueryParam("SAMLResponse") を取得し、Vert.x はクエリ名をパーセントデコードするため、SAML%52esponse は SAMLResponse に畳み込まれ、最初の値が優先されます。verifyRedirectSignature は生のクエリに対して getQueryParameters(false) から署名対象文字列を再構築しますが、そこではその名前は別のキーのままです。
すでに記録済みの誤った道筋:#53354 をこの残存物(セミコロンではなくパーセントエンコードされた名前)として扱うこと。ラボクライアントで ACS 302 を追跡し AUTH_SESSION_ID を破棄すること。VERIFY_PROFILE で空の firstName/lastName をミスと読むこと(メールはすでにブローカリングされた NameID です)。リバースシェル。茶番です。
cryptography を備えた Python 3.11+。Docker が必要です。
python3 keycloak-saml-redirect-param-split-Abraxas-Labs.py
または:
cd lab
./run.sh
対象は http://127.0.0.1:18084 のみです。イメージは quay.io/keycloak/keycloak:26.8.1 です。ランチャーは lab/ に chdir して run.sh を exec します。ラボの IdP はインプロセスで動作します。実際の ID プロバイダーとは一切通信しません。
IOC image=quay.io/keycloak/keycloak:26.8.1 label-version=26.8.1
IOC serverinfo-version=26.8.1
IOC validate-signature=true want-assertions-signed=absent
IOC review-profile=DISABLED
IOC [email protected]
IOC victim-before=0
IOC [email protected]
IOC victim-after=1
SUCCESS KC-H04-SAML-REDIRECT kcs04b8n1
验证端点所解析的同一查询文档。在重建签名之前对参数名进行百分号解码,或在解码后拒绝包含多个 SAMLResponse 的查询。为重定向代理(包括元数据导入路径)开启 wantAssertionsSigned。
24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b)SAMLEndpoint.redirectBinding(SAMLEndpoint.java:182-183、:779-813、:896-914)SamlProtocolUtils.verifyRedirectSignature(SamlProtocolUtils.java:216-249)KeycloakUriInfo.getQueryParameters(KeycloakUriInfo.java:147-163)GNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境。不提供任何担保。
Abraxas Labs [email protected]
请将 CVE 归功于 Abraxas Labs。