Analizando una DLL maliciosa

Gabriel Martí
12 min readMay 5, 2022

--

Ingeniería inversa sobre un malware implicado en una campaña de phishing suplantando el Ministerio de Sanidad.

Photo by Martin Sanchez on Unsplash

Inicio este artículo inspirado en un post en twitter de @nuria_imeq que lleva a un artículo publicado también en Medium, titulado “Analizando la campaña Ministerio Sanidad”.

https://twitter.com/nuria_imeq/status/1515578360193335296

Aconsejo, encarecidamente, leer previamente dicho artículo para situarse en contexto sobre la citada campaña y el malware que aquí se analiza.

De entre los diferentes ejecutables de malware que se mencionan en el artículo, me decido por descargar el que se muestra en la imagen. Básicamente, porque el servidor sigue activo y es posible descargarlo. Los demás no funcionan o han sido neutralizados por barreras de protección en la red.

Vista parcial del artículo de @nuria_imeq y la descarga del archivo a analizar

Tras descargar este archivo en una VM preparada para ello, vemos que al extraer el contenido del archivo contiene 3 archivos:

Contenido del archivo JavaRuntime-ERC482042.zip

Como se menciona ya en el artículo de @nuria_imeq, el malware es el archivo .dll contenido dentro del archivo comprimido. Ese archivo es el que analizaré más adelante, pero veamos antes los otros dos archivos.

Archivo QF00x00H6x5plXteB38EkWbbb

Este archivo, de tan solo 167 bytes, sin extensión y con un nombre claramente ofuscado, es el primero que vamos a analizar.

Podríamos abrirlo con un simple Bloc de Notas, o cualquier otro editor de texto, pero por precaución lo abro inicialmente con un editor hexadecimal.

Vista Hexadecimal de QF00x00H6x5plXteB38EkWbbb

Tras comprobar que no hay datos binarios, pues todo el contenido son caracteres ASCII imprimibles, paso a abrirlo con un editor de texto, donde podemos observar lo que parecen ser instrucciones en algún lenguaje de scripting.

Contenido del archivo QF00x00H6x5plXteB38EkWbbb

De hecho, @nuria_imeq ya menciona esto en su artículo cuando hace referencia a “AutoHotkey”.

¿Qué es AutoHotkey?

Es un lenguaje de scripting, Open Source, el cual es aprovechado por los ciberdelincuentes para distribuir su malware. Buscando en Google el término “SetWorkingDir” que aparece dentro del script nos lleva a la ayuda de dicho lenguaje.

Página de documentación de la aplicación AutoHotkey

Por lo que podemos deducir que es un script de AutoHotkey y es llamado en algún momento desde el dropper descargado previamente y que se encarga de descargar y ejecutar estos ficheros.

¿Y si el equipo de la víctima no tiene AutoHotkey?

Tiene sentido hacerse esta pregunta. Por lo general, ningún usuario va a tener instalado el intérprete de AutoHotkey que es necesario para que se ejecute el código de dicho script, por lo tanto, ¿Cómo puede ejecutarse el script si no está el programa que lo interprete?

Esto nos lleva al siguiente archivo, uL1Ti1AagGYxw4OTxpJ5cSKaaa, de 884 Kbs, el cual, simplemente mirando las propiedades del archivo, el mismo Windows nos indica que es AutoHotkey. Es decir, tenemos un archivo sin extensión que en realidad es un ejecutable (.exe), por lo que, en algún momento de todo el proceso, se cambiará la extensión de este archivo para que se ejecute en el sistema.

Si cargamos el binario con Ghidra, podemos ver en el sumario de la importación que es un ejecutable PE de 32 bits (se comenta un poco más adelante) compilado con Visual Studio.

Y un poco más adelante tenemos identificado el nombre del programa y su versión. Y debe de quedar claro que este NO ES EL MALWARE, es simplemente un intérprete de un lenguaje de scripting, como podría ser PowerShell, Python, o incluso PHP, que es usado como soporte para descargar el Malware.

El malware

Llegados a este punto, ya sabemos que los dos archivos anteriores son, por un lado, el intérprete del lenguaje de scripting de AutoHotkey, y el script que se ejecuta, el cual cargará la DLL citada.

Si analizamos dicha DLL con VirusTotal veremos que es detectado por un amplio número de motores, aunque no por todos.

Muestra parcial del análisis hecho por Virustotal a la DLL maliciosa.

Si accedemos a la pestaña de detalles y vemos los nombres de funciones que exporta podremos observar que hay 4 funciones y la que tiene el nombre más ininteligible, rm5MLoUr43vZ510sxf6Pi, es precisamente la función que se ejecuta al cargar la DLL y la que da paso al malware. Aunque no directamente como comprobaremos más adelante. También podemos observar que existe una función dbkFCallWrapperAddr, que forma parte de la API de las librerías de Embarcadero (Delphi y C++) y que es ampliamente usada en malware.

Funciones de la DLL en el apartado Exports

Tras cargar dicha DLL en Ghidra, podemos confirmar en este caso que el malware está desarrollado con el lenguaje Borland Pascal (actualmente Embarcadero), con su IDE Delphi, y en 32 bits. No han usado la última versión del compilador, y es habitual que una gran mayoría del malware esté compilado en 32 bits para, de esta manera, poder llegar a un mayor número de víctimas.

Cabe decir, que Borland Delphi, al ser programación orientada a objetos, genera un código en ensamblador bastante más complejo y detectar las llamadas a funciones se complica más frente a un ejecutable desarrollado en un lenguaje no orientado a objetos como el lenguaje C.

Magic Bytes

Abro un paréntesis para hablar sobre los Magic Bytes, también conocidos como Magic Numbers. ¿Sabes que son? Es un conjunto de bytes ubicados, normalmente, al principio de los ficheros binarios, que se usan como de firmas de estos archivos para identificar un tipo concreto.

Pensarás, esto no hace falta en Windows, porque ya tenemos las extensiones de los archivos, pero a un archivo se le puede cambiar la extensión para ocultar su tipo o engañar al usuario, aunque la firma sigue siendo la misma. De la misma manera, en Linux, los archivos no tienen por qué tener una extensión y son identificados por estas firmas.

Es por este motivo que el archivo uL1Ti1AagGYxw4OTxpJ5cSKaaa se sabía que era el ejecutable de AutoHotkey. Bueno, en realidad se sabía que era un ejecutable PE de Windows, y no tenía la extensión .exe, pero si la firma “4D 5A” correspondiente a los caracteres “MZ” que es la firma para este tipo de archivos.

Vista hexadecimal de un archivo donde los Magic Bytes identifican que es un ejecutable de Windows

Y en el caso de la DLL del Malware tenemos un byte adicional en la firma, quedando con “4D 5A 50” que corresponde a las letras “MZP”. Este 50 hexadecimal, que corresponde a la letra “P”, indican algunas fuentes que es para informar de que el programa está desarrollado en lenguaje Pascal, y es la firma que incluyen todos los binarios compilados con Delphi.

Vista hexadecimal de la DLL compilada con Delphi y con los Magic Bytes que identifican el tipo

No sé si es casualidad o hecho aposta, pues todos los ejecutables que he visto compilados con Delphi tienen la misma firma, y es probable que sea una firma de identidad de dicho compilador. Pero la realidad es que este valor indica el número de bytes de la última página del fichero, tal y como se define en la estructura de la cabecera de los ficheros ejecutables de MS-DOS.

Definición de la estructura de las cabeceras DOS que forman parte de los ficheros ejecutables de Windows.

Hashes

Un paso importante en cualquier análisis de un binario es obtener los hashes. Por un lado, para tener unos indicadores de compromiso, y por otro porque estos hashes nos pueden llevar a otras fuentes que ya han analizado o detectado previamente estos archivos maliciosos.

Existen varias herramientas que podemos usar. En este caso, analizando toda la cabecera del PE con PE Studio, obtenemos los siguientes hashes.

Y buscando directamente el hash sha256 llegamos directamente a un análisis dentro de Joe Sandbox, donde nos muestra un detallado análisis de la ejecución.

Muestra parcial del gráfico de llamadas del malware

Nombre de la DLL

Cabe decir que el nombre de la dll que tenemos es “gSvUPwSCLS.dll”. En cambio, dentro de la información del producto aparece otro nombre, “f63oC4kyXi7P1”. Esto podría significar que el ciberdelincuente ha renombrado el archivo en un intento burdo de ocultar el malware sin tener en cuenta que la firma sigue siendo la misma. O simplemente es un proceso automatizado que tienen para ir generando diferentes DLL con nombres aleatorios y (intentar) confundir así a los sistemas de detección.

Identificador de la DLL con nombre diferente del nombre de fichero que se está analizando

Descompilando en Ghidra

Lo he mencionado ya un par de veces, pero para el que no lo sepa, Ghidra es una herramienta de ingeniería inversa que nos permite analizar y diseccionar estáticamente binarios de diversas arquitecturas.

Una vez cargado el binario le damos a la opción de Analysis → Auto Analyze para que identifique las funciones principales del código y detecte el EntryPoint.

Normalmente, el punto de entrada (entry) en la imagen suele definir ciertos parámetros del entorno y seguidamente llama a lo que normalmente es la función principal (main). En este caso se detecta un punto de entrada, y solo dos funciones. En los Exports nos aparecen los nombres de funciones que ya habíamos visto en el análisis previo con Virustotal.

Árbol de símbolos generado por Ghidra.

Si nos situamos sobre la función “entry” (el punto de entrada), en la ventana de Listing nos aparecen las direcciones de memoria, y el código desensamblado.

Muestra de parte del código en ensamblador del punto de entrada

Al mismo tiempo, podemos tener en otra ventana llamada “Decompile” el código en ensamblador traducido en lenguaje C. Esto normalmente ayuda bastante a entender ciertas partes de código en ensamblado que puedan ser complejas. En el caso de los binarios compilados con lenguajes orientados a objeto, como pueden ser C++ o Delphi (Object Pascal) esta parte no ayuda mucho, y es lo que pasa en este caso. Ya que los lenguajes orientados a objeto generan un código en ensamblador bastante más complejo. A esto habrá que añadir otra característica que comento más adelante.

Muestra de punto de entrada descompilado en lenguaje C

Volviendo al Arbol de Símbolos, podemos identificar una etiqueta (que también está en el apartado de Exports) que ya he mencionado al principio. Es el valor que se pasa como parámetro a la llamada de la DLL desde el script de AutoHotkey, y que revela el nombre de función que se ejecutará al cargar la DLL.

Muestra de etiqueta coincidente con el parámetro pasado en la llamada de la DLL

El hecho de que tenga únicamente dos funciones, y un código muy corto ya hace sospechar de la existencia de código oculto que Ghidra no ha podido identificar, y ahora veremos por qué.

Existen una sección de memoria, llamada “YPm1” que tiene permiso de lectura y ejecución, pero no de escritura.

Mapa de memoria con las diferentes secciones de la DLL

Y si examinamos la entropía de dicha sección veremos que el valor es de 7.8. Esto significa que los datos de esta sección están probablemente cifrados y/o comprimidos (también se los denomina empaquetados) y, por lo tanto, con la intención de ocultar su código y dificultar aún más, si cabe, su análisis por herramientas de ingeniería inversa.

Parte de la sección YPm1 que muestra una alta entropía

Conociendo todo lo anterior, ya hemos de suponer en parte que puede hacer el código que encontremos a continuación, que será principalmente: Leer el código cifrado dentro de una sección y descifrarlo sobre otra parte de memoria, y una vez finalizado, pasarle el control.

Esto se dice rápido, aunque no es sencillo y en muchos casos implica muchas horas de análisis de código y pruebas, por lo que en este caso voy a tener que especular, ya que en este punto no he conseguido desempaquetar dicho código.

Por ejemplo, aquí tenemos una parte del código donde parece ser que se descifran datos, evidenciado por el uso de rotación de bits (ROR) y de operaciones de OR exclusivo sobre bits (XOR).

Fragmento de código donde aparentemente se descifran datos

Una vez decodificado el código, se obtiene un Handle. Esto viene a ser un puntero a la función de la DLL. A partir de este momento entra en funcionamiento el código malicioso que hasta ahora ha estado oculto, pues todo lo visto es simplemente código para descifrar el verdadero malware.

Fragmento de código que pasa el control a la función maliciosa de la DLL

La cadena “rm5MLoUr43vZ510sxf6Pi

¿Qué es esa cadena que se pasa como parámetro en la llamada de la DLL?

Si nos remitimos al script de AutoHotkey veremos que se llama a una función llamada DllCall, la cual recibe como parámetros un variable que contiene el nombre de la DLL, y le concatena el nombre de la etiqueta que tenemos en Ghidra.

JQ4NW4s7gqqH8zD2w9 := “gSvUPwSCLS.dll”
DllCall(JQ4NW4s7gqqH8zD2w9 . “\rm5MLoUr43vZ510sxf6Pi”)

Si miramos la documentación de AutoHotkey tendremos la confirmación de que esta cadena es el nombre de la función dentro de la DLL.

Así que ya sabemos que ahí está el inicio del código del malware. No obstante, es una deducción que voy haciendo sobre la marcha y podría estar equivocado hasta obtener más datos.

Dirección de memoria apuntada por rm5MLoUr43vZ510sxf6Pi

Inicialmente, esta cadena apunta a la dirección de memoria 0x00d70440 y en dicha dirección se encuentra la dirección 0x5422e4 que es supuestamente el vector de entrada del código, el cual en este momento está vacío, pues no se ha descifrado el código que tiene que ir almacenado a partir de esa dirección.

Valor inicializado con el posible vector de entrada de la función de la DLL

Para poder simular todo este proceso se debería de poder descifrar toda esa parte de código, lo que implica ejecutarlo directamente. O, dicho de otra manera, pasar de un análisis estático a un análisis dinámico.

Malware protegido

A partir de aquí, como he dicho, el análisis estático se hace complicado y se debería de combinar con el análisis dinámico. Para ello hay que usar un Debugger, pero el código usa técnicas anti-debugging que hay que sortear.

He gastado múltiples intentos ejecutando el código paso a paso, tanto con OllyDBG como con x64dbg, y el código acababa bloqueando en parte la VM o se cerraba el debugger. Finalmente llego a un nuevo descubrimiento, y la pista me la da Detect It Easy, que me muestra que la DLL está protegida con VMProtect, un software que, además de empaquetar el código, permite añadir controles para evitar que se ejecute en máquinas virtuales. Es una técnica enfocada totalmente a evitar el análisis forense del malware.

Resultado del análisis de la DLL maliciosa con Detect It Easy

Dicha herramienta también nos confirma que la sección comentada anteriormente tiene el código empaquetado y/o cifrado.

Tras esta nueva evidencia, he retomado el análisis estático con Ghidra y llevo a cabo una nueva revisión de strings que me confirman la existencia de varias funciones relacionadas con dicha protección.

Llegado a este punto, cierro aquí esta parte de análisis estático y dejo para otra ocasión un análisis dinámico, si es que tiene éxito, pues la cuestión de la protección de la DLL no es algo sencillo y está hecha con uno de los productos estrella del mercado. Así que llegar a destriparlo (por mi parte) puede ser una árdua tarea y con resultados inciertos.

Herramientas usadas

Para los que tengan curiosidad por las herramientas que he usado aquí os dejo un listado:

--

--

Gabriel Martí

Ex-Docente CFGM, CFGS Ciberseguridad. Actualmente Consultor en Ciberseguridad. Intereses en robótica, ciberseguridad, reversing. Twitter @gmarti @310hkc41b