Image
Análisis del troyano Dridex (2021)

Análisis del troyano Dridex (2021)

¡Hola! Hoy os traemos el análisis de un troyano, Dridex, el cual apareció por primera vez en 2014 como resultado de la evolución del troyano Cridex que, a su vez, se basa en el código fuente de la familia Zeus filtrado con anterioridad. Desde entonces se encuentra activo debido al constante desarrollo por parte de los actores que operan el código y su complejidad en cuanto a diseño, con una arquitectura basada en capas de proxies que ocultan los servidores de mando y control (C2) reales que se encuentra detrás de estos.

En este artículo se abordará el análisis del cargador principal, el loader, del malware a partir de una muestra de abril de 2021, cuyo hash SHA256 es:

126d93e7ddca40c743fac22376d2c6e5a654a9b0e3abcf3b42c1885f7c34029b

Antecedentes

El desarrollo del troyano se le atribuye al grupo cibercriminal ruso autodenominado como “Evil Corp”, también conocido como “TA505”, “SectorJ04”, “Indrik Spider”, “GRACEFUL SPIDER”, “Gold Tahoe” o “Dudear”. Se estima que este ha podido generar más de 100 millones de dólares de beneficio con sus actividades.

Maksim Yakubets e Igor Turashev son señalados por el Departamento de Justicia de Estados Unidos, en diciembre de 2019, como desarrolladores del malware y, al primero, como líder del grupo, ofreciendo una recompensa de hasta 5 millones de dólares por cualquier información que pudiera derivar en sus detenciones.

La principal funcionalidad de Dridex es robar claves de acceso a aplicaciones de banca online mediante técnicas como la inyección de scripts maliciosos cuando estas son visitadas por el usuario. Sin embargo, su alcance no se queda ahí, pues se trata de un malware modular e incluye otros módulos para realizar actividades fraudulentas como keylogger, VNC o proxy SOCKS, además de servir para la distribución de otras familias de amenazas y tipos de malware como Pony, spammers, stealers de emails o ransomware.

Con respecto al ransomware, en agosto de 2017 se atribuye al mismo grupo el desarrollo de BitPaymer, el cual es utilizado como segunda etapa en la infección del troyano en algunos casos, aunque también ha sido distribuido mediante otras amenazas como Emotet o Ursnif. Posteriormente, en 2019, aparece un nuevo ransomware, DoppelPaymer, también atribuido a alguno de los miembros del grupo debido a sus similitudes con el anterior.

Unpacking

El cargador principal de Dridex consiste en una librería (DLL) que, usualmente, se encuentra empaquetada con el fin de proteger el código original del binario y evitar detecciones de sistemas antivirus. Una vez desempaquetada se obtiene la librería original cuyo hash SHA256 es:

89e8098dbe0d736bbd3f04b3836fabea19ca22ee01f79ae419e2c16e4a4cbe21

Lo primero que puede destacar al analizar estáticamente la librería es la ausencia de las funciones esperadas en un troyano de sus características en la tabla de importaciones, así como de cadenas de caracteres relevantes. Ambas técnicas de ofuscación las describimos más adelante.

Resolución de API dinámica y manejador de excepciones

Los nombres de las librerías y de las API que utiliza el binario se encuentran ofuscados. Dridex utiliza una técnica conocida como “Call API by hash” que se ha implementado del siguiente modo: contiene insertados en el propio código los hashes tanto de los nombres de DLL como de las API, aplicando CRC32 y una clave XOR.

Se pueden encontrar dos funciones diferentes que resuelven direcciones: una recibe con la instrucción “push” los parámetros del hashes ofuscados con XOR de la librería y de la API a resolver; mientras que la otra recibe la dirección de la DLL resuelta previamente en otra función y el hash, pero, en este caso, mediante la instrucción “mov”.

Inicialmente, resuelve tanto las librerías ntdll como kernel32, que se encuentran cargadas, a través del PEB (Process Environment Block) del programa. A partir de aquí, el resto de librerías son cargadas mediante un proceso que consiste en utilizar GetSystemDirectoryW/GetSystemWow64DirectoryW y la combinación de FindFirstFileExW y FindNextFileW para recorrer el directorio de Windows y cargar la DLL a buscar mediante LdrLoadDLL.

En versiones previas de Dridex, el nombre de la DLL a cargar era transformado a mayúsculas antes de calcular su CRC32. Sin embargo, en las últimas muestras analizadas son transformados a minúsculas.

Dridex incluye una peculiaridad, además de la implementación de “Call API by Hash”, y es que, en lugar de llamar directamente a la dirección de la API obtenida, utiliza un manejador de interrupciones que desvía el flujo normal del programa. Tras observar detenidamente el desensamblado del código de la librería, se pueden localizar una serie de excepciones que el propio programa lanza mediante la instrucción “int 3”. Esta instrucción viene precedida siempre de una llamada a la función de resolución de API y, a continuación, contiene la instrucción “retn”.

El manejador de excepciones programado comprueba si el código de excepción es la constante EXCEPTION_BREAKPOINT y modifica el valor del registro ESP, de tal forma que la instrucción “retn“ que se ejecuta al devolver el flujo del programa a la línea siguiente hace que se llame a la API en cuestión, que se ha resuelto previamente la función propia, que es similar a GetProcAddress.

Por tanto, la combinación “int3; ret” podría sustituirse por la instrucción “call eax” para recuperar el flujo normal del programa.

Cadenas de caracteres

Las cadenas de caracteres utilizadas por Dridex se encuentran también ofuscadas. La función que se encarga de resolverlas toma tres parámetros: un buffer de salida con el resultado, un puntero al buffer con las cadenas cifradas y un índice. La función descifra el buffer mediante RC4 con una clave que se encuentra en los primeros 40 bytes (0x28) y en orden inverso. Tras esto, el índice selecciona la cadena a devolver.

Información del sistema

La funcionalidad principal de este cargador es recopilar diferentes tipo de información que identifique al equipo infectado y realizar una petición de registro a la botnet vinculada a la muestra, a través de uno de los proxies configurados. Dentro del código se puede encontrar una función que se encarga de dicha recopilación y la información es almacenada en una estructura global para ser utilizada en las operaciones posteriores.

En primer lugar, obtiene el número de compilación, la versión de Windows y el tipo de producto mediante la API GetVersionExW.

A continuación, obtiene la arquitectura del procesador mediante IsWow64Process.

Tras esto, comprueba qué tipo de usuario ejecuta el binario mediante GetTokenInformation, pasándole el valor TokenGroups al parámetro TokenInformationClass.

De esta forma, obtiene el SID con AllocateAndInitializeSid y lo compara con el de administrador local (S-1-5-32-544) con EqualSid.

Comprueba mediante el valor TokenElevation si el proceso actual está siendo ejecutado con permisos elevados y mediante el valor TokenIntegrityLevel asigna un valor entre 1 y 7 al nivel de integridad.

Además, obtiene el ID de sesión de Terminal Services asociado al proceso mediante el PEB (Process Environment Block).

Por otra parte, Dridex comprueba la clave de registro “SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System” mediante RegQueryValueExA para consultar los valores EnableLUA, ConsentPromptBehaviorAdmin y PromptOnSecureDesktop. En base a estos le asigna un nivel entre 0 y 5.

Finalmente, Dridex utiliza GetSystemInfo para obtener el número de procesadores, tamaño de página, dirección de aplicación mínima y máxima y granularidad en la reserva de memoria.

De esta forma, se obtiene una estructura que recoge toda la información:

Comunicación de red

Una vez realizado el proceso de inicialización y recogida de la información necesaria, la finalidad principal del cargador de Dridex es registrar a la víctima en el panel C2 y descargar el módulo principal junto con el listado de nodos.

 

Toda la comunicación con los servidores C2 es cifrada mediante RC4 y encapsulada con el protocolo HTTPS. La función que inicializa la petición al servidor recibe un parámetro, que se trata de un hash CRC32 que identifica el tipo de respuesta.

El malware accede a la sección “.data” del binario y construye una estructura con el ID de la botnet y un listado de direcciones IP insertadas en el código.

Los valores del binario analizado son:

  • ID de botnet: 40112
  • Número de IP: 3
    • 172.227.10:443
    • 93.133.123:2303
    • 168.61.147:8172

A continuación, construye una estructura para formar la petición a partir de la información recopilada anteriormente. La estructura se compone de los siguientes campos:

  • len(victim_id): longitud del victim_id
  • victim_id: nombre del equipo seguido del hash MD5 del nombre del equipo + nombre de usuario + “\x00\x00\x00” + fecha de instalación + “\x00\x00”. La fecha de instalación es obtenida de la clave de registro “HKLM\Software\Microsoft\Windows NT\CurrentVersion\InstallDate”.
  • system_id_hash: hash MD5 del serial del disco + fecha de instalación + x64_or_x32 + “\x00\x00”.
  • botnet_id: el ID de la botnet
  • sys_info: estructura calculada con información como el Windows Build Number, productType, arquitectura, el flag UAC y si tiene permisos de administrador, así como un byte que indica la versión de Windows mediante el cálculo ((majorversion << 4 - 80) + minorversion)

  • command: código CRC32 del comando a enviar:
    • bot == 0x11041f01
    • list == 0x18f8c844
    • dmod5 == 0xd3ef7577
    • dmod6 == 0x69be7cee
    • dmod11 == 0x32dc1df8
  • x64_or_x32: un byte con 32 (0x20) o 64 (0x40) en función de la arquitectura.
  • len(installed_software): longitud del installed_software.
  • installed_software: listado de software instalado en el equipo obtenido de la clave de registro HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall.
  • len(envs): longitud de envs.
  • envs: cadena que contiene las variables de entorno obtenidas con la API GetEnvironmentStringsW.

Una vez construida la estructura para la petición, esta es cifrada utilizando RC4 con una clave que se encuentra almacenada entre las cadenas de caracteres cifradas del binario. La clave RC4 de la muestra analizada es: AhGDjKatq8OVBsCNBxsJHbQSf84QZXMd170Lw0k.

La petición final se forma concatenando el hash CRC32 del contenido una vez cifrado y el propio contenido cifrado.

El paquete es enviado por HTTP mediante el método POST, utilizando la librería WinINet mediante InternetOpenA, InternetConnectW, HttpOpenRequestW y HttpSendRequestW.

La respuesta del servidor es leída mediante InternetReadFile y, si el código de respuesta es 200 o 404, el contenido es descifrado utilizando RC4 con la misma clave que cifró la petición enviada. Esta respuesta siempre comienza con un hash CRC32 del contenido seguido del propio contenido.

Una vez llegado a este punto, en función de diferentes factores como la localización del punto de acceso a Internet, el listado de software instalado y variables de entorno, el nombre de usuario de la víctima o la antigüedad del binario, los servidores responderán favorablemente o, por el contrario, con un código 403 FORBIDDEN, quedando además bloqueado su acceso (baneado) para futuras peticiones.

Este hecho dificulta enormemente poder contactar y recibir información de los servidores C2 desde un entorno de análisis, ya que no se puede asegurar a ciencia cierta cuáles son los parámetros que espera o no recibir la botnet.

Los intentos de conexión desde la máquina de análisis obtienen siempre un código 403, incluso con muestras recientes.

Comunicación entre el Loader y el bot

Si el bot se descarga del C2 y comienza a ejecutarse, el loader necesita una forma de compartir la información obtenida del comando “list” con este. Para ello, el loader utiliza la clave de registro Software\Microsoft\Windows\CurrentVersion\Explorer\CLSID\%s\shellfolder, donde %s es un CLISD construido a partir del hash victim_id con el byte 0x1C.

En dicha clave se guardará información como el ID de la botnet, el tamaño de la lista de nodos, el listado de nodos y una URI utilizada posteriormente para crear una tarea programada que asegure la persistencia del bot.

Persistencia

Dridex copia un programa desde el directorio de Windows a un nuevo directorio en %AppData% con un nombre aleatorio, donde la DLL del bot descargado es copiada y renombrada como una de las librerías que carga el binario copiado. Tras esto, se crea una tarea programada que lanza el binario legítimo y la DLL es hijacked por la de Dridex.

Para ello, el loader escanea todos los ficheros con extensión “.exe” que se encuentren en el directorio de Windows y selecciona uno que no tenga la propiedad “AutoElevated”. A continuación, enumera las librerías importadas por el binario y comprueba si el nombre de alguna de ellas concuerda con una lista blanca de hashes CRC32.

Cuando la DLL es seleccionada, el loader obtiene el listado de funciones exportadas por dicha librería y lo copia a la DLL del core de Dridex. Finalmente, la librería es copiada al nuevo directorio aleatorio en %AppData% con el nombre de la librería legítima.

Una vez el binario ha sido copiado, y en función del nivel de privilegio con que se haya ejecutado el loader, Dridex crea una o dos tareas programadas mediante objetos COM. Si el binario obtiene permisos de administrador, se generan dos tareas programadas con una URI aleatoria dentro de dentro de la ruta \Tasks\Microsoft\Windows del directorio de instalación de Windows.

La diferencia entre ambas tareas reside en el nombre de la URI. La primera toma un fichero de tarea legítima y le añade el SID del usuario. El comando ejecutado es el binario legítimo copiado al directorio de instalación en %AppData%.

En la segunda tarea programada, el comienzo de la URI es seleccionado de forma aleatoria y el nombre de fichero es construido con un algoritmo pseudoaleatorio basado en el hash previo victim_id, donde genera hashes MD5 y selecciona los caracteres ASCII hasta construir una cadena de longitud 0x30. El comando ejecutado es un directorio aleatorio dentro del directorio de Windows, por lo que si no existe no se ejecutará nada, así que no queda del todo claro el uso de esta tarea.

Para registrarlas se utilizan objetos COM mediante la llamada a la API CoCreateInstance.

Ambas tareas son lanzadas al iniciar sesión y cada 30 minutos. Las diferencias entre la versión con privilegios y sin ellos son que el path de la URI de la tarea se encuentra en la carpeta raíz y el nombre es generado con la función pseudoaleatoria.

Mutex

Antes y después de registrar la tarea programada, el loader comprueba la presencia de un Mutex. Si existe, quiere decir que la DLL del bot ha sido ejecutada por la tarea programada de forma correcta y se encuentra inyectada en el proceso explorer.exe. En caso de no existir este, intenta volver a lanzar la tarea.

El nombre del mutex es calculado mediante hash MD5 de forma similar a identificadores anteriores. En este caso, los datos son computer_name + username + “\x00\x02\x00” + installDate + “\x00\x00” y es formateado como un CLSID.

Y aquí terminamos este artículo acerca del troyano Dridex. Si os ha gustado y resultado de utilidad, os gradeceríamos mucho que lo compartieseis, ¡así aprende más gente! :D

¡Nos vemos en el próximo!

Referencias