
CVE-2026-74970, contournement de l'isolation de site Fission dans Firefox WebRender
Ce répertoire contient un échafaudage de reproduction honnête pour un contournement d'autorisation d'isolation de site Fission dans Firefox WebRender. Sous Fission, chaque processus de contenu possède un espace de noms PipelineId distinct, mais le code du processus GPU qui enregistre un PipelineId compositable n'a jamais vérifié que l'espace de noms du PipelineId appartenait au processus émetteur. Un processus de contenu compromis peut donc enregistrer un PipelineId dans l'espace de noms d'un autre processus et écraser le pipeline d'image asynchrone de cette origine dans le gestionnaire partagé.
Tout ce qui se trouve ici cible une arborescence Firefox construite localement avec un contenu synthétique uniquement. Il n'attaque, ne contacte, ne sonde et ne référence jamais aucun système actif ou tiers. Il ne s'agit pas d'exécution de code à distance ni de corruption de mémoire ; le modèle de menace est un processus de contenu déjà compromis.
Dépôt PoC correspondant : https://github.com/defineid/Trespasser
Writeup : writeup-webrender-pipelineid.html dans ce portfolio.
| Champ | Valeur |
|---|---|
| CVE | CVE-2026-74970 |
| Composant | Core / Graphics: WebRender |
| Classe | Contournement d'isolation de site |
| CWE | 862 (Autorisation manquante) · 284 (Contrôle d'accès inapproprié) |
| Corrigé dans | Firefox 154 · ESR 153.1 |
| Bug | https://bugzilla.mozilla.org/show_bug.cgi?id=2056558 |
| Dépôt PoC | https://github.com/defineid/Trespasser |
Sous Fission, WebRender attribue à chaque processus de contenu son propre espace de noms PipelineId. Le pont du processus GPU est censé rejeter toute ressource dont l'espace de noms n'appartient pas au processus qui l'a envoyée, afin qu'un processus de contenu ne puisse pas adresser ou écraser les ressources de rendu d'un autre.
WebRenderBridgeParent::AddPipelineIdForCompositable, qui s'exécute dans le processus GPU, validait le pipeline entrant par rapport au garde root/WebRender et par rapport à l'ensemble des pipelines enregistrés de ce pont, mais n'appelait jamais MatchesNamespace sur le PipelineId. Les chemins image-key, font-key et blob-key dans le même fichier effectuent tous cette vérification de propriété MatchesNamespace ; le chemin PipelineId ne le faisait tout simplement pas. RemovePipelineIdForCompositable est également non vérifié, de sorte que le même trou de propriété d'espace de noms existe lors de la suppression.
En aval, AsyncImagePipelineManager::AddAsyncImagePipeline protège l'unicité d'un pipeline uniquement avec MOZ_ASSERT, qui se compile en no-op dans les builds de release. Ainsi, lorsqu'un PipelineId forgé, issu d'un autre espace de noms, atteint le AsyncImagePipelineManager partagé, le gestionnaire ne rejette pas le doublon : il écrase silencieusement l'entrée de pipeline d'image asynchrone existante de l'origine victime.
Enchaînés, un processus de contenu compromis A peut forger un PipelineId dont l'espace de noms appartient au processus de contenu B (une origine différente, dans un processus séparé sous Fission), le soumettre sur le chemin d'enregistrement compositable, et faire accepter celui-ci par le processus GPU qui écrase alors le pipeline d'image de B dans le gestionnaire partagé.
Un processus de contenu déjà compromis sous Fission. On suppose que l'attaquant dispose déjà d'une exécution de code dans un processus de contenu (par exemple via un bug distinct du processus de contenu) et qu'il peut envoyer des messages IPC WebRender avec des champs choisis par l'attaquant. Ce défaut est l'autorisation manquante qui permet à ce processus de franchir la frontière d'isolation de site pour atteindre les ressources de rendu d'une autre origine.
Il s'agit d'un contournement d'autorisation d'isolation de site Fission, et non d'une corruption de mémoire. L'impact est une confusion de rendu inter-origines et un déni de service inter-onglets. Les consommateurs en aval sont protégés par des vérifications de nullité et comptés par références, de sorte que cela ne permet pas d'exécution de code. Le PoC ne doit pas et ne revendique pas d'exécution de code.
README.md — ce fichier.forge-namespace.patch — un patch de recherche pour un arbre Firefox local. Côté contenu, protégé par XRE_IsContentProcess() et la variable d'environnement MOZ_POC_FORGE_WR_NAMESPACE, il falsifie l'espace de noms du PipelineId sortant. Côté parent du processus GPU, il affiche un diagnostic ACCEPTED lorsqu'un PipelineId inter-espaces de noms est enregistré, démontrant que la vérification manquante l'a laissé passer.poc.html — un cas de test brut côté contenu : un canvas WebGL (afin qu'un pipeline d'image asynchrone soit créé) plus une iframe cross-origin hors processus (afin que Fission la place dans un processus de contenu séparé). Sur une build standard, il s'affiche normalement et ne produit rien d'observable.victim.html — la seconde origine synthétique chargée par l'iframe. C'est une page triviale, sans script, dont l'unique rôle est d'être un document cross-origin actif, afin que sous Fission elle atterrisse dans son propre processus de contenu avec son propre espace de noms PipelineId (l'espace de noms que ciblerait un enregistrement falsifié).poc-render.png — une capture d'écran de poc.html chargé dans le Firefox 140.11.0esr standard local. Elle montre la page de test s'affichant normalement : le canvas WebGL dessine son image et l'iframe hors processus charge victim.html. Elle ne montre qu'un rendu bénin. Le contournement lui-même nécessite le patch de recherche et une build vulnérable et n'a pas été déclenché ici (voir le statut de vérification).Ces étapes sont destinées à être réalisées par l'auteur contre une copie de travail locale de Firefox. Rien ici n'a été exécuté contre Firefox dans cet environnement (voir le statut de vérification ci-dessous).
Récupérez un arbre source Firefox vulnérable (antérieur à 154 / antérieur à ESR 153.1) et confirmez que Fission est activé (fission.autostart = true, la valeur par défaut sur les builds récentes).
Appliquez le patch de recherche depuis la racine de l'arbre source :
patch -p1 < forge-namespace.patch
Le patch touche gfx/layers/wr/WebRenderBridgeChild.cpp (côté contenu,
forge le namespace) et gfx/layers/wr/WebRenderBridgeParent.cpp
(côté processus GPU, imprime le diagnostic ACCEPTED). Les deux modifications sont inertes
sauf si les conditions de garde sont remplies.
Compiler normalement (./mach build).
Servir ce répertoire afin que l'iframe hors processus de poc.html charge le
fichier victim.html compagnon depuis un domaine enregistrable véritablement différent, afin
que Fission place l'iframe dans son propre processus de contenu. Par exemple,
python3 -m http.server 8000 --bind :: et charger l'iframe depuis
http://victim.localtest.me:8000/victim.html. Voir le commentaire en ligne dans
poc.html.
Lancer la compilation avec la variable de forge définie et charger la page de test :
MOZ_POC_FORGE_WR_NAMESPACE=999999 ./mach run --temp-profile poc.html
999999 est une valeur d'espace de noms arbitraire choisie pour n'appartenir à aucun processus réel ; elle rend la fausse inscription inter-espaces de noms évidente dans le journal.
Sur une version vulnérable avec le correctif appliqué, le parent du processus GPU accepte et enregistre un PipelineId dont l'espace de noms n'appartient à aucun processus de ce type, et affiche (c'est la forme exacte présentée dans l'analyse) :