SOC Prime Bias: Moyen

08 Mai 2026 18:33

NWHStealer se propage via Bun JavaScript Runtime

Author Photo
SOC Prime Team linkedin icon Suivre
NWHStealer se propage via Bun JavaScript Runtime
shield icon

Detection stack

  • AIDR
  • Alert
  • ETL
  • Query

Résumé

L’article explique comment les acteurs malveillants utilisent le runtime JavaScript Bun pour distribuer le NWHStealer basé sur Rust. Des archives ZIP malveillantes contiennent un installateur principal qui lance un chargeur JavaScript emballé par Bun ainsi qu’un chargeur secondaire nommé dw.exe. Le composant JavaScript effectue des vérifications anti-machine virtuelle, se connecte à l’infrastructure de commande et de contrôle, décrypte une charge utile suivante et utilise les API natives de Windows pour injecter le stealer en mémoire. Pour réduire la suspicion, la campagne dissimule des fichiers malveillants derrière des plateformes d’hébergement légitimes telles que GitHub, MediaFire et SourceForge.

Enquête

Les chercheurs ont découvert que Bun était utilisé pour empaqueter le JavaScript malveillant à l’intérieur de la section .bun du chargeur. Deux scripts, sysreq.js and memload.js, se chargeaient des vérifications de l’environnement et de la communication avec l’infrastructure de l’attaquant. Le chargeur récupérait des données de configuration et des charges utiles chiffrées à partir de domaines tels que silent-harvester.cc, les décryptait avec AES-256-CBC et les injectait via des appels d’API Win32. La charge utile finale était NWHStealer, qui vole des identifiants, des données de navigateur et des informations de portefeuille de cryptocurrency, et peut également lancer une activité de minage supplémentaire.

Atténuation

Les utilisateurs devraient éviter de télécharger des exécutables à partir de sites web non fiables et devraient vérifier les signatures des installateurs avant l’exécution. Les équipes de sécurité devraient surveiller toute activité inhabituelle du runtime Bun, les requêtes PowerShell CIM utilisées pour les vérifications de virtualisation et les appels d’API suspects tels que VirtualAlloc and LoadLibraryA. Les organisations devraient également bloquer les domaines malveillants connus et appliquer des politiques d’exécution de moindre privilège pour limiter les abus.

Réponse

Si cette activité est détectée, isolez l’endpoint affecté, collectez les images mémoire et disque, et recherchez Installer.exe, dw.exe, et des sections suspectes .bun . Bloquez les domaines et URL de commande et de contrôle identifiés au périmètre du réseau. Déployez des détections d’endpoint pour les commandes PowerShell observées et les modèles d’API Win32. Faites tourner les identifiants exposés et surveillez les systèmes affectés pour déceler des signes de minage de cryptocurrency non autorisé.

graph TB %% Définitions des classes classDef technique fill:#ffcc99 classDef action fill:#99ccff %% Nœuds node_initial_access[« <b>Accès initial</b><br/><b>T1036.008 Masquage : type de fichier</b> : ZIP malveillant déguisé en fichier bénin.<br/><b>T1204.002 Exécution utilisateur : fichier malveillant</b> : l’utilisateur exécute le fichier téléchargé. »] class node_initial_access technique node_obfuscated_loader[« <b>Chargeur obfusqué</b><br/><b>T1027.016 Insertion de code inutile</b> : JavaScript non pertinent ajouté pour masquer la logique malveillante.<br/><b>T1027.008 Suppression de parties du payload</b> : suppression du code non essentiel pour réduire la taille et éviter l’analyse. »] class node_obfuscated_loader technique node_discovery[« <b>Découverte</b><br/><b>T1082 Collecte d’informations système</b> : système d’exploitation, matériel et logiciels.<br/><b>T1016 Découverte de configuration réseau</b> : interfaces réseau, adresses IP et routage. »] class node_discovery technique node_credential_access[« <b>Accès aux identifiants</b><br/><b>T1555.003 Identifiants de navigateurs</b> : extraction des mots de passe enregistrés et données d’autoremplissage.<br/><b>T1550.004 Utilisation de matériel d’authentification alternatif</b> : vol de cookies de session. »] class node_credential_access technique node_c2[« <b>Commande & contrôle</b> : récupération de la clé AES et du payload chiffré depuis un serveur distant. »] class node_c2 action node_execution[« <b>Exécution</b><br/><b>T1620 Chargement de code réflexif</b> : exécution en mémoire sans écriture disque.<br/><b>T1055.009 Injection dans un processus</b> : injection dans un processus actif. »] class node_execution technique node_persistence[« <b>Persistance</b><br/><b>T1053 Tâche planifiée</b> : création d’une tâche planifiée pour exécution récurrente. »] class node_persistence technique %% Connexions node_initial_access –>|conduit à| node_obfuscated_loader node_obfuscated_loader –>|conduit à| node_discovery node_discovery –>|conduit à| node_credential_access node_credential_access –>|conduit à| node_c2 node_c2 –>|conduit à| node_execution node_execution –>|conduit à| node_persistence

Flux d’Attaque

Exécution de Simulation

Prérequis : Les vérifications de télémétrie et de référence de pré-vol doivent être réussies.

Justification : Cette section détaille l’exécution précise de la technique de l’adversaire (TTP) conçue pour déclencher la règle de détection. Les commandes et le récit DOIVENT refléter directement les TTP identifiés et viser à générer la télémétrie exacte attendue par la logique de détection. Des exemples abstraits ou sans rapport entraîneront un diagnostic erroné.

  • Narrative d’Attaque & Commandes :

    1. Accès Initial : L’attaquant livre un fichier JavaScript malveillant (loader.js) sur le système de la victime et l’exécute à l’aide du bun runtime (bun loader.js).
    2. Routine d’Auto-Injection : Le code JavaScript appelle les API natives Win32 via un aide PowerShell qui utilise Add‑Type pour définir des signatures P/Invoke pour VirtualAlloc, VirtualProtect, et LoadLibraryA.
    3. Allocation de Mémoire : VirtualAlloc réserve une région RWX dimensionnée pour le shellcode intégré.
    4. Injection de Shellcode : Le script PowerShell copie le shellcode décodé base64 dans la mémoire allouée en utilisant Marshal.Copy.
    5. Changement de Permission : VirtualProtect bascule la protection de page à PAGE_EXECUTE_READ.
    6. Exécution de la Charge Utile : LoadLibraryA est invoqué sur une DLL malveillante qui réside uniquement en mémoire (l’en-tête DLL est écrit dans la région allouée, puis l’adresse est passée à LoadLibraryA).
    7. Aucun Nouveau Fil : L’attaquant évite délibérément d’appeler CreateThread; le DllMain de la DLL s’exécute dans le contexte du processus actuel, satisfaisant la clause d’exclusion de la règle.
  • Script de Test de Régression :

    # -------------------------------------------------
    # NWHStealer JavaScript Loader – Démo d'Auto-Injection
    # -------------------------------------------------
    # Ce script imite le comportement du véritable chargeur NWHStealer.
    # Il appelle délibérément VirtualAlloc, VirtualProtect, et LoadLibraryA
    # sans invoquer CreateThread.
    
    # 1. Définir les signatures des API Win32
    $sig = @"
    using System;
    using System.Runtime.InteropServices;
    public class Win32 {
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
        [DllImport("kernel32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
        public static extern IntPtr LoadLibraryA(string lpFileName);
    }
    "@
    Add-Type $sig
    
    # 2. Allouer de la mémoire exécutable (RWX)
    $size = 0x1000
    $MEM_COMMIT = 0x1000
    $PAGE_EXECUTE_READWRITE = 0x40
    $mem = [Win32]::VirtualAlloc([IntPtr]::Zero, $size, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    if ($mem -eq [IntPtr]::Zero) { throw "VirtualAlloc failed" }
    
    # 3. Exemple de shellcode (MessageBox) – encodé en base64 pour lisibilité
    $b64 = "AQAAANAAAABAAEAAAABAAAAAgAAAAEAAABWAAAAAQAAAAIAAAD/////AQAAAAAAAAA="
    $shellcode = [Convert]::FromBase64String($b64)
    
    # 4. Copier le shellcode dans la région allouée
    [System.Runtime.InteropServices.Marshal]::Copy($shellcode, 0, $mem, $shellcode.Length)
    
    # 5. Changer la protection vers EXECUTE_READ
    $oldProtect = 0
    $PAGE_EXECUTE_READ = 0x20
    $ok = [Win32]::VirtualProtect($mem, $size, $PAGE_EXECUTE_READ, [ref]$oldProtect)
    if (-not $ok) { throw "VirtualProtect failed" }
    
    # 6. Charger la "DLL" en mémoire (ici, nous appelons simplement LoadLibraryA sur kernel32 en tant que substitut)
    # Dans le véritable chargeur, ce serait une DLL personnalisée écrite dans $mem.
    $hLib = [Win32]::LoadLibraryA("kernel32.dll")
    if ($hLib -eq [IntPtr]::Zero) { throw "LoadLibraryA failed" }
    
    Write-Host "Étapes d'auto-injection complétées – le processus devrait maintenant être marqué par la règle de détection."
    # -------------------------------------------------
  • Commandes de Nettoyage :

    # Terminer l'instance PowerShell qui a effectué l'injection
    Stop-Process -Id $PID -Force
    
    # (Optionnel) Si une DLL personnalisée a été écrite sur disque pour les tests, supprimez-la
    Remove-Item -Path "$env:TEMPmalicious.dll" -ErrorAction SilentlyContinue

Validation Post-Exécution

  1. Confirmer Génération d’Alerte : Exécutez la requête KQL suivante pour vérifier que la règle a été déclenchée :

     // Règle de détection : VirtualAlloc/VirtualProtect/LoadLibraryA sans CreateThread
     Sysmon
     | where EventID == 1
     | where CallTrace contains "VirtualAlloc" or CallTrace contains "VirtualProtect" or CallTrace contains "LoadLibraryA"
     | where not(CallTrace contains "CreateThread")
     | project TimeGenerated, Process, CallTrace, EventID
  2. Vérifiez Pas de Faux Positif : Réexécutez la commande bénigne de Notepad et assurez-vous que la requête retourne zéro résultat pour cette exécution.


Fin du Rapport