Connexion Premium

L’attaque sans précédent sur NPM relance les débats sur la chaîne d’approvisionnement

Interface chaise-clavier

L’attaque sans précédent sur NPM relance les débats sur la chaîne d’approvisionnement

Illustration : Flock

Dans l’après-midi du 8 septembre, une attaque a eu lieu contre la chaine d’approvisionnement de plusieurs paquets NPM. Les dégâts ont été limités et tout est vite rentré dans l’ordre. Mais les conséquences auraient pu être bien pires, limitées uniquement par les compétences des pirates. L’affaire relance les débats autour de l’authentification et du contrôle de la provenance des modifications.

Elle est qualifiée désormais de plus grande attaque contre la chaine d’approvisionnement jamais enregistrée. Une telle attaque consiste pour rappel à viser la chaine menant à la production d’un logiciel ou d’un service. Si elle aboutit, un code se retrouve distribué ou mis à disposition des victimes, qui croient récupérer un logiciel, la dernière version d’un composant, etc. Elle permet d’arroser un nombre important de personnes, d’autant plus que le composant ou logiciel est populaire.

Dans l’après-midi du lundi 8 septembre, des pirates ont ainsi compromis le compte NPM (Node Packet Manager, gestionnaire de paquet par défaut pour Node.js) d’un développeur. La récupération des accès leur a permis d’infecter le code de 18 paquets : backslash, chalk-template, supports-hyperlinks, has-ansi, simple-swizzle, color-string, error-ex, color-name, is-arrayish, slice-ansi, color-convert, wrap-ansi, ansi-regex, supports-color, strip-ansi, chalk, debug, ansi-styles.

Certains sont téléchargés des centaines de millions de fois par semaine. Pourtant, tout a été réglé en deux heures environ et la casse a été limitée. Que s’est-il passé ?

L’ingénierie sociale, toujours elle

L’histoire commence avec une détection de l’entreprise belge Aikido. Elle est spécialisée dans la sécurité, et plus particulièrement dans la surveillance des mises à jour de code dans les principaux dépôts open source. Comme elle raconte dans un billet de blog, elle détecte le 8 septembre à 15h16 (heure de Paris) des modifications suspicieuses dans 18 paquets. Ils sont très populaires sur NPM : ensemble, ils cumulent deux milliards de téléchargements par semaine.

L’analyse du code révèle sa fonction : intercepter silencieusement l’activité crypto et web3 dans le navigateur, manipuler les interactions avec le portefeuille et rediriger les paiements vers des comptes contrôlés par les pirates. Le code malveillant peut détourner à la fois le trafic réseau et les API des applications, indique Aikido. « Ce qui le rend dangereux, c’est qu’il fonctionne à plusieurs niveaux : modifier le contenu affiché sur les sites Web, falsifier les appels API et manipuler ce que les applications des utilisateurs croient signer », ajoute l’entreprise.

La société belge indique avoir alors contacté le développeur concerné, Josh Junon, surnommé Qix. Celui-ci répond alors qu’il a découvert avoir été piraté. Comme il l’indique lui-même dans un message sur BlueSky deux heures plus tard, il dit avoir été victime d’un email qui l’invitait à réinitialiser ses codes d’authentification à deux facteurs (2FA). Le courrier semblait parfaitement légitime selon lui, avec un lien renvoyant vers une copie conforme de la page de connexion de NPM. Cette page interceptait les informations d’authentification et le jeton 2FA pour les envoyer aux pirates. Après quoi, ces derniers ont simplement modifié l’adresse de connexion utilisée pour se connecter à NPM.

Le 9 septembre, Josh Junon a publié un message d’excuses sur Hacker News, dans lequel il admet honteusement : « yep I got pwned ». Le même jour, jFrog indiquait de son côté que la campagne continuait et que de nouveaux paquets contaminés avaient été découverts, dont DuckDB. Toujours le 9 septembre, SlowMist avertissait que d’autres développeurs recevaient le même e-mail, signe que Josh Junon n’était pas un cas isolé.

Tout s’enchaine très vite

Une chaine d’approvisionnement contaminée sur des paquets aussi populaires aurait constitué une catastrophe pour de nombreux produits. Ces attaques sont notamment très efficaces contre des composants impliqués dans le web de manière générale. Or, avec la multiplication des applications web encapsulées, cela pouvait signifier une diffusion à très grande échelle du code vérolé. Pourtant, tout s’est achevé en quelques heures, sans grande casse.

Sur le blog de la Security Alliance, on peut lire dans le billet du 9 septembre une note ironisant sur les 5 cents d’ETH ou encore les 20 dollars d’un memecoin. Dans les heures qui ont suivi la compromission, seuls 588 dollars de transactions auraient été détectés. Ce qui fait dire à l’Alliance que le plus gros impact financier de l’attaque réside finalement dans « les milliers d’heures passées collectivement par les équipes d’ingénierie et de sécurité du monde entier à nettoyer les environnements compromis, et les millions de dollars de contrats de vente qui seront inévitablement signés à la suite de cette nouvelle étude de cas ».

Cité par Brian Krebs, le pentester Philippe Catureli, responsable sécurité chez Seralys, s’en étonne également : « Ce qui est fou, c’est qu’ils ont compromis des milliards de sites Web et d’applications juste pour cibler quelques crypto-monnaies. Il s’agissait d’une attaque de la chaîne d’approvisionnement, et cela aurait facilement pu être quelque chose de bien pire que la récolte de crypto-monnaies ».

Même son de cloche chez Florian Roth, chercheur en sécurité chez Nextron Systems : « Étant donné que la plupart des entreprises exécutent au moins une application React ou Angular, elles ont eu la possibilité d’exécuter du code sur des millions de systèmes dans des milliers d’organisations. Et ils l’ont utilisé pour lacher un voleur de cryptomonnaies obscurci de manière amateur, ont été attrapés par des règles élémentaires de détection, et le problème a été résolu après 2 heures ». Pas mieux du côté du chercheur Kevin Beaumont.

L’authentification des développeurs à nouveau en question

Dans son billet de blog, Aikido relève également le vaste danger évité de peu. De faibles conséquences qui ne semblent dues qu’au manque de compétences des pirates, en dépit de leur réussite sur la chaine d’approvisionnement.

Pour montrer à quel point ce type d’incident peut être grave, la société belge rappelle une autre compromission qui s’est déroulée fin août. Là aussi, un autre développeur NPM avait été visé, permettant la récupération de ses identifiants et l’insertion d’un code malveillant dans nx, une boite à outils pour le développement open source, totalisant six millions de téléchargements par semaine.

Le code malveillant avait servi à analyser l’ordinateur du développeur pour y récupérer des jetons d’authentification, ainsi que des clés SSH et API. Ces informations n’ont cependant pas été transmises aux pirates : elles ont été publiées dans un référentiel public créé pour l’occasion dans le compte GitHub du développeur, afin qu’elles soient visibles de tous et téléchargeables.

À Brian Krebs, Charlie Eriksen, chercheur chez Aikido, affirme que tous les paquets les plus populaires devraient exiger des attestations pour les modifications de code. De manière plus générale, il est d’avis que des plateformes comme GitHub et NPM devraient relever le niveau de sécurité, en s’assurant que les commits (une proposition de modification du code, pour schématiser) sont proposés par des personnes étant bien qui elles prétendent être.

S’il s’en est fallu de peu pour échapper à une catastrophe, les évènements ne semblent pas avoir surpris les chercheurs en sécurité, qui répètent les mêmes éléments depuis des années. Kevin Beaumont s’en moquait justement avec acidité, rappelant – encore une fois – que certaines des briques logicielles les plus utilisées ne sont gérées que par une poignée de personnes. Renvoyant une fois de plus au célèbre dessin de xkcd sur les dépendances.

Rappelons également que l’authentification à facteurs multiples, si elle apporte un gain majeur de protection, n’est pas absolue. En août 2022, Microsoft avait expliqué en détail par exemple comment un acteur malveillant avait mis en place toute une infrastructure pour récupérer des jetons d’authentification, via notamment des serveurs mimant un comportement légitime. Ce type d’attaque passe systématiquement par la compromission d’une personne, le plus souvent par un e-mail soigneusement préparé.

Commentaires (31)

votre avatar
votre avatar
Précision ajoutée, merci :)
votre avatar
Pourquoi les noms des paquets sont traduits ?
votre avatar
En effet, je ne connais pas nodeJS, mais ces noms me paraissaient bizarre. Une explication serait bienvenue, en ces temps de ronchonnades sur les traductions IA moisies.
votre avatar
backslash, gabarit-craie, supports-hyperliens, has-ansi, simple-swizzle, chaîne-couleur, error-ex, nom_couleur, is-arrayish, tranche-ansi, conversion des couleurs, wrap-ansi, ANSI-Regex, supports-couleur, strip-ansi, craie, déboguer et ansi-styles
On est sur un site genAI ? Faut le rajouter dans l'extension, vite !
votre avatar
Je suis allé voir le lien pour avoir les vrais noms, que je colle ici du coup :
backslash, chalk-template, supports-hyperlinks, has-ansi, simple-swizzle, color-string, error-ex, color-name, is-arrayish, slice-ansi, color-convert, wrap-ansi, ansi-regex, supports-color, strip-ansi, chalk, debug, ansi-styles
votre avatar
L’attaque sans précédent sur NPM relance les débats sur la chaîne d’approvisionnement
Pour moi, ca lance surtout le débat sur deux sujets:

  • la complexité de l'écosystème de développement JS qui nécessite d'avoir un gestionnaire de package.

  • la confiance que les devs JS accordent à la registry publique de npm (registry.npmjs.org).

votre avatar
en fait, on est plutôt dans la question de la confiance à tout registre public de packages, pas seulement NodeJS, mais aussi Java à travers Maven (souvenir ému de Log4Shell, et c'était pas une attaque !), Python, et, encore pire, Docker.
Je crois qu'il y a un vrai travail en cours sur l'ensemble de ces éléments...
votre avatar
Docker reste potentiellement à mes yeux l'une des pires possibles, puisqu'une image de container jamais mise à jour est un joli paquet cadeau de failles à tous les étages (image de base, runtime, middleware, dépendances...) !
votre avatar
Pour un développeur il n'est pas possible d'auditer sérieusement chaque mise à jour de chaque dépendance, surtout lorsqu'il est exigé de se tenir à jour fréquemment, justement pour combler les failles existantes. Je ne vois guère d'autre option que d'avoir des tiers de confiance. Et ça vaut pour tous les langages, pas seulement JS.

Le problème amha est plutôt le zèle qui est fait pour appliquer les toutes dernières mises à jour: c'est à double tranchant. Il faudrait toujours une petite période de "probation".
votre avatar
Ce qui explique l'importance que commencent à prendre les outils de SCA (Software Composition Analysis), chargés de scanner automatiquement l'ensemble des dépendances d'une application donnée (y compris dans l'idéal les dépendances transitives) pour y détecter celles qui présentent une vulnérabilité.
La fondation OWASP en fournit un basique pour Java et .Net, on en trouve des plus perfectionnés — rarement en open source par contre.
votre avatar
Je recommande Grype qui a un éventail de plateformes/langages supportés assez large github.com GitHub
votre avatar
C'est aussi loin d'être la panacée, ces outils. Peut-être que certains sont meilleurs que d'autres, je connais pas tout, mais bien souvent c'est la foire aux faux positifs tout simplement parcequ'ils s'arrêtent à l'analyse du SBOM sans chercher à déterminer si le code est effectivement vulnérable ou pas (ce qui est autrement plus difficile à montrer).
Exemple simple et courant, une CVE dans golang net/http concernant http2 sera généralement remontée à tous les consommateurs du package peu importe si http/2 est effectivement actif ou pas. Ou une vulnérabilité au ddos sur un type de regex qu'on n'utilise de toute façon pas.
Selon les dépendances, ça peut être plus ou moins gênant. Certaines s'updatent facilement, d'autres entraînent leur lot de breaking changes qui peuvent être un casse-tête à gérer... Tout ça juste pour "satisfaire" les scanners de vulnérabilité et leurs faux positifs.
votre avatar
Les pull-requests ne doivent pas être validées par d'autres dev avant de passer en prod ?
votre avatar
Si tu es tout seul sur ton projet open source, ce n'est pas possible.

Après, vient tout le débat de la confiance en la personne et comment être certains que c'est bien elle.
Pour Linux, M. Torvald vérifie les PRs avec son compte. Si son compte est piraté pour valider une PR vérolée, le résultat est vérolée.
Dans ce cas là, nous sommes sauvé par la communauté de testeurs et le délai avant la distribution. Mais il faut qu'une personne compétente ait le temps de tomber dessus.

Pour les petites librairies, il n'y a ni les personnes, ni les compétences et c'est livré directement.
Pour les développeurs les utilisant, on peut "retarder" le passage à la nouvelle version en espérant que quelqu'un d'autre essuie les plâtres. Mais si tout le monde fait ça, on revient à la problématique de base.
On tombe actuellement sur la "solution magique" de l'IA vendue pour s'assurer que c'est bon...

On peut aussi se baser des librairies. Certaines encapsulaient des RegEx, donc, si on s'en aperçoit, on s'en passe. D'autres permettent d'éviter d'entrer dans certains contenus compliqués comme les dessins vectoriels...
votre avatar
Certaines libraries sont nécessaires dans certains projets (au hasard React et ses milliards de dépendances, ou encore discord.js si tu veux créer un bot discord) d'autres peuvent être recodée en 2secondes (is-number :'))

Dans tous les cas sur des projets qui embarquent beaucoup de dépendances (souvent le cas sur du React) ça peut vite devenir impossible de tout vérifier.

On peut cependant espérer que les gestionnaires de packets (ainsi que npm/pnpm/bun/deno...) fassent un minimum d'effort pour éviter des dingueries.
votre avatar
Tout à fait d'accord.

Après, j'ai l'impression que le web est plus sujet à être géré par des "bidouilleurs". Ils ne prennent même pas la peine de questionner les dépendances.

J'étais arrivé dans une équipe "fullstack" avec React sur un projet tout récent (3 mois).
Ils avaient juste fait un copier-coller des dépendances sans réfléchir. Résultat, il y avait déjà une quarantaine de dépendances dont certaines avec des versions de plus de 3 ans. Et personne ne s'était posé de question. J'ai passé deux semaines pour tout vérifier, supprimer 3/4 des librairies car inutilisées et mis à jour le reste.

Il y a toute une culture à changer chez les développeurs et encore plus dans les technos webs qui sont maltraitées.
Ce n'est pas normal de crouler sous les librairies sans pouvoir en expliquer l'utilité.
Certains ne font pas que "pisser" du code en se cachant derrière le framework utilisé : "je suis obligé pour que ça fonctionne" 😥
votre avatar
Un des soucis dans toute cette histoire c'est qu'il n'y a toujours pas de moyen général d’authentifier la provenance des e-mail.
votre avatar
Non. Le problème est comme d'habitude un problème d'interface chaise clavier.
L'e-mail venait bien d'où il disait, un domaine créé pour cette opération quelques jours plus tôt.

Ensuite, s'il suffit que l'on fasse un commit dans un dépôt dans github pour que ça soit à la disposition de tous dans l'heure qui suit, il y a un énorme problème de process. Je sais bien que tester, c'est douter, mais depuis quand on publie du logiciel qui n'a subit aucun test qui vérifie que l'ensemble des commits sont bien valides ?
L'absence d'authentification à double facteur est aussi criminelle.
votre avatar
Il y a surtout une très grosse négligence de la personne concernée qui a saisi des identifiants sur un site qui n'était pas le bon.

Pourtant, les gestionnaires de mot de passe comme protonpass, 1password & Co sont très répandus. Ils constituent une bonne défense contre le hameçonnage car ils ne suggèrent de saisir le login et le mot de passe que si on est sur le bon site. C'est l'une des raisons pour laquelle l'ANSSI conseille l'usage des gestionnaires de mots de passe.

Avoir une double authentification incluant une passkey est aussi une solution car chaque passkey est lié à un unique site et si on est sur un site de hameçonnage, la passkey ne sera pas proposée/ ne marchera pas.

C'est fou de voir des informaticiens passer à côté de mesures de base et se faire avoir.
votre avatar
Tout dans JavaScript & consorts, du langage jusqu'à la chaine d'approvisionnement des paquets NPM, est à vomir.
Mais aujourd'hui, même les plus simples des sites Web sont générés avec cette chose.

Vous souvenez-vous de https://arxiv.org/abs/2112.10165 ?

Je ne compte plus les aberrations dans cet écosystème. L'argument du manque de gens ne fait pas long feu : d'autres projets aussi peu humainement chanceux n'ont pas la porte béante réclamant les problèmes à ce point.
Il y a un problème de fond avec cet écosystème : peut-être n'y a-t-il tout simplement pas assez de gens compétents à la mise en place & gestion d'une infrastructure d'approvisionnement qui s'y intéressent.
votre avatar
"L’attaque sans précédent sur NPM"
"la société belge rappelle une autre compromission qui s’est déroulée fin août"
votre avatar
votre avatar
J'ai jamais entendu parler de ce genre de soucis avec pypi.org (comme NPM mais côté Python). C'est que c'est mieux foutu ? Ou juste que c'est moins médiatisé ?
votre avatar
Les packages python malveillants sont pourtant régulièrement dézingués et Python est tout autant attaqué par les patterns de supply chain. La seule diff que je vois, c'est que l'un est géré par GitHub / Microsoft, l'autre non.

Pypi propose le MFA sur ses comptes, mais il ne l'impose pas à ma connaissance.
votre avatar
Il me semble justement que le MFA est obligatoire sur Pypi depuis quelque temps (voir ici).

Par contre, c'est uniquement si tu es mainteneur d'un paquet.
votre avatar
J'avais cherché dans les ToS de Pypi.org avant de poster justement, ils ne contraignent à rien dedans.

Je me demande si c'est vraiment d'actualité ou s'ils ont oublié un truc :D
votre avatar
Je viens de voir cet article de Korben : https://korben.info/github-immutable-releases-fin-attaques-supply-chain.html

Je ne suis pas compétent niveau code mais il se pourrait qu'il y ai un début de solution à adapter à npm and co.
votre avatar
Ça évite de compromettre une release existante, mais ça n'empêche pas de créer une nouvelle release compromise, qui sera téléchargée lors des updates de dépendances. Donc c'est bien mais pas suffisant ...
votre avatar
Par ailleurs il semble que c'est déjà le principe de fonctionnement de npm: npm publish
Once a package is published with a given name and version, that specific name and version combination can never be used again, even if it is removed with npm unpublish.
votre avatar
La gueule des dépendances sérieusement, qui a besoin d'un "color-string" / "color-convert" ?
Ce type de lib n'a aucune raisons d'être et encore moins d'être mis à jour (enfin à chaque fois qu'il y'a une nouvelle couleur :mdr2: )
Franchement on parle d'une division et d'un modulo, pourquoi créer une dep externe pour si peu ?
ça illustre la médiocrité d'un grand nombre de "dev" web : des incompétents biberonnés au copier / coller depuis stack overflow, ceux-là même qui t'expliquent gagner du temps grâce à l'IA. Forcément si tu passais 4h sur google à chercher un framework qui fait une division, autant faire un prompt le résultat sera tout aussi incertain (est-cela bonne division ? )