Cargas úteis e sinalizadores de malware: tipos de cargas maliciosas
Na post anterior do blog, discutimos como as cargas úteis facilitam as comunicações maliciosas e como um invasor pode obter o controle de um sistema após executar com sucesso as cargas e os beacons. Usamos um canal de comunicação reverse_tcp facilitado pela carga útil do Meterpreter (do Metasploit Framework).
Esta postagem se concentrará em entender mais sobre os diferentes tipos de cargas úteis e exemplos das técnicas de manipulação de memória que elas podem empregar.
Malware tornou-se mais sofisticado em composição e execução — especialmente em contraste com os dias de vírus mais simples, como o Rastejador programa no início dos anos setenta. Para agentes de ameaças motivados principalmente por objetivos clandestinos, ficar fora do radar para persistir em uma rede sem ser detectada por um longo período geralmente é o foco principal. Portanto, eles empregarão técnicas correspondentes, como iscas, codificação, ofuscação, criptografia e imitação, para atingir o nível desejado de segurança operacional.
Há vários formatos executáveis disponíveis para um agente de ameaças. A escolha do atacante depende dos vetores de ataque iniciais e das ações subsequentes após a exploração. Aqui estão alguns dos Metasploit Formatos executáveis e de transformação do Framework.

Conforme demonstrado em parte um, o farol ou carga útil é o implante na máquina ou rede da vítima que permite ao atacante uma entrada e depois um ponto de apoio. É uma parte importante do arsenal de malware e do ciclo de vida geral do ataque, permitindo que o agente da ameaça tenha acesso prático para realizar mais atividades maliciosas.
Quando se trata de categorização ampla real, uma carga útil pode ser “escalonada” ou “sem etapas”. Um agente de ameaças pode escolher um em vez do outro, dependendo de vários fatores, dos quais os principais podem ser considerações de segurança operacional.
O que são cargas úteis escalonadas?
As cargas em estágios dividem as fases distintas de um ataque, geralmente usando várias fases de cargas que, de outra forma, uma única carga teria sido realizada. Essas cargas são normalmente divididas em um executável de “estágio” (carga inicial ou beacon) e um executável de “estágio” (carga útil principal).
Um stager é um pequeno executável que é uma carga inicial. É um trecho de código relativamente pequeno que é executado para se preparar para uma carga útil muito maior e mais capaz, conhecida como carga útil de estágio. Isso significa que “o encenador prepara o cenário”. Um stager normalmente faz parte de algum código de exploração quando a entrada inicial aproveita uma vulnerabilidade. Aqui, o código de exploração explorará com sucesso a vulnerabilidade alvo e, em seguida, executará o código stager (carga útil). O encenador então entra em ação.
A principal tarefa do stager é executar com sucesso sem detectar, entrar em contato com a infraestrutura do atacante para baixar a carga principal desejada e, em seguida, configurar o sistema para executar essa carga. O estágio baixado ou a carga principal maior pode ser uma ou mais cargas úteis, dependendo da capacidade exigida pelo atacante. Depois que o estágio é baixado, o programador passa pelo controle de execução para continuar a atividade maliciosa.
No caso de uma exploração de vulnerabilidade, o programa de exploração inicial faz algo semelhante para o stager e o stager faz para a carga útil do estágio principal, em termos de alocação de recursos no sistema comprometido. Ele seguirá um padrão semelhante a este:

O exemplo abaixo mostra uma carga em estágios como um executável do Windows.

As cargas em estágios acomodam cenários em que pode haver restrições relacionadas ao sistema, como espaço em disco e memória, quando se trata de entrega e execução da carga, como no caso do shellcode usado para explorar uma vulnerabilidade de estouro de buffer.
O que são cargas úteis sem estágios?
O oposto das cargas úteis escalonadas são as cargas sem estágios. As cargas úteis sem etapas são independentes e geralmente muito maiores do que as cargas úteis escalonadas. Normalmente, eles combinam todos os recursos necessários de um atacante em um único executável.
Aqui, normalmente não há necessidade de uma carga inicial (stager) que baixe a carga principal (stager). Depois que a carga sem estágios for executada, ela terá todos os recursos necessários para realizar ações maliciosas, como injeção de memória, chamada de volta à infraestrutura do atacante e entrega de um shell para o atacante.
O exemplo abaixo mostra uma carga útil tcp reversa do Meterpreter sem estágios como um executável do Windows.

A decisão sobre qual tipo de carga útil usar como parte de uma campanha maliciosa é conhecida como “considerações de segurança operacional” para esse agente de ameaças. O tipo de infraestrutura do invasor correspondente que dá suporte à campanha maliciosa é parcialmente influenciado por essas considerações.
Cargas úteis escalonadas versus cargas sem estágio
As cargas úteis preparadas do Metasploit têm o símbolo de barra (/) após a palavra Meterpreter. A captura de tela abaixo mostra exemplos de cargas úteis do Meterpreter preparadas para Windows.

Cargas úteis sem etapas empregam o símbolo de sublinhado (_) após a palavra Meterpreter. A captura de tela abaixo mostra exemplos de cargas úteis sem estágios do Windows Meterpreter.

E o exemplo abaixo mostra as duas categorias de cargas úteis.

As cargas sem etapas são independentes e não exigem a etapa extra de enviar um estágio (carga principal) para a máquina da vítima, uma vez que o malware faz um retorno de chamada para a infraestrutura do invasor. Observe na captura de tela abaixo que, após o início do manipulador TCP reverso, a próxima etapa é abrir uma sessão prática de shell remota do Meterpreter na máquina vítima imediatamente, sem a necessidade de enviar mais cargas úteis, como um estágio.

Na captura de tela abaixo, podemos ver que há uma etapa extra de enviar o estágio depois que o organizador faz um retorno de chamada para a infraestrutura do atacante: “enviar estágio (175174) para 203.0.113.1”.

Outra diferença entre estágios e sem estágios é o tamanho das cargas úteis. Na captura de tela abaixo, a carga útil sem estágios (meeting_update_stageless.exe) é muito maior, com 245 KB, em comparação com a carga inicial em estágios (web1_meeting_update.exe), com 73 KB.

O que é shellcode?
O Shellcode é um código malicioso que tenta sequestrar o fluxo normal de um programa em execução na memória do computador. Em seguida, ele redireciona o fluxo para que o código malicioso seja executado, em vez do programa normal, dando ao atacante um shell ou acesso prático. Geralmente, são beacons ou cargas úteis na forma de código de programação de baixo nível ou código de máquina combinado com uma exploração. Os exploits são partes de código nativo ou de baixo nível que aproveitam com sucesso uma vulnerabilidade.
As vulnerabilidades exploradas geralmente envolvem um estouro de buffer na memória de um aplicativo, em que o invasor invadiu a memória alocada para redirecionar o fluxo normal do programa. Uma exploração bem sucedida levará então à execução de uma carga útil, que é o malware.
Em suas formas mais puras, o Shellcode será um código nativo ou de montagem comumente usado em explorações relacionadas à memória.
O exemplo abaixo mostra o shellcode do Powershell (ps1).

Este exemplo específico usa um Biblioteca de links dinâmicos do Windows (DLL) injetado na memória por meio de um carregador reflexivo. O shellcode é gerado em formato alfanumérico. Uma vez executado com sucesso, ele pode se conectar novamente ao atacante por meio de uma sessão DNS TCP reversa gerada a partir do Metasploit Framework.
A escolha do mecanismo de entrega, o tipo de exploração e o sistema alvo vulnerável determinam a escolha do farol ou da carga útil vinculada ao ataque. O exploit é usado para tirar proveito de um aplicativo vulnerável antes de obter acesso ao sistema operacional subjacente. Nesse caso, um código específico para o aplicativo correspondente pode ser usado (por exemplo, PHP ou ASP para aplicativos front-end do servidor web).
Características do Shellcode
Há algumas considerações e características importantes para garantir a execução bem-sucedida do Shellcode e manter a alta segurança operacional.
O código deve:
- Tenha todas as instruções necessárias para executar o shell desejado sem deixar de ter um tamanho relativamente pequeno.
- Seja “independente da posição” na memória — isso é crucial, pois muitas vezes não é possível saber de antemão onde ele será carregado na memória do processo vulnerável alvo.
- Não contém nada que possa causar possíveis erros ou causar falhas em todo o processo; por exemplo, devido a caracteres nulos (0x00).
- Seja capaz de aproveitar alguma alocação de memória existente usando algumas técnicas de injeção — injeção de código ou reflexão.
A partir desse ponto, o invasor precisará selecionar o tipo de executável pós-exploração apropriado para executar no sistema de destino, como EXE ou DLL para Windows, ELF para Linux e APKs para Android. Novamente, as técnicas de pós-exploração somente de memória são preferidas para aumentar a segurança operacional.
O que é injeção de código e injeção de DLL?
A injeção de DLL é o processo de execução de código (DLL) no contexto de outro processo. As cargas úteis do Meterpreter usam técnicas de injeção de DLL para mecanismos furtivos e de evasão.
No Windows, uma biblioteca de links dinâmicos ou DLL (“Biblioteca compartilhada” no Linux) é um trecho de código armazenado como um arquivo de biblioteca compartilhada. Isso significa que ele pode ser usado por diferentes programas de computador de acordo com e quando precisarem. O sistema operacional (nesse caso, o Windows) lida com a gravação e o carregamento da biblioteca, o que é feito em tempo de execução. Um programa pode simplesmente chamar ou referenciar o arquivo DLL necessário para usar o código contido nele.
Isso é útil para programadores porque eles escrevem código apenas uma vez, compilam e armazenam como uma biblioteca compartilhada ou DLL e, em seguida, o usam sempre que necessário e por vários programas.
A principal diferença entre uma DLL e um arquivo EXE é que uma DLL não pode ser executada de forma independente. Ele precisa de um programa como um EXE para chamar ou referenciar e depois executá-lo. O exemplo a seguir mostra arquivos DLL em um sistema operacional Windows, que normalmente são armazenados na pasta C:\Windows\ WinSxS (WinSxS significa Windows lado a lado).

Os recursos das DLLs também se tornam muito úteis para agentes de ameaças. A injeção de código no nível básico envolve a tentativa de um processo (malicioso) de anexar (ou obter um identificador) a um processo remoto (processo vítima). Em seguida, ele aloca memória suficiente ou altera as permissões da página no processo vítima para executar um novo código, como uma DLL, e depois copiar (injetar) o código malicioso da DLL no espaço de memória do processo vítima novo ou já em execução.
Um novo thread, que é como os processos executam tarefas específicas, é então iniciado no processo vítima para executar as instruções contidas no código injetado ou na DLL.
Um thread compartilha o mesmo espaço de memória do processo que o iniciou, enquanto processos diferentes têm espaços de memória alocados distintos, especialmente nos casos em que não compartilham nenhuma variável. Isso é imposto pelo sistema operacional. No entanto, os sistemas operacionais fornecem mecanismos para que os processos se comuniquem quando necessário usando a comunicação entre processos (IPC), como tubos (nomeados ou anônimos), soquetes, semáforos, memória compartilhada e linhas de mensagens.
Nos sistemas operacionais Windows, a injeção de código envolve o uso de APIs e funções legítimas do Windows para fins maliciosos. Por exemplo:
- Processo aberto é usado para obter um controle sobre um processo,
- Virtual AlloceX então facilita a alocação de memória suficiente nesse processo remoto ou,
- VirtualProtecteX pode ser usado para substituir as permissões de memória (página) e, em seguida,
- Memória do processo de gravação gravava o código malicioso, como uma DLL, no processo da vítima.
- CreateRemoteThread, RTLCreateUserThread ou NTCreateThreadEx é usado para criar um novo thread (que é como os processos executam tarefas específicas) e executar a capacidade maliciosa, como roubar credenciais ou executar ransomware.
Carregar uma DLL no Windows requer chamar as funções LoadLibraryA ou LoadLibraryEXA, que fazem parte da libloaderapi.h. Essas funções, como diz a Microsoft, “carregam o módulo especificado no espaço de endereço do processo de chamada”. Usar LoadLibrary para carregar uma DLL significa que as DLLs devem ser carregadas do disco.
No entanto, usando a técnica de injeção reflexiva de DLL, uma DLL pode ser carregada diretamente da memória, um recurso não oferecido atualmente pela LoadLibrary. Um agente de ameaças pode usar a injeção reflexiva de DLL para carregar automaticamente seu código malicioso inteiramente na memória, sem a necessidade de invocar o carregador nativo do Windows no disco. Aqui, eles usam um carregador personalizado em vez do Windows LoadLibrary.
Os atacantes também podem usar outras técnicas de injeção e manipulação de processos, como:
- Processo de esvaziamento — Onde o malware iniciará o processo da vítima em um estado suspenso. Em seguida, ele esvazia a memória para abrir espaço para novos códigos, alterar as permissões da página, injetar o código malicioso e retomar o processo para executar o código malicioso injetado.
- Carregamento lateral de DLL — Quando um programa Windows vulnerável legítimo e, muitas vezes, mais antigo (processo vítima) é forçado a carregar uma DLL maliciosa, que é deliberadamente chamada da mesma que legítima esperada pela vítima e colocada no mesmo diretório (lado a lado) do programa vulnerável. O processo do programa da vítima examinará primeiro sua pasta imediatamente para localizar a DLL maliciosa renomeada (personificada). Essa técnica capitaliza a ordem de pesquisa da DLL usada pelo carregador do Windows para obter sucesso.
Essas técnicas tentam fazer com que a atividade maliciosa pareça legítima, evitando assim que a detecção persista em um sistema comprometido.
Das técnicas de manipulação de processos discutidas em parte uma desta série, podemos ver abaixo que a migração do web1_meeting_update.exe malicioso original envolveu alguma injeção de código.
Neste exemplo, a carga maliciosa iniciou (gerou) um processo notepad.exe completamente novo como o processo de vítima desejado para injetar código. O processo malicioso original (PID 2472) é então injetado ou migrado para o novo processo de vítima do notepad.exe (PID 1768).

O Notepad.exe é um processo confiável da Microsoft e, como tal, dá ao atacante a capacidade de disfarçar o processo original de aparência maliciosa em um mais confiável. O Notepad.exe é um editor de texto básico do Windows que não precisa chamar (referenciar) nenhuma API, função ou DLL do Windows necessária para conexão de rede — o que, em nosso exemplo, foi executado por um processo malicioso.
A análise dinâmica é necessária para detectar a injeção acontecendo na memória. Mapeamento de dependências de aplicativos também é necessário detectar as comunicações de rede suspeitas do editor de texto, neste caso, o processo notepad.exe, fazendo uma conexão de rede.
Conclusão
Na sequência de parte um Nesta série de blogs, analisamos algumas das categorias e tipos de carga úteis que um agente de ameaças pode usar e por que ele pode decidir usar um tipo em vez de outro.
A maioria dos atacantes de ameaças não mede esforços para garantir que tenham uma entrada inicial bem-sucedida, mantenham a persistência e evitem a detecção. Até agora, vimos que a combinação de ferramentas, técnicas e procedimentos significa que uma combinação igualmente capaz de ferramentas e procedimentos de segurança é necessária para evitar um ataque bem-sucedido — ou com maior probabilidade de presumir violação e evitar que um incidente cibernético inicial se torne uma grande violação.
No final desta série, discutiremos mais algumas técnicas e capacidades e, o mais importante, exploraremos a análise e a mitigação.