Daniel Stepanic

Poner pegajoso con GULOADER: desofuscando el descargador

Elastic Security Labs repasa las contramedidas de análisis de GULOADER actualizadas.

13 min de lecturaAnálisis de malware
Poner pegajoso con GULOADER: desofuscando el descargador

Overview

Elastic Security Labs continúa monitoreando amenazas activas como GULOADER, también conocido como CloudEyE , un descargador de shellcode evasivo que estuvo muy activo durante años mientras estaba en constante desarrollo. Uno de estos cambios recientes es la adición de excepciones a su Controlador de Excepciones Vectorizadas (VEH) en una nueva campaña, lo que agrega más complejidad a su ya larga lista de trucos anti-análisis.

Si bien la funcionalidad principal de GULOADER no cambió significativamente en los últimos años, estas actualizaciones constantes en sus técnicas de ofuscación hacen que el análisis de GULOADER sea un proceso que requiere mucho tiempo y recursos. En este post, abordaremos los siguientes temas a la hora de clasificar GULOADER:

  • Revisión del shellcode inicial y del proceso de desempaquetado
  • Encontrar el punto de entrada del shellcode descifrado
  • Discutir la actualización del VEH de GULOADER que ofusca el flujo de control
  • Proporcionar una metodología para parchear la VEH

Shellcode inicial

En nuestro ejemplo, GULOADER viene preempaquetado dentro de un instalador NSIS (Nullsoft Scriptable Install System). Cuando se extrae el instalador, los componentes principales son:

  • Script NSIS : este archivo de script describe todos los aspectos de configuración e instalación.

  • System.dll - Ubicado debajo del $PLUGINSDir. Este archivo se coloca en una carpeta temporal para asignar/ejecutar el shellcode de GULOADER.

  • Shellcode: el shellcode cifrado está enterrado en una carpeta anidada.

Una metodología rápida para identificar el archivo que aloja el shellcode se puede hacer monitoreando ReadFile eventos desde el Monitor de Procesos de SysInternal luego de ejecutar GULOADER. En este caso, podemos ver que el shellcode se lee desde un archivo (Fibroms.Hag).

GULOADER ejecuta shellcode a través de devoluciones de llamada empleando diferentes funciones de la API de Windows. El razonamiento principal detrás de esto es evitar detecciones centradas en las API tradicionales de Windows empleadas para la inyección de procesos, como CreateRemoteThread o WriteProcessMemory. Observamos EnumResourceTypesA y CallWindowProcW empleados por GULOADER.

Al revisar la documentación de MSDN para EnumResourceTypesA, podemos ver que el segundo parámetro espera un puntero a la función de devolución de llamada. En la captura de pantalla anterior, podemos ver que el shellcode recién asignado se coloca en este argumento.

Encontrar el punto de entrada principal del shellcode

En ejemplos recientes, GULOADER aumentó la complejidad al comienzo del shellcode inicial al incluir muchas instrucciones y saltos basura diferentes. La ingeniería inversa del descargador puede requerir lidiar con un largo proceso de ofuscación de código diseñado para romper el desensamblaje y controlar el flujo en algunas herramientas, lo que hace que sea frustrante encontrar el inicio real del código de shell central de GULOADER.

Una metodología para encontrar la llamada inicial puede ser aprovechar la vista de gráfico dentro de x64dbg y usar un enfoque de abajo hacia arriba para buscar la instrucción call eax .

Otra técnica para rastrear el flujo de control inicial consiste en aprovechar el marco de ingeniería inversa Miasm. A continuación se muestra un ejemplo rápido donde podemos pasar el shellcode y desensamblar las instrucciones para seguir el flujo:

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)

Miasm corta las instrucciones 142 jmp y navega a través de las instrucciones no deseadas donde lo configuramos para detener en la instrucción de llamada a EAX (dirección: 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

Fin de la cola del miasma

GULOADER’s VEH Update

Una de las técnicas distintivas de GULOADER se centra en su capacidad de manejo de excepciones vectorizadas (VEH). Esta característica proporciona a las aplicaciones de Windows la capacidad de interceptar y controlar excepciones antes de que se enruten a través del proceso de excepción estándar. Las familias de malware y las aplicaciones de protección de software emplean esta técnica para dificultar que los analistas y las herramientas sigan el código malicioso.

GULOADER inicia este proceso agregando el VEH usando RtlAddVectoredExceptionHandler. A lo largo de la ejecución del shellcode GULOADER, hay código colocado a propósito para desencadenar estas diferentes excepciones. Cuando se activan estas excepciones, el VEH comprobará si hay puntos de interrupción de hardware. Si no se encuentra, GULOADER modificará el EIP directamente a través de la estructura CONTEXT empleando una clave XOR de un byte (cambios por muestra) con un desplazamiento de un byte desde donde se produjo la excepción. Revisaremos un ejemplo específico de esta técnica en la siguiente sección. A continuación se muestra la descompilación de la VEH de nuestra muestra:

Aunque esta técnica no es nueva, GULOADER continúa agregando nuevas excepciones con el tiempo; Recientemente observamos estas dos excepciones agregadas en los últimos meses:

  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_ILLEGAL_INSTRUCTION

A medida que se agregan nuevas excepciones a GULOADER, puede terminar rompiendo las herramientas empleadas para acelerar el proceso de análisis para los investigadores.

EXCEPTION_PRIV_INSTRUCTION

Veamos las dos excepciones agregadas recientemente para seguir el flujo de trabajo de VEH. La primera excepción (EXCEPTION_PRIV_INSTRUCTION), se produce cuando se intenta ejecutar una instrucción privilegiada en el conjunto de instrucciones de un procesador en un nivel de privilegio en el que no está permitido. Ciertas instrucciones, como el ejemplo siguiente con WRSMR esperan privilegios del nivel del kernel, por lo que cuando el programa se ejecuta desde el modo de usuario, activará la excepción debido a licencias incorrectas.

EXCEPTION_ILLEGAL_INSTRUCTION

Esta excepción se invoca cuando un programa intenta ejecutar una instrucción de CPU no válida o indefinida. En nuestro ejemplo, cuando nos encontramos con instrucciones de virtualización de Intel, como vmclear o vmxon, esto desencadenará una excepción.

Una vez que se produce una excepción, el código VEH de GULOADER determinará primero qué código de excepción fue responsable de la excepción. En nuestro ejemplo, si la excepción coincide con cualquiera de las cinco siguientes, el código tomará la misma ruta independientemente.

  • EXCEPTION_ACCESS_VIOLATION
  • EXCEPTION_ILLEGAL_INSTRUCTION
  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_SINGLE_STEP
  • EXCEPTION_BREAKPOINT

A continuación, GULOADER comprobará los puntos de interrupción de hardware recorriendo el registro CONTEXT que se encuentra dentro de la estructura EXCEPTION_POINTERS . Si se encuentran puntos de interrupción de hardware en los diferentes registros de depuración, GULOADER devolverá un 0 en el registro CONTEXT, lo que terminará causando que el shellcode se bloquee.

Si no hay puntos de interrupción de hardware, GULOADER recuperará un solo byte que esté a 7 bytes de distancia de la dirección que causó la excepción. Al usar el último ejemplo con vmclear, recuperaría el byte (0x8A).

Luego, usando ese byte, realizará una operación XOR con un byte codificado de forma rígida diferente. En nuestro caso (0xB8), esto es único por muestra. Ahora, con un 0x32 de desplazamiento derivado (0xB8 ^ 0x8A), GULOADER modificará la dirección EIP directamente desde el registro CONTEXT agregando 0x32 a la dirección anterior (0x7697630) que causó la excepción que resultó en que el siguiente código se ejecutara desde la dirección (0x7697662).

Con diferentes instrucciones basura en el medio, y golpeando repetidamente excepciones (contamos 229 excepciones únicas en nuestra muestra), no es difícil ver por qué esto puede romper diferentes herramientas y aumentar el tiempo del analista.

Limpieza de flujo de control

Para facilitar el seguimiento del flujo de control, un analista puede omitir el VEH rastreando la ejecución, registrando las excepciones y parcheando el shellcode empleando el algoritmo de modificación de EIP discutido anteriormente. Para este procedimiento, aprovechamos TinyTracer, una herramienta escrita por @hasherezade que aprovecha Pin, un marco de instrumentación binaria dinámica. Esto nos permitirá capturar las diferentes direcciones que activaron la excepción, por lo que usando el ejemplo anterior con vmclear, podemos ver que la dirección estaba 0x7697630, generó una excepción llamando a KiUserExceptionDispatcher, una función responsable de manejar las excepciones en modo de usuario.

Una vez que se recopilan y filtran todas las excepciones, estas se pueden pasar a un script IDAPython donde recorremos cada dirección, calculamos el desplazamiento usando el 7º byte y la tecla XOR (0xB8), luego parcheamos todas las instrucciones que generan excepciones con saltos cortos.

La siguiente imagen es un ejemplo de instrucciones de aplicación de parches que desencadenan excepciones en las direcciones 0x07697630 y 0x0769766C.

A continuación se muestra un gráfico que representa el gráfico de flujo de control antes de que se aplique la aplicación de parches globalmente. Nuestro bloque básico con la instrucción vmclear está resaltado en naranja. Al implementar el VEH, GULOADER aplana el gráfico de flujo de control, lo que dificulta el seguimiento de la lógica del programa.

Luego de parchear el VEH con instrucciones jmp , esto transforma los bloques básicos conectándolos entre sí, reduciendo la complejidad detrás del flujo del shellcode.

El uso de esta técnica puede acelerar el proceso de limpieza, pero es importante tener en cuenta que no es un método a prueba de balas. En este caso, todavía termina habiendo una buena cantidad de código/funcionalidad que aún deberá analizar, pero esto definitivamente contribuye en gran medida a simplificar el código al eliminar el VEH. El script POC completo se encuentra aquí.

Conclusión

GULOADER tiene muchas características diferentes que pueden romper el desmontaje, dificultar el flujo de control y dificultar el análisis para los investigadores. A pesar de esto y de que el proceso es imperfecto, podemos contrarrestar estos rasgos a través de diferentes procesos estáticos o dinámicos para ayudar a reducir el tiempo de análisis. Por ejemplo, observamos que con nuevas excepciones en el VEH, todavía podemos rastrearlas y parchear el shellcode. Este proceso pondrá al analista en el camino correcto, más cerca de acceder a la funcionalidad principal con GULOADER.

Al compartir parte de nuestro flujo de trabajo, esperamos ofrecer múltiples conclusiones si te encuentras con GULOADER en la naturaleza. Con base en los cambios de GULOADER, es muy probable que los comportamientos futuros requieran estrategias nuevas y diferentes. Para detectar GULOADER, la siguiente sección incluye las reglas de YARA, y el script IDAPython de esta publicación se puede encontrar aquí. Para obtener nuevas actualizaciones sobre las últimas investigaciones sobre amenazas, consulta nuestra sección de análisis de malware del equipo de Elastic Security Labs.

YARA

Elastic Security creó diferentes reglas de YARA para identificar esta actividad. A continuación se muestra un ejemplo de una regla 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
}

Observaciones

Todos los observables también están disponibles para su descarga tanto en formato ECS como en STIX.

En esta investigación se discutieron los siguientes observables.

ObservableTipoNombreReferencia
6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99SHA-256Windows.Trojan.GuloaderDescargador de GULOADER
101.99.75[.]183/MfoGYZkxZIl205.binURLNAGULOADER C2 URL
101.99.75[.]183IPv4-ADDRNAGULOADER C2 IP

Referencias