L'attaque de la chaîne d'approvisionnement de Tensorlake sur npm et le piège de la suppression des jetons

Les correctifs php2shell (CVE-2026-109618 et CVE-2026-109619) pour les versions de PHP en fin de vie sont désormais disponibles. Les réponses du service commercial étant plus lentes que d'habitude en raison d'une forte demande, le moyen le plus rapide d'obtenir les paquets corrigés est d'acheter ELS for Runtimes en ligne (s'ouvre dans un nouvel onglet). Consulter la FAQ sur les correctifs

Accédez directement au résumé généré par l'IA de cet article de blog

  • Résumez cet article avec Perplexity
  • Résume cet article avec Gemini

En bref

Quoi : Un fichier malveillant [email protected] Il a été publié sur npm le 8 octobre 2026 à 01 h 12 UTC. Pas de « typosquat » cette fois-ci. Il s’agit du véritable SDK TypeScript de Tensorlake, créé selon le workflow propre au projet et dont la provenance est validée par Sigstore. A preinstall hook utilise Bun pour exécuter une charge utile obscurcie de 856 Ko de type ChainDrop / Shai-Hulud.

Impact : Socket enregistre environ 12 000 téléchargements hebdomadaires, tandis qu’Aikido compte plus de 100 000 installations cumulées. Là où le hook s’est exécuté, il vole les identifiants, les mots de passe des navigateurs et les configurations des outils d’IA. Si le jeton GitHub volé appartient à un compte n’appartenant à aucune organisation, il installe également un programme de surveillance qui tente d’effacer le répertoire personnel lorsque ce jeton est révoqué.

Statut : Socket l'a signalé en moins de 11 minutes, et The Hacker News Selon certaines informations, npm l'aurait retiré. Tensorlake a annulé les commits dans demande de modification n° 1016. À l'heure où nous écrivons ces lignes, la version 0.5.145 n'avait pas encore été publiée et six tensorlake-native-* Les paquets étaient toujours disponibles à la version 0.5.144. Vous l'avez installé ? Faites une sauvegarde, retirez le moniteur, puis effectuez la rotation.

La version longue

Êtes-vous concerné ?

Deux questions se posent. La version 0.5.144 a-t-elle été installée sur un système qui vous intéresse, et son hook d'installation s'est-il exécuté sur ce système ?

Le premier point dépend de la manière dont vous procédez à l'installation. tensorlake est toujours dans la branche 0.x, donc une plage telle que ^0.5.140 accepte volontiers toute version 0.5 ultérieure. Les projets s'installant à partir d'un fichier de verrouillage existant avec npm ci est resté en place. Le risque venait du fait de procéder à une mise à jour alors que la version défectueuse était encore active, via npm update, une pull request Dependabot ou Renovate fusionnée ce matin-là, une première installation, ou un fichier Dockerfile qui copie package.json sans le fichier de verrouillage. Toute personne ayant récupéré la branche principale du dépôt entre le 7 octobre et la restauration effectuée par Tensorlake et ayant exécuté npm install dans typescript/ J'ai aussi utilisé la commande « hook », sans passer par le registre.

En ce qui concerne la deuxième question, il n'était pas nécessaire d'importer le SDK. Il suffisait de l'installer avec les scripts de cycle de vie activés, même dans le cas d'une compilation qui échouait une minute plus tard. Avec ignore-scripts=true dans .npmrc, le hook ne s'est jamais déclenché. À partir de la version 12 d'npm et de la version 10 de pnpm, les scripts de dépendances sont mis en attente jusqu'à ce que vous les validiez (npm 11 se contente d'afficher un avertissement), et Bun ne les exécute que pour les paquets figurant sur sa liste de confiance ; vérifiez toutefois votre configuration avant de vous fier à cela. StepSecurity Il a été constaté que le chargeur se ferme dès qu’il détecte une intégration continue (CI), ce qui donne la priorité aux ordinateurs portables des développeurs. Cela réduit le risque lié à l’intégration continue sans toutefois l’éliminer, puisque Socket a tout de même détecté la collecte d’identifiants d’intégration continue dans la charge utile.

Le rg Cette ligne sert au triage sommaire, donc traitez tout 0.5.144 sur un tensorlake ligne en tant que piste. Elle ignore les fichiers « .gitignored » et les fichiers binaires, ce qui signifie qu'un ancien fichier binaire bun.lockb n'apparaîtra pas :

npm ls tensorlake --all
npm ls --all | grep tensorlake-native
pnpm why tensorlake
yarn why tensorlake
rg -n 'tensorlake' -g '*lock*' -g 'npm-shrinkwrap.json' .

Vérifiez ensuite le disque et le profil utilisateur :

find "$HOME" -path '*/node_modules/tensorlake/lib/Math_Symbol.js' 2>/dev/null
ls -la ~/.config/gh-token-monitor ~/.local/bin/gh-token-monitor.sh 2>/dev/null

Élargir le find pour créer des caches et des runners, ou pour utiliser l'EDR sur des parcs d'appareils gérés. Vérifiez que Math_Symbol.js frappé par les hachages dans « Mitigation ». A gh-token-monitor Si le répertoire est présent, cela signifie que le piège est activé ; passez donc directement à la section « Atténuation ». S'il est absent, l'hôte n'est pas validé, car le moniteur ne s'active que pour certains comptes.

En quoi consiste l'attaque « Tensorlake » ?

Tensorlake exécute du code non fiable, généré par des modèles, dans des environnements isolés (sandboxes), et le SDK npm permet aux applications TypeScript de les piloter. Les équipes isolent tout ce que leurs agents écrivent, puis installent le SDK sur un ordinateur portable ayant accès à l'ensemble du système. L'environnement isolé n'a jamais connu de défaillance. Le logiciel malveillant s'est exécuté sur la machine d'installation avant même qu'un environnement isolé n'intervienne.

L'attaquant a réussi à s'introduire via le dépôt source. Selon StepSecurity, le premier commit malveillant, e90c47b, a atterri sur la piste principale à 01 h 20 UTC le 7 octobre, suivi de sept autres qui ont réglé la charge utile et ajouté le preinstall ligne. Aucune n'a fait l'objet d'une demande de fusion. Selon le rapport publié par Tensorlake, ces modifications provenaient d'un compte administrateur du dépôt via l'interface web de GitHub, ce qui explique pourquoi Aikido les a vues marquées comme « vérifiées ». GitHub signe lui-même les commits effectués via son interface web ; le badge indiquait donc simplement qu'un compte était connecté, sans plus. La manière dont ce compte a été piraté n'a pas été rendue publique.

Environ un jour plus tard, ce même compte a publié manuellement le repo’s publish_npm.yaml flux de travail. Il a compilé, signé et publié la version 0.5.144. Le scanner de Socket l'a détectée à 01:23:10 UTC, et StepSecurity l'a signalée aux responsables de la maintenance dans Tique GitHub n° 1014. Les noms de fichiers correspondent à la vague ChainDrop du mois d'août, à savoir keyv, et l'aïkido a trouvé un WORMTAG un indicateur suggérant une nouvelle infection plutôt qu'une réinfection résiduelle.

Fonctionnement du jeton « otage »

Lorsque la charge utile obtient un jeton GitHub à partir d'un compte n'appartenant à aucune organisation, elle installe un service appelé gh-token-monitor, selon Décodage de SafeDep. Les jetons des comptes appartenant à une organisation sont tout de même volés, même sans ce piège. Il s’agit d’une unité utilisateur systemd sous Linux, d’un LaunchAgent sous macOS et d’une tâche planifiée au démarrage sous Windows. Toutes les 60 secondes, pendant 24 heures maximum, elle appelle api.github.com/user avec le jeton volé. Selon StepSecurity, dès que GitHub rejette le jeton, il exécute rm -rf ~/ sous Linux et macOS, ou via une commande PowerShell supprimant le profil utilisateur sous Windows. Le code contient même la chaîne de caractères IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner, et GMO Flatt Security signale que des fichiers ont été supprimés sur des machines infectées.

Si vous consultez pratiquement n’importe quel guide de procédure en cas de vol d’identifiants, la première étape consiste à révoquer le jeton. Sur un hôte où ce moniteur est toujours en cours d’exécution, cette étape peut entraîner la suppression du répertoire personnel du développeur, que la révocation soit effectuée par le développeur lui-même, par un administrateur ou par un playbook automatisé. Si l’automatisation révoque les jetons liés à des hôtes signalés, suspendez cette opération pour les hôtes concernés jusqu’à ce que les moniteurs aient été supprimés.

Débrancher le câble réseau n'est pas non plus une solution garantie, car personne n'a encore expliqué comment l'écran réagit en cas de perte de connexion. Sauvegardez les données critiques, contactez votre équipe d'intervention en cas d'incident pour mettre en place les mesures de confinement, et agissez rapidement, car l'attaquant peut continuer à utiliser ce jeton pendant tout ce temps.

À l'intérieur de la charge utile

Le programme de chargement vérifie la présence de CI ; s'il en détecte, il se ferme, télécharge Bun, puis passe le relais à Math_Symbol.js, de sorte que le véritable travail s'effectue dans un processus qui n'est pas node.

Il cible les jetons npm, y compris l'échange OIDC utilisé par la publication de confiance, et extrait des données des métadonnées des instances AWS, de Secrets Manager et de SSM Parameter Store ; GMO Flatt Security a constaté qu'il lisait ces données dans toutes les régions. Il interroge un Vault local à l'adresse 127.0.0.1:8200 et analyse les jetons Kubernetes, les clés SSH, .env fichiers, configurations Docker et portefeuilles cryptographiques. Aikido a détecté que le malware déposait un fichier « HackBrowserData » contenant les mots de passe et les cookies enregistrés. Il cible également les fichiers de configuration et les fichiers MCP de Claude, Cursor, Kiro, Windsurf et Zed, qui contiennent souvent des clés API en clair.

Les données volées sont transmises, sous forme chiffrée, à iseekaigogo.com, le domaine C2 codé en dur dans la charge utile, ou vers un nouveau dépôt public sur le compte GitHub de la victime, intitulé « Shai-Hulud : Here We Go Again ». À défaut, Aikido a constaté que le logiciel malveillant lisait un contrat Ethereum contrôlé par l'attaquant à l'adresse 0xb614155Fd88114d40549b259457Bcf921Df091B9 pour un autre domaine. Sa dernière mise à jour, le 21 septembre, provient du portefeuille 0x779f83aE56309682beDb04816c19d358c4B21040, pointe vers le même domaine. Une suppression peut aider, mais une seule transaction suffit à le rediriger.

Puis il se propage. À l’aide d’un jeton npm, il s’injecte dans les paquets de la victime, modifie le numéro de version, génère une provenance Sigstore, puis republie le tout. À l’aide d’un jeton GitHub, il effectue un commit .claude/settings.json et .vscode/tasks.json vers les dépôts auxquels il a accès, en utilisant l'auteur [email protected] et le message « chore : mise à jour des dépendances ». Les hooks de Claude Code peuvent exécuter des commandes shell et les tâches VS Code peuvent s'exécuter à l'ouverture d'un dossier ; ces fichiers peuvent donc constituer un moyen de revenir en arrière, selon leur configuration et les paramètres de confiance du prochain développeur.

Ce qui devrait changer lors de l'accueil

La provenance indiquée dans la version 0.5.144 est authentique. Elle précise correctement que le paquet provient de tensorlakeai/tensorlake via le workflow propre au projet, ce qui correspond exactement à ce que la traçabilité prétend offrir. Lorsqu'un compte administrateur effectue un commit directement dans la branche « main », le pipeline réel compile le logiciel malveillant et le signe. Une politique qui vérifie la traçabilité mais qui n'examine jamais le code certifié aurait accepté cette version.

Les fichiers de verrouillage n'ont aidé que par inertie, et aucun scanner CVE n'a trouvé de correspondance. En revanche, la différence entre les versions 0.5.143 et 0.5.144 était difficile à manquer, avec un nouveau preinstall un script, un nouveau chargeur et un fichier obfusqué de 856 Ko dans une version corrective. Peu d’équipes consultent les différences des archives tar pour les mises à jour de patch, mais dans les fichiers de verrouillage npm plus récents, une modification apportée à un paquet hasInstallScript « entry » est un indicateur peu coûteux à surveiller.

Le temps aide aussi. Socket a détecté ce problème en 11 minutes ; il faudrait donc un délai de 24 heures avant la publication (selon la politique de npm) min-release-age, ou celles de pnpm minimumReleaseAge, dont la valeur par défaut est de 24 heures depuis la version 11 de pnpm), l’aurait exclu de la plupart des builds. La réponse de Tensorlake, présentée dans la pull request n° 1016, constitue également un modèle satisfaisant : suppression du contournement administrateur de la branche « main », obligation de commits signés, présence d’un deuxième réviseur pour les publications sur npm, et un déclencheur CI qui échoue si un manifeste publié déclare un script d’installation. Pour les utilisateurs, suspendez toute mise à jour de dépendance qui ajoute ou modifie un script d’installation. Un paquet qui n’en contient pas peut toujours exécuter du code lors de l’importation, mais l’ajout d’un nouveau script justifie de s’arrêter.

Atténuation

La sécurité des ordinateurs revêt une importance encore plus grande que d'habitude. Considérez tout hôte sur lequel le hook a été exécuté comme compromis et suivez votre procédure de gestion des incidents.

Commencez par sauvegarder les données essentielles. Copiez sur un support de stockage externe les travaux non validés et les autres données irremplaçables (et non les outils) avant d'intervenir sur les jetons, les services ou le réseau. Conservez les fichiers de verrouillage et les journaux d'installation à titre de preuve.

Vérifiez les artefacts. Vérifiez les postes de travail, les hôtes de compilation et les images créées les 7 ou 8 octobre à l'aide des valeurs SHA-256 publiées. lib/setup.mjs est 25a0735d0db7dc40e5d45ce42d9c106067e6a66e184d967cfecfab17c3bcb5ef et lib/Math_Symbol.js est b50a00900399ba99fb6ce1fc151519cb99d44320ef2a631f2237e1aea0ad6fec. GMO Flatt Security répertorie un fichier qu'elle appelle artifact.tar.gz comme 33108f494dad71369a9aa7cd6daa5402335d1a718b78320b9b687c0b2174fa67, et la copie du journal de publication conservée par SafeDep indique que la valeur SHA-1 de l'archive npm est 843a898ab72793568d77c53ff9282e6ce7c66aea. Les hachages permettent de confirmer une correspondance, mais ne détectent pas les copies modifiées.

Désactivez le moniteur avant de révoquer quoi que ce soit. Le fichier dans ~/.config/gh-token-monitor/ contient le jeton surveillé. Considérez-le comme une preuve sensible et ne le mentionnez pas dans les tickets ni dans les discussions. StepSecurity a publié les commandes Linux et macOS ci-dessous, avec un daemon-reload Ajouté ici. Vérifiez les noms et les chemins d'accès sur la machine concernée avant de les exécuter :

# Linux
systemctl --user disable --now gh-token-monitor.service
rm -rf ~/.config/gh-token-monitor
rm -f ~/.local/bin/gh-token-monitor.sh
rm -f ~/.config/systemd/user/gh-token-monitor.service
systemctl --user daemon-reload

# macOS
plist=~/Library/LaunchAgents/com.user.gh-token-monitor.plist
launchctl bootout gui/(id-u)"plist"
rm -rf ~/.config/gh-token-monitor
rm -f ~/.local/bin/gh-token-monitor.sh
rm -f "$plist"

Sous Windows, cette commande PowerShell en lecture seule répertorie les tâches planifiées qui s'exécutent monitor.ps1, vous pouvez donc les supprimer à l'aide de vos outils de gestion des terminaux :

Get-ScheduledTask | Where-Object {
  "(_.Actions.Execute) (_.Actions.Arguments)" -match 'monitor\.ps1'
}

Assurez-vous qu’il a bien disparu avant de passer à autre chose. Supprimer node_modules Je n'y toucherai pas, et le fait de supprimer ce moniteur ne prouve pas qu'il n'y ait pas d'autres données persistantes.

Effectuez la rotation à partir d'une machine propre. Révocer le jeton GitHub surveillé, puis tous les accès dont disposait ce compte, y compris les identifiants pour npm, le cloud, SSH, Kubernetes, Vault et le registre, .env les secrets, ainsi que les clés d'accès aux outils d'IA et à l'API MCP. Considérez que les mots de passe et les sessions du navigateur ont été compromis, et fermez les sessions actives tant que vous y êtes.

Nettoyez vos dépôts et vérifiez ce que vous publiez. Recherchez les commits provenant de [email protected] avec « tâche : mise à jour des dépendances », errant .claude/settings.json ou .vscode/tasks.json des fichiers, des workflows inconnus et de nouveaux dépôts publics portant la description « Shai-Hulud : Here We Go Again ». Si le jeton npm d’un responsable a été compromis, recherchez les versions que personne de votre équipe n’a publiées.

Bloquer et vérifier la sortie. Bloc iseekaigogo.com par domaine. Listes de Flatt 172.67.207.33 et 104.21.22.217 comme adresses, mais il s'agit d'adresses IP Cloudflare partagées ; utilisez-les donc pour les recherches dans les journaux, et non dans les règles de pare-feu.

En cas de doute, épinglez, appliquez les règles et réinstallez l'image. Attendez tensorlake à la version 0.5.143, ce qui fixe également ses paquets natifs à la version 0.5.143, jusqu’à ce que Tensorlake publie et confirme la version 0.5.145. Conserver tensorlake-native-* à partir de la version 0.5.144. Réinstallez le logiciel sur un hôte vierge, enregistrez le fichier de verrouillage, puis utilisez npm ci dans CI. Si vous ne parvenez pas à identifier tous les points de persistance, reconstruisez la machine à partir d'une image dont vous savez qu'elle fonctionne correctement.

Détection

La chaîne de processus est le meilleur indicateur. Recherchez npm ou node frai node lib/setup.mjs, qui télécharge et lance ensuite bun. L'apparition de « Bun » dans la liste des paquets installés sur une machine qui ne l'a jamais eu est en soi suspecte, tout comme le sont les éléments d'installation qui indiquent .npmrc, .aws, .kube, les clés SSH ou les profils de navigateur.

Pour assurer la persistance, surveillez les unités utilisateur systemd, les LaunchAgents ou les tâches ONLOGON créées dans le cadre d'un processus Node ou Bun. Un processus autre qu'un navigateur effectuant une interrogation api.github.com/user « Une fois par minute » : c'est à peu près la description la plus précise que vous puissiez obtenir.

Comment SecureChain pour JavaScript change la donne

SecureChain pour JavaScript déplace la décision de confiance en amont, avant même qu’un paquet n’atteigne un ordinateur portable. Il recompose les paquets à partir du code source dans des environnements sécurisés et isolés, puis les analyse à la recherche de logiciels malveillants connus, de modèles de code suspects et de risques liés à la chaîne d’approvisionnement, en bloquant tout ce qui ne passe pas l’inspection. Une simple recompilation n’aurait pas suffi à protéger qui que ce soit dans ce cas précis, car le code corrompu se trouvait dans le code source en amont. C’est l’analyse qui permet de détecter ce genre de problème, et la publication d’un correctif qui ajoute soudainement un preinstall Le fait de s'introduire dans le système, de télécharger un deuxième moteur d'exécution et d'exécuter 856 Ko de code JavaScript obfusqué correspond exactement au type de modification que ce système est conçu pour empêcher.

Découvrez SecureChain pour JavaScript →

Il s'agit d'une solution de remplacement de référentiel prête à l'emploi, compatible avec JFrog Artifactory, Sonatype Nexus et GitHub Packages, donc le npm Les commandes restent identiques, et la solution fournit des informations de provenance conformes à la norme SLSA niveau 3, ainsi que des fichiers SBOM et VEX. Elle prend en charge JavaScript, Python, Java, Go, Rust et PHP. Et lorsque des incidents comme celui-ci poussent les équipes à geler une dépendance jusqu’à ce qu’elle atteigne sa fin de vie, le service « Endless Lifecycle Support » de TuxCare peut continuer à fournir des correctifs de sécurité pour cette version tant que vous en avez besoin. Pas de mises à niveau obligatoires, ni de réécriture du code.

Pourquoi cela va bien au-delà d'un simple forfait

En mai, une vague « Mini Shai-Hulud » a déferlé sur TanStack, accompagnée d'une provenance SLSA valide, et en juin, on a assisté au « typosquat » de Mastra. En août, ChainDrop a fait son apparition via keyv et la faille « arrayref » sur crates.io. Aujourd’hui, cette même famille de vers a atteint l’infrastructure des agents d’IA. On constate sans cesse que les défenseurs considèrent un éditeur authentifié ou une attestation valide comme la preuve que le code est fiable. Dans ce cas précis, le nom du paquet et sa provenance étaient tous deux authentiques. Ils ont simplement répondu à une question différente de celle qui importait.

Découvrez SecureChain pour JavaScript →

FAQ

Quelles versions de Tensorlake sont sûres ?

La version 0.5.143 est la dernière version « propre » et était encore celle de npm latest au moment où nous écrivons ces lignes. Passez à la version 0.5.145 dès que Tensorlake l'aura publiée, et conservez tensorlake-native-* à partir de la version 0.5.144.

Est-il sans risque de révoquer immédiatement le jeton GitHub volé ?

Pas si le moniteur est peut-être encore en cours d'exécution. Sur un hôte où gh-token-monitor Si le moniteur est actif, la révocation de son jeton peut entraîner la suppression du répertoire personnel ou du profil utilisateur. Sauvegardez les données, désinstallez le moniteur, puis révoquez et remplacez le jeton, et faites-le rapidement.

Rejoignez plus de 4 500 professionnels de Linux et de l'Open Source!

2 fois par mois. Pas de spam.

Table des matières
Obtenez les réponses à vos questions sur la sécurité des logiciels libres

Rejoignez plus de 4 500 professionnels de Linux et de l'Open Source!

2 fois par mois. Pas de spam.