John Uhlmann

Pilhas de Chamadas: Chega de Passe Livre para Malware

Exploramos o imenso valor que as pilhas de chamadas trazem para a detecção de malware e por que a Elastic as considera uma telemetria de endpoint vital do Windows, apesar das limitações arquitetônicas.

19 min de leituraInternos
Pilhas de Chamadas: Chega de Passe Livre para Malware

As pilhas de chamadas fornecem quem

Um dos principais diferenciais da telemetria de endpoint do Windows do Elastic são as pilhas de chamadas.

A maioria das detecções depende do que está acontecendo — e isso geralmente é insuficiente, pois a maioria dos comportamentos tem dupla finalidade. Com pilhas de chamadas, adicionamos a capacidade refinada de também determinar quem está executando a atividade. Essa combinação nos dá uma capacidade incomparável de descobrir atividades maliciosas. Ao alimentar essa telemetria profunda ao mecanismo de regras no host do Elastic Defend , podemos responder rapidamente a ameaças emergentes.

Pilhas de chamadas são uma bela mentira

Em ciência da computação, uma pilha é uma estrutura de dados do tipo "último a entrar, primeiro a sair". Semelhante a uma pilha de itens físicos, só é possível adicionar ou remover o elemento superior. Uma pilha de chamadas é uma pilha que contém informações sobre as chamadas de sub-rotinas ativas no momento.

Em hosts x64, essa pilha de chamadas só pode ser gerada com precisão usando recursos de rastreamento de execução na CPU, como Intel LBR, Intel BTS, Intel AET, Intel IPT e x64 Architectural LBR. Esses recursos de rastreamento foram projetados para fins de criação de perfil de desempenho e depuração, mas também podem ser usados em alguns cenários de segurança. Entretanto, o que está mais geralmente disponível é uma pilha de chamadas aproximada que é recuperada da pilha de dados de um thread por meio de um mecanismo chamado stack walking.

Na arquitetura x64, o “registrador de ponteiro de pilha” (rsp) aponta, sem surpresa, para uma estrutura de dados de pilha, e há instruções eficientes para ler e gravar os dados nesta pilha. Além disso, a instrução call transfere o controle para uma nova sub-rotina, mas também salva um endereço de retorno no endereço de memória referenciado pelo ponteiro de pilha. Uma instrução ret recuperará posteriormente este endereço salvo para que a execução possa retornar de onde parou. As funções na maioria das linguagens de programação são normalmente implementadas usando essas duas instruções, e tanto os parâmetros de função quanto as variáveis de função locais normalmente serão alocados nessa pilha para desempenho. A parte da pilha relacionada a uma única função é chamada de quadro de pilha.

Stack walking é a recuperação apenas dos endereços de retorno dos dados heterogêneos armazenados na pilha de threads. Os endereços de retorno precisam ser armazenados em algum lugar para o fluxo de controle — então, o stack walking coopta esses dados existentes para aproximar uma pilha de chamadas. Isso é totalmente adequado para a maioria dos cenários de depuração e criação de perfis de desempenho, mas um pouco menos útil para auditoria de segurança. O problema principal é que não é possível desmontar ao contrário. Você sempre pode determinar o endereço de retorno de um determinado site de chamada, mas não o contrário. A melhor abordagem que você pode adotar é verificar cada um dos 15 possíveis comprimentos de instruções precedentes e ver qual se resume exatamente a uma instrução de chamada. Mesmo assim, tudo o que você recuperou foi um site de chamada anterior — não necessariamente o site de chamada anterior exato. Isso ocorre porque a maioria dos compiladores usa otimização de chamada final para omitir quadros de pilha desnecessários. Isso cria cenários irritantes para a segurança, como não haver garantia de que a função Win32StartAddress estará na pilha mesmo que tenha sido chamada.

Então, o que normalmente chamamos de pilha de chamadas é, na verdade, uma pilha de endereços de retorno.

Os autores de malware usam essa ambiguidade para mentir. Eles criam quadros de pilha de trampolim por meio de módulos legítimos para ocultar chamadas originadas de código malicioso ou forçam o stack walk a prever endereços de retorno diferentes daqueles que a CPU executará. É claro que o malware sempre foi apenas uma tentativa de mentir, e o antimalware é apenas o processo de expor essa mentira.

“... mas com o tempo a verdade será revelada.”

  • William Shakespeare, O Mercador de Veneza, Ato 2, Cena 2

Tornando as pilhas de chamadas bonitas

Até agora, uma caminhada pela pilha é apenas uma lista de endereços de memória numérica. Para torná-los úteis para análise, precisamos enriquecê-los com contexto. (Observação: atualmente não incluímos quadros de pilha do kernel.)

O enriquecimento mínimo útil é converter esses endereços em deslocamentos dentro dos módulos (por exemplo ntdll.dll+0x15c9c4). Mas isso só detectaria os malwares mais notórios — podemos ir mais a fundo. Os módulos mais importantes no Windows são aqueles que implementam as APIs Native e Win32. A interface binária do aplicativo para essas APIs exige que o nome de cada função seja incluído no Diretório de Exportação do módulo que a contém. Essas são as informações que a Elastic usa atualmente para enriquecer suas pilhas de chamadas de endpoint.

Um enriquecimento mais preciso pode ser alcançado usando símbolos públicos (se disponíveis) hospedados na infraestrutura do fornecedor (especialmente Microsoft). Embora esse método ofereça maior fidelidade, ele tem custos operacionais mais altos e não é viável para nossos clientes sem acesso a dados.

Uma regra prática para o kernel da Microsoft e símbolos nativos é que a interface exportada de cada componente tenha um prefixo em maiúsculas, como Ldr, Tp ou Rtl. Funções privadas estendem esse prefixo com um p. Por padrão, funções privadas com vinculação externa são incluídas na tabela de símbolos pública. Um deslocamento muito grande pode indicar uma função muito grande, mas também pode indicar uma função sem nome para a qual você não tem símbolos. Uma diretriz geral seria considerar quaisquer deslocamentos de três dígitos ou maiores em uma função exportada como provavelmente pertencentes a outra função.

Pilha de chamadasCaminhada pela pilhaMódulos Stack WalkExportações Stack Walk (abordagem elástica)Símbolos públicos do Stack Walk
0x7ffb8eb9c9c2 0x12d383f0046 0x7ffb8eb1a9d8 0x7ffb8eb1aaf4 0x7ffb8ea535ff 0x7ffb8da5e8cf 0x7ffb8eaf14eb0x7ffb8eb9c9c4 0x7ffb8c3c71d6 0x7ffb8eb1a9ed 0x7ffb8eb1aaf9 0x7ffb8ea53604 0x7ffb8da5e8d4 0x7ffb8eaf14f1ntdll.dll+0x15c9c4 kernelbase.dll+0xc71d6 ntdll.dll+0xda9ed ntdll.dll+0xdaaf9 ntdll.dll+0x13604 kernel32.dll+0x2e8d4 ntdll.dll+0xb14f1ntdll.dll!NtProtectVirtualMemory+0x14 kernelbase.dll!VirtualProtect+0x36 ntdll.dll!RtlAddRefActivationContext+0x40d ntdll.dll!RtlAddRefActivationContext+0x519 ntdll.dll!RtlAcquireSRWLockExclusive+0x974 kernel32.dll!BaseThreadInitThunk+0x14 ntdll.dll!RtlUserThreadStart+0x21ntdll.dll!NtProtectVirtualMemory+0x14 kernelbase.dll!VirtualProtect+0x36 ntdll.dll!RtlTpTimerCallback+0x7d ntdll.dll!TppTimerpExecuteCallback+0xa9 ntdll.dll!TppWorkerThread+0x644 kernel32.dll!BaseThreadInitThunk+0x14 ntdll.dll!RtlUserThreadStart+0x21

Comparação de níveis de enriquecimento da pilha de chamadas

No exemplo acima, o shellcode em 0x12d383f0000 usou deliberadamente uma chamada de cauda para que seu endereço não aparecesse na caminhada da pilha. Essa mentira por omissão é aparente mesmo apenas na caminhada de perseguição. O Elastic relata isso com a heurística proxy_call , pois o malware registrou uma função de retorno de chamada do temporizador para fazer proxy da chamada para VirtualProtect de um thread diferente.

Tornando as pilhas de chamadas poderosas

As pilhas de chamadas das chamadas do sistema que monitoramos com o Event Tracing for Windows (ETW) têm uma estrutura esperada. Na parte inferior da pilha está o thread StartAddress - normalmente ntdll.dll!RtlUserThreadStart. Em seguida, vem a entrada do thread da API do Win32 - kernel32.dll!BaseThreadInitThunk e, em seguida, o primeiro módulo do usuário. Um módulo de usuário é um código de aplicativo que não faz parte da API Win32 (ou nativa). Este primeiro módulo de usuário deve corresponder ao Win32StartAddress do thread (a menos que a função tenha usado uma chamada final). Mais módulos de usuário seguirão até que o módulo de usuário final faça uma chamada para uma API Win32 que faz uma chamada de API nativa, o que finalmente resulta em uma chamada de sistema para o kernel.

Do ponto de vista de detecção, o módulo mais importante nesta pilha de chamadas é o módulo do usuário final. O Elastic mostra este módulo, incluindo seu hash e quaisquer assinaturas de código. Esses detalhes auxiliam na triagem de alertas, mas, mais importante, eles melhoram drasticamente a granularidade com a qual podemos estabelecer uma linha de base para os comportamentos de softwares legítimos que às vezes se comportam como malware. Quanto mais precisamente pudermos estabelecer uma linha de base normal, mais difícil será para o malware se camuflar.

{
  "process.thread.Ext": {
    "call_stack_summary": "ntdll.dll|kernelbase.dll|file.dll|rundll32.exe|kernel32.dll|ntdll.dll",
    "call_stack": [
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtAllocateVirtualMemory+0x14" }, /* Native API */
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualAllocExNuma+0x62" }, /* Win32 API */
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualAllocEx+0x16" }, /* Win32 API */
      {
        "symbol_info": "c:\\users\\user\\desktop\\file.dll+0x160d8b", /* final user module */
        "callsite_trailing_bytes": "488bf0488d4d88e8197ee2ff488bc64883c4685b5e5f415c415d415e415f5dc390909090905541574156415541545756534883ec58488dac2490000000488b71",
        "callsite_leading_bytes": "088b4d38894c2420488bca48894db8498bd0488955b0458bc1448945c4448b4d3044894dc0488d4d88e8e77de2ff488b4db8488b55b0448b45c4448b4dc0ffd6"
      },
      { "symbol_info": "c:\\users\\user\\desktop\\file.dll+0x7b429" },
      { "symbol_info": "c:\\users\\user\\desktop\\file.dll+0x44a9" },
      { "symbol_info": "c:\\users\\user\\desktop\\file.dll+0x5f58" },
      { "symbol_info": "c:\\windows\\system32\\rundll32.exe+0x3bcf" },
      { "symbol_info": "c:\\windows\\system32\\rundll32.exe+0x6309" }, /* first user module - typically the ETHREAD.Win32StartAddress module */
      { "symbol_info": "c:\\windows\\system32\\kernel32.dll!BaseThreadInitThunk+0x14" }, /* Win32 API */
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlUserThreadStart+0x21" /* Native API - the ETHREAD.StartAddress module */
      }
    ],
    "call_stack_final_user_module": {
      "path": "c:\\users\\user\\desktop\\file.dll",
      "code_signature": [ { "exists": false } ],
      "name": "file.dll",
      "hash": { "sha256": "0240cc89d4a76bafa9dcdccd831a263bf715af53e46cac0b0abca8116122d242" }
    }
  }
}

Exemplo de pilha de chamadas enriquecida

Enriquecimentos do módulo de usuário final da pilha de chamadas:

nameO nome do arquivo do call_stack_final_user_module. Também pode ser "Sem backup", indicando memória executável privada, ou "Indeterminado", indicando uma pilha de chamadas suspeita.
caminhoO caminho do arquivo do call_stack_final_user_module.
hash.sha256O sha256 do call_stack_final_user_module ou do módulo protection_provenance, se houver.
assinatura_de_códigoAssinatura de código do call_stack_final_user_module ou do módulo protection_provenance, se houver.
alocação_bytes_privadosO número de bytes nesta região de memória que são +X e não compartilháveis. Valores diferentes de zero podem indicar ganchos, patches ou esvaziamento de código.
protectionA proteção de memória para a região de atuação das páginas é incluída se não for RX. Corresponde a MEMORY_BASIC_INFORMATION.Protect.
protection_provenanceO nome da região de memória que causou a última modificação da proteção desta página. "Sem suporte" pode indicar shellcode.
caminho_de_proveniência_de_proteçãoO caminho do módulo que causou a última modificação da proteção desta página.
razãoO call_stack_summary anômalo que levou a uma protection_provenance "Indeterminada".

Um glossário rápido de pilha de chamadas

Ao examinar pilhas de chamadas, há algumas funções da API nativa com as quais é útil se familiarizar. Ken Johnson, agora da Microsoft, nos forneceu um catálogo de modos kernel NTDLL para retornos de chamada de modo usuário para nos ajudar a começar. Sério, você deveria parar aqui e ler isso primeiro.

Conhecemos RtlUserThreadStart anteriormente. Tanto ele quanto seu irmão RtlUserFiberStart devem aparecer somente na parte inferior de uma pilha de chamadas. Esses são os pontos de entrada para fios e fibras do usuário, respectivamente. A primeira instrução em cada thread, no entanto, é na verdade LdrInitializeThunk. Depois de executar o componente de modo de usuário da inicialização do thread (e processo, se necessário), esta função transfere o controle para o ponto de entrada via NtContinue, que atualiza o ponteiro de instrução diretamente. Isso significa que ele não aparece em nenhuma caminhada de pilha futura.

Então, se você vir uma pilha de chamadas que inclui LdrInitializeThunk, isso significa que você está no início da execução de um thread. É aqui que o Shim Engine de compatibilidade de aplicativos opera, onde os produtos de segurança baseados em ganchos preferem se instalar e onde o malware tenta obter execução antes dos outros produtos de segurança. Marcus Hutchins e Guido Miggelenbrink escreveram blogs excelentes sobre esse tópico. Essa corrida de inicialização não existe para produtos de segurança que utilizam kernel ETW para telemetria.

{
  "process.thread.Ext": {
    "call_stack_summary": "ntdll.dll|file.exe|ntdll.dll",
    "call_stack": [
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!ZwProtectVirtualMemory+0x14" },
      { "symbol_info": "c:\\users\\user\\desktop\\file.exe+0x1bac8" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlAnsiStringToUnicodeString+0x3cb" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitShimEngineDynamic+0x394d" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0x1db" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0x63" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0xe" }
    ],
    "call_stack_final_user_module": {
      "path": "c:\\users\\user\\desktop\\file.exe",
      "code_signature": [ { "exists": false } ],
      "name": "file.exe",
      "hash": { "sha256": "a59a7b56f695845ce185ddc5210bcabce1fff909bac3842c2fb325c60db15df7" }
    }
  }
}

Exemplo de execução pré-ponto de entrada

O próximo par é KiUserExceptionDispatcher e KiRaiseUserExceptionDispatcher. O kernel usa o primeiro para passar a execução para um manipulador de exceção estruturado no modo de usuário registrado após a ocorrência de uma condição de exceção no modo de usuário. Este último também gera uma exceção, mas em nome do kernel. Essa segunda variante geralmente só é detectada por depuradores, incluindo o Application Verifier, e ajuda a identificar quando o código do modo de usuário não está verificando suficientemente os códigos de retorno das chamadas de sistema. Essas funções geralmente serão vistas em pilhas de chamadas relacionadas ao tratamento de falhas específicas do aplicativo ou ao Relatório de Erros do Windows. No entanto, às vezes, o malware o utilizará como um pseudo-ponto de interrupção — por exemplo, se quiser flutuar as proteções de memória para ocultar novamente seu shellcode imediatamente após fazer uma chamada de sistema.

{
  "process.thread.Ext": {
    "call_stack_summary": "ntdll.dll|file.exe|ntdll.dll|file.exe|kernel32.dll|ntdll.dll",
    "call_stack": [
      {
        "symbol_info": "c:\\windows\\system32\\ntdll.dll!ZwProtectVirtualMemory+0x14",
        "protection_provenance": "file.exe", /* another vendor's hooks were unhooked */
        "allocation_private_bytes": 8192
      },
      { "symbol_info": "c:\\users\\user\\desktop\\file.exe+0xd99c" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlInitializeCriticalSectionAndSpinCount+0x1c6" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlWalkFrameChain+0x1119" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!KiUserExceptionDispatcher+0x2e" },
      { "symbol_info": "c:\\users\\user\\desktop\\file.exe+0x12612" },
      { "symbol_info": "c:\\windows\\system32\\kernel32.dll!BaseThreadInitThunk+0x14" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlUserThreadStart+0x21" }
    ],
    "call_stack_final_user_module": {
      "name": "file.exe",
      "path": "c:\\users\\user\\desktop\\file.exe",
      "code_signature": [ { "exists": false }],
      "hash":   { "sha256": "0e5a62c0bd9f4596501032700bb528646d6810b16d785498f23ef81c18683c74" }
    }
  }
}

Exemplo de flutuação de proteção via manipulador de exceção

O próximo é o KiUserApcDispatcher, que é usado para entregar APCs do usuário. Essas são uma das ferramentas favoritas dos autores de malware, pois a Microsoft fornece apenas visibilidade limitada sobre seu uso.

{
  "process.thread.Ext": {
    "call_stack_summary": "ntdll.dll|kernelbase.dll|ntdll.dll|kernelbase.dll|cronos.exe",
    "call_stack": [
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtProtectVirtualMemory+0x14" },
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualProtect+0x36" }, /* tail call */
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!KiUserApcDispatcher+0x2e" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!ZwDelayExecution+0x14" },
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!SleepEx+0x9e" },
      {
        "symbol_info": "c:\\users\\user\\desktop\\file.exe+0x107d",
        "allocation_private_bytes": 147456, /* stomped */
        "protection": "RW-", /* fluctuation */
        "protection_provenance": "Undetermined", /* proxied call */
        "callsite_leading_bytes": "010000004152524c8d520141524883ec284150415141baffffffff41525141ba010000004152524c8d520141524883ec284150b9ffffffffba0100000041ffe1",
        "callsite_trailing_bytes": "4883c428c3cccccccccccccccccccccccccccc894c240857b820190000e8a10c0000482be0488b052fd101004833c44889842410190000488d84243014000048"
      }
    ],
    "call_stack_final_user_module": {
      "name": "Undetermined",
      "reason": "ntdll.dll|kernelbase.dll|ntdll.dll|kernelbase.dll|file.exe"
    }
  }
}

Flutuação de proteção via exemplo de APC

O gerenciador de janelas do Windows é implementado em um driver de dispositivo em modo kernel (win32k.sys). Majoritariamente. Às vezes, o gerenciador de janelas precisa fazer algo no modo de usuário, e o KiUserCallbackDispatcher é o mecanismo para fazer isso. É basicamente uma chamada de sistema reversa que tem como alvo funções user32.dll. Substituir uma entrada na KernelCallbackTable de um processo é uma maneira fácil de sequestrar um thread de GUI, então qualquer outro módulo que siga essa chamada é suspeito.

O conhecimento do propósito de cada um desses pontos de entrada do modo kernel para o modo usuário ajuda muito a determinar se uma determinada pilha de chamadas é natural ou se foi apropriada indevidamente para atingir objetivos alternativos.

Tornando as pilhas de chamadas compreensíveis

Para facilitar a compreensão, também marcamos o evento com vários process.Ext.api.behaviors que identificamos. Esses comportamentos não são necessariamente maliciosos, mas destacam aspectos relevantes para triagem de alertas ou detecção de ameaças. Para pilhas de chamadas, estes incluem:

API nativaUma chamada foi feita diretamente para a API nativa em vez da API Win32.
chamada_de_sistema_diretaA syscall instruction originated outside of the Native API layer.
chamada de proxyA pilha de chamadas pode indicar uma chamada de API com proxy para mascarar a verdadeira origem.
ShellcodeMemória executável não-imagem de segunda geração, chamada de API sensível.
chamada_indireta_de_imagemAn entry in the call stack was preceded by a call to a dynamically resolved function.
imagem_ropNenhuma instrução de chamada precedeu uma entrada na pilha de chamadas.
imagem_rwxUma entrada na pilha de chamadas é gravável. O código deve ser somente leitura.
rwx sem suporteUma entrada na pilha de chamadas não é imagem e pode ser gravada. Até mesmo o código JIT deve ser somente leitura.
pilha_truncadaThe call stack seems to be unexpectedly truncated. This may be due to malicious tampering.

Em alguns contextos, esses comportamentos por si só podem ser suficientes para detectar malware.

Spoofing — desvio ou responsabilidade?

A falsificação de endereço de retorno tem sido uma técnica básica de hacking de jogos e malware por muitos e muitos anos. Esse truque simples permite que o código injetado tome emprestada a reputação de um módulo legítimo com poucas consequências. O objetivo da inspeção profunda da pilha de chamadas e das linhas de base de comportamento é parar de dar passe livre ao malware.

Pesquisadores ofensivos têm auxiliado nesse esforço ao analisar abordagens para falsificação completa de pilha de chamadas. Mais notavelmente:

SilentMoonwalk, além de ser uma excelente pesquisa ofensiva, é um excelente exemplo de como mentir pode causar o dobro de problemas — mas somente se você for pego. Muitas técnicas de Evasão de Defesa dependem de segurança por obscuridade — e, uma vez expostas pelos pesquisadores, podem se tornar um risco. Neste caso, a pesquisa incluiu conselhos sobre as oportunidades de detecção introduzidas pela tentativa de evasão.

{
  "process.thread.Ext": {
    "call_stack_summary": "ntdll.dll|kernelbase.dll|kernel32.dll|ntdll.dll",
    "call_stack": [
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtAllocateVirtualMemory+0x14" },
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualAlloc+0x48" },
      {
        "symbol_info": "c:\\windows\\system32\\kernelbase.dll!CreatePrivateObjectSecurity+0x31",
        /* 4883c438 stack desync gadget - add rsp 0x38 */
        "callsite_trailing_bytes": "4883c438c3cccccccccccccccccccc48895c241057498bd8448bd2488bf94885c90f84660609004885db0f845d060900418bd14585c97411418bc14803c383ea",
        "callsite_leading_bytes": "cccccccccccccccccccccccccccccc4883ec38488b4424684889442428488b442460488944242048ff15d9b21b000f1f44000085c00f8830300900b801000000"
      },
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!Internal_EnumSystemLocales+0x406" },
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!SystemTimeToTzSpecificLocalTimeEx+0x2d1" },
      { "symbol_info": "c:\\windows\\system32\\kernelbase.dll!WaitForMultipleObjectsEx+0x982" },
      { "symbol_info": "c:\\windows\\system32\\kernel32.dll!BaseThreadInitThunk+0x14" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!RtlUserThreadStart+0x21" }
    ],
    "call_stack_final_user_module": {
      "name": "Undetermined", /* gadget module resulted in suspicious call stack */
      "reason": "ntdll.dll|kernelbase.dll|kernel32.dll|ntdll.dll"
    }
  }
}

Exemplo de pilha de chamadas SilentMoonwalk

Uma técnica padrão para descobrir artefatos ocultos é enumerá-los usando várias técnicas e comparar os resultados para verificar discrepâncias. É assim que o RootkitRevealer funciona. Essa abordagem também foi usada em Get-InjectedThreadEx.exe, que sobe na pilha de threads e também desce.

Em certas circunstâncias, podemos recuperar uma pilha de chamadas de duas maneiras. Se houver discrepâncias, você verá a pilha de chamadas menos confiável emitida como call_stack_summary_original.

{
  "process.thread.Ext": {
    "call_stack_summary": "ntdll.dll",
    "call_stack_summary_original": "ntdll.dll|kernelbase.dll|version.dll|kernel32.dll|ntdll.dll",
    "call_stack": [
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtContinue+0x12" },
      { "symbol_info": "c:\\windows\\system32\\ntdll.dll!LdrInitializeThunk+0x13" }
    ],
    "call_stack_final_user_module": {
      "name": "Undetermined",
      "reason": "ntdll.dll"
    }
  }
}

Exemplo original de resumo da pilha de chamadas

Pilhas de chamadas são para todos

Por padrão, você encontrará apenas pilhas de chamadas em nossos alertas, mas isso pode ser configurado por meio de uma política avançada.

eventos.callstacks.emit_in_eventsSe definido, as pilhas de chamadas serão incluídas em eventos regulares onde são coletadas. Caso contrário, eles são incluídos apenas em eventos que acionam regras de proteção comportamental. Observe que essa configuração pode aumentar significativamente o volume de dados. Padrão: falso

Mais informações sobre pilhas de chamadas do Windows estão disponíveis nos seguintes artigos do Elastic Security Labs:

Compartilhe este artigo