Le service worker qui ne veut pas mourir, et la VM qui s'en moque
Google a republié par accident la semaine dernière un bug Chromium vieux de quatre ans — un service worker qui continue d'exécuter du JavaScript après la fermeture du navigateur, sur tous les principaux navigateurs Chromium, toujours non corrigé. Le proof-of-concept est désormais dans la nature. La question intéressante n'est pas comment il fonctionne. C'est ce que « persistance » veut dire sur un navigateur dont la machine sous-jacente cesse d'exister dès que vous fermez l'onglet.
La persistance n'est pas une propriété du code. C'est une propriété de l'endroit où le code est autorisé à vivre. Un service worker qui « ne meurt jamais » a un problème quand la machine sur laquelle il a été installé est détruite à chaque fois que vous fermez la fenêtre.
Le 20 mai 2026, le bug tracker de Chromium a discrètement levé les restrictions d'accès sur un bug qui restait en statut restreint depuis 2022. En quelques heures, le bug — ainsi qu'un proof-of-concept fonctionnel — était mis en miroir, archivé et commenté un peu partout sur le web. Google a tenté de refermer le ticket. C'était trop tard ; la page était déjà dans la Wayback Machine, et l'article de BleepingComputer est sorti le lendemain.
Le bug est réel, le proof-of-concept fonctionne, et quatre ans après son signalement initial il n'est toujours pas corrigé dans Chrome Dev 150 ni Edge 148. Il affecte tous les navigateurs dérivés de Chromium : Chrome, Edge, Brave, Opera, Vivaldi, Arc.
Ce qu'il permet à une page de faire est, formulé en une phrase, banal : enregistrer un service worker — un petit morceau de JavaScript que le navigateur accepte de laisser tourner en arrière-plan — qui ne s'arrête pas quand la page se ferme, quand l'onglet se ferme, quand le navigateur se ferme, quand la machine redémarre. La chercheuse Lyra Rebane, qui a initialement déclaré le bug en décembre 2022, a résumé le pire cas à BleepingComputer : sur Microsoft Edge le menu de téléchargement n'apparaît même pas, si bien que le résultat est « une RCE JS complètement silencieuse qui continue de tourner même après que vous avez fermé le navigateur. »
Le détail technique du pourquoi du bug compte moins que la classe d'attaque qu'il permet. C'est une primitive de persistance. Une page que vous avez visitée une fois, peut-être il y a des mois, peut-être une publicité que vous ne vous souvenez pas avoir vue, est autorisée à continuer d'exécuter du code sur votre ordinateur indéfiniment. Enrôlement dans un botnet, fraude publicitaire, cryptojacking, participation à des DDoS, exfiltration lente de données, récolte opportuniste d'identifiants chaque fois que vous vous reconnectez au même site — tout cela devient trivial dès que vous disposez d'un point d'appui qui survit à la fermeture du navigateur.
C'est un bon bug, au sens où il est éclairant. C'est un mauvais bug, au sens où il sera utilisé.
Ce qu'est réellement un service worker
La plateforme web autorise, depuis une dizaine d'années environ, les pages à installer de petits programmes JavaScript appelés service workers. Ils tournent en arrière-plan, séparément de tout onglet, et existent pour que les applications web puissent faire des choses raisonnables — mettre en cache des assets pour qu'un site charge quand vous êtes hors-ligne, délivrer des notifications push, synchroniser des données quand le réseau revient.
Un service worker est enregistré contre une origine (par
exemple, la combinaison de https, du nom d'hôte example.com
et du port par défaut). Une fois enregistré, le navigateur le
stocke sur disque et le réveille la fois suivante où vous
visitez quoi que ce soit sur cette origine — ou, dans le cas de
fonctionnalités d'arrière-plan comme les messages push, le
réveille de lui-même périodiquement. Du point de vue de
l'utilisateur, le worker est invisible. Du point de vue du
système d'exploitation, le worker fait partie du navigateur,
tournant avec les permissions qu'a le processus du navigateur.
Le bug rapporté par Rebane est, en gros, qu'une page hostile peut abuser d'une API de téléchargement en arrière-plan pour enregistrer un worker que le navigateur refuse ensuite de terminer. D'autres bugs Chromium de service-worker de la même famille ont été livrés avant ; celui-ci est au moins le deuxième des douze derniers mois. Le motif — la page plante quelque chose, le navigateur le stocke, le navigateur le ramène plus tard de lui-même — est la partie qui mérite d'être retenue. C'est aussi le motif qui se heurte à un mur dès lors que le navigateur cesse de posséder un morceau de disque à longue durée de vie.
Ce qu'exige la persistance
La persistance — au sens sécurité, pas au sens plateforme web — est le mouvement qu'un attaquant fait immédiatement après l'accès initial. Une page ou un binaire atterrit sur votre machine et, avant de faire quoi que ce soit d'autre d'intéressant, s'arrange pour qu'une partie de lui-même survive aux étapes de nettoyage évidentes : fermer l'onglet, quitter le navigateur, redémarrer, mettre le portable en veille dans le train du retour. Sur un OS de bureau, la persistance a tout un vocabulaire — launch agents sur macOS, tâches planifiées sur Windows, unités systemd utilisateur sur Linux, extensions de navigateur, entrées de démarrage automatique.
Un service worker est, de fait, un launch agent intégré au navigateur. Il est enregistré, stocké et ramené à la vie selon un planning que le navigateur contrôle. C'est l'une des primitives de persistance les plus élégantes qu'offre la plateforme web, ce qui explique en partie pourquoi elle ne cesse d'attirer des bugs.
Crucialement, toutes les formes de persistance de cette liste
ont la même exigence, énoncée en trois mots :
quelque chose doit persister. Un launch agent persiste
parce qu'il existe un système de fichiers sur lequel le
.plist de l'agent est écrit, et l'agent est relu depuis ce
système de fichiers à chaque démarrage. Un service worker
persiste parce qu'il existe un profil de navigateur sur lequel
l'enregistrement du worker est écrit, et l'enregistrement est
relu à chaque démarrage du navigateur. Retirez la couche de
stockage et la « persistance » part avec.
Cette phrase — retirez la couche de stockage et la persistance part avec — est la partie de l'architecture autour de laquelle Bromure a été écrit.
Comment Bromure fait tourner un navigateur
Une session Bromure n'est pas un processus. C'est une machine virtuelle. Quand vous ouvrez une fenêtre de navigateur, l'hôte lance un petit guest Linux jetable sur l'hyperviseur d'Apple Silicon. Le processus de navigateur à l'intérieur de ce guest est celui dans lequel vos onglets tournent. Le guest a son propre système de fichiers, sa propre mémoire, son propre noyau. Aucune de ces choses n'est celle de l'hôte.
Il y a deux morceaux de cette architecture qui comptent en particulier pour le bug de service-worker.
Premièrement, le profil sur disque dans lequel l'enregistrement d'un service worker vivrait normalement est à l'intérieur du système de fichiers du guest, pas du système de fichiers de votre Mac. Quand le guest disparaît, ce système de fichiers part avec. Deuxièmement, le guest lui-même a une durée de vie que l'utilisateur définit — par défaut, un guest est lié à un profil (travail, personnel, banque, bac à sable de coding LLM) et est effacé quand l'utilisateur le choisit, mais pour les onglets éphémères le guest est détruit à la fermeture de la fenêtre. Même en mode profil, le guest est un instantané de volume de disque que l'utilisateur peut faire revenir à tout moment.
Dans le cas jetable, le worker « qui ne meurt jamais » n'a nulle part où ne pas mourir. La ligne sur disque depuis laquelle il aurait été relu au prochain lancement du navigateur était à l'intérieur d'une machine virtuelle qui n'existe plus. Le JavaScript qui « continuait de tourner après la fermeture du navigateur » ne continuait à tourner que parce qu'il y avait quelque chose sur quoi le faire tourner. Quand la fermeture du navigateur ferme aussi la machine sous-jacente, le worker s'arrête en même temps que le navigateur — exactement le comportement que la spec originale supposait.
Dans le cas profil — disons que vous gardez un profil « réseaux sociaux » à longue durée de vie qui est censé se souvenir de vous entre les sessions — le worker survit comme il l'a toujours fait, limité à la VM de ce seul profil. Il ne peut toujours pas voir vos Documents, votre trousseau, vos autres profils, ni le reste de votre ordinateur. Et si jamais vous soupçonnez que ce profil a attrapé quelque chose qu'il ne devrait pas avoir, vous pouvez ramener l'ensemble de la VM à son dernier snapshot propre, ce qui prend à peu près autant de temps que de relancer un navigateur.
Le point général
Il serait facile de lire ce qui précède comme « Bromure se trouve par hasard vaincre ce bug de service-worker spécifique. » C'est vrai, mais ce n'est pas la prétention intéressante. La prétention intéressante est plus générale, et cet incident en est une petite pièce à conviction.
Les zero-days de navigateurs continueront d'atterrir. C'est au moins le deuxième bug de service-worker Chromium des douze derniers mois. Les grands de 2024 et 2025 étaient des confusions de types V8 et des évasions de sandbox Mojo, payés à six chiffres, utilisés par des éditeurs commerciaux de spyware et des groupes alignés sur des États. Il y a eu des compromissions de renderer avant que les service workers n'existent ; il y aura des compromissions de renderer longtemps après que ce bug en particulier sera corrigé. L'économie de la découverte de nouveaux — surtout avec les auditeurs IA de classe Mythos maintenant dans la boucle des deux côtés de la fenêtre de divulgation — va contre le défenseur, pas dans son sens.
La question pertinente, quand un zero-day part, n'est pas le navigateur était-il sans faille. Il ne l'était pas. La question pertinente est ce que l'exploit a réellement atteint.
Une évasion de sandbox Chromium — le « zero-day de navigateur » canonique dont vous avez lu parler tous les quelques mois depuis dix ans — est une chaîne qui prend un bug mémoire dans un morceau du renderer (V8, Skia, WebP, Dawn, libxml2) et l'utilise pour convaincre le processus broker du navigateur de faire quelque chose que le renderer n'était pas censé pouvoir faire. Ces bugs sont payés à six chiffres parce qu'ils franchissent le mur que les ingénieurs de Chromium passent l'essentiel de leur temps à maintenir : la sandbox in-process.
Ce mur est une remarquable pièce d'ingénierie. C'est aussi, généalogiquement, fait du même genre de matière que le bug qui l'a franchi — du C++, des espaces d'adressage partagés, des primitives IPC comme Mojo. Quand un nouveau bug mémoire apparaît dans V8 ou dans l'un des cent parseurs livrés avec Chromium, c'est le genre de chose qui peut, avec assez de travail, faire levier sur la sandbox.
Une VM est un autre genre de mur. Le navigateur qui tourne à
l'intérieur n'a pas d'adresse mémoire virtuelle qui mappe vers
le noyau de l'hôte ; le noyau de l'hôte ne parse pas la
page que le navigateur parse ; la pile de virtualisation
est une surface minuscule comparée à un navigateur, et ce
qu'elle parse (états de registres vCPU, hypercalls paravirt,
descripteurs de buffers virtio) n'est pas des octets adversaires
arrangés par une page web. Des bugs à cette frontière existent.
Ils sont dans une autre catégorie de difficulté, sont plusieurs
ordres de grandeur plus rares, et quand ils apparaissent ils
sont en général annoncés par l'éditeur de l'hyperviseur avec
une CVE.
Ce que cela change pour l'utilisateur
Dans le cas spécifique du bug de service-worker exposé la semaine dernière, un utilisateur Bromure n'a pas besoin d'attendre un correctif. Le bug existe aussi dans son navigateur — Bromure embarque Chromium upstream — mais le comportement que le bug permet est le comportement que l'architecture de Bromure désactive déjà. Un worker qui « vit pour toujours » ne vit qu'aussi longtemps que la VM invitée dans laquelle il a été enregistré, ce qui est au plus aussi long que le profil que vous gardez, et au moins aussi long que l'onglet est ouvert.
Dans le cas plus général de « un zero-day de navigateur va
atterrir quelque part dans le mois prochain », un
utilisateur Bromure obtient une forme d'issue différente.
L'exploit tire, dans le pire des cas, contre le contenu du
guest : la session de navigateur, les cookies des sites
actuellement connectés à l'intérieur de ce profil, tout ce que
l'utilisateur a tapé dans ce navigateur. C'est une vraie perte
et nous n'essayons pas de la cacher. Ce qui n'est pas perdu,
c'est l'hôte : pas de clés SSH, pas de ~/.aws, pas de
trousseau, pas de Documents, pas d'arbre source, pas d'espace
de travail d'agent LLM.
Ce compromis — la session de navigateur peut être compromise, l'hôte non, et la session elle-même est courte — est le compromis qui continue d'avoir du sens à mesure que le taux de nouveaux bugs de navigateur augmente.
Ce que nous ne prétendons pas
Deux mises en garde, énoncées ouvertement :
Les VM jetables ne patchent pas Chromium
Le bug de service-worker est un bug Chromium. Les bonnes personnes pour le corriger sont les ingénieurs de Chromium, et ils devraient le faire. L'architecture de Bromure change le coût du bug non corrigé ; ce n'est pas un substitut à sa correction. Nous remontons les choses en amont quand nous en trouvons, et nous lisons les mêmes advisories que tout le monde.
Une évasion de VM est une catégorie réelle
Un bug dans l'hyperviseur lui-même — le framework Virtualization d'Apple, ou l'une des plus petites surfaces autour — permettrait, en principe, à un attaquant d'atteindre l'hôte. Ces bugs existent. Ils sont aussi plusieurs ordres de grandeur plus rares que les bugs mémoire de navigateur, et la surface est dramatiquement plus petite. Bromure rend l'exposition de l'hôte à une compromission de renderer conditionnée à cette classe de bug, plutôt qu'à la classe qui livre toutes les quelques semaines.
Le titre que vous lirez le mois prochain
Il y aura un autre bug de service-worker Chromium. Il y aura
une autre confusion de types V8. Il y aura un autre heap
overflow libwebp. Il y aura un autre zero-day qu'un
adversaire extrêmement bien doté a utilisé contre des
personnes extrêmement spécifiques pendant extrêmement
longtemps, qui devient public un mardi matin avec une
advisory de correctif d'urgence.
La question utile, au matin de l'un quelconque de ces événements, n'est pas si vous avez déjà redémarré votre navigateur. La question utile est ce qui se trouvait réellement à l'intérieur de la boîte dans laquelle le bug a atterri. Pour la plupart des utilisateurs, aujourd'hui, cette boîte est la même boîte qui contient leurs fichiers et leur trousseau et leurs identifiants enregistrés. Pour un utilisateur Bromure, la boîte est une machine virtuelle invitée dont la perte dans le pire des cas est la fenêtre qu'il s'apprêtait de toute façon à fermer.
La persistance exige quelque chose sur quoi persister. Rendez ce quelque chose jetable, et toute une classe d'attaques cesse d'être une classe d'attaques.