PoC — ошибка проверки origin, позволяющая осуществить эксфильтрацию SSO-cookie PRT Entra ID в linux-entra-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 (Средний) |
| Слабость | CWE-346, CWE-20 |
Platform.SSO_URL в linux-entra-sso не имеет завершающего разделителя пути, поэтому защитная проверка onBeforeSendHeaders в Firefox/Thunderbird (e.url.startsWith(Platform.SSO_URL)) обходится любым зарегистрированным злоумышленником доменом вида login.microsoftonline.com.<attacker-domain>, что заставляет расширение получить и внедрить действующий SSO-cookie Primary Refresh Token (PRT) Entra ID в запросы, отправляемые на собственный хост злоумышленника.
siemens/linux-entra-sso — браузерный плагин для Linux для SSO в Microsoft Entra ID через локальный Microsoft Identity Broker (Intune). Сборка для Firefox практически эксплуатируема; Thunderbird использует идентичный уязвимый код, но в настоящее время недостижим через этот вектор (см. Подробности); Chrome/Chromium не затронут (см. Подробности).
v1.10.0 (коммит 676854c, текущая main на 2026-08-03). Подтверждено наличие в текущем релизе, т.е. не исправлено GHSA-52rj-42vh-2rxc / CVE-2026-42177 (то исправление затронуло только адаптер Chrome declarativeNetRequest).
5.3 (Средний) — 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 — украденный SSO-cookie PRT напрямую воспроизводим против login.microsoftonline.com для выпуска access/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 не имеет завершающего разделителя, любой URL, чей хост — login.microsoftonline.com.<anything> — синтаксически допустимое 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 расширяет PlatformFirefox без переопределения update_request_handlers() или #onBeforeSendHeaders, поэтому 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 + "/*", полученный из активной вкладки) — достаточно для регистрации слушателя для этого конкретного хоста. Разрешение https://*/* для всех хостов не требуется, в отличие от предварительного условия эксплуатации, описанного в GHSA-52rj-42vh-2rxc; здесь достаточно одного целевого разрешения на собственный похожий домен злоумышленника, что является более низким порогом, чем 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