Retour à tous les articles
Publié le · par Renaud Deraison

La commande s'appelait aipoison

Socket a découvert une grappe de 18 paquets npm imitant le scope privé @ali d'Alibaba. Aucune des archives ne contenait de malware. Le RAT s'assemblait à l'installation à partir d'un fichier JSON hébergé dans un dépôt GitHub que l'attaquant contrôle encore, et son plan de persistance incluait de patcher du Python dans les répertoires .skills des outils de code IA, via des commandes nommées aipoison, aipoison_inject et aipoison_deploy. Lire le code ne pouvait pas trouver cela. Bromure Agentic Coding juge ce qui était visible depuis le début : un paquet sans réputation attend votre consentement au proxy hôte, les trois requêtes qui assemblent le payload traversent un fil qui ne parle qu'aux destinations que vous avez approuvées, et chaque fichier écrit par le RAT atterrit dans une VM qui vit le temps d'une tâche.

Quelque part dans le payload final se trouve une commande nommée aipoison. Son travail : plonger dans le répertoire de skills de votre outil de code IA et patcher un fichier Python, pour que la prochaine fois que vous vous mettez au travail, l'attaquant s'y mette aussi.

Le 3 août 2026, The Hacker News a couvert la recherche que Socket a publiée fin juillet : une grappe d'environ dix-huit paquets npm, mis en place fin avril, restés trois mois sur le registre avant que quiconque ne les repère. Ils imitent les paquets privés d'Alibaba sous le scope @ali en publiant les mêmes noms sans scope (aone-cloud-cli, def-open-client, open-worker-cli, uniapi-bridge), de sorte qu'un développeur ou un build au sein de l'organisation ciblée résout le sosie public et se retrouve avec un cheval de Troie d'accès distant greffé sur l'arbre de dépendances. Karlo Zanki, chercheur chez Socket, lit le mobile comme de l'espionnage industriel.

Rien dans les paquets n'était malveillant

Ouvrez n'importe laquelle de ces archives et vous ne trouvez rien à signaler. Pas de hook postinstall, la première chose que tout le monde greppe. Les paquets-appâts dépendent de smart-config-manager, qui dépend de cloud-config-fetcher et de local-config-parser. Un gestionnaire de config, un fetcher, un parseur : des utilitaires banals, chacun lisible en une minute.

La malveillance vit dans la composition, et dans un fichier que l'attaquant n'a jamais livré. À l'import, le fetcher télécharge defaults/preferences.json depuis un dépôt GitHub que l'attaquant contrôle, et contrôle toujours, puis l'écrit sur disque sous le nom .cloud-preferences.json. Le parseur évalue les « règles de transformation » de ce fichier dans un vm Node, et c'est là que vit l'attaque. L'une des règles est une évasion de sandbox au statut de folklore :

var F = items.constructor.constructor;   // recover Function
var p = F('return process')();           // recover process

Depuis process, il descend une échelle de repli (getBuiltinModule, mainModule.require, mainModule.constructor._load) jusqu'à ce que l'une d'elles livre require sur la version de Node où il a atterri. Puis il récupère setting.js depuis un bucket de stockage Alibaba Cloud, qui prend l'empreinte de l'hôte et installe le payload : sous Windows, tuer Alilang, l'application de sécurité d'Alibaba elle-même, et remplacer son app.asar par une copie piégée ; sous Linux, déposer un binaire dans /tmp, le lancer détaché, supprimer le fichier ; sous macOS, s'injecter dans ~/.zshrc et enregistrer un LaunchAgent qui se déclenche toutes les dix minutes.

GBHackers a mis le design dans son titre : les attaquants ont réparti le RAT entre les paquets pour échapper aux revues de code isolées. L'attaquant a construit cette campagne pour survivre à sa lecture. Relisez chaque paquet isolément : il passe. Relisez l'arbre entier : il passe encore, parce que la partie hostile est un fichier JSON sur un serveur, récupéré après la fin de votre revue, modifiable par l'attaquant entre votre audit et votre installation.

Le plan de persistance inclut votre agent

Socket a récupéré le payload final sous le nom aone-cli. Il fait ce que font ces choses-là : commandes shell, envoi et téléchargement de fichiers, reconnaissance de l'hôte, captures d'écran, un proxy TCP inverse chiffré, et une commande dws_lateral qui se déplace via DingTalk. Son trafic C2 porte des en-têtes Origin et Referer forgés, prétendant venir d'un domaine de documentation interne d'Alibaba.

Un groupe de commandes fait de cette histoire une histoire d'agents de code. À côté des verbes habituels, le RAT embarque aipoison, aipoison_inject et aipoison_deploy. Elles patchent des scripts Python installés dans les répertoires .skills d'outils de développement précis, en marquant l'extrait injecté d'un # __INJECT_MARKER__, pour qu'un script tiré du C2 s'exécute plus tard, depuis l'intérieur de l'outillage, en votre nom. Parmi les cibles nommées figure Qoder, l'IDE agentique d'Alibaba lui-même, qui a dépassé les cinq millions d'utilisateurs quelques mois après son lancement en août dernier.

Quelqu'un s'est assis, a regardé la machine de développeur moderne, et a choisi le dossier de skills de l'agent comme point d'ancrage durable : un répertoire de scripts qui s'exécute avec vos permissions au début de chaque session, et que presque personne ne diffe jamais. Nous avons déjà vu ce geste, quand un ver s'est copié dans .claude pour que le prochain démarrage d'agent réinfecte la machine. Il est passé du bricolage improvisé d'un ver à une commande nommée dans une boîte à outils d'espionnage.

Les parties que l'attaquant ne pouvait pas cacher

Lire le code n'allait jamais trouver cela. L'attaque avait pourtant des prérequis, et tous étaient visibles : un paquet sans historique et sans réputation, trois connexions sortantes avant de pouvoir agir (le fichier de règles sur GitHub, l'étape sur le stockage cloud, le C2), et une machine encore là le lendemain.

Bromure Agentic Coding pose une décision sur chacun d'eux, et la prend sur votre Mac, hors de la boîte où tourne l'agent, là où rien à l'intérieur ne peut discuter.

L'installation marque une pause au registre. Chaque récupération de paquet traverse le proxy côté hôte, qui applique la politique supply chain de votre espace de travail à la réponse avant qu'un octet n'atteigne la VM. Activez le filtrage de paquets et le proxy consulte un fournisseur de réputation pour chaque artefact. L'un des deux fournisseurs sélectionnables est Socket, dont la recherche fait l'objet de ce billet. Quand aucune source activée ne peut se porter garante d'un paquet, le proxy retient le téléchargement et vous demande. Des sosies d'un scope privé, publiés le trimestre dernier avec une poignée de téléchargements et aucune réputation nulle part, s'arrêtent là. L'application se fait sur l'hôte à dessein : le .npmrc à l'intérieur de la VM peut durcir la politique et ne peut pas l'assouplir, donc la règle tient même quand la chose compromise est celle qui fait la police.

Le fil refuse l'assemblage. Ce design se brise contre le consentement de sortie, parce qu'avec rien d'hostile dans l'archive, le réseau est l'attaque. Le proxy hôte vérifie chacune des trois requêtes (le fichier raw GitHub de l'attaquant, le bucket qui sert setting.js, l'hôte C2) contre les destinations que vous avez approuvées pour ce profil. Le registre d'où vous installez est sur cette liste. Le chemin raw d'un inconnu et un bucket nommé d'après un CLI dont vous n'avez jamais entendu parler n'y sont pas. Le proxy n'a aucune opinion sur items.constructor.constructor et n'en a pas besoin. Il lit la destination, ne trouve rien d'approuvé, et coupe la connexion. Votre installation se termine, votre build tourne, et le RAT ne s'assemble jamais, parce que ses pièces ne se retrouvent jamais dans la même pièce.

La persistance obtient une machine dont la durée de vie est une tâche. Supposez que toute la chaîne s'exécute quand même, parce que vous avez approuvé une destination plus large que prévu et que les étapes passent. La ligne dans ~/.zshrc, le LaunchAgent aux dix minutes, l'app.asar piégé, le Python .skills patché : tout cela s'écrit dans une VM Linux jetable, à un hyperviseur de macOS, qui héberge l'espace de travail de cette tâche, et que vous jetez quand le travail est mergé. aipoison parie que l'outillage qu'il patche et l'hôte qu'il a compromis sont la même machine à longue durée de vie. Dans un profil, ce pari est perdant. La reconnaissance qui s'exécute d'abord revient avec des leurres du broker : vos vrais identifiants vivent sur l'hôte et ne sont injectés au fil que pour les destinations approuvées, donc ils n'ont jamais été dans la VM pour être collectés.

Assemblage du RAT : rien d'hostile dans le paquet1 · npm installarchives sainespas de postinstall3 utilitaires2 · preferences.jsonGitHub de l'attaquantévasion vm en règlemodifiable après l'audit3 · setting.jsdu stockage cloudpayload selon l'OS.zshrc · LaunchAgent4 · C2shell · fichiers · proxyaipoison → .skillspatche votre outil IAÉtapes 2, 3 et 4 : des événements réseau. Le code lisible n'exécutait que l'étape 1.Mêmes quatre étapes, dans un profil BromureVM Linux jetableagent · npm · tout ce qu'écrit le payload.zshrc, LaunchAgent, .skills → cette machineidentifiants à portée : des leurresjetée à la fin de la tâchechaque sautProxy hôte, sur votre Mac1 · registre → réputation, sinon on demande2 · raw GitHub de l'attaquant → refusé3 · bucket inconnu → refusé4 · C2 → refusé
L'attaque s'assemble à partir de trois requêtes. Rien dans les archives npm n'est malveillant : la logique hostile arrive d'un fichier GitHub que l'attaquant contrôle encore, les étapes arrivent du stockage cloud, puis le RAT appelle la maison. Dans un profil Bromure, le proxy hôte vérifie chacun de ces sauts contre les destinations que vous avez approuvées pour cet espace de travail, et chaque fichier écrit par le payload atterrit dans une VM qui existe le temps d'une tâche.

Relire un artefact vous renseigne sur l'artefact

Les chercheurs redonnent cette leçon chaque semaine dans une bibliothèque différente. Hier, un flag de confiance est tombé parce que la vérification et le chargement se produisaient à deux moments différents. En juin, un dépôt est resté propre parce que le reverse shell vivait dans un enregistrement DNS. Aujourd'hui, dix-huit paquets se lisent propres parce que la partie malveillante est un fichier JSON que l'attaquant tient encore.

Les agents aiguisent le problème, parce qu'ils sont rapides. Un agent à qui l'on demande de câbler une API interne ajoute la dépendance, lance l'installation et passe à l'étape suivante de la tâche le temps que vous changiez de fenêtre. Cette vitesse est la raison même pour laquelle vous l'avez acheté. Placez le jugement là où la vitesse ne peut pas le dépasser : à la récupération sur le registre et au fil, où votre Mac décide contre une politique que vous avez définie une fois, pour chaque paquet et chaque connexion de l'agent.

Continuez à laisser l'agent installer des choses, parce que c'est là qu'est le levier. Installez Bromure Agentic Coding, activez les couches supply chain, et donnez au profil une liste de destinations qui ont du sens pour le travail. La prochaine campagne cachera son payload ailleurs, la seule prédiction sûre dans ce métier, et elle devra quand même le récupérer à travers un fil qui vous demande d'abord.