- Le 8 septembre, l’injection de code malveillant dans des packages npm populaires a été détectée
- Au total, 18 packages ont été touchés, avec plus de 2 milliards de téléchargements par semaine à l’échelle mondiale
- Le code de l’attaquant permettait d’intercepter discrètement les opérations liées aux cryptomonnaies et au Web3 dans le navigateur des visiteurs de sites web, et de rediriger les autorisations et les flux de fonds des portefeuilles vers des comptes contrôlés par l’attaquant
- Il a été confirmé que du code JavaScript obfusqué avait été ajouté au fichier principal du package (
index.js)
- L’incident semble avoir commencé en même temps que la mise à jour des packages visés, et la communauté est actuellement en train de réagir
Aperçu de l’incident
- Au 8 septembre à 13:16 UTC, le flux de surveillance de sécurité d’Aikido a détecté le téléversement sur npm de plusieurs packages contenant du code malveillant
- Ces packages sont très populaires sur npm et totalisent plus de 2 milliards de téléchargements par semaine
Mode opératoire et contenu de l’attaque
- Après la mise à jour malveillante, il a été confirmé qu’un malware JavaScript s’exécutait secrètement dans le navigateur des visiteurs des sites utilisant ces packages
- L’objectif de ce code est de surveiller les activités liées aux cryptomonnaies et au Web3, de modifier les interactions avec les portefeuilles et de changer sans autorisation les adresses de destination des paiements
- Sans changement visible à l’écran, les fonds et les autorisations des utilisateurs peuvent être transférés vers des adresses de cryptomonnaie désignées par l’attaquant
Analyse détaillée du code malveillant
- Par exemple, dans
is-arrayish, le fichier index.js a été altéré pour y insérer un JavaScript obfusqué et complexe
- Le code exploite l’interface
window.ethereum pour vérifier les informations du compte du portefeuille et valider les conditions de déclenchement de la charge malveillante
- Il intègre en interne de nombreuses adresses de cryptomonnaie (Bitcoin, Ethereum, etc.) et une logique permettant de remplacer l’adresse du portefeuille et les détails des transactions par des adresses contrôlées par l’attaquant
- Cela crée un risque que les actifs en cryptomonnaie des utilisateurs soient exfiltrés et transférés sans autorisation à leur insu
Situation actuelle et réponse de la communauté
- Les versions malveillantes des packages concernés sont en cours de suppression rapide des principaux registres npm
- Dans les communautés IT et open source, des consignes d’arrêt d’utilisation et de mise à niveau des packages concernés sont diffusées, tandis que les efforts de détection et de remédiation se poursuivent activement
- Cet incident contribue fortement à renforcer la vigilance autour de la sécurité de la supply chain des packages, de la détection du code obfusqué et de la protection des extensions de navigateur Web3
2 commentaires
> 8 septembre 2023,
La date est incorrecte à cause d’une hallucination. Il ne s’agit pas de 2023, c’est actuel.
J’ai l’impression qu’on entend assez souvent parler de piratages sur npm. Il semble y avoir un problème.
Avis Hacker News
Oui, je me suis fait pirater. J’ai vraiment honte et je suis désolé pour tout le monde. Pour les détails, merci de consulter ici et ici. J’y ai publié la liste des paquets affectés. Cette attaque semble ciblée. Je continuerai à mettre à jour jusqu’à la fin du délai de modification du commentaire. Le paquet Chalk a été restauré, mais les autres sont toujours compromis (8 à 17:50 CEST). Toujours aucune nouvelle de NPM. Mon compte NPM est inaccessible, et la récupération de mot de passe ne fonctionne pas non plus. Tout ce que je peux faire maintenant, c’est attendre. L’email du support venait de npmjs dot help et paraissait tout à fait crédible. Ce n’est pas une excuse, mais c’était un matin fatigant, et en voulant simplement régler une tâche de plus, j’ai cliqué sur le lien au lieu d’aller comme d’habitude directement sur le site officiel (sans doute parce que j’étais sur mobile). Cette attaque n’a affecté que NPM, et je continuerai à publier des mises à jour sur
/debug-js. Encore une fois, je suis vraiment désoléDans une situation aussi stressante, ta réaction rapide et transparente est vraiment exemplaire. On pense toujours qu’on ne se fera pas avoir par du phishing, mais j’ajouterais quelques conseils : 1) n’utilisez jamais de lien reçu par email pour vous connecter. Il est devenu trop difficile de distinguer un email de phishing d’un email légitime, donc la seule façon de ne pas se faire piéger est de ne jamais essayer. 2) utiliser une clé de sécurité U2F/Webauthn comme second facteur protège presque parfaitement contre le phishing. Ce n’est pas le cas du TOTP. Au final, n’importe qui peut faire une erreur quand il est fatigué ou pressé. Cette fois, c’est simplement tombé sur toi. Encore bravo pour la façon dont tu gères ça
D’après l’indication de sindresorhus, vous pouvez vérifier si votre arbre de dépendances contient le malware avec la commande suivante :
rg -u --max-columns=80 _0x112fa8(ripgrep requis, installation avecbrew install rg). Voir aussi le commentaire d’origineÀ l’époque de l’université, je me suis déjà fait piéger par du phishing en étant ivre (ça remonte à longtemps). Ça peut arriver à n’importe qui. En revanche, je suis surpris par la lenteur de la réponse de NPM. J’ai l’impression que cela ne fait qu’avantager davantage les attaquants
Socket a détecté la situation immédiatement. C’est aussi traité dans leur billet de blog. Ce qui est arrivé est regrettable, mais je trouve positif que l’écosystème open source ait réagi très vite. Ce genre d’incident rappelle une fois de plus pourquoi le scan de paquets est important
Merci d’avoir donné l’alerte rapidement. J’ai envoyé un email à porkbun pour demander le blocage du domaine
Il y a un aspect particulièrement sournois de cette charge malveillante qui n’a pas reçu assez d’attention. Elle ne remplace pas simplement les adresses de portefeuille au hasard : elle calcule la distance de Levenshtein entre la bonne adresse et chacune des adresses de sa propre liste, puis choisit le portefeuille de l’attaquant le plus similaire. Elle est donc conçue pour contourner l’habitude de sécurité très répandue qui consiste à ne vérifier qu’approximativement le début et la fin d’une adresse. Quelqu’un a désobfusqué et analysé l’intégralité de la charge, y compris cette fonctionnalité particulière, et en a fait un article détaillé. Restez prudents
Il y a un point de l’article qui me perturbe : je croyais que package-lock.json verrouillait une version précise. package.json peut définir « version x ou plus », mais dans un lockfile, la version exacte de chaque dépendance ainsi que l’URL du tarball sont inscrites noir sur blanc. Avec un lockfile, un paquet ne devrait normalement pas se mettre à jour automatiquement en environnement CI. Je me demande donc si je comprends mal le fonctionnement des lockfiles npm/yarn/pnpm. Voir aussi cette citation de la documentation npm
Je pense que si l’on affichait une valeur de hachage avec une couleur différente pour chaque caractère (couleurs de premier plan/arrière-plan déterminées par le hachage et l’index), il serait beaucoup plus facile de distinguer visuellement les hachages imités de près
Je me demande s’il existe des informations permettant de relier cette technique à un groupe de pirates en particulier
Ce code d’attaque est malin, mais sur le web, on lutte contre les attaques par adresses similaires depuis des décennies ; ce n’est qu’une version plus dynamique de la même chose. Je ne suis pas d’accord avec le fait de le présenter comme quelque chose de remarquablement sophistiqué. Honnêtement, tout cet article d’analyse donne plutôt l’impression d’avoir été écrit par une IA, donc je ne le trouve pas très rigoureux
J’ai laissé un commentaire similaire il y a 12 jours quand la même chose est arrivée à Nx. Ce n’est pas l’échec d’une seule personne, c’est l’échec de toute une industrie. Les attaques sur la supply chain ont un impact énorme et, selon moi, ce sont des problèmes déjà résolus depuis longtemps. Nous sommes des développeurs logiciels : si nous appliquions partout des mesures de sécurité standard (signature de code, signature d’artefacts, détection des comportements anormaux sur les comptes, 2FA, etc.), on pourrait les empêcher. Si toutes les plateformes de packaging ne le font toujours pas, ce n’est pas à cause d’une limite technique, mais parce que personne ne les y oblige. Avec les progrès de l’IA et le succès répété des attaques réelles, cela va devenir encore plus grave. Il est temps de rendre obligatoires des normes de sécurité strictes
Ces mesures de sécurité impliquent des compromis. Par exemple, des mécanismes heuristiques ou fondés sur des preuves peuvent exclure beaucoup de systèmes automatisés ou d’utilisateurs ordinaires. La 2FA par SMS est peu sûre, et l’email est vulnérable au phishing. Le TOTP n’a d’intérêt que lorsqu’il est utilisé comme standard ouvert, mais même là, il n’empêche pas complètement le phishing. Seule l’authentification matérielle est réellement efficace, mais elle a ses propres limites pratiques à grande échelle
Ce n’est pas aussi simple que de dire « il suffit d’appliquer correctement les standards de sécurité pour tout empêcher ». Même avec des mesures parfaites, une erreur humaine peut fragiliser l’ensemble du système. Un système totalement sûr n’existe pas. Les progrès de l’IA rendent les emails de phishing de plus en plus indiscernables des vrais, mais à l’inverse, on peut aussi utiliser l’IA pour mieux détecter ces attaques. Au final, il faudra se défendre avec l’IA
Autrefois, il y avait beaucoup de piratages visant Windows, mais aujourd’hui la population de développeurs JavaScript et Python est bien plus grande. Ce type d’attaque va donc empirer avec le temps
Je pense que NPM porte aussi une part de responsabilité. Divers prestataires de sécurité externes et startups repèrent rapidement ce type d’activité malveillante, alors il est difficile de comprendre pourquoi NPM, qui voit en temps réel tous les paquets et tous les événements de sécurité, reste aussi régulièrement impuissant. On en est presque au niveau du refus délibéré de voir le problème
NPM appartient désormais à GitHub, donc à Microsoft. Ils semblent occupés à intégrer de l’IA générative comme Copilot dans toutes sortes d’applications
Pour les paquets gérés par plusieurs personnes, il devrait au minimum y avoir une option imposant l’approbation d’un autre mainteneur avant qu’une publication soit autorisée
Le même attaquant a injecté en une seule fois une charge obfusquée — et très suspecte — dans plus de 22 paquets restés inactifs pendant longtemps, puis les a publiés simultanément. Je pense qu’il est presque impossible d’exiger de NPM qu’il détecte cela. De mon côté, pour avoir déjà publié des applications/extensions sur d’autres plateformes logicielles, il est courant d’attendre parfois plusieurs jours ou semaines. Ce qui me surprend surtout, c’est qu’alors même que Microsoft et GitHub vendent toutes sortes de solutions de sécurité, rien n’indique qu’ils aient réellement investi dans ce service
Je ne pense même pas que NPM ait une raison particulière de changer quoi que ce soit. Cela fait plus de 10 ans que c’est une source de diffusion de malwares, mais personne n’arrête de l’utiliser, donc l’activité n’en souffre pas
L’une des causes est la prolifération excessive des gestionnaires de paquets. Je n’ai jamais aimé cette dépendance à des paquets pour des choses aussi triviales. Et je déteste aussi l’idée d’aller chercher aléatoirement les dernières versions pour ensuite casser l’environnement. Ce n’est pas seulement npm que je n’aime pas : les gestionnaires de paquets en général m’agacent tous
À ce stade, je pense que quiconque saisit encore manuellement ses mots de passe sur un site qui ne correspond pas au domaine officiel, sans gestionnaire de mots de passe, ne devrait pas faire de choses importantes sur Internet
Un gestionnaire de mots de passe ou l’autocomplétion du navigateur aurait probablement signalé ce faux domaine et aurait pu bloquer une incohérence comme npmjs.help au lieu du domaine officiel, comme dans ce phishing NPM
C’est vrai, mais j’ai aussi plusieurs fois vu des applications officielles et leurs sites web utiliser des domaines complètement différents. Les pires cas sont ceux où l’app mobile et le web n’ont carrément pas le même domaine. Je ne sais pas qui conçoit ce genre de chose
Chaque fois qu’un incident comme celui-ci se reproduit, je ne comprends pas pourquoi les registres de paquets n’imposent pas une signature cryptographique pour tous les paquets. Bien sûr, si la signature est automatisée dans le CI/CD et que ce système est lui aussi compromis, l’attaque peut passer, mais cela empêcherait la plupart des problèmes. En contrepartie, cela obligerait les développeurs à faire quelques étapes supplémentaires comme télécharger manuellement les artefacts, les signer et les envoyer
Un vrai registre le fait déjà, comme Debian. npm est amateur au point d’être souvent interdit dans les environnements d’entreprise
La méthode que je préfère est la vérification a posteriori. Après un upload automatique via CI/CD, il suffirait qu’un humain clique une fois de plus sur le web pour que la publication soit réellement effectuée. Cela réduirait la friction du processus de release tout en ajoutant une validation humaine finale
Mais il reste le problème difficile de « quelle clé de signature faut-il considérer comme digne de confiance ? ». Toute personne ayant compromis la 2FA peut aussi téléverser une nouvelle clé et signer avec. Il faudrait donc sans doute retarder l’enregistrement de nouvelles clés de signature lorsqu’une activité de compte suspecte est détectée
J’en arrive à la conclusion qu’il vaut bien mieux éviter le npm registry. Je pense qu’il vaut mieux récupérer les paquets directement depuis des dépôts (git). Le npm registry est un canal majeur pour les attaques sur la supply chain, et il y a aussi le problème de la séparation complète entre le code source et le code distribué.
npm publishpermet de téléverser directement n’importe quel code local, ce qui facilite l’insertion de code malveillant par un acteur malintentionnéIl existe une fonctionnalité de vérification d’authenticité lors de la publication vers npm depuis GitHub builds, mais comme il semble toujours possible de publier depuis d’autres environnements, je ne comprends pas très bien les garanties exactes
En tant que développeur C, c’est une sensation très étrange d’avoir été critiqué à une époque pour vouloir minimiser les dépendances, utiliser des bibliothèques single-header ou faire du vendoring, et de voir seulement maintenant tout le monde redécouvrir que ces approches étaient en fait nécessaires
La fonctionnalité récente de provenance de npm résout ce problème. Elle est assez facile à configurer et aidera à prévenir ce type d’attaque. Je suis content de voir les grands paquets l’adopter progressivement
Je me demande si cette approche est aussi utilisée en environnement CI. Est-ce qu’il s’agit simplement de remplacer un
npm installcôté serveur par ungit clone?« Récupérer les paquets directement depuis un dépôt git » paraît bien en théorie, mais en pratique npm a beaucoup de bugs, y compris sur l’installation de dépendances git. Comme je l’ai signalé dans cet issue, cela n’a pas vraiment fonctionné jusqu’en 2020 à cause de problèmes d’étapes de build, et il y a encore des soucis avec
npm installen global. Même si le script prepack est documenté dans npm, il ne fonctionne en réalité pas avec les dépendances basées sur git. L’équipe du compilateur TypeScript a aussi dû recourir à un contournement étrange à cause de ce bug, avec du code de contournement et un bug toujours visibles. Le fait que l’échec de prepack ne remonte même pas de code de sortie et quenpm installse termine donc dans un état cassé est aussi un problème. À voir tout cela, j’ai l’impression que npm a désespérément besoin d’une vraie gouvernance opérationnelle, ou alors d’être remplacé par un nouveau gestionnaire de paquetsVu de l’extérieur de l’écosystème npm, il est étonnant de voir à quel point ces nombreux paquets sont utilisés pour la moindre chose triviale
C’est parce que la bibliothèque standard est trop pauvre, et qu’il faut recourir à des paquets externes même pour des fonctions élémentaires. Les réimplémenter soi-même demande énormément de travail, même pour des détails apparemment simples
Avec 15 ans d’expérience en développement, je dirais qu’une grande partie des développeurs JavaScript salariés savent en réalité à peine coder. Ce n’est pas une question de capacité intellectuelle, mais de formation et de culture. Il y a une peur extrême d’écrire du code soi-même, et cela mène à dépendre systématiquement d’outils externes, même pour des éléments mineurs. Les développeurs de projets hobby ont souvent moins ce problème et produisent parfois un code plus robuste. Si cela vous intéresse, demandez à des collègues d’équipe de construire quelque chose sans gros framework, et vous verrez tout de suite
Le fait de prendre de petits modules triviaux séparés sert à éviter d’embarquer dans le client du code inutile. Les bibliothèques tout-en-un offrent certes une meilleure DX, mais elles emportent aussi du code dont on ne veut pas forcément (même si le tree-shaking existe, ce n’est pas une solution miracle)
Depuis l’affaire leftpad, ce débat revient sans cesse. C’est peut-être simplement parce que la bibliothèque standard de JS est trop limitée
Dans une PR de modification de code, importer une ligne depuis un module qui paraît « trusted » semble beaucoup plus facile à examiner, pour un reviewer, qu’un gros changement de code. Ce n’est pas réellement plus sûr, mais quand on est fatigué, cela paraît plus crédible
Lors de l’affaire nx précédente, j’avais déjà le même avis : les gestionnaires de paquets devraient avoir une « période de grâce » qui ignore systématiquement les nouveaux paquets pendant un certain temps, par exemple 24 heures. La plupart de ces attaques sont détectées et bloquées rapidement après publication, donc si les utilisateurs n’installaient pas automatiquement la toute dernière version pendant cette fenêtre, les dégâts réels seraient réduits
J’imagine la douleur et le stress qu’a dû vivre l’auteur. Devoir continuer à s’expliquer pour une seule erreur doit être très éprouvant. Cela montre aussi à quel point l’écosystème open source dépend fortement de paquets détenus de fait par une seule personne. Il faut admettre que n’importe qui peut se faire pirater à cause d’une erreur. Sur le plan technique, alors que l’IA est déjà très utilisée, il faudrait sans doute que deno/node/bun affichent des alertes sur le code suspect, qu’un système de publication de type @verified fondé sur une validation à la Debian renforce la confiance, et qu’on change les habitudes pour privilégier des versions vérifiées plutôt que la toute dernière. L’auteur reste un être humain, et nous devrions tous faire preuve de bienveillance. Une fois la situation stabilisée, j’aimerais aussi lire une analyse technique plus détaillée ou un postmortem