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.
Community Posts
No posts yet. Be the first to write about this video!
Write about this video