
PoC — linux-entra-sso における Entra ID PRT SSO クッキー窃取を可能にするオリジン検証エラー(GHSA-g9vc-5j77-f2cm、CVE-2026-87005、CVSS 5.3)。
CVE ステータス: 申請済み、割り当て待ち。この脆弱性は GHSA-g9vc-5j77-f2cm として公開されています。CVE が割り当てられ次第、このリポジトリは
CVE-YYYY-NNNNN-linux-entra-sso-PoCに改名され、このバナーは CVE リンクに置き換えられます。
| 研究者 | Dostxodjayev Abdullox (@squeeze440) |
| アドバイザリ | GHSA-g9vc-5j77-f2cm |
| CVSS 3.1 | 5.3 (Medium) |
| 脆弱性 | CWE-346, CWE-20 |
linux-entra-sso の Platform.SSO_URL には末尾のパス区切り文字が欠けているため、Firefox/Thunderbird の onBeforeSendHeaders ガード (e.url.startsWith(Platform.SSO_URL)) は、login.microsoftonline.com.<attacker-domain> 形式の攻撃者登録ドメインによってバイパスされます。これにより、拡張機能は有効な Entra ID Primary Refresh Token (PRT) SSO Cookie を取得し、攻撃者自身のホストへ送信されるリクエストに注入してしまいます。
siemens/linux-entra-sso — ローカルの Microsoft Identity Broker (Intune) を介して Microsoft Entra ID で SSO を行う Linux 用ブラウザプラグイン。Firefox ビルドは実質的に悪用可能です。Thunderbird は同一の脆弱なコードパスを共有していますが、現在このベクターでは到達可能ではありません (詳細を参照)。Chrome/Chromium は影響を受けません (詳細を参照)。
v1.10.0 (コミット 676854c、2026-08-03 時点の最新 main)。現在のリリースに存在することを確認済み、すなわち GHSA-52rj-42vh-2rxc / CVE-2026-42177 では修正されていません (その修正は Chrome の declarativeNetRequest アダプタのみに触れていました)。
5.3 (Medium) — CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
AC:H — 悪用には、拡張機能が攻撃者の細工したホスト名をカバーする付与済みホスト権限を保持している必要があります。実際には、攻撃者は被害者をソーシャルエンジニアリングして、攻撃者のページ上で拡張機能の「Background SSO (enable)」リンクをクリックさせる必要があり、これは攻撃者の直接制御外の条件であり、姉妹アドバイザリ GHSA-52rj-42vh-2rxc で用いられた複雑度の根拠と一致します。UI:R — 権限付与は明示的なユーザークリックです。C:H — 盗まれた PRT SSO Cookie は login.microsoftonline.com に対して直接リプレイ可能で、被害者が同意した任意のアプリのアクセス/ID トークンを発行できます (完全な SSO セッションハイジャック)。I:N/A:N — このバグは資格情報を開示しますが、それ自体はデータや可用性を変更しません。根本原因 — src/platform.js:9:
static SSO_URL = "https://login.microsoftonline.com";
末尾の / がありません。
脆弱なチェック — platform/firefox/js/platform-firefox.js:55-59:
async #onBeforeSendHeaders(e) {
// filter out requests that are not part of the OAuth2.0 flow
if (!e.url.startsWith(Platform.SSO_URL)) {
return { requestHeaders: e.requestHeaders };
}
...
String.prototype.startsWith はホスト名/オリジンの比較ではなく、生の文字列プレフィックス比較を行います。Platform.SSO_URL に末尾の区切り文字がないため、ホストが login.microsoftonline.com.<anything> である任意の URL — 攻撃者が完全に制御する構文的に有効な DNS 名 — もこのチェックを満たします。例:
"https://login.microsoftonline.com.attacker.example/phish".startsWith("https://login.microsoftonline.com")
=> true
これは GHSA-52rj-42vh-2rxc の説明自体が「Chrome アダプタには欠けている正規の防御チェック」と呼んだものと同じ防御チェックです — ここには存在しますが、それ自体がバイパス可能であるため、アドバイザリの修正 (Chrome の declarativeNetRequest ルールを強化しただけ) はこのパスを開いたままにしました。platform/thunderbird/js/platform-thunderbird.js は update_request_handlers() や #onBeforeSendHeaders をオーバーライドせずに PlatformFirefox を継承しているため、Thunderbird も同じ欠陥を継承しています。
トリガーパス (update_request_handlers, platform/firefox/js/platform-firefox.js:36-53): リスナーは urls: this.well_known_app_filters で登録され、これは chrome.permissions.getAll().origins から設定されます (src/platform.js:78-79, update_host_permissions())。ユーザーが付与した任意のホスト権限 — ポップアップの「Background SSO (enable)」リンク (popup/menu.js:171-260、アクティブタブから導出された current_filter = "https://" + tab_hostname + "/*" を使用) による単一サイト付与を含む — があれば、その正確なホストに対してリスナーを登録するのに十分です。GHSA-52rj-42vh-2rxc で説明された悪用前提条件とは異なり、包括的な https://*/* 権限は不要です。ここでは攻撃者自身の類似ドメインに対する単一の標的付与で十分であり、これは以前のアドバイザリの PoC よりも低いハードルです。
シンク — 同一ファイル、60-70 行目:
let prt = await this.#broker.acquirePrtSsoCookie(this.account, e.url);
ssoLog("inject PRT SSO into request headers");
e.requestHeaders.push({ name: prt.cookieName, value: prt.cookieContent });
e.url (完全な攻撃者 URL) はそのまま ssoUrl としてネイティブブローカーに渡されます。linux-entra-sso.py:27-30 は、Microsoft ブローカーバックエンドが ssoUrl をサーバー側で検証しないことを記述しています (「正しい値は認可バックエンドによってチェックされない」)。そのため Cookie は無条件に発行され、実際に攻撃者自身のホストへ送信されるリクエストのヘッダーに注入されます — Microsoft 宛リクエストに設定されるヘッダーではなく、直接的な持ち出しです。
Chrome は影響を受けません: platform/chrome/js/platform-chrome.js:96-99 (GHSA-52rj-42vh-2rxc 修正後、コミット 8183759) は "|" + Platform.SSO_URL + "/" (文字列先頭アンカー + 末尾スラッシュ) でアンカーし、さらに明示的な requestDomains: [URL.parse(Platform.SSO_URL).hostname] 許可リストを追加しています — どちらも login.microsoftonline.com.attacker.example ホストを正しく拒否します。
Thunderbird — 同じ脆弱なコード、現在このベクターでは到達不能: platform/thunderbird/js/platform-thunderbird.js は PlatformFirefox を変更なしで継承しているため、同一の startsWith バイパスが Thunderbird ビルドのバイナリに存在します。しかし platform/thunderbird/manifest.json は固定の "host_permissions": ["https://login.microsoftonline.com/*"] のみを宣言し、optional_host_permissions キーはありません。WebExtensions は chrome.permissions.request() が optional_permissions/optional_host_permissions で事前宣言されたオリジンのみを付与することを許可します。Thunderbird は何も宣言していないため、ポップアップの「Background SSO (enable)」フロー (popup/menu.js:259-261) は現在 Thunderbird 上で攻撃者が選択したドメインの権限を取得できません。このバグはそこでは潜在しており、現在攻撃者が到達可能ではありませんが、多層防御のため、またそのマニフェストに optional_host_permissions が追加された場合に備えて、修正すべきです。
実際に出荷されている定数 (手でコピーしたものではなく、ターゲットの src/platform.js から直接インポート) に対して脆弱な述語を動的に確認しました。スクリプトは ~/engagements/linux-entra-sso/evidence/prt_bypass_poc.mjs に保管されています。
$ node prt_bypass_poc.mjs
Real Platform.SSO_URL from src/platform.js: "https://login.microsoftonline.com"
Legit MS URL passes filter (expected true): true
Attacker domain-suffix URL: https://login.microsoftonline.com.attacker.example/phish
Attacker URL passes filter (SHOULD be false, is): true
[CONFIRMED] Platform.SSO_URL lacks a trailing '/', so startsWith()
treats 'login.microsoftonline.com.attacker.example' as an in-scope
SSO endpoint. onBeforeSendHeaders would call:
broker.acquirePrtSsoCookie(this.account, "https://login.microsoftonline.com.attacker.example/phish")
and inject the returned PRT cookie into the request headers sent to
the attacker-controlled host.
チェーンの残りの部分 (ブローカーが検証されていない ssoUrl に対して Cookie を発行すること、および付与された単一ホスト権限に対して WebExtensions の webRequest リスナーが発火すること) は静的にトレースされたものであり、動的に確認されたものではありません — エンドツーエンドで再現するには、実際の Entra テナントに対して microsoft-identity-broker を実行している登録済みの Linux ホストが必要であり、これはこの環境の範囲外です。トレースされた手順:
login.microsoftonline.com.attacker.example を登録し、そこにページを配信します。popup/menu.js:259-261 → request_host_permission(["https://login.microsoftonline.com.attacker.example/*"]))。chrome.permissions.onAdded を発火 → on_permissions_changed() (src/background.js:30-34) → update_host_permissions() が well_known_app_filters を更新 → update_request_handlers() が新しいホストパターンを含めて Firefox の webRequest.onBeforeSendHeaders リスナーを再登録します。https://login.microsoftonline.com.attacker.example/... への main_frame/sub_frame リクエストがリスナーを発火し、startsWith チェック (上記でバイパスが示されています) が通過します。broker.acquirePrtSsoCookie(account, e.url) が実際の PRT Cookie を返します (linux-entra-sso.py:27-30 によれば、ssoUrl はブローカーバックエンドによって検証されません)。linux-entra-sso の Firefox ユーザーに、攻撃者が制御する 1 つの類似ドメインに対して「Background SSO」を付与させることに成功した攻撃者は、被害者の Entra ID PRT SSO Cookie を自身のサーバー上で直接受け取ります。その Cookie は login.microsoftonline.com に対してリプレイ可能で、被害者が同意した任意のアプリケーションのアクセス/ID トークンを取得できます — 完全な SSO セッションハイジャックです。以前のアドバイザリとは異なり、これには広範な https://*/* 包括権限は不要で、攻撃者自身のドメインに対する単一サイト付与のみで済みます。Thunderbird は同じ欠陥のあるチェックを出荷していますが、そのマニフェストには現在、任意ドメイン権限を実行時に付与する方法がないため、今日このベクターでは悪用できません (詳細を参照)。
生の文字列プレフィックスではなく、適切なオリジン/ホスト名チェックに比較をアンカーし、Chrome アダプタに既に適用された修正を反映します:
if (!e.url.startsWith(Platform.SSO_URL + "/")) {
return { requestHeaders: e.requestHeaders };
}
または、より堅牢には、new URL(e.url).origin を new URL(Platform.SSO_URL).origin と比較します (userinfo/ポートのトリックも拒否します)。Platform.SSO_URL 定数レベルで修正を適用する (src/platform.js で末尾の / を一度追加する) と、Firefox のチェックとその定数の他のすべての利用者に対してこれを閉じることができ、startsWith が https://login.microsoftonline.com.attacker.example/... を拒否することをアサートする回帰テストは、Chrome 修正の推奨 testMatchOutcome 回帰テストが GHSA-52rj-42vh-2rxc に対して行ったのと同じ方法でカバレッジのギャップを閉じるでしょう。
Dostxodjayev Abdullox (GitHub: squeeze440)
リポジトリに SECURITY.md は存在せず、GitHub API はリポジトリレベルのセキュリティポリシーを報告していませんが、siemens/linux-entra-sso では GitHub Private Vulnerability Reporting が有効になっています (リポジトリの Security タブの「Report a vulnerability」ボタンで確認済み)。そのチャネルを通じて報告してください: https://github.com/siemens/linux-entra-sso/security/advisories/new。