El estudio de uno malicioso

imagen

Me encontré con un archivo de documento recientemente malicioso que se envió con correos electrónicos de phishing. Decidí que esta es una buena razón para practicar ingeniería inversa y escribir algo nuevo en Habr. Debajo del gato: un ejemplo de análisis de un macro dropper malicioso y no menos dlls maliciosos.

Macro


Sha256 del archivo - abb052d9b0660f90bcf5fc394db0e16d6edd29f41224f8053ed90a4b8ef1b519 . En el archivo doc en sí, en la primera página hay una imagen que informa que este archivo está protegido y explica cómo habilitar macros; hay dos tablas grandes con números en el archivo. Los números se escriben en forma decimal, los más largos son de diez dígitos, hay tanto positivos como negativos.

Cuando la víctima permite la ejecución de macros (¿hay quienes la tienen habilitada de manera predeterminada?), Se inicia una cadena de acciones, que finalmente realiza la función updatedb_network, que cambia el directorio actual a uno temporal y crea el archivo "icutils.dll", en el que escribe los números de la primera tabla en una fila como un entero de 32 bits con un signo. El resultado es el dll correcto. La función de clonación se importa de este dll :

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

Y comienza con dos parámetros:

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

Si la llamada de clonación devuelve False, el archivo icutils.dll se sobrescribe con datos de la segunda tabla y se vuelve a llamar a clone con los mismos parámetros.

Mirando hacia el futuro, diré que el primer dll es de 64 bits y no se ejecutará en sistemas de 32 bits. Por lo tanto, la macro selecciona la arquitectura correcta del código binario.

Curiosamente, la función updatedb_network tiene un código que no tiene un propósito 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

¿Quizás está aquí para dar la apariencia de trabajo útil para aquellos que se desplazan rápidamente por el código, ven algunas líneas en SQL y piensan que todo está bien? Yo no sé. Además, la mayoría de las funciones y variables tienen nombres aleatorios o de nombre no real (como updatedb_network , que no interactúa con la base de datos o la red). Aunque existe, por ejemplo, la función dump_payload , que almacena 4 bytes en icutil.dll. Pero, en cualquier caso, la presencia de la función Document_Open debería alertar de inmediato , los autores del malware no pueden renombrarlo arbitrariamente (aunque pueden usar otra función iniciada automáticamente).

Entonces, la funcionalidad de macro es más o menos clara, es hora de descargar el dll y proceder a su análisis.

Primer dll


El primer dll (sha256 7427cc4b6b659b89552bf727aed332043f4551ca2ee2126cca75fbe1ab8bf114 ) de 64 bits.

La lista de funciones importadas contiene las funciones CreateProcessW (inicio del programa), CreateRemoteThread (creando un hilo en otro proceso), VirtualAllocEx (asignando un bloque de memoria en otro proceso), WriteProcessMemory (escribiendo en la memoria de otro proceso), lo que sugiere inmediatamente la inyección de código en otro proceso. Ahora veamos qué hace exactamente usando IDA Free y Ghidra.
El punto de entrada principal simplemente devuelve 1, no hace nada más. La segunda función exportada es clone, que es llamada por la macro y contiene código malicioso.
Los parámetros con los que se llama no parecen afectar nada. La aplicación descifra dos bloques de datos. El primer bloque de datos 0x78 con los siguientes contenidos (ya descifrados):

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

El segundo bloque de datos tiene una longitud de 0x1D4C y contiene código ejecutable.

Además, un puntero al módulo kernel32, el resultado de la función GetTickCount () y la dirección de la función ZwDelayExecution () del kernel32 se escriben en la estructura de bytes 0x90 .

Luego se crea el proceso ( CreateProcessW ) "cmd.exe". Usando VirtualAllocEx , se asignan dos memorias intermedias: con permisos RW con una longitud de 0x108 y con permisos RWX con una longitud de 0x1D4C. El búfer RW copia el bloque de datos anterior y la estructura mencionada anteriormente con una longitud de 0x90. La estructura también escribe un puntero en el bloque de datos descifrado (en el espacio de direcciones del proceso secundario (cmd.exe)). En RWX, el búfer se copia ( WriteProcessMemory) bloque de datos descifrado con código.
Luego, en el proceso cmd.exe, se crea una secuencia ( CreateRemoteThread ) con un punto de entrada al comienzo del búfer RWX, se pasa un puntero al búfer RW como argumento. Esto completa la función de clonación, la acción continúa en el proceso cmd.exe.

Curiosamente, la función de clonación parece ser un código inalcanzable que importa ( LoadLibraryW ) la biblioteca " WorkPolyhistor ".

El código inyectado en cmd.exe


Realiza las siguientes acciones:

  • encuentra las direcciones de las funciones necesarias de kernel32.dll (su dirección se obtiene del proceso padre)
  • carga las bibliotecas ntdll , Ole32 , User32
  • Encuentra direcciones de funciones necesarias en estas bibliotecas.

Es interesante que para la mayoría de las funciones en el código no hay un nombre de función, sino solo CRC32 en nombre de (se buscan todos los nombres de función de la biblioteca cargada hasta que haya una función con el CRC32 deseado en nombre de). Quizás esta sea una protección contra la obtención de la lista de funciones importadas por la utilidad de cadenas, aunque es extraño que el código almacenado en forma encriptada tenga dicha protección, mientras que el dll importa las funciones simplemente por su nombre. En total, se detectan las siguientes funciones:

kernel32:

  • GetProcAddress
  • Loadlibrary
  • Globalalloc
  • GetTempPath
  • GetFileAttributesW
  • CreateProcessW
  • Globalfree
  • GlobalRealloc
  • Archivo de escritura
  • CreateFileW (encontrado por nombre)
  • Archivo de escritura
  • Closehandle
  • Gettickcount
  • ReadFile
  • GetfileSize

ntdll:

  • RtlCreateUnicodeStringFromAsciiz
  • ZwDelayExecution
  • ZwTerminateProcess
  • swprintf

ole32:

  • CoInicializar (encontrado por nombre)
  • CoCreateInstance (encontrado por nombre)

msvcrt:

  • rand
  • srand

A continuación, el proceso que usa GetTempPath obtiene la ruta al directorio temporal, crea un archivo con el nombre del formulario 26342235.dat, donde el nombre del archivo es el registro decimal de TickCount recibido del proceso padre e itera en cada nuevo intento (es decir, si la descarga falla en el primer intento, luego en el segundo intento se creará un archivo con el nombre 26342236.dat). Después de eso, se carga la biblioteca wininet y hay punteros a las siguientes funciones:

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

Usando InternetGetConnectedState, verifica si hay una red; de lo contrario, la aplicación llama a la función a una dirección inexistente y falla (tal protección contra la determinación de la dirección desde donde se obtiene la carga útil usando una máquina aislada de la red. Este es el único caso cuando la aplicación finaliza de manera anormal, en el resto se hacen 3 intentos , después de lo cual cmd.exe termina con ZwTerminateProcess ). Si hay una red, utilizando las funciones encontradas, la carga útil se descarga desde la URL pasada desde el proceso principal (https://pastebin.com/raw/Jyujxy7z) y se guarda en el archivo creado previamente con la extensión .dat.

A continuación, la carga útil se lee del archivo .dat, se decodifica (base64), se descifra usando XOR con CRC32 de la URL, verifique que los primeros 2 bytes de los datos descifrados sean 'MZ', de ser así, el resultado se guarda en un archivo con el mismo nombre pero extensión. exe

Código de descifrado de 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 la función CreateProcessW , se inicia el archivo guardado. Si todo va bien, el proceso cmd.exe se cierra con ZwTerminateProcess . Si algo salió mal (excepto por la falta de una red), entonces todo se repite nuevamente, se hacen un máximo de 3 intentos, los nombres de los archivos dat y exe se incrementan en 1 cada vez.

Segunda dll


El segundo dll (sha256 006200fcd7cd1e71be6c2ee029c767e3a28f480613e077bf15fadb60a88fdfca ) 32 bit.

En él, la principal funcionalidad maliciosa se implementa en la función de clonación . También descifra 2 buffers. El primer búfer tiene un tamaño de 0x78 (120) bytes, dichos datos se descifran y se escriben en él (en forma descifrada):

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

Se puede ver que al principio es la misma URL que en la versión x64.

Se asigna un segundo búfer de tamaño 0x4678 bytes con permisos RWX. El código se descifra en él, después de lo cual se llama a una función con un desplazamiento de 0x4639 desde el comienzo del búfer.

No analicé este código en detalle. También encuentra funciones en CRC32, lanza notepad.exe, inyecta código allí que descarga la carga útil desde la misma URL en pastebin.

Carga útil con pastebin


La carga útil descifrada con pastebin es un archivo exe de 32 bits (sha256 9509111de52db3d9a6c06aa2068e14e0128b31e9e45ada7a8936a35ddfaf155f ) No lo desarme en detalle todavía por falta de tiempo.

Conclusión


En general, el malware me impresionó bastante mal escrito, con muchos errores (no los llamo aquí intencionalmente). Como si estuvieran azotando.
dll encuentra en la memoria funciones que luego no se usan en ningún lado. Quizás este código, como una macro, es reescrito regularmente por los ciberdelincuentes cada vez que los antivirus comienzan a detectarlo, como resultado de lo cual dichos artefactos permanecen en el código.

También es interesante que en la versión "dll" de la dll que se deja caer en el disco x64, los nombres de las funciones importadas "peligrosas" no están enmascarados de ninguna manera (al menos puede ver las cadenas), y en el código que descifra en la memoria y no cabe en el disco, se encuentran en CRC32 desde nombre, no solo por nombre.

La carga útil de Pastebin fue eliminada unos días después.

PS KDPV tomado de aquí

All Articles