## https://sploitus.com/exploit?id=A499A82D-32A2-55AC-ACDD-0B68A5A31587
# Summary
> **CVE status:** requested, pending assignment. This finding is published as
> [GHSA-g9vc-5j77-f2cm](https://github.com/siemens/linux-entra-sso/security/advisories/GHSA-g9vc-5j77-f2cm). On CVE assignment this repository is renamed
> `CVE-YYYY-NNNNN-linux-entra-sso-PoC` and this banner is replaced with the CVE link.
| | |
|---|---|
| Researcher | Dostxodjayev Abdullox ([@squeeze440](https://github.com/squeeze440)) |
| Advisory | [GHSA-g9vc-5j77-f2cm](https://github.com/siemens/linux-entra-sso/security/advisories/GHSA-g9vc-5j77-f2cm) |
| CVSS 3.1 | 5.3 (Medium) |
| Weakness | CWE-346, CWE-20 |
---
# Summary
`Platform.SSO_URL` in `linux-entra-sso` lacks a trailing path separator, so the Firefox/Thunderbird `onBeforeSendHeaders` guard (`e.url.startsWith(Platform.SSO_URL)`) is bypassed by any attacker-registered domain of the form `login.microsoftonline.com.`, causing the extension to acquire and inject a live Entra ID Primary Refresh Token (PRT) SSO cookie into requests sent to the attacker's own host.
# Product
`siemens/linux-entra-sso` β Browser plugin for Linux to SSO on Microsoft Entra ID via the local Microsoft Identity Broker (Intune). Firefox build is practically exploitable; Thunderbird shares the identical vulnerable code path but is not currently reachable via this vector (see Details); Chrome/Chromium is not affected (see Details).
# Tested Version
`v1.10.0` (commit `676854c`, current `main` as of 2026-08-03). Confirmed present in the current release, i.e. **not fixed by GHSA-52rj-42vh-2rxc / CVE-2026-42177** (that fix touched only the Chrome `declarativeNetRequest` adapter).
# Estimated CVSS v3.1
**5.3 (Medium)** β `CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N`
- `AC:H` β exploitation requires the extension to hold a granted host permission that covers the attacker's crafted hostname. In practice the attacker must social-engineer the victim into clicking the extension's "Background SSO (enable)" link while on the attacker's page β a condition outside the attacker's direct control, matching the complexity rationale used for the sibling GHSA-52rj-42vh-2rxc.
- `UI:R` β the permission grant is an explicit user click.
- `C:H` β a stolen PRT SSO cookie is directly replayable against `login.microsoftonline.com` to mint access/ID tokens for any app the victim has consented to (full SSO session hijack).
- `I:N`/`A:N` β the bug discloses a credential but does not itself modify data or availability.
# Details
**Root cause β `src/platform.js:9`:**
```js
static SSO_URL = "https://login.microsoftonline.com";
```
No trailing `/`.
**Vulnerable check β `platform/firefox/js/platform-firefox.js:55-59`:**
```js
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` performs a raw character-prefix comparison, not a hostname/origin comparison. Because `Platform.SSO_URL` has no trailing separator, any URL whose host is `login.microsoftonline.com.` β a syntactically valid DNS name the attacker fully controls β also satisfies the check, e.g.:
```
"https://login.microsoftonline.com.attacker.example/phish".startsWith("https://login.microsoftonline.com")
=> true
```
This is the same defensive check that GHSA-52rj-42vh-2rxc's own description called "the canonical defensive check ... absent from the Chrome adapter" β it exists here, but is itself bypassable, so the advisory's fix (which only hardened Chrome's `declarativeNetRequest` rule) left this path open. `platform/thunderbird/js/platform-thunderbird.js` extends `PlatformFirefox` without overriding `update_request_handlers()` or `#onBeforeSendHeaders`, so Thunderbird inherits the same flaw.
**Trigger path (`update_request_handlers`, `platform/firefox/js/platform-firefox.js:36-53`):** the listener is registered with `urls: this.well_known_app_filters`, which is populated from `chrome.permissions.getAll().origins` (`src/platform.js:78-79`, `update_host_permissions()`). Any host permission the user has granted β including a single-site grant via the popup's "Background SSO (enable)" link (`popup/menu.js:171-260`, using `current_filter = "https://" + tab_hostname + "/*"` derived from the active tab) β is enough to register the listener for that exact host. No catch-all `https://*/*` permission is required, unlike the exploitation prerequisite described in GHSA-52rj-42vh-2rxc; a single targeted grant on the attacker's own look-alike domain is sufficient here, which is a *lower* bar than the prior advisory's PoC.
**Sink β same file, lines 60-70:**
```js
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` (the full attacker URL) is passed straight through as `ssoUrl` to the native broker. `linux-entra-sso.py:27-30` documents that the Microsoft broker backend does not validate `ssoUrl` server-side ("the correct value is not checked ... by the authorization backend"), so a cookie is issued regardless, and it is injected into headers on the request actually going to the attacker's own host β a direct exfiltration, not merely a header set on a Microsoft-bound request.
**Chrome is not affected:** `platform/chrome/js/platform-chrome.js:96-99` (post GHSA-52rj-42vh-2rxc fix, commit `8183759`) anchors with `"|" + Platform.SSO_URL + "/"` (start-of-string anchor + trailing slash) and adds an explicit `requestDomains: [URL.parse(Platform.SSO_URL).hostname]` allow-list β both of which correctly reject a `login.microsoftonline.com.attacker.example` host.
**Thunderbird β same vulnerable code, not currently reachable via this vector:** `platform/thunderbird/js/platform-thunderbird.js` extends `PlatformFirefox` unchanged, so the identical `startsWith` bypass exists in the Thunderbird build's binary. However `platform/thunderbird/manifest.json` declares only a fixed `"host_permissions": ["https://login.microsoftonline.com/*"]` with **no `optional_host_permissions`** key. WebExtensions only allows `chrome.permissions.request()` to grant origins that are pre-declared in `optional_permissions`/`optional_host_permissions`; since Thunderbird declares none, the popup's "Background SSO (enable)" flow (`popup/menu.js:259-261`) cannot obtain permission for an attacker-chosen domain on Thunderbird today. The bug is latent there, not currently attacker-reachable, and should still be fixed for defense-in-depth / in case `optional_host_permissions` is ever added to that manifest.
# Proof of Concept
Dynamically confirmed the vulnerable predicate against the real shipped constant (imported directly from the target's `src/platform.js`, not hand-copied). Script kept at `~/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.
```
The remainder of the chain (broker issuing a cookie for an unvalidated `ssoUrl`, and the WebExtensions `webRequest` listener firing for a granted single-host permission) is **statically traced, not dynamically confirmed** β reproducing it end-to-end requires a live enrolled Linux host running `microsoft-identity-broker` against a real Entra tenant, which is outside this environment. The traced steps:
1. Attacker registers `login.microsoftonline.com.attacker.example` and serves a page there.
2. Victim (linux-entra-sso installed, active Entra account) visits the page and is nudged to open the extension popup and click "Background SSO (enable)" for the current site (`popup/menu.js:259-261` β `request_host_permission(["https://login.microsoftonline.com.attacker.example/*"])`).
3. Grant fires `chrome.permissions.onAdded` β `on_permissions_changed()` (`src/background.js:30-34`) β `update_host_permissions()` refreshes `well_known_app_filters` β `update_request_handlers()` re-registers the Firefox `webRequest.onBeforeSendHeaders` listener including the new host pattern.
4. A `main_frame`/`sub_frame` request to `https://login.microsoftonline.com.attacker.example/...` fires the listener; the `startsWith` check (shown bypassed above) passes.
5. `broker.acquirePrtSsoCookie(account, e.url)` returns a real PRT cookie (per `linux-entra-sso.py:27-30`, `ssoUrl` is not validated by the broker backend).
6. The cookie is pushed into the request's headers β sent to the attacker's own server.
# Impact
An attacker who gets a linux-entra-sso **Firefox** user to grant "Background SSO" for one attacker-controlled look-alike domain receives the victim's Entra ID PRT SSO cookie directly on their own server. That cookie is replayable against `login.microsoftonline.com` to obtain access/ID tokens for any application the victim has consented to β full SSO session hijack. Unlike the prior advisory, this does not require the broad `https://*/*` catch-all permission, only a single-site grant on the attacker's own domain. Thunderbird ships the same flawed check but its manifest currently has no way to runtime-grant an arbitrary-domain permission, so it is not exploitable via this vector today (see Details).
# Weaknesses
- CWE-20: Improper Input Validation
- CWE-346: Origin Validation Error
# Remediation
Anchor the comparison to a proper origin/hostname check instead of a raw string prefix, mirroring the fix already applied to the Chrome adapter:
```js
if (!e.url.startsWith(Platform.SSO_URL + "/")) {
return { requestHeaders: e.requestHeaders };
}
```
or, more robustly, compare `new URL(e.url).origin` against `new URL(Platform.SSO_URL).origin` (rejects userinfo/port tricks too). Applying the fix at the `Platform.SSO_URL` constant level (adding the trailing `/` once in `src/platform.js`) would close this for both the Firefox check and any other consumer of the constant, and a regression test asserting `startsWith` rejects `https://login.microsoftonline.com.attacker.example/...` would close the coverage gap the same way the Chrome fix's suggested `testMatchOutcome` regression test did for GHSA-52rj-42vh-2rxc.
# Credit
Dostxodjayev Abdullox (GitHub: squeeze440)
# Reporting Channel
No `SECURITY.md` is present in the repository and the GitHub API reports no repository-level security policy, but GitHub Private Vulnerability Reporting is enabled on `siemens/linux-entra-sso` (verified via the repo's Security tab "Report a vulnerability" button). Report through that channel: `https://github.com/siemens/linux-entra-sso/security/advisories/new`.