Une illustration abstraite générée sur un fond bleu marine sombre : le contour d’une fenêtre de navigateur, à gauche, envoie un ruban de formes pâles évoquant des glyphes à travers une étroite prise lumineuse jusqu’à un panneau de terminal, à droite. Un second ruban, plus sombre, rejoint la même prise par le bas et ses glyphes arrivent dans le terminal sans qu’on puisse les distinguer du premier. Aucun texte, aucune personne ni logo d’organisation n’apparaît. Le logo OrcaRouter est placé dans la bande blanche de pied de page, en bas à droite.
AI Safety Incidents

Tencent BrowserSkill ne vérifiait que l'apparence de réalité d'un identifiant d'extension — n'importe quelle extension pouvait fournir à l'agent des pages falsifiées

Auteur

Rowan Sterling

Date de publication

Derniers modèles · 20Voir tous les modèles →
Benchmarks : Artificial Analysis · mis à jour quotidiennement
Retour à tous les articles

Le 17 septembre 2026, un utilisateur de GitHub se présentant comme carfeii a ouvert le ticket #273 à l'encontre du projet open source BrowserSkill de Tencent, avec une affirmation précise : le démon local qui relie l'extension de navigateur à un agent d'IA ne vérifiait pas quelle extension se connectait, seulement que la connexion ressemblait à celle d'une extension. Trois jours plus tard, le 20 septembre, VulnCheck a publié la découverte sous CVE-2026-94111, décrivant un démon qui « accepte toute origine chrome-extension comportant 32 caractères dans la plage a-p », la classe comme CWE-346 (erreur de validation d'origine), et en attribue le crédit à George Chen. L'avis lui attribue CVSS 4.0 6,9, moyen, avec un impact sur l'intégrité jugé élevé et des impacts sur la confidentialité et la disponibilité faibles. Rien dans le dossier ne suggère que la faille a été exploitée : l'archive ne consigne aucune exploitation et attribue à l'entrée une confiance de niveau A, avec real_harm à false. Il s'agit d'une entrée dans l'archive des incidents d'IA, où chaque entrée comporte ses propres sources, sa gravité, son niveau de confiance et son indicateur de contestation.

Un contrôle de format qui n'a jamais vérifié l'identité

Le code au cœur de l'enregistrement est court. Dans crates/bsk-cli/src/daemon/ws.rs, la fonction origin_allowed() prend l'en-tête Origin de la poignée de main WebSocket, en retire la chaîne chrome-extension://, et renvoie true si ce qui reste fait exactement 32 caractères de long et que chaque caractère se situe entre a et p. C'est là tout le test. Il est exécuté une seule fois, à l'intérieur du callback de mise à niveau HTTP, et en cas d'échec, il renvoie un 403 dont le message est « Origin not in chrome-extension allow-list » — alors que, tel que publié, il n'y avait aucune liste dans laquelle figurer.

Le motif n’est pas arbitraire. Les ID d’extension Manifest V3 de Chrome sont composés de 32 caractères tirés exactement de ces seize lettres, donc la vérification reconnaît correctement la forme d’une véritable origine d’extension. Le défaut, c’est que toutes les extensions ont cette forme. La vérification demande si l’appelant ressemble à une extension de navigateur, jamais s’il s’agit de l’extension de navigateur de ce projet, et l’identifiant auquel il aurait dû être comparé était déjà public : le README du projet renvoie à la fiche du Chrome Web Store, dont l’ID est hhcmgoofomhgciiibhipgmgkgnoenaoi, ainsi qu’à l’ID Edge Add-ons juste à côté. Aucune des deux chaînes n’apparaît nulle part dans le contrôle.

Le démon écoute sur un port local fixe, 52800, déclaré comme DEFAULT_WS_PORT dans la CLI et utilisé sans variation dans le chemin de connexion local. Le port est documenté, constant et joignable par tout ce qui s'exécute déjà dans le profil de navigateur de l'utilisateur. C'est l'emplacement qui explique pourquoi le vecteur de l'avis commence par AV:L — l'attaquant a besoin d'un point d'appui sur la machine, et PR:L indique qu'un privilège faible suffit. Une extension installée dans le même profil est précisément ce genre de point d'appui.

Ce que vaut le siège, et dans quelle mesure cela est établi.

La phrase relative à l’impact dans l’avis est générale : un attaquant qui enregistre une extension malveillante en tant que client de navigateur peut « intercepter et manipuler le contenu de la page, le DOM et les captures d’écran renvoyés à l’agent IA ». C’est la description que portent la CVE, l’avis GitHub GHSA-x6f2-qp26-2xrh et l’archive, et elle explique la note d’intégrité CVSS élevée. Il s’agit aussi d’un résumé rédigé par l’entité qui a attribué la CVE, et non du compte rendu d’une attaque démontrée.

Un compte rendu plus minutieux est arrivé la même semaine, dans le fil de discussion du ticket lui-même. Le 20 septembre, un commentateur intervenant sous le nom de bb6894 a rapporté avoir revérifié le code par rapport à main au commit fa953dc6 (18 septembre) et avoir retracé ce qu’un client étranger peut réellement atteindre après la poignée de main. La plupart des méthodes d’outil entrantes, indique le commentaire, renvoient UnknownMethod à un pair WebSocket ; les demandes d’audit sont limitées aux enregistrements du client lui-même ; et les actions intersessions telles que window_closed, user_interrupt et update_interaction sont protégées contre les sessions détenues par un autre navigateur. Ce qui survit à ce filtre est plus restreint et, pour un agent, sans doute pire que l’interception : une extension étrangère peut prendre un emplacement de navigateur sous un instance_id choisi par l’attaquant, et si elle est le seul navigateur enregistré — démarrage à froid ou redémarrage du démon —, elle reçoit les demandes d’outil de l’agent et y répond avec du contenu de page, du DOM ou des captures d’écran fabriqués. Le commentateur appelle cela « falsification de résultats à destination de l’agent » et « un canal vers la perception de l’agent ». Un second effet est un déni de service : lorsqu’un navigateur authentique est également en ligne et qu’aucun sélecteur n’est présent, le démarrage de session peut échouer avec MultipleBrowsersOnline.

Aucun des deux récits n’a été démontré publiquement. Le journaliste a décrit une preuve de concept comme « disponible sur demande » ; personne n’en a publié, et aucune exploitation n’est consignée. Ainsi, la différence entre les deux récits est une différence entre deux lectures d’un même fichier source, et non entre une théorie et un exploit fonctionnel — et l’article rapporte les deux plutôt que de choisir. Ce que l’on peut affirmer sans réserve, c’est qu’il n’y a jamais eu de seconde vérification d’identité après la poignée de main, de sorte que tout ce que la porte a admis est resté admis pendant toute la durée de la connexion.

Le code savait déjà

Le document le plus gênant de ce dossier n’est pas l’avis. C’est le commentaire situé directement au-dessus de la fonction, livré dans la version étiquetée à laquelle l’avis renvoie. Il marque la lacune comme TODO(M10/M12), nomme la liste d’autorisation qu’il compte construire — un ensemble DaemonConfig::allowed_extension_ids rempli à partir d’un fichier de configuration ou d’un flux d’appairage — puis énonce la raison de son report : « écart acceptable en matière de défense en profondeur pour le moment, car l’appairage se fait via la fenêtre pop-up, mais une extension chargée par sideload sur la même machine franchit actuellement le contrôle. »

À lire sans détour, les mainteneurs savaient quel appelant le filtre laissait passer, ont consigné qu’ils le savaient, et ont publié. Cela ne rend pas l’exposition plus large que ne l’indique l’avis ; cela la rend moins surprenante et déplace la question de « comment cela a-t-il pu passer la revue » à « qu’est-ce qui aurait rendu urgent le correctif reporté ». L’hypothèse de conception qui atténue le risque est réelle, et elle est aussi documentée : la connexion locale nécessite une activation explicite. D’après le README, l’utilisateur installe l’extension, ouvre sa fenêtre contextuelle et active la connexion locale, et le démon doit être en cours d’exécution pour que tout cela soit accessible. Un profil avec l’extension installée mais l’interrupteur désactivé, ou sans bsk daemon en cours d’exécution, n’est pas une cible à l’écoute.

Cinq semaines plus tard, le portail est inchangé.

An HTML timeline card titled 'From a filed issue to an unmerged patch', subtitled 'Nine dated entries in the BrowserSkill daemon origin-check record', listing: 16 September 2026 BrowserSkill extension 0.3.0 tagged; 17 September the bsk CLI 0.3.0 release, and issue #273 filed the same day at 15:14 UTC; 20 September CVE-2026-94111 published by VulnCheck and listed by NVD and GitHub, with a narrower impact analysis posted in the thread; 21 September CISA's SSVC assessment records exploitation none and automatable no; 23 September CLI 0.3.1 released with the same origin check; 28 September pull request #363 opened from a fork to pin the store extension IDs; 30 September CLI 0.3.2, same check; 15 October CLI 0.3.3, same check; 27 October issue still open and the patch still unmerged. A footer cites the GitHub issue, the VulnCheck advisory, NVD and the project's release tags, with the OrcaRouter logo in a white strip at the bottom right.

L'enregistrement indique une plage affectée allant « jusqu'à 0.3.0 », et c'est la plage attribuée par le CNA. Ce n'est pas la fin de l'histoire dans le dépôt. La version 0.3.0 de la CLI a été étiquetée le 17 septembre à 02:22 UTC ; le ticket a été déposé le même jour à 15:14 UTC, environ treize heures plus tard. Trois autres versions de la CLI ont été publiées depuis — 0.3.1 le 23 septembre, 0.3.2 le 30 septembre, et 0.3.3 le 15 octobre — et la fonction origin_allowed() à chacun de ces tags est le même contrôle que celui décrit ci-dessus, avec le TODO toujours au-dessus. La branche main la contient aussi à la date du 27 octobre.

Un correctif existe, mais uniquement à l’état de proposition. Le 28 septembre, un contributeur publiant sous le nom de dangzitou a ouvert la pull request n° 363 depuis un fork personnel, intitulée « fix(daemon) : épingler le contrôle d’origine WS sur les identifiants d’extension propres à BrowserSkill ». Elle remplace le test de forme par une comparaison avec les deux identifiants du magasin, et ajoute BSK_ALLOWED_EXTENSION_IDS comme chemin d’admission explicite pour les versions installées hors magasin (sideloadées) et de développement, dont les identifiants non empaquetés sont dérivés du chemin d’installation. Le corps de la pull request affirme qu’elle ferme à la fois la voie de falsification et le déni de service MultipleBrowsersOnline. Au 27 octobre, elle est toujours ouverte et non fusionnée, et le problème reste ouvert sans aucune déclaration du fournisseur qui y soit associée.

La conséquence pratique est qu’il n’existe aucune version vers laquelle effectuer une mise à niveau qui contienne le correctif. Quiconque conseille « de mettre à niveau au-delà de 0.3.0 » conseille une étape qui n’existe pas encore : les versions ultérieures sont plus récentes, pas corrigées. L’intervalle indiqué dans la fiche doit être lu comme l’intervalle évalué par le CNA le 20 septembre, et non comme une affirmation que 0.3.1 et les versions suivantes sont exemptes de vulnérabilité.

Ce qui est établi, et ce qui ne l’est pas

• Établi : la source publiée au tag cli-v0.3.0 contient un contrôle d’origine qui accepte toute origine chrome-extension de 32 caractères a–p. Le rapporteur, la CNA et le fichier lui-même concordent, et le code est public.

• Établi : le contrôle a été effectué une seule fois, lors de la mise à niveau WebSocket, sans vérification d'identité ultérieure, sur un port local fixe 52800.

• Constaté :le commentaire dans le code source du projet reconnaissait qu’une extension installée manuellement serait acceptée, et reportait la liste d’autorisation.

• Établi : CVSS 4.0 6.9 et CVSS 3.1 6.6 sont tous deux des scores de VulnCheck, enregistrés par NVD comme secondaires. Le statut propre de NVD pour l'entrée est Deferred, ce qui signifie qu'il a publié les données du CNA sans ajouter d'analyse rédigée par NVD.

• Établi :L’évaluation SSVC de la CISA, datée du 21 septembre, classe l’exploitation comme « aucune », l’automatisation comme « non » et l’impact technique comme « partiel ». L’EPSS de FIRST situe la probabilité d’exploitation au cours des trente prochains jours à 0,00135, soit le 2,9e centile, au 26 octobre.

• Non établi : aucune utilisation réelle. Aucune exploitation, aucune preuve de concept publiée, aucun décompte des installations affectées. Les métriques du dépôt du projet ne constituent pas un décompte des installations, et personne n’en a publié.

• Non établi : quel niveau d’impact est retenu. Les formulations du CNA « intercepter et manipuler » et du fil « falsification de résultats et déni de service » décrivent des plafonds différents, et aucune des deux n’a été démontrée en dehors de la lecture du code.

• Non établi : que la faille s’arrête à la version 0.3.0. La chaîne de version correspond à l’évaluation de la CNA ; le code est inchangé dans les trois versions publiées après celle-ci, et aucune déclaration du fournisseur ne les disculpe.

• Contradiction non résolue : un agrégateur, DevGuard, affiche un signal « des tentatives d'exploitation ont été détectées » pour cette CVE sans nommer de source, d'IOC ni de fournisseur de télémétrie. Cela contredit l'évaluation consignée par la CISA selon laquelle aucune exploitation n'a eu lieu, ainsi que l'absence de tout rapport public ; les archives ne consignent non plus aucune exploitation. Considérez le signalement de l'agrégateur comme non étayé plutôt que comme un second point de données.

An HTML card titled 'Established, and not', split into two columns. The left column, headed 'Established', lists: an origin gate that accepts any 32-character a-p chrome-extension origin, in the released cli-v0.3.0 source; checked once at the WebSocket upgrade on local port 52800; the project's own comment conceded sideloaded extensions pass; CVSS 4.0 6.9 and 3.1 6.6 are VulnCheck's scores, NVD status Deferred; CISA SSVC records exploitation none and automatable no; EPSS 0.00135 as of 26 October. The right column, headed 'Not established', lists: any real-world use or published proof of concept; which impact account holds, interception or result forgery; that the flaw stops at 0.3.0, since 0.3.1 to 0.3.3 carry the same check; an installation count; an unsourced aggregator claim of exploitation attempts. A footer cites VulnCheck, NVD, CISA, FIRST and the GitHub issue, with the OrcaRouter logo in a white strip at the bottom right.

Pourquoi le transport est la cible intéressante

La plupart des consignes relatives aux injections de prompts tracent la même frontière : considérer le texte de page récupéré comme non fiable, et considérer les mécanismes qui le fournissent comme fiables. Un outil qui lit une page web est supposé lire la page ; la question est de savoir ce que dit la page. Ce document inverse cela. Ce que l’agent reçoit comme contenu de page, DOM et captures d’écran est ce que le client à l’autre extrémité d’une socket locale choisit d’envoyer, et la seule barrière sur cette socket posait une question à laquelle chaque candidat pouvait répondre correctement. Si le client est extérieur, « vérifier ce que dit la page » n’a plus rien sur quoi s’appuyer pour vérifier.

C’est aussi pourquoi la gravité atteint ce niveau plutôt qu’un niveau supérieur. L’attaque est locale, elle présuppose qu’une extension est déjà installée dans le profil ou qu’un autre code local est présent, et elle exige que le démon soit en cours d’exécution avec la connexion locale activée. Ces conditions sont ordinaires pour les utilisateurs visés par l’outil et inhabituelles pour tous les autres. La gravité moyenne de l’archive et l’absence de préjudice enregistrée sont compatibles avec ce profil : un élargissement réel de la confiance à la frontière exacte que la conception était censée contrôler, sans preuve que quiconque l’ait franchie.

Le conseil pratique étroit qui suit n’est pas une mesure d’atténuation que quiconque a publiée, seulement la limite que le code laisse en place. Comme l’admission est décidée une fois lors de la poignée de main sur un port publié, la surface accessible est déterminée par les extensions installées dans le profil qui exécute le démon, et par le fait que ce démon et la connexion locale de l’extension soient activés à ce moment-là. Tant qu’une liste d’autorisation d’ID d’extension n’est pas disponible, ces deux faits constituent l’intégralité du contrôle.

A generated abstract illustration on a dark navy field: two identical hexagonal tokens of the same size and colour approach a narrow blue aperture from opposite sides; both pass through, and beyond it their outputs merge into a single stream of pale glyph shapes entering a terminal panel. A faint dotted outline of a browser window sits above. No text, people or organisation logos appear. The OrcaRouter logo is set in the white footer strip at the bottom right.

CVE-2026-94111 vieillira de l’une de deux manières. Soit la pull request est fusionnée et l’enregistrement devient une courte entrée sur une lacune connue qui a mis du temps à être comblée, soit le même contrôle de forme est encore dans l’arborescence dans un an et l’entrée est citée comme un exemple précoce de la classe à laquelle elle appartient : non pas une page contenant une instruction hostile, mais le tuyau qui décide quelle page l’agent est en train de lire.