Análisis de la familia de Stealers W32\ISR Stealer. Parte I
¡Hola! Este post va a inaugurar una serie que nos hace mucha ilusión compartir con vosotros. En los últimos meses hemos llevado a cabo varios análisis de diferentes familias de malware. Se trata de troyanos del tipo Stealer y Downloader cuyas características técnicas hemos ido recogiendo y que ahora os vamos a dar a conocer. Concretamente, os vamos a presentar los Stealers W32\ISR stealer, BALDR y Evrial y el Downloader Rietspoof.
Cada uno de estos análisis estará dividido en dos entradas que iremos publicando en Security Garage. En este primer artículo vamos a recoger algunas de las principales características que presenta W32\ISR stealer, un troyano utilizado como complemento de otras campañas de malware. ¡Empezamos!
El malware inicial se distribuye simulando ser una aplicación legítima, sirviéndose para ello de una página web tras el nombre de ejecutable Bank.msi:
http://groupofcompany.website/don/bank.msi
El archivo descargado contiene el icono siguiente.
Tras revisar la cabecera del ejecutable con 4n4ldetector se identifica la siguiente información de interés:
Information:
Size: 888,00 KB
md5 Hash: E1FFE06955DBC8505F6908C90BCE417F
MajorOSVersion: 4
CheckSum: 0
EntryPoint (rva): 780F8
SizeOfHeaders: 400
SizeOfImage: E5000
ImageBase: 400000
Characteristics: 818E
TimeDateStamp: 2A103240
Date: 12/05/1992 21:01:52
Architecture: x86
File Type: EXE
Number Of Sections: 8
Number Of Executable Sections: 1
Section Names: CODE, DATA, BSS, .idata, .tls, .rdata, .reloc, .rsrc
Subsystem: Windows GUI
Compiler: Borland Delphi 7
El lenguaje de compilación utilizado para esta muestra es Borland Delphi 7, el cual no es fácilmente decompilable.
Una vez se inicializa el programa, este comienza llamando a una serie de funciones anidadas hasta llegar finalmente a una función que recorre un array con múltiples funciones. De estas, muy pocas son importantes. El resto sirve básicamente para complicar el análisis estático del código.
La penúltima función que se encuentra dentro del array se encarga de la carga y descifrado del shellcode que utiliza el empaquetador para descifrar el código protegido, cargarlo en memoria y ejecutarlo. Este presenta una condición, y es que el año del equipo sea superior o igual a 2017 y que haya diferente actividad del usuario en la máquina infectada con la llamada a la función GetLasInputInfo, que devuelve el momento de la última actividad en el equipo.
Dentro de esta misma función también se encuentran múltiples bucles y llamadas a funciones que, además de no tener ninguna utilidad, complican el análisis de código. Asimismo, se realiza una reserva de memoria que se rellena completa con el mismo valor y que, finalmente, se libera.
Una vez se cumplen las condiciones anteriormente descritas, llama a la función encargada de cambiar los permisos de la zona de memoria para que se pueda ejecutar y comienza con el descifrado del shellcode con una clave XOR que se encuentra escrita dentro del código.
Cuando finaliza el descifrado, termina la ejecución con la llamada a una función que modifica la dirección de la función de retorno para que, cuando finalice, ejecute el shellcode descifrado.
Realizar un análisis estático del código resulta imposible debido a la complejidad del shellcode. El grafo que tiene el código es el siguiente:
Por tanto, este análisis se centrará en cómo realiza el descifrado del código que descarga.
Dentro del ejecutable se pueden observar múltiples recursos con contenido con una alta entropía.
También se observa un recurso en una clase de recurso desconocido con el nombre 17 y con ID 1000.
El shellcode accede primeramente a este recurso más pequeño para obtener la clave con la que realizará el descifrado de la carga, la cual se encuentra almacenada dentro del resto de recursos con alta entropía.
Para realizar la carga de cualquiera de los recursos a los que va a acceder, llama la función sub_6860. Poniendo un punto de parada en dicha función se puede saber a qué información de los recursos quiere acceder. Antes de esta función, realiza la búsqueda del recurso, del tamaño de este, y hace una reserva de memoria con VirtualAlloc para almacenar ahí el recurso.
La primera vez que el shellcode llama a esta función es para cargar el recurso, donde se encuentra almacenada la configuración para el descifrado de ejecutable almacenado en él. Esta configuración se encuentra cifrada con una clave XOR de 16 Bytes, que son los primeros de este mismo recurso.
Se puede observar que al final del recurso se repite esta misma clave varias veces. Esto se debe a que el contenido de esta zona, una vez descifrada, es todo ceros.
Para descifrar los recursos hace uso de la función sub_3C8F. Estableciendo un punto de interrupción en esta función se puede ver el resultado del descifrado de cada uno de ellos.
La función recibe la clave, el tamaño (que debe ser de 16) y el contenido que debe descifrar. El descifrado comienza con el segundo carácter de la clave y la recorre de forma circular, es decir, una vez llega al último carácter vuelve a tomar el primer valor de esta y sigue con el descifrado. El valor descifrado sobrescribe al valor cifrado, por lo que no requiere de una reserva de memoria previa.
Una vez se descifra la configuración, el contenido de este proporciona al loader la información para realizar el descifrado. A continuación, se pasan a describir los parámetros que aparecen dentro de esta configuración:
Parámetro |
Valor |
Posición |
Tipo de variable |
Número de recursos |
0x24 |
0x08 |
Byte |
Número de bytes de contenido antes del salto |
0x38 |
0x0E |
Byte |
Identificador del primer recurso |
0x0168 |
0x1E |
WORD |
Número de bytes que se deben saltar |
0x06 |
0x28 |
Byte |
Clave de cifrado |
6B63767927AB62A615C4B0382C58AC5C |
0x38 |
Array(16) |
Tamaño de la carga en bytes |
0x041000 |
0x48 |
DWORD |
Tipo de recurso |
0x01 |
0x54 |
Byte |
Con estos parámetros ya extraídos, el loader comienza reservando una zona de memoria donde entren todos los recursos. Después, sigue con la búsqueda del ID del primer recurso, con la llamada a FindResourceW, y lo almacena empleando el mismo método que utilizó para extraer la configuración. Esta operación la repite con cada uno de los siguientes recursos hasta llegar al “número de recursos”.
Y, llegados a este punto, como ya os hemos contado muchas cosas 😉, vamos a dar por concluida la Parte I del análisis de este stealer. El próximo y último post sobre este troyano estará enfocado principalmente a la fase de descifrado. ¡Hasta entonces!