Ragnarok Stopper: Desarrollo de una vacuna
El campo de la ingeniería inversa, específicamente el análisis de malware, dentro del proceso de Respuesta a incidentes tiene especial relevancia. Más allá del análisis de registros, eventos, conexiones de red, alertas generadas por IDS y firewalls, etc., la experiencia nos dice que un análisis preliminar lo suficientemente temprano de un binario sospechoso (siempre y cuando sea posible) puede ofrecer inteligencia de alto valor no solo para obtener mayor contexto sobre un incidente (TTPs, C2, persistencia, timeline de los hechos, etc.,) sino para desarrollar herramientas o técnicas para mitigar una campaña aún en curso. El siguiente caso es un ejemplo de ello.
En un incidente reciente, obtuvimos un binario empaquetado con Ragnarok, un malware ampliamente utilizado en los últimos meses en diversas campañas. El propósito de este post, es describir el desarrollo de una posible vacuna que pueda detener la amenaza de algunas de las muestras que hemos analizado hasta el momento.
Desarrollando la vacuna
Justo antes de que empiecen las acciones dañinas (básicamente el cifrado de ficheros mediante RSA 2048 + AES) el ransomware genera un identificador único de la máquina en base a los siguientes valores:
- MachineGUID
- Product Name
- Username
- Hostname
- Una cadena formada por los 4 valores anteriores
Para cada uno de estos strings, se genera un ID de 8 bytes, que posteriormente son concatenados con un ‘-‘. El resultado obtenido (un string con la siguiente forma: XXXXXXXX- XXXXXXXX- XXXXXXXX- XXXXXXXX- XXXXXXXX) es utilizado para crear un Event Object por medio de la API CreateEventW
que le permitirá comprobar si ya existe una instancia de Ragnar en ejecución.
Este tipo de acciones es muy común en malware para garantizar la ejecución de un solo proceso, generalmente con objetos Mutex y Event. Lo interesante es que estas comprobaciones son muy útiles para crear vacunas preventivas que eviten la ejecución de malware. La siguiente captura de pantalla muestra la lógica descrita anteriormente.
En este caso, obsérvese que una vez se genera el identificador éste se utilizado para crear el Event Object con nombre. Si el identificador devuelto pertenece a un objeto existente (GetLastError() == ERROR_ALREADY_EXISTS
) lo reintenta un total de 0x8000 veces. Si este contador alcanza dicho valor, se invoca TerminateProcess()
, evitando así las acciones destructivas del ransomware.
Con esta información, crear una vacuna que fuerce la finalización del ransomware sería trivial. Únicamente es necesario crear un objeto de este tipo cuyo nombre siga el formato utilizado por Ragnarok. La siguiente función muestra la lógica utilizada para generar el ID de cada uno de los valores que componen el identificador de la máquina.
Una versión más limpia de la función GenerateID
se muestra a continuación:
#define __ROL__(x, y) _rotl(x, y) inline unsigned int __ROL4__(unsigned int value, int count) { return __ROL__((unsigned int)value, count); } inline unsigned int __ROR4__(unsigned int value, int count) { return __ROL__((unsigned int)value, -count); } LPWSTR GenerateID(LPCWSTR lpString) { unsigned int inc; int len, i, acu; LPWSTR codeID; inc = 0; codeID = (LPWSTR)VirtualAlloc(0, 0x7Fu, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); len = lstrlenW(lpString); for (i = 0; i < len; inc = (acu ^ 0xAB01FF3C) + inc - __ROL4__((acu ^ 0xAB01FF3C) + inc, 13)) acu = lpString[i++]; wsprintfW(codeID, L"%08X", inc); return codeID; }
El código completo de esta PoC (Ragnarok Stopper) se puede encontrar en nuestro repositorio. Es importante tener en cuenta que dicho código es una vacuna que aplica a algunas de las muestras que hemos analizado hasta el momento. Las nuevas variantes de Ragnarok pueden usar otros criterios para identificar una instancia en ejecución.