Parmi les attaques quotidiennes qui sont observées par les analystes SOC durant leur supervision, les malwares de type stealer ont une place de choix. Ces logiciels qui volent les mots de passe stockés de manière non sécurisée sur les machines sont en effet massivement utilisés par les attaquants dans des campagnes pour récupérer des informations de connexion. Les attaques sont simples, rapides et facilement industrialisables. Parmi les infostealer se retrouvent des noms désormais connus comme Lumma Stealer, Vidar, RedLine, etc.
Vers la mi-juillet 2026, une des instances EDR déployées chez un client a bloqué une communication réseau issue d’une exécution Powershell. Les analystes du SOC ont remonté l’alerte à l’équipe du CERT ITrust et malgré le fait que l’attaque ait été bloquée, nous étions curieux d’analyser la charge qu’il y avait derrière. C’est comme ceci qu’a débuté notre analyse de la nouvelle campagne de PureLogs stealer, un programme malveillant datant de 2022-2023 qu’HudsonRock avait analysé dans son article « Pure Logs Stealer Fails to Impress ».
Dans cette analyse nous décortiquons la nouvelle campagne de PureLogs et observons que, même s’il n’est pas encore le meilleur stealer du marché, il a pu se moderniser et évoluer par rapport à ses anciennes campagnes.
Vecteur initial de compromission et stager PowerShell
Pour recontextualiser rapidement l’incident, la compromission débute par un mail de phishing qui chaîne en plusieurs étapes vers une exécution PowerShell. Il s’agit d’une technique très commune de Social Engineering appelée « ClickFix » qui pousse l’utilisateur à exécuter du code malveillant sur sa machine en simulant un crash ou un problème technique.
La commande exécutée en question est la suivante :
Il s’agit d’un cmdlet PowerShell qui effectue une requête HTTP, télécharge et exécute du code arbitraire hébergé à distance en une seule instruction, tout cela entièrement en mémoire et sans jamais écrire de fichier sur le disque. Il s’agit d’un vecteur initial de compromission très effectif utilisé et comparable à différentes campagnes « ClickFix ». Ce dernier mobilise différentes techniques d’évasion de défenses comme la polymorphie anti-signature à l’aide des commentaires aléatoires générés faisant partie de cette commande. Le principe de cette technique repose sur le fait de rendre chaque instance du code malveillant unique afin d’éviter les détections par signature. Un autre élément intéressant provient de la page accédée provenant de l’URL, ici “?sid= 1785227193742-lf6op6dy” qui semblerait être un moyen de tracking déployé par l’attaquant permettant d’obtenir un identifiant unique par victime, ce qui est complémentaire au côté polymorphique de la charge malveillante.
A la suite de cette chaîne d’exécution, une autre commande d’exécution Powershell est lancée :
Cette commande est écrite sur une seule ligne, ce qui peut entraver légèrement la compréhension de sa structure et seule l’obfuscation des noms de variables est présente. Une réécriture au propre avec renommage des variables fournit le résultat suivant :
La commande est un loader qui va récupérer une ressource à l’URL ‘hxxp[:]//158[.]94[.]211[.]92/s_enterprise’ pour la charger directement en mémoire. L’attaquant utilise ici des techniques classiques d’injection en mémoire en utilisant les fonctions de la librairie partagée kernel32.dll qui sont importées directement dans le code. La technique d’exécution en mémoire et d’injection de thread (Local Process Injection T1055) est assez commune : L’attaquant alloue dynamiquement une zone mémoire via la fonction VirtualAlloc(). Les arguments de la fonction indiquent que la mémoire est attribuée sans « protection » avec des droits ReadWrite directement. La charge malveillante récupérée via la réponse web est envoyée en mémoire dans un buffer alloué dynamiquement. Un thread est créé avec la fonction CreateThread() dans le processus courant pour exécuter la charge. Dans le cas où le thread est vide, c’est-à-dire si le loader n’a pas fonctionné, quelle qu’en soit la raison, l’ensemble de la chaîne d’exécution est stoppé. Le loader PowerShell est très standard et c’est à partir de la seconde étape de chargement que le malware commence à devenir intéressant.
Stage 1 : reverse du downloader ‘s_entreprise’
La première étape se termine en injectant « s_enterprise » récupéré à distance dans un thread mémoire. Cette charge a pu être téléchargée en environnement protégé et une analyse statique sommaire donne les résultats suivants :
| IDENTIFICATION | |
| Nom du fichier | s_entreprise |
| MD5 Hash | 287f1c7775b1e76cd31ed10033e1a111 |
| SHA-1 Hash | 155b0d50a983cf737394a1e5b9b69412e99f73b7 |
| SHA-256 Hash | 6270f38bdf27aebb108331a484220a8bc0b9a1e6a2c4bba6dad89a2f3665bca5 |
| Format | PE 32+, MS Windows 5.02 Console, x86-64 |
| DOS Header | MZER |
Plusieurs points interpellent dans ces premiers éléments. Il est possible à partir des fonctions importées telles que WinHTTPConnect() de supposer des capacités de communication web et de chargement. Il peut donc s’agir d’un second loader. La partie la plus étrange néanmoins est le header de ce fichier. En effet, la charge commence par le header ‘MZER’ (MZ qui correspond au format Portable Executable Windows). Il s’agit donc théoriquement d’un fichier exécutable classique cependant, le chargement en mémoire effectué par les commandes PowerShell décrites plus haut est trop simple pour un PE. Pour le charger, il serait nécessaire en plus de l’allocation mémoire de mapper les sections du PE (.text, .data), de résoudre la table des imports, etc. L’attaquant utilise ici une astuce connue et souvent implémentée dans les logiciels générateurs de shellcode comme Metasploit ou Donut. Il joue sur la correspondance entre l’ASCII avec des commandes assembleur et utilise l’en-tête MZER comme un shellcode :
1. Il est possible d’établir une correspondance entre l’ASCII et l’assembleur. MZ peut ainsi se transcrire 0x4D5A en hexadécimal ce qui correspond à une instruction pop r10 tandis que ER correspond à push r10 (0x4552). Les deux instructions s’annulent. Le fichier reste un PE valide mais devient aussi un shellcode exécutable.
2.Dans un exécutable Windows, les adresses mémoire sont protégées par ASLR (l’adresse de chargement n’est pas prédictible), donc le shellcode doit retrouver sa propre base au runtime. Les prochaines instructions constituent un ‘trick’ (souvent nommé GetPC) pour retrouver la position du shellcode embarqué en mémoire, et pouvoir plus tard réaliser un jump dessus. Call empile l’adresse du EIP suivi de l’instruction pop rcx qui va récupérer dans RCX cette adresse. A ce moment-là, EIP est située à base+0x9 (0x2 pour le pop r10 + 0x2 pour le push r10 + 0x5 pour le call = 0x9).
3.RCX contient maintenant l’adresse du début du PE + 0x9. L’attaquant veut ici se replacer à l’adresse de base du PE (sans l’offset 0x9) donc effectue un sub rcx, 0x9. Il a maintenant retrouvé sa position de base en mémoire malgré l’ASLR.
4. Il effectue ensuite un saut pour pointer vers une adresse précise du PE (début du shellcode embarqué) avec un offset hardcodé. On décale vers (base+offset) via les instructions mov rax, rcx ; add rax, 0xc000 ; call rax
Toutes ces manipulations permettent de traiter la charge à la fois en tant qu’exécutable et shellcode. Au terme des instructions liées au header et en suivant les différentes redirections au sein du code, le shellcode charge la fonction main du fichier.
2.1. Analyse fonctionnelle – orchestrateur main
1. Téléchargement d’une charge
2. Récupération du pid de svchost.exe
3. Fallback sur explorer.exe si la recherche svchost.exe est en échec
4.Injection de la charge récupérée via création d’un thread
5. Libération de la mémoire
Détonation du Stage 2
3.1. Analyse en sandbox
La détonation de la charge du stage 2 indique clairement que la menace est un infostealer. En quelques secondes le malware consulte des registres et des fichiers sensibles à la recherche de clés de session, mots de passe et wallet cryptos.
Il est aussi possible d’observer les échanges réseau du malware avec son C2 via un échange de 892 octets. Ces communications sont chiffrées ainsi, une capture réseau PCAP de ces échanges a été effectuée pour tenter un déchiffrement plus tard. Il est néanmoins probable qu’il s’agisse d’un comportement de beaconing ou d’exfiltration de données.
De la même manière que pour ‘s_enterprise.bin’, il s’agit d’un « hybride » PE/shellcode reprenant l’en-tête MZER. Les multiples redirections effectuées à partir du header mènent à un loader hors des sections déclarées (.reloc : 0x4B000 + 0x1000 alors que le fichier complet fait 0x4C600). Ce chargeur lance un second PE (un vrai PE pour une fois) qui se situe à l’offset 0x1BA40 qui est le stealer PureLogs.
3.2. Techniques anti-VM/debug, d’évasion, furtivité et contrôle d’environnement
A travers la détonation et l’analyse statique de ce programme malveillant, diverses techniques de reconnaissance, d’anti-vm et d’anti-debug ont été répertoriées à travers les différentes étapes d’exécution. A travers ces différents composants, ce dernier met en œuvre un ensemble de contrôles environnementaux avant toute exécution de sa charge utile principale.
1. Au niveau système, il interroge la base WMI (à l’aide de diverses requêtes hardcodées dans le programme) contenant diverses informations provenant du matériel physique de la machine mais aussi du système d’exploitation. Ces éléments permettent d’établir une signature de son environnement d’exécution (fabricant, contrôleur graphique, nombre de cœurs CPU, température) et la présence d’un utilisateur/domaine factice typique des environnements d’analyse automatisés. Il complète cette vérification par l’énumération du registre des imprimantes installées et de l’activité des périphériques USB. La présence de matériel réel utilisable constitue un indicateur fort d’une machine physique plutôt que d’une sandbox possiblement détectable.
2.Sur le plan de l’anti-débogage, le malware neutralise le rapport d’erreur Windows par défaut (SetUnhandledExceptionFilter) pour limiter la génération de rapports d’erreur (WER) exploitables permettant un export de la mémoire vive (dump mémoire post crash), et implémente un mécanisme de dérivation de clé de chiffrement basé sur l’inspection de la pile d’appels (StackTrace) à l’exécution. De ce fait, toute instrumentation runtime (breakpoints, hooking) modifie cette pile propre au programme et donc invalide “silencieusement” la clé, rendant les données de configuration illisibles sans que l’analyste ne reçoive d’erreur explicite.
3. Lors de l’analyse statique, l’utilisation d’un overlay a été observée à l’offset 0x0004C000 de taille relativement petite (1.6 kB). Ce dernier n’était pas présent dans l’analyse statique initiale du premier échantillon, il a été découvert lors de l’investigation du deuxième. Il correspondrait potentiellement à une clé de chiffrement ou à la configuration, qui serait donc protégée et invisibilisée hors runtime, contre toute tentative d’accès ne correspondant pas à l’exécution attendue.
4. De manière générale, le code est protégé par différents mécanismes anti-analyse : d’une part l’obfuscation source-à-source multiple observée à travers ce rapport, combinée à l’attribut “Debuggable” désactivant les références de débogage lors de la compilation du programme, ce qui dégrade et ralentit considérablement la précision des outils de rétro-ingénierie ainsi que les tentatives d’analyse.
5. Enfin, à plusieurs reprises, l’échantillon intègre une logique de temporisation implémentée de différentes manières notamment : des délais d’exécution de plusieurs dizaines de secondes, expiration programmée de l’infrastructure à une date fixe. Ces mécanismes sont destinés à dépasser la fenêtre d’observation typique des systèmes de détection par heuristique ainsi que les tentatives d’analyse par sandbox automatisées.
Nom du fichier Payload.bin MD5 Hash 5e7c0273fb6a3e926a9d99af6e70a754 SHA-1 Hash 798db80c73642460864aad45402c8265da57f9b1 SHA-256 Hash 4a0a96834cc600a524d4b2348b00aec0361c2d7c97cca719372229e036961b0a Taille 174 176 octets Format PE 32+, MS Windows 4.00 (GUI), x86-64 Mono .NET Assembly DOS Header MZ
Reverse Stage 3 : PureLogs stealer 4.1. premiers éléments
| IDENTIFICATION | |
| Nom du fichier | Payload.bin |
| MD5 Hash | 5e7c0273fb6a3e926a9d99af6e70a754 |
| SHA-1 Hash | 798db80c73642460864aad45402c8265da57f9b1 |
| SHA-256 Hash | 4a0a96834cc600a524d4b2348b00aec0361c2d7c97cca719372229e036961b0a |
| Taille | 174 176 octets |
| Format | PE 32+, MS Windows 4.00 (GUI), x86-64 Mono .NET Assembly |
| DOS Header | MZ |
Cette charge finale est en clair. Il s’agit d’un PE .NET embarqué qui, une fois décompilé via ILSpy, constitue un fichier C# de 311 Ko. Le fichier est fortement obfusqué via des opérations binaires XOR, des chaînes constituées via une série de append(), des concaténations de char et plusieurs fonctions « inutiles ».
Une première lecture permet d’identifier dans sa section configuration un ID de campagne (91cba2b9-bec8-45d3-ab2a-e7207f94ce93) ainsi qu’une kill date mise en place au 1er septembre 2026 :
L’IP associée au C2 de cette campagne a déjà été reportée à plusieurs reprises sur AbuseIP. Une première lecture permet d’identifier dans sa section configuration un ID de campagne (91cba2b9-bec8-45d3-ab2a-e7207f94ce93) ainsi qu’une kill date mise en place au 1er septembre 2026 :
Il est intéressant de noter que dans la fonction Main(), le malware cherche les langues installées et ne se déclenche pas si “ru-RU” est présent sur la machine cible :
4.2. Fonctionnalités identifiées
Les fonctionnalités de PureLogs en tant que stealer sont assez standards :
Vol de données de navigateur (basés sur Chromium et Firefox) avec collecte des identifiants “Login Data”, données bancaires / formulaires « Web Data”, Cookies de session, historique de navigation, clés de chiffrement stockées dans “Local State”, stockage web “Local Storage”, données d’extensions pour recherche de portefeuilles cryptos.
▸ Cibles applicatives :
- Configuration et données de session Steam (config.vdf, loginusers.vdf)
- Clés API, données de session et GPS sur Telegram
- Cache local et vol de token d’authentification Discord (LocalCache, leveldb)
▸ Reconnaissance système :
- Win32_OperatingSystem pour collecter l’architecture, la version et la langue
- Win32_ComputerSystem pour collecter le nom de la machine, le domaine et l’utilisateur
- Win32_VideoController pour collecter le modèle de la carte graphique
- Lecture du registre “HARDWARE\DESCRIPTION\System\CentralProcessor\0” pour les informations processeur
En plus du comportement typique d’un stealer, il implémente différentes fonctionnalités de lancement de charges et commandes :
▸ Un module PowershellLauncher pour lancer des charges via “powershell.exe – NoProfile -ExecutionPolicy Bypass -File <file>”
▸ Module de lancement de DLL via rundll32 ▸ Obfuscation des chaînes critiques de l’injection et liées à “ntdll.dll” et “kernel32.dll”.
▸ Exécution furtive en utilisant la technique d’APC Queue Injection :
- IAT Camouflage en utilisant de la résolution dynamique. Conséquence : pas de présence des fonctions suspectes dans la table d’importation.
Asynchronous Procedure Call (APC) est un mécanisme des systèmes Windows qui permet l’exécution de tâches de manière asynchrone indépendamment de l’exécution principale du programme.
En développement offensif cette fonctionnalité est souvent utilisée pour mettre une charge dans la file APC, et lancer son exécution plus tard dans le programme. Cette chaîne est implémentée ici dans un nouveau processus créé en état suspendu (EarlyBird APC Injection). PureLogs en fait l’implémentation suivante :
NtCreateSection (host577) –> NtMapViewOfSection RW (pulse7) –> copie du shellcode –> NtMapViewOfSection RX (pulse7) –> NtQueueApcThread (wrap6) –> NtResumeThread (tail1) –> NtUnmapViewOfSection (finally)
Il s’agit là d’une évolution du stealer par rapport à sa version précédente qui utilisait l’injection par APC mais dans une version plus standard.
4.3. Déchiffrement de la trame
4.3.1 Clé
AES La configuration du stealer présentée plus haut mentionne une clé AES. Cette dernière sert à chiffrer les communications avec le serveur de C2. Il s’agit d’une amélioration de PureLogs par rapport à l’ancienne version où le chiffrement DES était implémenté.
La clé AES est hardcodée et extraite via une fonction nommée InitializeArray(array2, RuntimeFieldHandle). ILSpy n’a pas su restituer le tableau statique contenant la clé pendant le processus de décompilation. En utilisant objdump et xxd, il est possible de retrouver la donnée directement dans la section .text :
Le header va donc se trouver à 0x200 (offset récupéré grâce à la commande ci-dessus). Les premiers octets du header vont nous permettre de connaître sa taille (ici 0x48), on pourra ensuite afficher la donnée en hexa qui se situe après :
A l’offset 0x248 on a la clé AES 128 bits hardcodée : 7B41c330b576fa8d0ce909c2b1e4d150
4.3.2. Recherche de l’IV
4.3.3. Déchiffrement de la capture réseau
Avec la clé, il est maintenant possible de déchiffrer la capture réseau. Le premier échange qui a été capturé correspond à la détection et l’envoi d’informations génériques concernant la machine compromise
Cette trame correspond bien à un beacon envoyé au C2. La capture effectuée ne contenait pas d’autres données. Plusieurs hypothèses sont possibles pour expliquer l’absence d’exfiltration comme notamment le fait qu’il attende une instruction avant d’exfiltrer les données.
Conclusion et détection
La détection statique des IoCs est essentiellement adaptée aux stages 1 et 2 avec l’adresse IP de l’infrastructure malveillante et hash des charges. Les indicateurs présents dans le stage 3 sont obfusqués et par conséquent, difficiles à détecter statiquement. Différentes règles firewall, EDR, antivirus peuvent être mises en place pour la détection de la campagne :
| IoCs | |
| Indicateur | Valeur |
| IP C2 | 158.94.208.92 |
| IP Serveur Malveillant | 158.94.211.92 (héberge les charges) |
| User-Agent | powershell |
| URLs | hxxp://*/entreprise/student_s.bin’ |
| Clé AES-128 | 7B41C330B576FA8D0CE909C2B1E4D150 (Offset 0x248 du stage 3) |
| MD5 Hash | 287f1c7775b1e76cd31ed10033e1a111 |
| SHA-1 Hash | 155b0d50a983cf737394a1e5b9b69412e99f73b7 |
| SHA-256 Hash | 6270f38bdf27aebb108331a484220a8bc0b9a1e6a2c4bba6dad89a2f3665bca5 |
| MD5 Hash | 5e7c0273fb6a3e926a9d99af6e70a754 |
| SHA-1 Hash | 798db80c73642460864aad45402c8265da57f9b1 |
| SHA-256 Hash | 4a0a96834cc600a524d4b2348b00aec0361c2d7c97cca719372229e036961b0a |
Par rapport à ses précédentes versions observées, PureLogs a gagné en furtivité avec cette variante d’Early Bird APC injection. La souche étudiée reste assez similaire à ce qui a déjà été analysé. Il embarque toujours des fonctionnalités de collecte massive dans les applications de bureau et les navigateurs, communique de manière chiffrée avec le C2 de l’attaquant, et cible désormais les gestionnaires de mots de passe et les portefeuilles de cryptomonnaies. L’évolution constatée n’est pas une refonte complète du malware mais une modernisation de son implémentation. La première apparition du malware remonte à 2022. Avec le temps il continue de se perfectionner et reste utilisé par les cybercriminels principalement par son aspect low cost comme l’avait souligné FlashPoint dans son article de blog publié en 2023.
Article rédigé par l’équipe CERT ITrust