NWHStealer Disseminado Através do Tempo de Execução Bun JavaScript
Detection stack
- AIDR
- Alert
- ETL
- Query
Resumo
O artigo explica como atores de ameaça estão usando o runtime JavaScript Bun para entregar o infostealer NWHStealer baseado em Rust. Arquivos ZIP maliciosos contêm um instalador primário que lança um carregador JavaScript empacotado com Bun junto com um carregador secundário chamado dw.exe. O componente JavaScript executa verificações anti-máquina virtual, entra em contato com a infraestrutura de comando e controle, descriptografa uma carga adicional e abusa das APIs nativas do Windows para injetar o stealer na memória. Para reduzir a suspeita, a campanha esconde arquivos maliciosos atrás de plataformas de hospedagem legítimas como o GitHub, MediaFire e SourceForge.
Investigação
Pesquisadores descobriram que o Bun foi usado para empacotar JavaScript malicioso dentro da seção .bun do carregador. Dois scripts, sysreq.js and memload.js, cuidavam das verificações de ambiente e da comunicação com a infraestrutura do atacante. O carregador recuperou dados de configuração e cargas criptografadas de domínios como silent-harvester.cc, os descriptografou com AES-256-CBC e os injetou através de chamadas de API Win32. A carga final era o NWHStealer, que rouba credenciais, dados de navegador e informações de carteiras de criptomoeda, podendo também iniciar atividades adicionais de mineração.
Mitigação
Usuários devem evitar baixar executáveis de sites não confiáveis e verificar assinaturas de instaladores antes da execução. Equipes de segurança devem monitorar atividades incomuns do runtime Bun, consultas PowerShell CIM usadas para verificações de virtualização e chamadas de API suspeitas como VirtualAlloc and LoadLibraryA. As organizações também devem bloquear os domínios maliciosos conhecidos e aplicar políticas de execução com o menor privilégio para limitar abusos.
Resposta
Se esta atividade for detectada, isole o endpoint afetado, colete imagens de memória e disco, e procure por Installer.exe, dw.exe, e seções suspeitas .bun . Bloqueie os domínios e URLs de comando e controle identificados no perímetro da rede. Desdobre detecções de endpoint para os comandos PowerShell observados e padrões de API Win32. Gire credenciais expostas e monitore sistemas afetados para sinais de mineração de criptomoeda não autorizada.
Fluxo de Ataque
Detecções
Comando e Controle Suspeito por Solicitação de DNS de Domínio de Nível Superior (TLD) Incomum (via dns)
Ver
Verificações Possíveis de Evasão (via powershell)
Ver
Tentativa de Comunicação de Domínio de Pesquisa de IP Possível (via dns)
Ver
IOCs (HashSha256) para detectar: Atacantes adotam o runtime JavaScript Bun para espalhar o NWHStealer
Ver
Detectar Autoinjeção do Carregador JavaScript NWHStealer [Criação de Processo Windows]
Ver
Detecção de Comandos Anti-Virtualização PowerShell do NWHStealer [Windows Powershell]
Ver
Execução de Simulação
Pré-requisito: A Verificação Pré‑Voo Telemetria & Baseline deve ter sido aprovada.
Justificativa: Esta seção detalha a execução precisa da técnica do adversário (TTP) projetada para acionar a regra de detecção. Os comandos e a narrativa DEVEM refletir diretamente os TTPs identificados e visam gerar a telemetria exata esperada pela lógica de detecção. Exemplos abstratos ou não relacionados levarão a um diagnóstico incorreto.
-
Narrativa & Comandos do Ataque:
- Acesso Inicial: O atacante entrega um arquivo JavaScript malicioso (
loader.js) no sistema da vítima e o executa usando o bun runtime (bun loader.js). - Rotina de Autoinjeção: O código JavaScript chama APIs Win32 nativas via um auxiliar PowerShell que usa Add‑Type para definir assinaturas P/Invoke para
VirtualAlloc,VirtualProtect, eLoadLibraryA. - Alocação de Memória:
VirtualAllocreserva uma região RWX dimensionada para o shellcode embutido. - Injeção de Shellcode: O script PowerShell copia o shellcode decodificado em base64 para a memória alocada usando
Marshal.Copy. - Mudança de Permissão:
VirtualProtectaltera a proteção da página para PAGE_EXECUTE_READ. - Execução de Carga:
LoadLibraryAé invocada em uma DLL maliciosa que reside apenas em memória (o cabeçalho da DLL é escrito na região alocada, então o endereço é passado paraLoadLibraryA). - Sem Nova Thread: O atacante evita deliberadamente chamar
CreateThread; o DllMain da DLL é executado no contexto do processo atual, satisfazendo a cláusula de exclusão da regra.
- Acesso Inicial: O atacante entrega um arquivo JavaScript malicioso (
-
Script de Teste de Regressão:
# ------------------------------------------------- # NWHStealer JavaScript Loader – Demo de Autoinjeção # ------------------------------------------------- # Este script imita o comportamento do carregador real NWHStealer. # Ele chama deliberadamente VirtualAlloc, VirtualProtect, e LoadLibraryA # sem invocar CreateThread. # 1. Defina assinaturas de 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. Alocar memória executável (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. Shellcode de exemplo (MessageBox) – codificado em base64 para legibilidade $b64 = "AQAAANAAAABAAEAAAABAAAAAgAAAAEAAABWAAAAAQAAAAIAAAD/////AQAAAAAAAAA=" $shellcode = [Convert]::FromBase64String($b64) # 4. Copiar shellcode na região alocada [System.Runtime.InteropServices.Marshal]::Copy($shellcode, 0, $mem, $shellcode.Length) # 5. Alterar proteção para 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. Carregar a "DLL" na memória (aqui simplesmente chamamos LoadLibraryA em kernel32 como um espaço reservado) # No carregador real, esta seria uma DLL personalizada escrita em $mem. $hLib = [Win32]::LoadLibraryA("kernel32.dll") if ($hLib -eq [IntPtr]::Zero) { throw "LoadLibraryA failed" } Write-Host "Passos de autoinjeção concluídos – o processo agora deve ser sinalizado pela regra de detecção." # ------------------------------------------------- -
Comandos de Limpeza:
# Terminar a instância do PowerShell que realizou a injeção Stop-Process -Id $PID -Force # (Opcional) Se uma DLL personalizada foi escrita no disco para teste, remova-a Remove-Item -Path "$env:TEMPmalicious.dll" -ErrorAction SilentlyContinue
Validação Pós‑Execução
-
Confirme a Geração de Alerta: Execute a seguinte consulta KQL para verificar se a regra foi acionada:
// Regra de Detecção: VirtualAlloc/VirtualProtect/LoadLibraryA sem CreateThread Sysmon | onde EventID == 1 | onde CallTrace contém "VirtualAlloc" ou CallTrace contém "VirtualProtect" ou CallTrace contém "LoadLibraryA" | onde não(CallTrace contém "CreateThread") | projeto TimeGenerated, Process, CallTrace, EventID -
Verifique se Não há Falso Positivo: Re‑execute o comando benigno do Notepad e assegure-se que a consulta retorne zero resultados para essa execução.
Fim do Relatório