Swiss Post Cybersecurity a analysé une campagne d’infostealer. Les attaquants emploient la tactique ClickFix pour obtenir un accès initial, puis s’appuient sur DonutLoader pour exécuter le shellcode en mémoire. Dans cet article, nous expliquons pas à pas comment les utilisateurs se font piéger.
Le paysage des menaces actuelles se distingue par une complexité grandissante. Plutôt que d’envoyer de simples pièces jointes, les attaquants utilisent désormais des chaînes d'infection complexes qui intègrent des outils système légitimes afin de rester invisibles. Louis Schürmann, analyste en cybersécurité chez Swiss Post Cybersecurity, a enquêté sur un incident qui a débuté par un e-mail apparemment inoffensif et s'est transformé en une attaque en plusieurs étapes. Tout d'abord, une technique d'ingénierie sociale a été utilisée, suivie d'un dropper PowerShell, puis d'une charge utile infostealer en mémoire.
Cet article de blog présente une analyse détaillée de cette campagne. Nous allons examiner comment les pirates trompent les utilisateurs, utilisent la compilation dynamique de code pour dissimuler leurs outils et exploitent l'exécution en mémoire afin de ne laisser aucune trace sur le disque. En disséquant ces TTPs, nous pouvons mieux comprendre les stratégies d'évasion actuellement utilisées par les logiciels malveillants modernes et identifier les principales opportunités de détection et de défense.
Le point d'entrée initial est l'exécution par l'utilisateur d'une commande PowerShell, délivrée via une tactique d'ingénierie sociale connue sous le nom de ClickFix. L'utilisateur est redirigé vers une URL malveillante qui présente un faux CAPTCHA. Ce CAPTCHA demande à l'utilisateur d'exécuter une série de raccourcis clavier (Win + R, Ctrl + V, Entrée), qui lancent une commande PowerShell préalablement copiée dans le presse-papiers par le site web.
Des années de demandes de CAPTCHA légitimes et de questions de sécurité ont conditionné l'utilisateur à croire qu'il effectuait une étape de vérification nécessaire. En réalité, il exécute toutefois la charge utile initiale de l'attaquant. Cette méthode transforme l'utilisateur en complice involontaire de sa propre compromission. Cette approche "human-in-the-loop" est une technique d'évasion puissante conçue pour contourner les mesures de sécurité automatisées. L'attaque est structurée de telle sorte que ni l'e-mail contenant le lien, ni l'URL elle-même ne contiennent de logiciel malveillant au sens traditionnel du terme. De nombreux outils de sécurité des e-mails et filtres réseau sont ainsi rendus inefficaces. De fait, il n'y a pas de fichier malveillant à analyser ni de signature à faire correspondre au niveau du périmètre.
La commande elle-même est courte et discrète. Tout d'abord, une chaîne hexadécimale est décodée pour extraire une URL malveillante. Ensuite, un script PowerShell est récupéré à partir de cette URL via la commande Invoke-RestMethod
, puis exécuté directement dans le processus en cours. Cela marque le début de la première étape de l'infection.
Après l’accès initial, la commande PowerShell injectée via le presse-papiers télécharge et exécute un script jouant le rôle de dropper en mémoire. Ce script dissimule sa fenêtre de console en s’appuyant sur du code C# compilé dynamiquement et des appels aux API Windows.
Si cette approche échoue, un mécanisme de repli prend le relais : le script assigne un titre de fenêtre unique puis la rend invisible en la repérant via une recherche de processus.
Une fois masqué, le dropper récupère une charge utile chiffrée depuis un serveur distant.
Le Stager PowerShell injecte un loader .NET compact (tagué en interne "believemesh") directement dans son propre processus powershell.exe. L'objectif principal de ce loader est de déchiffrer un paquet de shellcode Donut, de le mapper à sa place et de le lancer, sans déposer de fichier ni créer de processus supplémentaire. À partir de la première instruction, toutes les opérations de décompression, de mappage et de transfert vers l’infostealer natif s'effectuent dans le runtime PowerShell d'origine.
Dans le projet .NET récupéré, le point d'entrée du loader se trouve dans sytuczzzsgolfnefxrmr.cs. La méthode Main() est concise, chaque instruction ayant été soigneusement choisie pour garantir une exécution transparente.
Tout d'abord, le code vérifie la langue de l'interface utilisateur de la machine et se ferme immédiatement s'il détecte une locale CIS, comme le russe (ru), le biélorusse (by) ou le kazakh (kz). Ce test simple (CultureInfo.CurrentUICulture.TwoLetterISOLanguageName
) empêche les infections accidentelles dans la région d'origine de l'acteur malveillant.
Si la locale est acceptable, le chargeur se concentre sur une chaîne ASCII-hex de 1,7 Mo intégrée juste en dessous de la définition de la classe.
Huit octets situés près du début de la chaîne remplissent une double fonction en tant que clé RC4. Le chargeur décode le texte, applique RC4 avec la clé extraite, puis transmet le résultat via un flux LZMA. En moins d'une seconde, un tampon de 0x69300 octets contenant deux binaires concaténés est conservé : un stub Donut 0.9.3 suivi du binaire de charge utile native final.
Maintenant que le package est en mémoire, le chargeur réserve un bloc d'espace d'adressage à l'aide d'un appel P/Invoke
direct à la fonction VirtualAlloc. Le package est alors copié, marqué comme exécutable et lancé dans un nouveau thread, le tout sans utiliser les API Win32 de haut niveau. Au lieu de cela, le loader utilise des appels système bruts fournis par un petit framework qui sera présenté dans la sous-section suivante. Une fois le thread lancé au début du blob Donut, le thread du loader d'origine entre dans une boucle de veille infinie afin que Powershell.exe ne se termine jamais.
Le recours à des importations ordinaires, telles que NtWriteVirtualMemory, rendrait le loader vulnérable aux hooks en mode utilisateur. Les auteurs ont donc développé leur propre couche d'indirection.
Ces hooks en mode utilisateur sont des techniques utilisées par les EDR (Endpoint Detection and Response) pour intercepter les appels API dans l'espace utilisateur avant qu'ils n'atteignent le noyau. Ces hooks permettent de surveiller ou de bloquer les activités suspectes, telles que la manipulation de la mémoire ou la création de threads, en injectant du code personnalisé dans les processus en cours d'exécution. Si un logiciel malveillant utilise les API Windows standard, ces hooks peuvent le détecter.
Pour éviter cela, le chargeur utilise une implémentation personnalisée des syscalls impliquant cinq fichiers source.
Étant donné que toutes les opérations à haut risque, telles que la copie d'octets, la modification de la protection et le démarrage d'un thread, passent par ces appels système artisanaux, les appels API standard à surveiller ne sont jamais détectés par les hooks EDR courants de l'espace utilisateur.
Une fois que le contrôle passe dans le code Donut (vérifié octet par octet comme Donut 0.9.3), le stub :
RVA 0xB0F8
sur un nouveau thread.Comme ni LoadLibrary
ni CreateProcess
ne sont impliqués, le binaire n'apparaîtra jamais dans une liste de modules ou dans les événements ETW ImageLoad, et l'intrusion se trouvera désormais exclusivement dans la mémoire volatile.
Une fois l'exécution transférée, le binaire mappé se révèle être un infostealer sur mesure qui ne correspond à aucune famille de logiciels malveillants répertoriée. Les identifiants statiques codés en dur dans plusieurs charges utiles suggèrent que le même kit d'outils a été utilisé dans plusieurs campagnes, mais aucune des signatures habituelles ne correspond. Ses objectifs fonctionnels sont toutefois indéniables.
Le code se concentre presque exclusivement sur les navigateurs. Dans le cas de Firefox, il accède directement au profil actif et récupère les fichiers cookies.sqlite, cert9.db, key4.db et places.sqlite. Ces quatre fichiers contiennent à eux seuls les cookies de session, les identifiants enregistrés, les certificats stockés et l'historique de navigation complet. Immédiatement après, le voleur prend une capture d'écran unique du bureau au premier plan et stocke l'image PNG dans la mémoire sous le nom Screenshot.png. Cette image est ensuite envoyée au canal de commande et de contrôle (C2) sans être stockée localement.
Les navigateurs basés sur Chromium sont traités différemment. Ici, le logiciel malveillant ouvre un WebSocket vers le port de débogage du navigateur et utilise des commandes natives de Chrome DevTools, telles que Network.getAllCookies
.
Cette approche permet d'obtenir des objets cookie décryptés en temps réel, ce qui signifie qu'il n'est pas nécessaire de localiser la base de données des cookies sur le disque ou de se débattre avec DPAPI. Les chemins d'accès codés en dur dans la référence binaire BinanceBrowser\Default mettent en évidence un intérêt particulier pour les sessions de cryptomonnaie.
Toutes les données collectées sont acheminées via un service TCP minimaliste écoutant sur 31.177.108.17:12345. Le protocole filaire commence par des mots de contrôle en texte clair (PING, PONG et ACK !), puis passe à un bloc de données obscurci par XOR. Chaque transmission intègre deux tags constants (em1 et un GUID tel que 66f89dce-5240-4124-b3ff-54c731c55507
), qui permettent à l'opérateur de suivre les victimes à travers plusieurs uploads. Les noms de fichiers (user.txt, server_info.txt, chemins d'accès du navigateur) sont envoyés en texte clair, suivis de la charge utile chiffrée de chaque fichier.
Aucun mécanisme de persistance n'a été implémenté, et aucun résidu n'est laissé sur le disque. Après le dernier ACK!, le processus se met brièvement en veille, libère ses tampons de travail et se termine, ne laissant que la région mémoire RX créée par Donut et un thread terminé dans la mémoire volatile.
Cette campagne illustre de manière frappante l'évolution des opérations malveillantes modernes. Des faux CAPTCHA incitant les utilisateurs à exécuter du code malveillant au loader multistade se cachant entièrement dans la mémoire et contournant pratiquement toutes les défenses traditionnelles, cette attaque illustre parfaitement le niveau de sophistication et de discrétion atteint aujourd'hui.
Bien que l'attribution reste incertaine, l'infrastructure, les modèles de codage et la réutilisation de modules dans les différentes campagnes suggèrent qu'il s'agit là de l'œuvre d'un acteur malveillant expérimenté disposant de ressources importantes.
Chez Swiss Post Cybersecurity, nous continuons à surveiller de près cette menace ainsi que les campagnes similaires. Le message est clair : les défenseurs doivent admettre que les IOCs traditionnels et l'analyse des fichiers ne suffisent plus. La détection comportementale, l'analyse de la mémoire et la formation des utilisateurs sont des éléments indispensables à toute stratégie de sécurité efficace.
Les attaquants placent la barre toujours plus haut ; nous devons en faire autant.
— Bruce Schneier
IOC | Typ | Name | Context |
admilzsolutions.co[.]ke | domain | ClickFix Subdomain | |
rtjj[.]store | domain | Payload delivery | |
rtjj[.]store/f/h | url | Stage 1 URL | |
rtjj.store/amountatom/believemesh | url | Stage 2 URL | |
31.177.108[.]17 |
ipv4-addr |
Stealer C2 server IP | |
12345 | port | Stealer C2 server port | |
9d9d5bebe19a536491720424d69cbbc 808e96f5dca9d18e0133ec45bdd39092e |
SHA-256 | believemesh | DONUTLOADER |
1e52ed52921eedcd858cc0d0ed9164d70840c742dd bad7d3fd65666d24c40cda |
SHA-256 | h(.ps1) | Stager |
e0038e6450f74f388e8952e1f7baa15de4631ed99568 b0bddf99ab336d7d6343 |
SHA-256 | DONUTINJECTOR | |
5a65621791cdcbce3cd1ee200454bec87f99a7e46d2 aa0ffcb8e15870e378ecd |
SHA-256 | Stealer Executable |
https://github.com/TheWover/donut
https://github.com/volexity/donut-decryptor
https://3xperience.substack.com/p/bite-sized-insights-diving-into-donut?utm_campaign=post&utm_medium=web
https://malware.tech/posts/unpacking-shellcode-loaders/#thewover-s-donut