Daniel Stepanic

Ficando pegajoso com o GULOADER: desofuscando o downloader

O Elastic Security Labs analisa as contramedidas de análise do GULOADER atualizadas.

13 min de leituraAnálise de malware
Ficando pegajoso com o GULOADER: desofuscando o downloader

Overview

O Elastic Security Labs continua monitorando ameaças ativas, como o GULOADER, também conhecido como CloudEyE – um downloader de shellcode evasivo que tem sido altamente ativo por anos e em constante desenvolvimento. Uma dessas mudanças recentes é a adição de exceções ao seu Vectored Exception Handler (VEH) em uma nova campanha, adicionando mais complexidade à sua já longa lista de truques antianálise.

Embora a funcionalidade principal do GULOADER não tenha mudado drasticamente nos últimos anos, essas atualizações constantes em suas técnicas de ofuscação tornam a análise do GULOADER um processo demorado e que exige muitos recursos. Nesta postagem, abordaremos os seguintes tópicos ao fazer a triagem do GULOADER:

  • Revisando o shellcode inicial e o processo de descompactação
  • Encontrando o ponto de entrada do shellcode descriptografado
  • Discuta a atualização do VEH do GULOADER que ofusca o fluxo de controle
  • Fornecer uma metodologia para corrigir VEH

Shellcode inicial

Em nosso exemplo, o GULOADER vem pré-empacotado dentro de um instalador NSIS (Nullsoft Scriptable Install System). Quando o instalador é extraído, os principais componentes são:

  • Script NSIS - Este arquivo de script descreve todos os vários aspectos de configuração e instalação.

  • System.dll - Localizado em $PLUGINSDir. Este arquivo é colocado em uma pasta temporária para alocar/executar o shellcode do GULOADER.

  • Shellcode - O shellcode criptografado é enterrado em uma pasta aninhada.

Uma metodologia rápida para identificar o arquivo que hospeda o shellcode pode ser feita monitorando eventos ReadFile do Process Monitor do SysInternal após executar o GULOADER. Neste caso, podemos ver que o shellcode é lido de um arquivo (Fibroms.Hag).

O GULOADER executa shellcode por meio de retornos de chamada usando diferentes funções da API do Windows. O principal raciocínio por trás disso é evitar detecções centradas em APIs tradicionais do Windows usadas para injeção de processos, como CreateRemoteThread ou WriteProcessMemory. Observamos EnumResourceTypesA e CallWindowProcW usados pelo GULOADER.

Ao revisar a documentação do MSDN para EnumResourceTypesA, podemos ver que o segundo parâmetro espera um ponteiro para a função de retorno de chamada. Na captura de tela acima, podemos ver que o shellcode recém-alocado é colocado neste argumento.

Encontrando o ponto de entrada do shellcode principal

Em amostras recentes, o GULOADER aumentou a complexidade no início do shellcode inicial, incluindo muitas instruções inúteis e saltos diferentes. A engenharia reversa do downloader pode exigir lidar com um longo processo de desvendamento de ofuscação de código projetado para interromper a desmontagem e o fluxo de controle em algumas ferramentas, tornando frustrante encontrar o início real do shellcode principal do GULOADER.

Uma metodologia para encontrar a chamada inicial pode ser aproveitar a visualização do gráfico dentro do x64dbg e usar uma abordagem de baixo para cima para procurar a instrução call eax .

Outra técnica para rastrear o fluxo de controle inicial envolve aproveitar a estrutura de engenharia reversa Miasm. Abaixo está um exemplo rápido onde podemos passar o shellcode e desmontar as instruções para seguir o fluxo:

from miasm.core.locationdb import LocationDB
from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine

with open("proctoring_06BF0000.bin", "rb") as f:
    code = f.read()

loc_db = LocationDB()
c = Container.from_string(code, loc_db)

machine = Machine('x86_32')
mdis = machine.dis_engine(c.bin_stream, loc_db=loc_db)
mdis.follow_call = True 
mdis.dontdis_retcall = True
asm_cfg = mdis.dis_multiblock(offset=0x1400)

O Miasm corta as instruções 142 jmp e navega pelas instruções inúteis onde o configuramos para parar na instrução de chamada para EAX (endereço: 0x3bde).

JMP        loc_3afd
->	c_to:loc_3afd 
loc_3afd
MOV        EBX, EAX
FADDP      ST(3), ST
PANDN      XMM7, XMM2
JMP        loc_3b3e
->	c_to:loc_3b3e 
loc_3b3e
SHL        CL, 0x0
PSRAW      MM1, MM0
PSRLD      XMM1, 0xF1
JMP        loc_3b97
->	c_to:loc_3b97 
loc_3b97
CMP        DL, 0x3A
PADDW      XMM3, XMM5
PXOR       MM3, MM3
JMP        loc_3bde
->	c_to:loc_3bde 
loc_3bde
CALL       EAX

Fim da cauda do miasma

GULOADER’s VEH Update

Uma das técnicas de destaque do GULOADER está centrada em sua capacidade de Tratamento de Exceções Vetoriais (VEH). Esse recurso dá aos aplicativos do Windows a capacidade de interceptar e manipular exceções antes que elas sejam roteadas pelo processo de exceção padrão. Famílias de malware e aplicativos de proteção de software usam essa técnica para dificultar que analistas e ferramentas rastreiem o código malicioso.

O GULOADER inicia esse processo adicionando o VEH usando RtlAddVectoredExceptionHandler. Durante a execução do shellcode GULOADER, há um código propositalmente colocado para acionar essas diferentes exceções. Quando essas exceções são acionadas, o VEH verificará se há pontos de interrupção de hardware. Se não for encontrado, o GULOADER modificará o EIP diretamente por meio da estrutura CONTEXT usando uma chave XOR de um byte (alterações por amostra) com um deslocamento de um byte de onde a exceção ocorreu. Analisaremos um exemplo específico dessa técnica na seção subsequente. Abaixo está a descompilação do VEH da nossa amostra:

Embora essa técnica não seja nova, o GULOADER continua adicionando novas exceções ao longo do tempo; observamos recentemente essas duas exceções adicionadas nos últimos meses:

  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_ILLEGAL_INSTRUCTION

À medida que novas exceções são adicionadas ao GULOADER, ele pode acabar quebrando ferramentas usadas para agilizar o processo de análise para pesquisadores.

EXCEPTION_PRIV_INSTRUCTION

Vamos analisar as duas exceções adicionadas recentemente para seguir o fluxo de trabalho do VEH. A primeira exceção (EXCEPTION_PRIV_INSTRUCTION) ocorre quando é feita uma tentativa de executar uma instrução privilegiada no conjunto de instruções de um processador em um nível de privilégio onde não é permitido. Certas instruções, como o exemplo abaixo com WRSMR, esperam privilégios do nível do kernel, então quando o programa é executado no modo de usuário, ele acionará a exceção devido a permissões incorretas.

EXCEPTION_ILLEGAL_INSTRUCTION

Esta exceção é invocada quando um programa tenta executar uma instrução de CPU inválida ou indefinida. Em nosso exemplo, quando encontramos instruções de virtualização da Intel como vmclear ou vmxon, isso acionará uma exceção.

Quando ocorre uma exceção, o código GULOADER VEH primeiro determinará qual código de exceção foi responsável pela exceção. Em nosso exemplo, se a exceção corresponder a qualquer uma das cinco abaixo, o código seguirá o mesmo caminho independentemente.

  • EXCEPTION_ACCESS_VIOLATION
  • EXCEPTION_ILLEGAL_INSTRUCTION
  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_SINGLE_STEP
  • EXCEPTION_BREAKPOINT

O GULOADER então verificará se há pontos de interrupção de hardware percorrendo o registro CONTEXT encontrado dentro da estrutura EXCEPTION_POINTERS . Se pontos de interrupção de hardware forem encontrados em diferentes registradores de depuração, o GULOADER retornará um 0 no registro CONTEXT, o que acabará causando a falha do shellcode.

Se não houver pontos de interrupção de hardware, o GULOADER recuperará um único byte que está a 7 bytes de distância do endereço que causou a exceção. Ao usar o último exemplo com vmclear, ele recuperaria o byte (0x8A).

Então, usando esse byte, ele executará uma operação XOR com um byte codificado diferente. No nosso caso (0xB8), isso é único por amostra. Agora, com um deslocamento derivado 0x32 (0xB8 ^ 0x8A), o GULOADER modificará o endereço EIP diretamente do registro CONTEXT adicionando 0x32 ao endereço anterior (0x7697630) que causou a exceção, resultando no próximo código a ser executado a partir do endereço (0x7697662).

Com diferentes instruções inúteis no meio e exceções recorrentes (contabilizamos 229 exceções únicas em nossa amostra), não é difícil entender por que isso pode quebrar diferentes ferramentas e aumentar o tempo do analista.

Limpeza de fluxo de controle

Para facilitar o acompanhamento do fluxo de controle, um analista pode ignorar o VEH rastreando a execução, registrando as exceções e corrigindo o shellcode usando o algoritmo de modificação EIP discutido anteriormente. Para este procedimento, utilizamos o TinyTracer, uma ferramenta escrita por @hasherezade que utiliza o Pin, uma estrutura de instrumentação binária dinâmica. Isso nos permitirá capturar os diferentes endereços que acionaram a exceção, então, usando o exemplo acima com vmclear, podemos ver que o endereço foi 0x7697630, gerando uma exceção chamando KiUserExceptionDispatcher, uma função responsável por manipular exceções no modo de usuário.

Depois que todas as exceções são coletadas e filtradas, elas podem ser passadas para um script IDAPython, onde percorremos cada endereço, calculamos o deslocamento usando o 7º byte e a tecla XOR (0xB8) e, em seguida, corrigimos todas as instruções que geram exceções com pequenos saltos.

A imagem a seguir é um exemplo de instruções de patch que acionam exceções nos endereços 0x07697630 e 0x0769766C.

Abaixo está um gráfico que representa o fluxo de controle antes que o patch seja aplicado globalmente. Nosso bloco básico com a instrução vmclear é destacado em laranja. Ao implementar o VEH, o GULOADER achata o gráfico de fluxo de controle, dificultando o rastreamento da lógica do programa.

Após corrigir o VEH com instruções jmp , isso transforma os blocos básicos conectando-os, reduzindo a complexidade por trás do fluxo do shellcode.

Usar essa técnica pode acelerar o processo de limpeza, mas é importante observar que esse não é um método infalível. Nesse caso, ainda há uma boa quantidade de código/funcionalidade que ainda precisará ser analisada, mas isso definitivamente ajuda muito a simplificar o código ao remover o VEH. O script completo do POC está localizado aqui.

Conclusão

O GULOADER tem muitos recursos diferentes que podem interromper a desmontagem, dificultar o fluxo de controle e dificultar a análise para os pesquisadores. Apesar disso e do processo ser imperfeito, podemos combater essas características por meio de diferentes processos estáticos ou dinâmicos para ajudar a reduzir o tempo de análise. Por exemplo, observamos que, com novas exceções no VEH, ainda podemos rastreá-las e corrigir o shellcode. Este processo colocará o analista no caminho certo, mais perto de acessar a funcionalidade principal com o GULOADER.

Ao compartilhar parte do nosso fluxo de trabalho, esperamos fornecer várias lições caso você encontre o GULOADER na natureza. Com base nas mudanças do GULOADER, é altamente provável que comportamentos futuros exijam estratégias novas e diferentes. Para detectar GULOADER, a seção a seguir inclui regras YARA, e o script IDAPython desta postagem pode ser encontrado aqui. Para novas atualizações sobre as últimas pesquisas sobre ameaças, confira nossa seção de análise de malware pela equipe do Elastic Security Labs.

YARA

O Elastic Security criou diferentes regras YARA para identificar essa atividade. Abaixo está um exemplo de uma regra YARA para identificar GULOADER.

rule Windows_Trojan_Guloader {
    meta:
        author = "Elastic Security"
        creation_date = "2023-10-30"
        last_modified = "2023-11-02"   
        reference_sample = "6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99"
        severity = 100
        arch = "x86"
        threat_name = "Windows.Trojan.Guloader"
        license = "Elastic License v2"
        os = "windows"
    strings:
        $djb2_str_compare = { 83 C0 08 83 3C 04 00 0F 84 [4] 39 14 04 75 }
        $check_exception = { 8B 45 ?? 8B 00 38 EC 8B 58 ?? 84 FD 81 38 05 00 00 C0 }
        $parse_mem = { 18 00 10 00 00 83 C0 18 50 83 E8 04 81 00 00 10 00 00 50 }
        $hw_bp = { 39 48 0C 0F 85 [4] 39 48 10 0F 85 [4] 39 48 14 0F 85 [7] 39 48 18 }
        $scan_protection = { 39 ?? 14 8B [5] 0F 84 }
    condition:
        2 of them
}

Observações

Todos os observáveis também estão disponíveis para download nos formatos ECS e STIX.

Os seguintes observáveis foram discutidos nesta pesquisa.

ObservableTipoNomeReferência
6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99SHA-256Windows.Trojan.GuloaderDownloader GULOADER
101.99.75[.]183/MfoGYZkxZIl205.binURLN / DGULOADER C2 URL
101.99.75[.]183endereço-ipv4N / DGULOADER C2 IP

Referências

Compartilhe este artigo