O estudo de um malicioso

imagem

Me deparei com um arquivo doc recentemente malicioso que foi enviado com e-mails de phishing. Decidi que esse é um bom motivo para praticar engenharia reversa e escrever algo novo sobre Habr. Sob o gato - um exemplo de análise de um conta-gotas de macro malicioso e dlls não menos maliciosos.

Macro


Sha256 do arquivo - abb052d9b0660f90bcf5fc394db0e16d6edd29f41224f8053ed90a4b8ef1b519 . No próprio arquivo doc, na primeira página, há uma figura informando que esse arquivo está protegido e explicando como ativar macros; há duas tabelas grandes com números no arquivo. Os números são escritos em forma decimal, os mais longos são de dez dígitos, são positivos e negativos.

Quando a vítima permite a execução de macros (existem aquelas que a habilitam por padrão?), Uma cadeia de ações é iniciada, que finalmente executa a função updatedb_network, que altera o diretório atual para um temporário e cria o arquivo "icutils.dll", no qual anota os números da primeira tabela em uma linha como um inteiro de 32 bits com um sinal. O resultado é a dll correta. A função clone é importada desta dll :

Declare PtrSafe Function clone Lib "icutils.dll" _
(ByVal Saved As String, ByVal Time As Integer) As Boolean

E começa com dois parâmetros:

R1 = Module1.clone("Cream", 0)

Se a chamada de clone retornar False, o arquivo icutils.dll será substituído pelos dados da segunda tabela e o clone com os mesmos parâmetros será chamado novamente.

Olhando para o futuro, direi que a primeira DLL é de 64 bits e não será executada em sistemas de 32 bits. Portanto, a macro seleciona a arquitetura correta do código binário.

Curiosamente, a função updatedb_network possui um pedaço de código que não possui finalidade funcional:

Sub updatedb_network()
...
Dim query_to_change As Variant
    Set query_to_change = CurrentDb.QueryDefs("query_name")
    
    query_to_change.SQL = "SELECT * FROM Table ORDER BY ID Asc"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table ORDER BY ID Asc"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE Fashion"
    query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE '" & something & "'"
...
End Sub

Talvez ele esteja aqui para dar a aparência de um trabalho útil para quem rapidamente lê o código, vê algumas linhas no SQL e acha que está tudo bem? Eu não sei. Além disso, a maioria das funções e variáveis ​​possui nomes aleatórios ou com nomes não reais (como updatedb_network , que não interage com o banco de dados ou a rede). Embora exista, por exemplo, a função dump_payload , que armazena 4 bytes no icutil.dll. Mas, em qualquer caso, a presença da função Document_Open deve alertar imediatamente , os autores do malware não podem renomeá-lo arbitrariamente (embora possam usar outra função iniciada automaticamente).

Portanto, a funcionalidade da macro é mais ou menos clara, é hora de descarregar a dll e prosseguir com a análise.

Primeira dll


A primeira dll (sha256 7427cc4b6b659b89552bf727aed332043f4551ca2ee2126cca75fbe1ab8bf114 ) de 64 bits.

A lista de funções importadas contém as funções CreateProcessW (início do programa), CreateRemoteThread (criando um encadeamento em outro processo), VirtualAllocEx (alocando um bloco de memória em outro processo), WriteProcessMemory (gravando na memória de outro processo), o que sugere imediatamente a injeção de código em outro processo. Agora vamos ver o que exatamente ela faz usando o IDA Free e o Ghidra.
O ponto de entrada principal simplesmente retorna 1, não faz mais nada. A segunda função exportada é o clone, que é chamado pela macro e contém código malicioso.
Os parâmetros com os quais é chamado não parecem afetar nada. O aplicativo descriptografa dois blocos de dados. O primeiro bloco de dados tem 0x78 de comprimento com o seguinte conteúdo (já descriptografado):

https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\xf2i\xe0\x1d\x95h\xbc\x03\xe4#\xe0\x1d<\x04\xe0\x1d\xe6\x00\xde\x01\xa4\x17\xbc\x03x\x01\xe0\x1d\xe2\x16x\x07Qy\xbc\x03@Fx\x07Df\xbc\x03\x89a\xde\x01q\x11\xe0\x1d|Ix\x07D@\xbc\x03\x8a\x01\xde\x01^9\xde\x01\xf2i\xe0\x1d\x95h\xbc\x03\xe4#\xe0\x1d\xab

O segundo bloco de dados tem 0x1D4C e contém código executável.

Além disso, um ponteiro para o módulo kernel32, o resultado da função GetTickCount () e o endereço da função ZwDelayExecution () do kernel32 são gravados na estrutura de 0x90 bytes .

Em seguida, o processo ( CreateProcessW ) "cmd.exe" é criado. Usando o VirtualAllocEx , dois buffers são alocados nele: com permissões de RW com um comprimento de 0x108 e com permissões de RWX com um comprimento de 0x1D4C. O buffer RW copia o bloco de dados acima e a estrutura mencionada com um comprimento de 0x90. A estrutura também grava um ponteiro no bloco de dados descriptografado (no espaço de endereço do processo filho (cmd.exe)). No RWX, o buffer é copiado ( WriteProcessMemory) descriptografou o bloco de dados com o código.
Em seguida, no processo cmd.exe, um fluxo ( CreateRemoteThread ) é criado com um ponto de entrada no início do buffer RWX, um ponteiro para o buffer RW é passado como argumento. Isso completa a função clone, a ação continua no processo cmd.exe.

Curiosamente, a função clone parece ser um pedaço de código inatingível que importa ( LoadLibraryW ) a biblioteca " WorkPolyhistor ".

O código injetado no cmd.exe


Ele executa as seguintes ações:

  • localiza os endereços das funções necessárias no kernel32.dll (o endereço é obtido no processo pai)
  • carrega bibliotecas ntdll , Ole32 , User32
  • Localiza endereços de funções necessárias nessas bibliotecas.

Curiosamente, para a maioria das funções no código, não há nome da função, mas apenas o CRC32 em nome de (todos os nomes de funções da biblioteca carregada são pesquisados ​​até que haja uma função com o CRC32 desejado em nome de). Talvez seja uma proteção contra a obtenção da lista de funções importadas pelo utilitário strings, embora seja estranho que o código armazenado na forma criptografada tenha essa proteção, enquanto a própria DLL importa funções simplesmente pelo nome. No total, as seguintes funções são detectadas:

kernel32:

  • GetProcAddress
  • Loadlibrary
  • Globalalloc
  • GetTempPath
  • GetFileAttributesW
  • CreateProcessW
  • Globalfree
  • GlobalRealloc
  • Writefile
  • CreateFileW (encontrado pelo nome)
  • Writefile
  • Closehandle
  • Gettickcount
  • ReadFile
  • GetfileSize

ntdll:

  • RtlCreateUnicodeStringFromAsciiz
  • ZwDelayExecution
  • ZwTerminateProcess
  • swprintf

ole32:

  • CoInitialize (encontrado pelo nome)
  • CoCreateInstance (encontrado pelo nome)

msvcrt:

  • rand
  • srand

Em seguida, o processo usando GetTempPath obtém o caminho para o diretório temporário, cria um arquivo nele com o nome 26342235.dat, em que o nome do arquivo é o registro decimal de TickCount recebido do processo pai e itera a cada nova tentativa (ou seja, se o download falhar) na primeira tentativa, na segunda tentativa, um arquivo será criado com o nome 26342236.dat). Depois disso, a biblioteca wininet é carregada e há ponteiros para as seguintes funções:

  • InternetCloseHandle (crc32: 0xe5191d24)
  • InternetGetConnectedState (crc32: 0xf2e5fc0c)
  • InternetOpenA (crc32: 0xda16a83d)
  • InternetOpenUrlA (crc32: 0x16505e0)
  • InternetReadFile (crc32: 0x6cc098f5)

Com o InternetGetConnectedState verificado se existe uma rede - caso contrário, o aplicativo chama o endereço inexistente e cai (essa proteção determina o endereço em que ele gira o peyload usando isolado da rede da máquina é o único caso em que o aplicativo termina de forma anormal, o resto -. , após o que o cmd.exe termina usando o ZwTerminateProcess ). Se houver uma rede, usando as funções encontradas, a carga útil será baixada da URL passada no processo pai (https://pastebin.com/raw/Jyujxy7z) e salva no arquivo criado anteriormente com a extensão .dat.

Em seguida, a carga útil é lida no arquivo .dat, decodificado (base64), descriptografado usando XOR com CRC32 a partir da URL, verifique se os 2 primeiros bytes dos dados descriptografados são 'MZ'; nesse caso, o resultado é salvo em um arquivo com o mesmo nome, mas com extensão. Exe

Código de descriptografia Python
from binascii import crc32
from base64 import b64decode


def decrypt_payload(payload_b64: bytes, url: str):
    payload_bin = b64decode(payload_b64.decode())
    key = str(crc32(url.encode())).encode()
    decrypted = bytearray()
    for i, b in enumerate(payload_bin):
        decrypted.append(b ^ key[i % len(key)])
    return bytes(decrypted)

Usando a função CreateProcessW , o arquivo salvo é iniciado. Se tudo der certo, o processo cmd.exe será encerrado usando o ZwTerminateProcess . Se algo der errado (exceto pela falta de rede), tudo será repetido novamente, serão feitas no máximo 3 tentativas, os nomes dos arquivos dat e exe serão aumentados em 1 a cada vez.

Segunda DLL


A segunda dll (sha256 006200fcd7cd1e71be6c2ee029c767e3a28f480613e077bf15fadb60a88fdfca ) 32 bits.

Nele, a principal funcionalidade maliciosa é implementada na função clone . Ele também descriptografa 2 buffers. O primeiro buffer tem 0x78 (120) bytes de tamanho, esses dados são descriptografados e gravados nele (na forma descriptografada):

https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\x1e(\xf0\x0e\xc5r\xc0;\x12)\xc0;Jr\xc0;Y4\xbc\x03/Mx\x07\x038\xde\x01\x9e\x05\xe0\x1d#\x08\xbc\x03\xeeU\xf0\x0e\x18{x\x078\x1a\xf0\x0e\xccg\xf0\x0eze\xde\x01\x89&\xe0\x1d\xf6\x1f\xe0\x1d

Pode-se observar que no início existe a mesma URL da versão x64.

Um segundo buffer de tamanho 0x4678 bytes é alocado com permissões RWX. O código é descriptografado para ele, após o qual uma função é chamada dele com um deslocamento de 0x4639 desde o início do buffer.

Não analisei esse código em detalhes. Ele também encontra funções no CRC32, lança o notepad.exe, injeta código lá que baixa carga útil da mesma URL no pastebin.

Carga útil com pastebin


A carga descriptografada com pastebin é um arquivo exe de 32 bits (sha256 9509111de52db3d9a6c06aa2068e14e0128b31e9e45ada7a8936a35ddfaf155f ) Ainda não o desmontei em detalhes devido à falta de tempo.

Conclusão


Em geral, o malware me impressionou bastante mal escrito, com muitos erros (não os chamo aqui intencionalmente). Como se estivessem se preparando.
A DLL encontra na memória funções que posteriormente não são usadas em nenhum lugar. Talvez esse código, como uma macro, seja reescrito regularmente por criminosos cibernéticos toda vez que ele começa a ser detectado por antivírus, como resultado dos quais esses artefatos permanecem no código.

Também é interessante que, na versão “DLL” da DLL que é “solta” no disco x64, os nomes das funções importadas “perigosas” não são mascarados (você pode pelo menos vê-las) e no código que é descriptografado na memória e não cabe no disco, eles estão localizados no CRC32 de nome, não apenas pelo nome.

A carga útil da pasta foi removida alguns dias depois.

PS KDPV retirado daqui

All Articles