PoC — erreur de validation d'origine permettant l'exfiltration du cookie SSO PRT d'Entra ID dans linux-entra-sso (GHSA-g9vc-5j77-f2cm, CVE-2026-87005, CVSS 5.3).
Statut CVE : demandé, en attente d'attribution. Cette découverte est publiée sous GHSA-g9vc-5j77-f2cm. À l'attribution du CVE, ce dépôt est renommé
CVE-YYYY-NNNNN-linux-entra-sso-PoCet cette bannière est remplacée par le lien CVE.
| Chercheur | Dostxodjayev Abdullox (@squeeze440) |
| Avis | GHSA-g9vc-5j77-f2cm |
| CVSS 3.1 | 5.3 (Moyen) |
| Faiblesse | CWE-346, CWE-20 |
Platform.SSO_URL dans linux-entra-sso ne possède pas de séparateur de chemin final, de sorte que la garde onBeforeSendHeaders de Firefox/Thunderbird (e.url.startsWith(Platform.SSO_URL)) est contournée par tout domaine enregistré par un attaquant de la forme login.microsoftonline.com.<attacker-domain>, ce qui amène l'extension à acquérir et à injecter un cookie SSO Entra ID Primary Refresh Token (PRT) actif dans les requêtes envoyées à l'hôte de l'attaquant lui-même.
siemens/linux-entra-sso — Plugin navigateur pour Linux permettant le SSO sur Microsoft Entra ID via le Microsoft Identity Broker local (Intune). La version Firefox est pratiquement exploitable ; Thunderbird partage le code vulnérable identique mais n'est actuellement pas atteignable via ce vecteur (voir Détails) ; Chrome/Chromium n'est pas affecté (voir Détails).
v1.10.0 (commit 676854c, main actuel au 2026-08-03). Confirmé présent dans la version actuelle, c'est-à-dire non corrigé par GHSA-52rj-42vh-2rxc / CVE-2026-42177 (ce correctif ne touchait que l'adaptateur Chrome declarativeNetRequest).
5.3 (Moyen) — CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
AC:H — l'exploitation nécessite que l'extension détienne une permission d'hôte accordée couvrant le nom d'hôte forgé par l'attaquant. En pratique, l'attaquant doit amener la victime par ingénierie sociale à cliquer sur le lien « Background SSO (enable) » de l'extension alors qu'elle se trouve sur la page de l'attaquant — une condition hors du contrôle direct de l'attaquant, correspondant à la justification de complexité utilisée pour le GHSA-52rj-42vh-2rxc frère.UI:R — l'octroi de permission est un clic explicite de l'utilisateur.C:H — un cookie SSO PRT volé est directement rejouable contre login.microsoftonline.com pour générer des jetons d'accès/ID pour toute application à laquelle la victime a consenti (détournement complet de session SSO).I:N/A:N — le bug divulgue un identifiant mais ne modifie pas lui-même les données ni la disponibilité.Cause racine — src/platform.js:9 :
static SSO_URL = "https://login.microsoftonline.com";
Pas de / final.
Vérification vulnérable — 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 effectue une comparaison brute de préfixe de caractères, et non une comparaison de nom d'hôte/origine. Comme Platform.SSO_URL n'a pas de séparateur final, toute URL dont l'hôte est login.microsoftonline.com.<anything> — un nom DNS syntaxiquement valide que l'attaquant contrôle entièrement — satisfait également la vérification, par exemple :
"https://login.microsoftonline.com.attacker.example/phish".startsWith("https://login.microsoftonline.com")
=> true
Il s'agit de la même vérification défensive que la description de GHSA-52rj-42vh-2rxc elle-même qualifiait de « vérification défensive canonique ... absente de l'adaptateur Chrome » — elle existe ici, mais est elle-même contournable, de sorte que le correctif de l'avis (qui n'a durci que la règle declarativeNetRequest de Chrome) a laissé ce chemin ouvert. platform/thunderbird/js/platform-thunderbird.js étend PlatformFirefox sans redéfinir update_request_handlers() ni #onBeforeSendHeaders, donc Thunderbird hérite de la même faille.
Chemin de déclenchement (update_request_handlers, platform/firefox/js/platform-firefox.js:36-53) : l'écouteur est enregistré avec urls: this.well_known_app_filters, qui est peuplé à partir de chrome.permissions.getAll().origins (src/platform.js:78-79, update_host_permissions()). Toute permission d'hôte accordée par l'utilisateur — y compris un octroi sur un seul site via le lien « Background SSO (enable) » du popup (popup/menu.js:171-260, utilisant current_filter = "https://" + tab_hostname + "/*" dérivé de l'onglet actif) — suffit à enregistrer l'écouteur pour cet hôte exact. Aucune permission générique https://*/* n'est requise, contrairement au prérequis d'exploitation décrit dans GHSA-52rj-42vh-2rxc ; un seul octroi ciblé sur le domaine usurpé de l'attaquant suffit ici, ce qui est un seuil plus bas que le PoC de l'avis précédent.
Puits — même fichier, lignes 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 (l'URL complète de l'attaquant) est transmise directement comme ssoUrl au broker natif. linux-entra-sso.py:27-30 documente que le backend du broker Microsoft ne valide pas ssoUrl côté serveur (« la valeur correcte n'est pas vérifiée ... par le backend d'autorisation »), donc un cookie est émis quoi qu'il arrive, et il est injecté dans les en-têtes de la requête allant effectivement vers l'hôte de l'attaquant lui-même — une exfiltration directe, et non simplement un en-tête défini sur une requête destinée à Microsoft.
Chrome n'est pas affecté : platform/chrome/js/platform-chrome.js:96-99 (après le correctif GHSA-52rj-42vh-2rxc, commit 8183759) ancre avec "|" + Platform.SSO_URL + "/" (ancre de début de chaîne + barre oblique finale) et ajoute une liste d'autorisation explicite requestDomains: [URL.parse(Platform.SSO_URL).hostname] — les deux rejetant correctement un hôte login.microsoftonline.com.attacker.example.
Thunderbird — même code vulnérable, actuellement non atteignable via ce vecteur : platform/thunderbird/js/platform-thunderbird.js étend PlatformFirefox sans modification, donc le contournement startsWith identique existe dans le binaire de la version Thunderbird. Cependant platform/thunderbird/manifest.json ne déclare qu'un "host_permissions": ["https://login.microsoftonline.com/*"] fixe avec aucune clé optional_host_permissions. WebExtensions n'autorise chrome.permissions.request() à accorder des origines que si elles sont préalablement déclarées dans optional_permissions/optional_host_permissions ; comme Thunderbird n'en déclare aucune, le flux « Background SSO (enable) » du popup (popup/menu.js:259-261) ne peut pas obtenir de permission pour un domaine choisi par l'attaquant sur Thunderbird aujourd'hui. Le bug y est latent, actuellement non atteignable par un attaquant, et devrait tout de même être corrigé pour la défense en profondeur / au cas où optional_host_permissions serait un jour ajouté à ce manifeste.
Confirmé dynamiquement le prédicat vulnérable contre la constante réellement livrée (importée directement depuis src/platform.js de la cible, non recopiée à la main). Script conservé à ~/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