Le ver NPM est de retour et c'est bien pire (TanStack piraté)

BBetter Stack
컴퓨터/소프트웨어경영/리더십AI/미래기술

Transcript

00:00:00Shai Hulud est de retour pour une quatrième suite.
00:00:02Cette fois, il cible des paquets comme TanStack,
00:00:04littéralement quelques heures après la publication de cette vidéo sur Next.js,
00:00:07ce qui était un timing brillant de ma part.
00:00:08Il s'agit en réalité d'une attaque massive sur la chaîne d'approvisionnement NPM
00:00:11qui impacte bien plus que TanStack.
00:00:13Cela a aussi touché des paquets comme UiPath, Mistral,
00:00:15et 160 autres paquets,
00:00:17y compris des paquets PyPI comme Guardrails.ai.
00:00:20Ce qui rend celui-ci encore plus amusant,
00:00:22c'est qu'il incluait un dispositif de sécurité,
00:00:24donc s'il détectait que vous aviez renouvelé vos clés volées,
00:00:26il effaçait tout votre PC,
00:00:28et il y avait même une dimension politique mondiale intégrée.
00:00:30Plongeons dans le vif du sujet.
00:00:36Pour cette suite, le ver a le même objectif :
00:00:39voler des identifiants sur les machines des développeurs et les runners CI/CD,
00:00:42puis utiliser ces identifiants pour atteindre d'autres paquets.
00:00:44Pour TanStack, cela a signifié publier 84 versions malveillantes
00:00:47à travers 42 paquets TanStack en seulement quelques minutes.
00:00:51Maintenant, je vais vous montrer comment ils ont réussi
00:00:52à infecter TanStack en premier lieu,
00:00:54mais voyons d'abord ce que fait le malware lui-même
00:00:56si vous veniez à installer l'un de ces paquets impactés.
00:00:58À l'intérieur des paquets malveillants,
00:00:59vous trouveriez un nouveau fichier appelé routerinit.js,
00:01:02ainsi qu'une dépendance optionnelle injectée,
00:01:04qui mène à ce qui ressemble à
00:01:05un lien GitHub légitime vers TanStack router,
00:01:08mais c'est en fait un commit orphelin sur le fork de l'attaquant.
00:01:10C'est juste la façon dont GitHub gère les liens de fork,
00:01:13donc l'URL peut effectivement ressembler à
00:01:14ce qu'elle appartient au projet original,
00:01:16même si le commit provient en fait d'un fork.
00:01:18Dans ce fork, il y a un script de cycle de vie,
00:01:20prepare, qui exécute bun run task runner JS,
00:01:22et se termine par exit 1.
00:01:24C'est juste un moyen intelligent de faire échouer la dépendance optionnelle
00:01:27après l'exécution de la charge utile,
00:01:28donc l'installation se poursuit normalement,
00:01:30et cela laisse moins de traces évidentes dans vos journaux d'installation.
00:01:33Aussi, vous avez peut-être remarqué que ce n'est pas ce fichier
00:01:35routerinit.js que j'ai mentionné comme étant injecté
00:01:37dans les paquets au début,
00:01:38mais pour l'instant, voyez ces deux fichiers
00:01:40comme jouant exactement le même rôle sous des noms différents.
00:01:42En résumé, quand vous installez ceci,
00:01:44ça va exécuter ce script.
00:01:46La première chose que le script tente de faire
00:01:47est de se découpler du flux d'installation classique,
00:01:50donc il vérifie s'il tourne déjà
00:01:51en arrière-plan, et si ce n'est pas le cas,
00:01:53il crée une copie détachée de lui-même
00:01:54et ferme le script parent proprement.
00:01:57De cette façon, vos logs d'installation npm
00:01:58ne montrent aucune sortie du script
00:02:00car le malware s'est désormais détaché
00:02:01de ce processus et tourne en arrière-plan.
00:02:04Après cela, il fait quelque chose de très malin.
00:02:06Il écrit des copies de lui-même
00:02:07dans votre répertoire de hooks de Claude Code,
00:02:08puis configure vos paramètres de Claude
00:02:10pour exécuter ce hook chaque fois que vous utilisez Claude Code
00:02:12dans ce projet.
00:02:13De cette façon, il peut survivre à l'installation initiale
00:02:16et continuer à se redéclencher chaque fois
00:02:17que vous ouvrez Claude Code dans ce projet.
00:02:20Il fait même la même chose avec le gestionnaire de tâches de VS Code,
00:02:22en s'y dupliquant,
00:02:23donc si vous utilisez les fonctionnalités d'exécution automatique d'espace de travail de VS Code,
00:02:26vous avez exactement le même problème.
00:02:28Il installe même un service au niveau du système d'exploitation
00:02:29appelé GitHub Token Monitor,
00:02:31mais nous y reviendrons
00:02:32car celui-ci est absolument diabolique.
00:02:34C'est aussi assez diabolique
00:02:35que vous ne vous soyez pas encore abonné.
00:02:37La prochaine chose que fait la charge utile,
00:02:38c'est qu'elle se met à voler vos identifiants,
00:02:40et elle essaie tout.
00:02:41Dans GitHub Actions, elle cherche des identifiants
00:02:43et des secrets dans l'environnement du runner.
00:02:45Plus précisément, en scrutant la mémoire
00:02:47du processus de travail du runner GitHub Actions
00:02:48pour vos secrets de workflow
00:02:50qui incluent des secrets masqués,
00:02:52et elle insère même un faux workflow GitHub qui ressemble à CodeQL
00:02:55qui sérialise les secrets de votre dépôt
00:02:57et les exfiltre plus tard.
00:02:58Elle cherche aussi des secrets AWS,
00:03:00s'attaquant d'abord à vos variables d'environnement
00:03:02et aux fichiers de configuration locaux,
00:03:03mais ensuite elle s'attaque aussi aux services de métadonnées AWS
00:03:06comme IMDS v2 et les métadonnées de tâche ECS.
00:03:09Pour Kubernetes, elle vole des jetons de compte de service
00:03:11et des certificats, ce qui lui permet un accès API dans le cluster
00:03:14à tous les privilèges de contrôle d'accès basé sur les rôles (RBAC)
00:03:17que possédait le compte de service de ce pod,
00:03:19ce qui, dans des clusters mal configurés,
00:03:21peut être extrêmement large,
00:03:22parfois pratiquement administrateur.
00:03:24Et pour empirer les choses,
00:03:25il s'en prend aussi à HashiCorp Vault,
00:03:27récupérant toutes vos variables d'environnement
00:03:29et vos jetons,
00:03:30puis utilise l'accès Kubernetes qu'il a obtenu
00:03:32pour récupérer tous vos secrets gérés par Vault.
00:03:34Et tout ça, ce n'est que ce qu'il fait
00:03:35à vos déploiements CI.
00:03:37S'il est sur votre poste de travail,
00:03:38il s'attaque à toutes vos clés SSH,
00:03:39vos identifiants NPM,
00:03:41vos identifiants Git,
00:03:42votre historique de shell,
00:03:43vos identifiants de cloud,
00:03:44vos clés de cryptomonnaies,
00:03:45Signal,
00:03:45Slack,
00:03:45et vos fichiers Discord.
00:03:46Et en plus de tout ça,
00:03:47il extrait votre historique de session Claude Code.
00:03:49Donc si vous avez déjà donné des identifiants à Claude
00:03:51ou lui avez permis de lire des fichiers contenant des identifiants,
00:03:53il y a aussi accès.
00:03:55Alors oui, comme je l'ai dit,
00:03:56ils cherchaient absolument tout
00:03:57ce sur quoi ils pouvaient mettre la main,
00:03:58puis ils exfiltraient ces données
00:04:00via le réseau de messagerie Session.
00:04:02Et en guise de sauvegarde,
00:04:02ils déposaient aussi ces données volées
00:04:04dans des dépôts GitHub.
00:04:05Et pour rester dans le thème de leurs attaques,
00:04:07ces branches portent des noms en référence à Dune.
00:04:09Donc, il a vos identifiants.
00:04:11Ça ne peut pas être pire, n'est-ce pas ?
00:04:12Eh bien, si.
00:04:13Oui, ça peut.
00:04:14En plus de tout ça,
00:04:15si vous vous souvenez de ce service
00:04:16que je disais qu'il installe sur votre machine,
00:04:18eh bien, celui-là surveille vos jetons GitHub
00:04:19et continue de les ré-exfiltrer.
00:04:21Mais aussi, chaque minute,
00:04:22il vérifie si le jeton est toujours valide.
00:04:24Et si ce n'est pas le cas,
00:04:25il lance un “rm -rf” sur votre répertoire utilisateur,
00:04:27effaçant tout.
00:04:28Il essaie aussi de créer un jeton NPM
00:04:30avec vos identifiants,
00:04:31avec pour description :
00:04:32si vous révoquez ce jeton,
00:04:33nous effacerons l'ordinateur du propriétaire,
00:04:35ce qui implique qu'il fait la même chose
00:04:36pour les jetons NPM aussi.
00:04:38Donc, si vous révoquez ces jetons
00:04:39avant d'isoler votre machine
00:04:40et de supprimer ce processus en arrière-plan,
00:04:42la charge utile peut autodétruire votre PC,
00:04:44ce qui est tout bonnement diabolique.
00:04:46Et en aparté ici,
00:04:47la variante Python de cette attaque
00:04:48fait à peu près la même chose,
00:04:49mais elle inclut aussi une vérification
00:04:51pour voir si la langue de votre machine est le russe.
00:04:53Si c'est le cas,
00:04:53elle s'arrête tout simplement.
00:04:54Et si votre machine semble provenir
00:04:55d'Israël ou d'Iran,
00:04:56elle génère un nombre aléatoire
00:04:58entre 1 et 6.
00:04:59Et si ce nombre est 2,
00:05:00elle lance une commande de suppression destructive
00:05:01et essaie de jouer à plein volume
00:05:03un fichier MP3.
00:05:04Malheureusement,
00:05:05je n'ai pas pu découvrir
00:05:05ce qu'est ce MP3.
00:05:07Bref,
00:05:07maintenant qu'il a fait tout ça,
00:05:08le pire reste encore à venir
00:05:09car ce n'était que l'étape 1.
00:05:11L'étape 2, c'est l'auto-propagation,
00:05:13et c'est la partie la plus dangereuse
00:05:15de cette attaque.
00:05:16D'abord,
00:05:16il va chercher sur votre machine
00:05:17tous les jetons NPM valides
00:05:19avec lesquels il peut publier
00:05:19sans authentification à deux facteurs.
00:05:21Et s'il en trouve un,
00:05:22il va scanner tous les paquets
00:05:24auxquels ce compte a accès,
00:05:26puis utiliser ces identifiants
00:05:26pour s'ajouter à ces paquets
00:05:28et publier de nouvelles versions infectées.
00:05:30C'est évidemment assez grave,
00:05:32mais vous ne devriez probablement pas
00:05:33laisser traîner de jetons
00:05:33publiés
00:05:34qui peuvent contourner
00:05:35l'authentification à deux facteurs.
00:05:36Donc la version bien plus effrayante,
00:05:38c'est ce qui arrive
00:05:39quand ça s'exécute dans votre CI-CD.
00:05:41Car en CI,
00:05:42l'attaquant n'a pas besoin
00:05:43d'un jeton NPM longue durée
00:05:44car les bonnes configurations
00:05:45reposent souvent sur l'OIDC,
00:05:47qui est censé être plus sûr.
00:05:48En gros,
00:05:49au lieu de stocker
00:05:50un jeton NPM comme secret,
00:05:51GitHub Actions prouve à NPM :
00:05:53hé,
00:05:53je suis ce dépôt,
00:05:54exécutant ce workflow
00:05:55sur cette branche,
00:05:56et NPM lui donne alors
00:05:57un jeton de publication à courte durée.
00:05:59Le problème avec ça, cependant,
00:06:00c'est que si le script obtient l'accès
00:06:01à un environnement GitHub Actions de confiance,
00:06:03il peut se placer au même endroit
00:06:04qu'un éditeur légitime.
00:06:06Donc le malware peut utiliser
00:06:07l'environnement lié à l'OIDC
00:06:08que GitHub expose au job
00:06:10pour demander un jeton JWT OIDC
00:06:12au point de terminaison de jeton de GitHub,
00:06:14puis il échange ce jeton JWT
00:06:16avec NPM
00:06:17contre un jeton de publication à courte durée
00:06:18via le système de publication
00:06:19approuvé par NPM,
00:06:20et maintenant il peut publier
00:06:22sans jamais voler
00:06:22un jeton NPM permanent
00:06:24et paraître totalement légitime.
00:06:26Dans ce cas,
00:06:26le malware inclut une copie
00:06:27de ce fichier router init.js
00:06:29dans la table du package,
00:06:30puis ajoute la dépendance
00:06:31optionnelle malveillante,
00:06:32puis publie le tout
00:06:33comme la dernière version
00:06:34pour ce package,
00:06:35donc quand quelqu'un
00:06:35ou un pipeline CI-CD
00:06:37installe ces packages,
00:06:38la boucle recommence,
00:06:40se propageant aussi loin
00:06:40qu'elle le peut.
00:06:42C'est donc
00:06:42assez dingue, non ?
00:06:43Mais concentrons-nous maintenant
00:06:44sur le patient zéro,
00:06:46TanStack.
00:06:46Comment ont-ils été infectés
00:06:47en premier lieu ?
00:06:48Eh bien,
00:06:49selon leur propre analyse post-mortem,
00:06:50l'attaquant a abusé
00:06:51de ce pipeline GitHub Actions.
00:06:53Ils ont commencé la veille
00:06:53du jour où les packages malveillants
00:06:54ont été publiés,
00:06:56en créant un fork
00:06:57de TanStack Router,
00:06:58mais ils l'ont renommé
00:06:59en configuration
00:06:59pour essayer de rendre plus difficile
00:07:01la recherche si vous cherchiez
00:07:02parmi les noms de forks évidents.
00:07:04Puis ils ont ajouté
00:07:04un commit malveillant
00:07:05à ce fork,
00:07:06qu'ils ont faussement signé
00:07:07au nom de Claude,
00:07:07et il y avait un message de commit
00:07:08préfixé
00:07:09par skip CI,
00:07:10pour ne pas déclencher
00:07:11immédiatement la CI sur un événement push.
00:07:13Le jour suivant,
00:07:13ils ont ouvert une PR
00:07:14contre TanStack Router
00:07:15appelée Work in Progress
00:07:16Simplify History Build.
00:07:18Et c'est là
00:07:18que l'attaque a vraiment lieu.
00:07:20Le TLDR est que
00:07:21TanStack avait un workflow GitHub Actions
00:07:22pour la taille de bundle
00:07:23qui utilisait pull request target,
00:07:25et c'est notable
00:07:26car pull request target
00:07:27s'exécute en fait
00:07:28dans le contexte de sécurité
00:07:29du dépôt de base,
00:07:30pas du fork.
00:07:31Cela signifie qu'il a accès
00:07:32au périmètre de cache du dépôt de base
00:07:33et à son jeton GitHub.
00:07:35Donc ce workflow
00:07:35a récupéré la PR,
00:07:36installé ses dépendances,
00:07:38et exécuté une construction de benchmark.
00:07:39Le problème, cependant,
00:07:40c'est que ce fork contenait
00:07:40du code malveillant.
00:07:41Dans ce cas,
00:07:42il s'agissait d'un script de configuration
00:07:43qui a empoisonné le
00:07:44magasin de packages PNPM
00:07:45avec exactement la clé de cache
00:07:47que l'action de versionnage
00:07:48utiliserait plus tard.
00:07:49Ils ont en fait pré-calculé
00:07:50cela à partir du fichier
00:07:51lock PNPM public
00:07:52en utilisant exactement la même formule
00:07:54que celle qu'utilise aussi le workflow.
00:07:56Une fois que ce cache empoisonné
00:07:57a été enregistré,
00:07:57ils ont en fait réinitialisé
00:07:58cette branche
00:07:59pour qu'elle corresponde à la branche
00:07:59main actuelle,
00:08:00donc la PR visible
00:08:01semblait être un no-op
00:08:02sans changement,
00:08:03puis ils ont fermé cette PR
00:08:04et supprimé
00:08:05la branche malveillante.
00:08:06Donc, de l'extérieur,
00:08:07on dirait qu'absolument
00:08:07rien
00:08:08ne s'est passé,
00:08:09mais ils ont empoisonné
00:08:10le cache de ce GitHub Action.
00:08:11Cela signifie que
00:08:12huit heures plus tard,
00:08:13quand un mainteneur normal
00:08:14a fusionné une PR sans rapport
00:08:15dans main,
00:08:16cela a déclenché le
00:08:17workflow de release de TanStack,
00:08:18qui a restauré le
00:08:19cache PNPM empoisonné,
00:08:20et maintenant, du code contrôlé par l'attaquant
00:08:22s'exécutait à l'intérieur
00:08:23de cette action de release.
00:08:24Il a ensuite utilisé la même logique
00:08:25avec l'OIDC
00:08:26pour obtenir un jeton de publication NPM,
00:08:28et a réussi à publier
00:08:2984 versions de lui-même
00:08:30à travers 42 packages TanStack,
00:08:32et il n'a même pas eu besoin
00:08:33d'arriver jusqu'à l'étape
00:08:34de publication du package
00:08:35de l'action.
00:08:36Curieusement,
00:08:36l'action a en fait échoué
00:08:37parce que certains tests ont échoué,
00:08:39donc elle n'a jamais atteint cette étape,
00:08:40mais le code malveillant s'est exécuté
00:08:41et les a tous publiés
00:08:43quand même.
00:08:43L'attaquant a donc réussi
00:08:44à enchaîner trois limites de confiance.
00:08:46D'abord,
00:08:47le code de la PR du fork
00:08:47a pu empoisonner
00:08:48le cache du dépôt de base,
00:08:49puis ce cache du dépôt de base
00:08:51a été restauré à l'intérieur
00:08:52du vrai workflow de release,
00:08:53puis le vrai workflow de release
00:08:54possède des permissions OIDC,
00:08:56qui se transforment en
00:08:57accès de publication NPM,
00:08:58donc ils peuvent publier
00:08:59ce qui semble être
00:08:59des packages totalement légitimes.
00:09:01Et c'est ce que je trouve
00:09:02vraiment effrayant
00:09:03à propos des attaques de la chaîne d'approvisionnement.
00:09:05Ils s'éloignent
00:09:05du vol
00:09:06du jeton d'un mainteneur
00:09:07pour abuser
00:09:08de tout le système CI-CD lui-même,
00:09:10et cela signifie
00:09:11que tous nos signaux de confiance
00:09:12commencent à fonctionner
00:09:13pour l'attaquant.
00:09:14Il s'agissait d'un package signé
00:09:15avec une provenance valide
00:09:16publié par un vrai workflow.
00:09:18Voilà donc,
00:09:19c'est ça ShaiHalud4,
00:09:20et si vous voulez vérifier
00:09:21si vous avez été compromis
00:09:21par l'un de ces packages,
00:09:23je laisserai des liens
00:09:23vers des articles de blog ci-dessous,
00:09:25qui couvriront
00:09:25comment vous pouvez savoir
00:09:26et ce que vous pouvez faire
00:09:27si vous avez installé
00:09:28l'un d'entre eux.
00:09:29Dites-moi dans les commentaires
00:09:30ce que vous pensez
00:09:30de tout ça
00:09:31et de l'écosystème NPM,
00:09:33pendant que vous y êtes,
00:09:33abonnez-vous,
00:09:34et comme toujours,
00:09:34à la prochaine.

Key Takeaway

L'attaque par empoisonnement du cache des pipelines CI/CD permet à des paquets malveillants de détourner les privilèges OIDC et de publier des mises à jour corrompues tout en maintenant une persistance agressive sur les machines des développeurs.

Highlights

  • Une attaque massive sur la chaîne d'approvisionnement NPM a compromis TanStack, UiPath, Mistral et 160 autres paquets, ainsi que des paquets PyPI.

  • Le malware s'installe en créant une copie détachée pour éviter de laisser des traces dans les journaux d'installation.

  • Le script malveillant scanne la mémoire des runners GitHub Actions pour exfiltrer des secrets masqués et insère des workflows de type CodeQL pour persister dans le vol de données.

  • Une fonctionnalité d'autodestruction efface le répertoire utilisateur via “rm -rf” si le jeton GitHub volé est révoqué par la victime.

  • L'infection initiale de TanStack a été rendue possible par l'empoisonnement du cache PNPM via une pull request malveillante utilisant le contexte “pull request target”.

  • Le malware utilise les permissions OIDC de GitHub Actions pour demander des jetons de publication temporaires à NPM, permettant la publication de versions infectées sans voler de jetons permanents.

Timeline

Portée et fonctionnement du malware

  • L'attaque cible les identifiants sur les machines des développeurs et les environnements CI/CD.
  • Le malware utilise un fichier routerinit.js et une dépendance optionnelle pour s'exécuter lors de l'installation.
  • Le processus se détache du flux d'installation npm pour tourner en arrière-plan sans laisser de logs.
  • Une persistance est établie via des hooks Claude Code et le gestionnaire de tâches VS Code.

Le ver, surnommé Shai Hulud, exploite des paquets légitimes pour injecter du code malveillant. En se détachant du processus parent, il évite la détection dans les journaux npm. Il s'infiltre dans les outils de développement comme Claude Code et VS Code pour s'exécuter automatiquement à chaque utilisation du projet.

Exfiltration de données et autodestruction

  • Le malware exfiltre des secrets AWS, Kubernetes, HashiCorp Vault et GitHub Actions.
  • Les données volées incluent des clés SSH, des identifiants Git, des clés de cryptomonnaies et l'historique de session Claude.
  • Un service surveille la validité des jetons GitHub volés et efface le répertoire utilisateur en cas de révocation.
  • La variante Python inclut des logiques conditionnelles destructives basées sur la localisation géographique de la machine.

L'ampleur du vol couvre l'ensemble de l'écosystème de développement, du cloud au poste de travail. L'aspect le plus agressif est le dispositif d'autodestruction qui efface les données locales si la victime tente de protéger ses accès en révoquant les jetons volés. La variante Python ajoute un comportement sélectif envers certaines régions géographiques.

Auto-propagation et abus de l'OIDC

  • Le malware recherche des jetons NPM sans authentification à deux facteurs pour infecter d'autres paquets.
  • L'utilisation de l'OIDC permet au malware d'obtenir des jetons de publication éphémères légitimes auprès de NPM.
  • Le code malveillant se propage automatiquement en ajoutant des dépendances corrompues aux nouvelles versions publiées.

L'étape de propagation transforme l'infrastructure de confiance CI/CD en outil d'attaque. En utilisant l'OIDC, le malware s'authentifie auprès de NPM en tant que processus légitime du pipeline, ce qui lui permet de publier des mises à jour malveillantes sans éveiller les soupçons des systèmes de sécurité classiques.

Infection initiale de TanStack

  • L'attaquant a empoisonné le cache PNPM via une pull request utilisant le contexte 'pull request target'.
  • Le workflow de release a restauré le cache empoisonné, exécutant le code malveillant lors de la fusion dans main.
  • Les permissions OIDC du workflow de release ont permis la publication de 84 versions infectées sur 42 paquets.
  • Le succès de l'attaque repose sur l'enchaînement de plusieurs limites de confiance au sein de la chaîne CI/CD.

TanStack a été compromis par une manipulation subtile des workflows GitHub Actions. En exploitant 'pull request target', l'attaquant a pu injecter des données dans le cache utilisé par la CI. Le workflow de release, considéré comme fiable, a ensuite récupéré cet environnement corrompu, menant à une publication automatique de versions vérolées avec une provenance valide.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video