Image
GuLoader, un malware precavido 

GuLoader, un malware precavido 

¡Hola a todos! En este post que os traemos hoy os vamos a mostrar en detalle las técnicas que emplea GuLoader, el cual fue detectado por primera vez en diciembre de 2019. Este malware suele ser distribuido a través de campañas de phishing con el objetivo de descargar y ejecutar otras familias tipo stealer o RAT, como pueden ser FormBookRemcos o AgentTesla, entre otros.

Caracterizado por las técnicas que utiliza para dificultar su análisis e impedir su ejecución en entornos virtuales, es considerado por los investigadores de CheckPoint no tanto como un malware, sino como un “protector” de binarios legítimo. Nació con el nombre de DarkEye y se empezó a comercializar en la DarkNet, si bien actualmente se puede adquirir a través del sitio web https://www.securitycode.eu/ comprando el producto con nombre CloudEye Protector.

Análisis

La muestra que ha sido analizada ha sido obtenida a través de la plataforma de VirusTotal y su firma SHA256 del fichero comprimido que contiene la muestra es la siguiente:

17a9c3c1468289b2ba1e963c283ab2d3d8017e37f7a07fb5de7dd916409e41b0

Este fichero contiene 4 binarios que se corresponden con la familia GuLoader. Todos ellos han sido analizados en su totalidad y poseen el mismo comportamiento.

La muestra es un ejecutable de 32 bits para plataformas Windows hecho en Visual Basic.

Realmente, aunque se indique que está implementado en Visual Basic, este únicamente actuará como envoltorio hasta descifrar, mediante XOR, y cargar en memoria la shellcode donde reside la mayoría de la funcionalidad del malware. Por tanto, estableciendo un breakpoint en VirtualAlloc y comprobando la escritura en la zona de memoria reservada, se obtendrá la shellcode.

Previamente a la llamada a VirtualAlloc que se busca, se realizan otras llamadas a dicha función que no se deben tener en cuenta. Se pueden distinguir las llamadas que no interesan mirando la pila dado que, cuando se llama a VirtualAlloc, tiene un aspecto parecido a lo siguiente:

Todas las direcciones pertenecen a librerías que han sido cargadas y no al propio código del binario. En cambio, las llamadas a VirtualAlloc que interesa analizar se distinguen por tener una pila de este tipo:

Esta vez sí que se llama desde la zona de memoria del propio binario y será en la dirección de memoria que devuelva en el registro EAX donde va a cargar una shellcode. En la siguiente imagen se puede apreciar la shellcode una vez descifrada y cargada en memoria:

Durante todo el código se aprecian varias instrucciones redundantes de llamada, salto incondicional y condicional con la intención de dificultar el análisis del flujo de ejecución. Entre estas instrucciones se encuentran las instrucciones que sí forman parte de la funcionalidad.

Primera técnica: Detección de software no deseado en memoria

Para empezar, se introducen en la pila los valores reflejados en  las siguientes instrucciones:

push 0xB314751D
push 0xA7C53F01
push 0x7F21185B
push 0x3E17ADE6
push 0xF21FD920
push 0x27AA3188
push 0xDFCB8F12
push 0x2D9CC76C

Los valores introducidos por estas instrucciones push, se corresponden con firmas calculadas con el algoritmo DJB2 y hacen referencia a software de depuración y de virtualización, como puede ser:

FIRMA

PREIMAGEN

DESCRIPCIÓN

B314751D

vmtoolsdControlWndClass

VMWare

A7C53F01

VBoxTrayToolWndClass

VirtualBox Guest Additions

7F21185B

HookLibraryx86.dll

ScyllaHide Plugin

Posteriormente, el malware hace uso de la función ZwQueryVirtualMemory para buscar en todas las zonas de memoria alguna coincidencia con alguna de las firmas introducidas en la pila. Si encuentra alguna coincidencia, lanza el siguiente MessageBox y termina la ejecución.

Segunda técnica: Detección de entorno virtual

Esta técnica utiliza la medición de los ciclos de reloj del procesador usando la instrucción RDTSC, para contabilizar el tiempo que tarda en ejecutar las siguientes instrucciones:

mov eax ,1

cpuid

bt ecx, 1fh

Si se está ejecutando en un entorno virtualizado, la última instrucción mostrada demorará más tiempo que si se está ejecutando en un entorno normal. Esta operación la realiza 100.000 veces, acumulando los tiempos medidos y, posteriormente, lo compara con 110.000.000 (0x68E7780). Si el resultado es mayor o igual asume que está en un entorno virtualizado o de análisis y entra en un bucle infinito.

Tercera técnica: Detección de Sandbox

Tras superar la técnica anterior, hace uso de la función EnumWindows para conocer el número de ventanas activas en el sistema y posteriormente lo compara con 12 (0xC). Si el resultado es menor, termina su ejecución llamando a TerminateProcess.

Esta técnica se basa en que en un entorno de análisis automatizado el número de ventanas suele ser pequeño mientras que en un entorno real, con un usuario real, tendría más.

Cuarta técnica: Detección del vínculo con el depurador

El objetivo de esta técnica es modificar las funciones DbgBreakPoint y DbgUiRemoteBreakin de la librería ntdll para impedir que el depurador pueda tomar el control del binario. Para ello, busca las direcciones de memoria de las funciones, accede a ellas y modifica el código.

En el caso de ntdll!DbgBreakPoint(), inicialmente se tiene el siguiente código:

Para modificarlo, primero almacena la dirección en [esp+18].

Y luego sustituye el primer byte por 0x90 (NOP).

Tras ello, la función queda de la siguiente manera:

De tal forma, cuando el depurador se intente vincular, no se producirá la interrupción y la vinculación fallará.

Por otro lado, en el caso de ntdll!DbgUiRemoteBreakin(), inicialmente se tiene:

Para modificarlo, almacena la dirección en [esp+1C].

Y luego sustituye los bytes de la función DbgUiRemoteBreakin, tal y como se muestra en las siguientes imágenes:

Dando lugar al siguiente código:

En este caso, cuando el depurador se intente vincular, se llamaría a ExitProcess y terminaría la ejecución. 

Quinta técnica: Detección del QEMU Guest Agent

Esta técnica hace uso de la función CreateFileA para buscar si existen dos binarios relacionados con el software de virtualización QEMU.

En primer lugar, busca “C:\Program Files\Qemu-ga\qemu-ga.exe”.

Y posteriormente, “C:\Program Files\qqa\qqa.exe”.

Si CreateFileA no encuentra los archivos, devolverá “FFFFFFFF” en “EAX” para, posteriormente, realizar la comparación y seguir con la ejecución. En caso contrario, devolverá un HANDLE al archivo y la comparación resultará negativa, por lo que tomará el salto y terminará la ejecución.

Sexta técnica: Detección del depurador

La siguiente técnica busca producir un final de la ejecución inesperado si el binario se está depurando. Para ello, utiliza la función NtSetInformationThread pasándole como argumento un 11, que se corresponde con ThreadHideFromDebugger.

Si el programa está siendo depurado, cuando se ejecute la instrucción “call eax”, se producirá una excepción y terminará la ejecución.

Séptima técnica: Detección de software instalado no deseado

En esta séptima técnica, se busca detectar software instalado que no se desea. Para ello, utiliza las funciones MsiEnumProductsA y MsiGetProductInfo junto a firmas generadas con el algoritmo DJB2.

En primer lugar, utiliza MsiEnumProductsA para listar todo el software instalado en forma de una cadena compuesta por caracteres alfanuméricos.

Posteriormente, utiliza la segunda función para convertir el resultado de la anterior función en cadenas que contienen el nombre del software instalado.

Una vez obtenido el nombre del software, le aplica el algoritmo DJB2 para generar la firma y posteriormente compararla.

En total busca 3 firmas, 7C8AA9FD, 555E1691 y CE81C85D.

Este proceso lo repite 255 (0xFF) veces. Si alguna coincide, termina su ejecución.

Superado este punto, el malware ya empieza a mostrar parte de la funcionalidad que terminará con la descarga y ejecución de otra familia. En primer lugar, carga todas las funciones de las distintas librerías que utilizará y posteriormente, ejecuta algunas de ellas para llevar a cabo la técnica de Process Hollowing que se explicará en detalle más adelante. Previamente a ello, se debe tener en cuenta una técnica que emplea cada vez que se va a ejecutar una función de las anteriormente cargadas.

Octava técnica: Detección de puntos de interrupción

Esta técnica busca si se ha establecido algún punto de interrupción tanto de hardware como de software. En el caso de los puntos de interrupción de hardware, utiliza la función NtGetContextThread para hallar la zona de memoria donde se almacenan las direcciones de memoria que se muestran en los registros [DR0-DR5] y la almacena en EAX.

Posteriormente, comprueba que todos ellos estén a 0, es decir, que no se haya almacenado ninguna dirección de memoria. En la siguiente tabla se puede comprobar la asociación entre posición dirección de memoria y registros DRx:

Dirección de memoria

Registro

[eax+4]

DR 0

[eax+8]

DR 1

[eax+C]

DR 2

[eax+10]

DR 3

[eax+14]

DR 4

[eax+18]

DR 5

Si algún registro contiene alguna dirección de memoria, entonces finaliza la ejecución. En caso contrario, pasa a comprobar si existen puntos de interrupción de software. En primer lugar, almacena en EAX la dirección de memoria de la función a ejecutar. Posteriormente, almacena en la parte baja del registro BX (BL) el primer byte y, finalmente, comprueba que no sea 0xCC que se corresponde con INT 3.

Este mismo proceso también lo realiza comprobando que no sea igual a 0xCD 0x03, con la diferencia de que carga los dos primeros bytes en el registro BX.

Otro tipo de punto de interrupción que comprueba es el 0x0F 0x0B que se corresponde con UD2.

Según la definición de este opcode, se producirá una excepción que el depurador podrá controlar con motivo de “opcode no válido”.

Novena técnica: Process Hollowing

Esta técnica inyecta código en la zona de memoria de otro proceso legítimo con el objetivo de evadir los mecanismos de seguridad. En este caso, busca inyectar otra shellcode en el espacio de memoria de uno de los siguientes binarios: RegAsm.exe, MSBuild.exe o RegSvcs.exe. A continuación, se detallan los pasos que sigue:

  1. Utiliza CreateProcessInternalW para crear el proceso de uno de los binarios anteriormente mencionados, en estado suspendido.

  1. Obtiene un HANDLE de “C:\Windows\syswow64\mstsc.exe” utilizando ZwOpenFile.
  2. Utiliza ZwCreateSection sobre el HANDLE anterior para crear un objeto que representa una porción de memoria que puede ser compartida.
  3. Mapea la porción de memoria anterior en la memoria del proceso creado en el primer punto, utilizando NtMapViewOfSection.
  4. Escribe la shellcode, marcada en rojo en la siguiente imagen, en la porción de memoria mapeada, con NtWriteVirtualMemory.

  1. Obtiene el contexto del hilo del proceso creado en el primer punto, utilizando NtGetContextThread.
  2. Modifica el registro EIP para que apunte al inicio de la shellcode copiada, utilizando NtSetContextThread.
  3. Utiliza NtResumeThread para activar el proceso creado en el primer punto y finaliza la ejecución del proceso actual. 

La shellcode inyectada es muy parecida a la shellcode que se ha analizado. Contiene todas las técnicas anteriormente descritas a excepción del Process Hollowing, que lo sustituye por una función que utiliza InternetOpenUrlA de la librería wininet.dll para acceder a la URL de descarga de otro binario y la función InternetReadFile, de la misma librería, para obtener la carga útil. Una vez obtenida, la descifra con un XOR y la ejecuta.

Conclusiones 

Como se ha podido ver, Guloader se toma muchas molestias en evitar que el binario final sea descargado y ejecutado si no se cumplen todas sus condiciones de depuración y entornos virtualizados. Así bien, aunque su función principal debería ser la de descargar y ejecutar un binario, esta pasa a un segundo plano, eclipsada por la función de protegerlo frente a los analistas de malware y entornos de análisis automatizado.

¡Hasta el siguiente post!

Referencias