1 points par GN⁺ 4 시간 전 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • Après avoir proposé sur LinkedIn un poste de software engineer, l’attaquant a transmis un dépôt GitHub privé déguisé en exercice React/Web3 légitime ; à l’exécution, un malware JavaScript caché dans tailwind.config.js s’activait sur l’ordinateur du développeur
  • Ce fichier de configuration de 30 987 octets dissimulait 27 Ko de code obfusqué derrière des milliers d’espaces, récupérait depuis un serveur distant une charge utile de second niveau chiffrée en AES-256-CBC, l’enregistrait dans %TEMP%\pack, puis l’exécutait avec node pack
  • Une observation d’environ 10 minutes dans une VM isolée Windows 11 ARM a montré que la charge utile activait quatre composants : une porte dérobée de contrôle à distance, un voleur de navigateurs et de portefeuilles, un scanner de fichiers récursif et un surveillant du presse-papiers
  • Via Socket.IO, le malware contrôlait le terminal, SSH, l’écran, le clavier et la souris, tout en collectant les bases de données Chromium, les stockages d’extensions de portefeuilles, les clés SSH, le code source, les fichiers de configuration, etc. ; 2 588 requêtes ont été observées sur le port d’upload de fichiers
  • Tout dépôt d’exercice envoyé par un inconnu doit être traité comme du code non fiable et analysé dans une VM ou un conteneur jetable sans vrai profil de navigateur, clés SSH, identifiants cloud ni portefeuilles ; en cas d’infection, il faut remplacer aussi les secrets après avoir coupé le réseau et préservé les preuves

Un dépôt déguisé en exercice de recrutement Web3 légitime

  • L’utilisateur LinkedIn Wayan Adrian a proposé un poste de software engineer chez shrapnel.com et transmis comme exercice le dépôt GitHub privé tech-active-workplace-frontend-main du compte yevhen-o
  • La plateforme de campagne NFT appelée BLAIEXS ressemblait en apparence à un projet React/Web3 ordinaire
    • Le frontend React fournissait le branding, un tableau de bord administrateur, la gestion de campagnes et des flux de création de NFT
    • L’API Express gérait l’authentification, les données du tableau de bord, les contrôles KYB, la création et la réclamation de NFT, l’upload de fichiers ainsi que certains endpoints factices
    • Les scripts de seed créaient des comptes de démonstration superadmin, marque et consommateur, une campagne Demo NFT Drop et 100 NFT Demo Collectible
  • Le vrai périmètre de l’exercice se limitait à une simple fonction d’affichage du réseau MetaMask
    • La fonction asynchrone getChainId() dans src/utils/ethereum.js devait appeler eth_chainId et renvoyer la valeur hexadécimale analysée ou null
    • getNetworkLabel(chainId) dans le même fichier devait retrouver un nom de réseau lisible dans l’objet NETWORKS existant
    • src/components/Wallet/ConnectWalletButton.js devait être modifié pour appeler ces deux fonctions après la connexion du portefeuille
  • Une fois l’implémentation terminée, l’utilisateur a lancé le serveur de développement, mais comme le démarrage tardait anormalement, il a détecté quelque chose d’inhabituel et débranché le câble Internet

Code d’exécution caché dans tailwind.config.js

  • Dans ls -la, tailwind.config.js faisait 30 987 octets, mais l’éditeur n’affichait que 97 lignes ; wc -c a confirmé la même taille
  • Vers la ligne 95, un énorme bloc JavaScript difficile à lire était caché derrière des milliers d’espaces
  • Le code utilisait des techniques classiques d’obfuscation JavaScript
    1. Stockage des chaînes importantes dans un grand tableau
    2. Rotation du tableau jusqu’à obtenir le checksum attendu
    3. Masquage de l’accès aux chaînes derrière des fonctions de décodage
    4. Remplacement des noms explicites par des index numériques et des appels wrapper
  • L’un des deux décodeurs reconstituait des chaînes au format Base64, tandis que l’autre appliquait une clé par chaîne et un chiffrement de flux proche de RC4 ; le tableau de chaînes était aussi tourné jusqu’à ce que les index de décodage correspondent
  • Sans exécuter le malware, il a été possible de lever l’obfuscation dans l’ordre suivant
    1. Extraire le tableau de chaînes
    2. Reproduire la rotation du tableau jusqu’au checksum attendu
    3. Réimplémenter les fonctions de décodage
    4. Remplacer des appels comme b(0x214), c(0x2f1, "key") par les chaînes réelles
    5. Simplifier les objets wrapper et les fonctions auxiliaires pour révéler l’intention d’exécution

Fonctionnement du dropper de premier niveau

  • Le code déobfusqué chargeait child_process, os, fs, path, crypto, puis installait axios et socket.io-client dans le répertoire temporaire
  • Les gestionnaires de processus uncaughtException et unhandledRejection étaient configurés pour ignorer les erreurs
  • Le flux d’exécution de la charge utile distante était le suivant
    • L’UID est 59e605dd78fb2aafccd1b622f06a00ca
    • Les données sont récupérées depuis http://45.146.252.17/api/service/59e605dd78fb2aafccd1b622f06a00ca
    • L’UID et la chaîne salt sont passés à crypto.scryptSync pour dériver une clé de 32 octets
    • La réponse est séparée au format base64_iv:base64_ciphertext, puis déchiffrée en aes-256-cbc
    • Le texte en clair est écrit dans le fichier pack du répertoire temporaire
    • L’exécution se fait via child_process.execSync("node pack", { windowsHide: true, cwd: os.tmpdir() })
  • Il s’agit d’un dropper de malware qui télécharge et exécute un autre logiciel malveillant plutôt que d’assurer lui-même la fonctionnalité finale
  • Le code téléchargé ne comportait ni vérification de signature, ni liste blanche de hachages, ni épinglage de provenance, ni restriction de chemin, ni garde d’empêchement d’exécution ; l’endpoint distant pouvait donc exécuter du code avec les privilèges du compte système ayant chargé la configuration Tailwind

Analyse dynamique réalisée dans une VM isolée

  • Une VM Windows 11 ARM jetable a été configurée avec UTM afin de ne pas exposer le système hôte
    • Mémoire 4096 MiB
    • Disque 64 GiB
    • Réseau shared/NAT
    • Dossiers partagés désactivés
  • Des outils ont été installés pour collecter le comportement du malware sous plusieurs angles
    • Wireshark : collecte des paquets et du trafic C2/exfiltration
    • Sysinternals Sysmon : télémétrie persistante des événements Windows
    • Procmon : collecte de l’activité des processus, du registre et du système de fichiers pendant l’exécution
  • Des données leurres ont été placées pour vérifier ce que le malware cherchait à collecter
    • Paires de clés SSH
    • Dépôts GitHub privés
    • Portefeuilles de cryptomonnaies
    • Données de navigateur
    • Autres fichiers susceptibles d’intéresser un infostealer
  • yarn start a été exécuté dans la VM, puis le comportement a été observé pendant environ 10 minutes, ce qui a permis de récupérer run1-full.pcapng, run1-sysmon.evtx, stage2-pack.bin

Chaîne d’infection observée sur le réseau

  • Les requêtes HTTP vers 45.146.252.17 étaient réparties par rôle selon les ports
    • Port 80 : récupération de la charge utile de premier niveau, enregistrement de l’hôte, envoi des logs
    • Port 7641 : porte dérobée de commande et contrôle Socket.IO
    • Port 7646 : 2 588 uploads de fichiers
    • Port 7649 : 3 uploads de données navigateur, plus volumineux mais moins nombreux
  • La première requête était GET /api/service/59e605dd78fb2aafccd1b622f06a00ca, avec une réponse au format base64_iv:base64_ciphertext et Content-Length: 150897
  • tailwind.config.js déchiffrait la réponse en AES-256-CBC, écrivait le texte en clair dans %TEMP%\pack, puis l’exécutait avec node pack
  • Les caractéristiques de la charge utile de second niveau récupérée étaient les suivantes
    • SHA-256 : 68a64d8c015c06fd70bcb8c5878c1e430da827dd00b62f8e6ef69e76bb94de5b
    • Taille : 113136 octets
    • Format : JavaScript ASCII sur une seule ligne
  • Le pack déchiffré était lui aussi à nouveau obfusqué, mais avec les mêmes techniques que la première étape, et a donc pu être levé avec la même procédure

Un payload en deux étapes composé de quatre programmes

  • pack n’était pas un script unique, mais un petit arbre de processus qui lançait les quatre programmes suivants
    1. Écrit et exécute la porte dérobée de commande et contrôle scdata dans un répertoire temporaire
    2. Écrit et exécute le voleur de navigateur et de portefeuille ldata dans un répertoire temporaire
    3. Exécute directement en mémoire un scanner de fichiers récursif avec node -e
    4. Exécute directement en mémoire un surveillant du presse-papiers avec node -e
  • La configuration principale comprenait l’UID 59e605dd78fb2aafccd1b622f06a00ca, la clé utilisateur 308, l’hôte 45.146.252.17, le port Socket.IO 7641, le port de clé 7648, le port d’upload navigateur 7649 et le port d’upload de fichiers 7646

scdata : porte dérobée de contrôle à distance interactive

  • scdata est écrit sous forme de fichier dans %TEMP%, puis exécuté via npm i axios socket.io-client ... && node scdata, et reste comme processus enfant de longue durée
  • Après son exécution, il se configure pour ressembler à un processus légitime
  • Il installe aussi des paquets supplémentaires nécessaires aux fonctions de contrôle à distance
    • socket.io-client, ssh2, node-pty : terminal et fonctions de type SSH
    • sharp, screenshot-desktop, clipboardy, @nut-tree-fork/nut-js : captures d’écran, presse-papiers, déplacement/clic/défilement de la souris, saisie clavier
  • Les événements Socket.IO montrent directement l’étendue du contrôle à distance
    • Terminal : start-terminal, terminal-input, terminal-resize, stop-terminal, command
    • Vérification de l’hôte et capture : whour, capture
    • Contrôle des entrées : mouseMove, mouseClick, mouseScroll, keyTap, keyCombo
    • Presse-papiers : copyText, pasteText
    • SSH et arrêt : start_ssh, ssh_input, kill
  • Dans la PCAP, les requêtes de polling et la poignée de main WebSocket sur le port 7641, ainsi que le premier événement serveur whour, ont été confirmés ; dans Sysmon, node.exe scdata, une requête PowerShell d’informations système et deux groupes d’installations de paquets npm ont été observés successivement
  • Au-delà du simple vol d’informations, il fournit à l’attaquant des capacités de shell et de contrôle du bureau

ldata : voleur de données de navigateur et de portefeuille

  • ldata est écrit dans %TEMP% et exécuté via npm i axios && node ldata ; son titre de processus est npm-cache
  • Il collecte les profils de navigateur et les stockages d’extensions de portefeuille, puis les envoie vers http://45.146.252.17:7649/upload, tandis que l’état de LevelDB est vérifié via /cldbs
  • Les navigateurs ciblés sont largement définis selon le système d’exploitation
    • Windows : Chrome, Edge, Brave, LT Browser
    • macOS : Chrome, Brave, Opera, LT Browser, Edge et le trousseau de connexion local
    • Linux : les dossiers de profil Chromium correspondants
  • Depuis les répertoires Default ou Profile*, il envoie les bases de données Chromium suivantes
    • Login Data
    • Login Data For Account
    • Web Data
  • Il parcourt ensuite Local Extension Settings/<extension-id> ; les ID d’extensions de portefeuille codés en dur incluent aussi nkbihfbeogaeaoehlefnkodbefgpgknn de MetaMask
  • Pour les huit premiers chemins d’extensions de portefeuille, il copie les répertoires LevelDB dans un dossier temporaire et envoie les fichiers individuellement, puis décide de terminer ou de réessayer selon la réponse cldbs du serveur
  • Les trois uploads confirmés sur le port 7649 correspondaient aux bases de données suivantes
    • Login_Data.sqlite : 51 200 octets
    • Login_Data_For_Account.sqlite : 51 200 octets
    • Web_Data.sqlite : 262 144 octets
  • Les données de test ne contenaient pas de mots de passe enregistrés ni d’entrées de carte, mais incluaient six valeurs d’autoremplissage et des métadonnées du navigateur
  • Les navigateurs basés sur Chrome chiffrent localement certains champs, donc la base de données seule ne permet pas toujours de récupérer les secrets en clair
    • Toutefois, combinée avec les secrets du système d’exploitation, les cookies, le stockage des extensions et un navigateur déverrouillé, elle devient un élément d’une chaîne de vol d’identifiants
  • Il s’arrête une fois les données de navigateur et LevelDB nécessaires envoyées, ou quand le serveur les marque comme complètes, sans attendre de commande supplémentaire de l’opérateur

Scanner de fichiers récursif et surveillant du presse-papiers

  • Le scanner de fichiers récursif ne crée pas de fichier séparé et s’exécute avec node -e, en commençant l’exploration depuis le répertoire personnel de l’utilisateur
    • Sous Windows, il énumère les lettres de lecteur via Get-CimInstance Win32_LogicalDisk et inspecte aussi la racine de chaque lecteur
    • Les motifs de fichiers collectés incluent *.env*, *.md, *.pem, *.ini, *.secret, *.json, *.js, *.ts, *.csv, *.txt, *.doc, *.docx, *.pdf, *.xlsx, .zsh_history, .bash_history
    • ~/.ssh, ~/.aws, ~/.azure, ~/.config, ~/.foundry sont automatiquement traités comme dossiers d’intérêt
    • Il recherche aussi des noms comme metamask, bitcoin, btc, solana, secret phrase, private key
    • Les résultats sont envoyés à http://45.146.252.17:7646/upload
  • Dans l’environnement leurre, id_ed25519, id_ed25519.pub, History.txt, seed.js, password.js, tokens.js, ConnectWalletButton.js, ExportWallet.js, RegisterWallet.js, tailwind.config.js, aptos-cli.json et d’autres ont effectivement été envoyés
  • Si ldata se charge des navigateurs et du stockage des portefeuilles, le scanner de fichiers collecte largement clés SSH, configurations, code source, secrets locaux, historiques shell et fichiers de projet
  • Le surveillant du presse-papiers s’exécute lui aussi avec node -e, et son titre de processus est npm-compiler.log
    • Il attend quelques secondes puis lit le presse-papiers en boucle
    • Sous macOS, il utilise pbpaste ; sous Windows, powershell -NoProfile -NonInteractive Get-Clipboard
    • Les valeurs modifiées sont envoyées à http://45.146.252.17/api/service/makelog
  • Même sans interpréter le format, il peut capturer tel quel ce que l’utilisateur copie : mots de passe, phrases de récupération, clés privées, jetons API, codes à usage unique, URL GitHub, adresses de portefeuille, secrets de déploiement

Principes à respecter avant d’exécuter un exercice de recrutement

  • Un dépôt d’exercice envoyé par une personne inconnue doit être traité comme du code exécutable non fiable tant que sa sûreté n’a pas été vérifiée
  • Avant yarn install, npm install, yarn build, yarn start, il faut examiner les éléments suivants
    • package.json
    • Scripts de cycle de vie
    • Fichiers de configuration
    • Hooks de dépendances manifestes
  • Dans ce cas, le malware se cachait dans tailwind.config.js, un fichier que beaucoup de gens risquent de négliger ; les fichiers de configuration, les dépendances et les outils de build peuvent eux aussi être exécutés comme du code
  • Les projets d’entretien doivent être exécutés dans une VM ou un conteneur jetable où aucun vrai secret n’est monté
    • Profil de navigateur personnel
    • Gestionnaire de mots de passe
    • Clés SSH
    • Portefeuille de cryptomonnaies
    • Jetons GitHub
    • Identifiants cloud
    • Sessions bancaires
    • Compte e-mail principal
  • Pour les exercices nécessitant un compte ou un portefeuille, il faut utiliser une nouvelle identité de test sans fonds et jamais réutilisée sur d’autres services
  • Pour un exercice Web3, il faut n’utiliser que des testnets et ne jamais connecter un vrai portefeuille à un projet inconnu, même s’il semble inoffensif

Indicateurs pour vérifier une éventuelle infection

  • Sur macOS et Linux, vérifier en priorité les éléments suivants
    • les processus en cours d’exécution liés à node, pack, scdata, ldata, npm-compiler, vhost.ctl
    • les connexions à 45.146.252.17 ou aux ports 7641, 7646, 7649
    • pack, scdata, ldata, vhost.ctl dans les répertoires temporaires, Downloads, Desktop, Documents et Library
    • le fichier marqueur */.npm/vhost.ctl
    • dans le projet téléchargé, les chaînes IP, UID, /api/service/makelog, node scdata, node ldata, node pack
  • Sous Windows, vérifier les éléments suivants
    • parmi les processus node, npm, cmd, powershell, ceux dont la ligne de commande contient pack, scdata, ldata, node -e, l’adresse IP ou Get-Clipboard
    • les connexions TCP ouvertes vers 45.146.252.17 ou les ports distants 7641, 7646, 7649
    • sous %TEMP%, pack, scdata, ldata, vhost.ctl, .npm\vhost.ctl
    • les chaînes C2 connues dans le répertoire où le dépôt d’entretien a été cloné
  • Si un processus Node actif, une connexion à cette IP ou l’un des artefacts pack·scdata·ldata est détecté, il faut considérer que le système a été exposé
  • Ces indicateurs sont des points de contrôle initiaux spécifiques aux échantillons analysés et ne constituent pas une procédure forensique complète

Assainissement du système infecté et rotation des secrets

  • Commencer par déconnecter l’ordinateur du réseau et éviter de continuer à explorer ou déboguer dans l’environnement infecté, ou d’y copier de nouveaux secrets
  • Si des preuves sont nécessaires, conserver les éléments suivants avant suppression
    • la liste des processus
    • les connexions réseau
    • les fichiers suspects
    • l’historique du navigateur
    • le dépôt malveillant
  • Ensuite, arrêter les processus liés à node pack, node scdata, node ldata, node -e, npm-compiler, vhost.ctl, puis supprimer pack, scdata, ldata, .npm/vhost.ctl dans les répertoires temporaires
  • S’il s’agit d’une VM jetable et qu’il n’est pas nécessaire de préserver un environnement Node légitime, on peut utiliser pkill node sur macOS·Linux ou forcer l’arrêt de tous les processus node sous Windows
  • Supprimer le dépôt malveillant et node_modules, mais conserver une copie ZIP hors ligne si une analyse complémentaire est nécessaire
  • La suppression des fichiers ne suffit pas : il faut aussi remplacer ou révoquer les secrets suivants
    • les clés SSH
    • les tokens GitHub·npm
    • les clés cloud et les clés API
    • les secrets de déploiement
    • les mots de passe enregistrés dans le navigateur
    • les sessions connectées actives
  • Si une seed de wallet ou une clé privée a pu être exposée sur le système infecté, il faut créer un nouveau wallet depuis un appareil propre et transférer les fonds

Une attaque qui exploite la frontière de confiance des outils de développement

  • Les antivirus traditionnels n’ont pas détecté ce dépôt, et même les agents de code IA populaires utilisés pour résoudre l’exercice n’ont pas identifié le code malveillant caché dans le projet
  • Tailwind évalue les fichiers de configuration JavaScript comme des modules Node : l’IIFE de la ligne 95 s’exécute donc dès qu’un outil de développement, un script de build, une intégration éditeur, Tailwind CLI, un bundler ou une tâche CI charge la configuration
  • La configuration Tailwind légitime s’arrêtait à la ligne 94, puis environ 27 KB de code obfusqué avaient été ajoutés
  • La charge utile téléchargée s’exécute avec les mêmes privilèges système que l’utilisateur qui a chargé la configuration
    • elle peut accéder aux fichiers locaux, aux profils de navigateur, aux identifiants enregistrés, aux données d’extensions de wallet, aux clés SSH, aux variables d’environnement, aux tokens de paquets, aux identifiants cloud, au code source et au presse-papiers
    • si elle s’exécute en CI, les secrets de déploiement, les artefacts de build, les identifiants de registre et les tokens d’accès à la production peuvent aussi être exposés
  • La correction nécessaire consiste à supprimer entièrement le bloc JavaScript obfusqué de tailwind.config.js

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.