Resumen - CVE-2024-55956
Huntress reportó anteriormente actividad maliciosa derivada de la explotación de una vulnerabilidad de día cero en el software Cleo. El malware entregado a través de esta explotación ya ha sido analizado, y este artículo incluye un desglose técnico de una nueva familia de malware que hemos llamado Malichus. El nombre es un juego de palabras con Cleopatra y proviene de Malichus I, quien, según se registra, quemó la flota naval de Cleopatra en venganza por las pérdidas sufridas durante una guerra que Cleopatra había iniciado.
Análisis técnico
Etapa 1: Descargador de PowerShell
El malware utiliza un pequeño cargador de PowerShell que prepara el host para una explotación posterior. Se almacena como un blob en base64, se decodifica y se usa para ejecutar un archivo Java que se despliega en el sistema con el nombre cleo.[identificador-numérico].
El cargador crea una conexión TCP a una dirección IP de C2 para obtener una carga útil de segunda etapa. El cargador formateado se muestra a continuación:
El cargador también define una variable llamada Query que se usa para obtener la dirección de C2 utilizada en una puerta trasera Java posterior y el identificador de dirección IP de la víctima. La dirección IP de C2 y el dropper de segunda etapa son diferentes para cada carga útil identificada por Huntress.
Etapa 2: Descargador Java
El archivo de segunda etapa descargado es descifrado por el cargador usando una clave AES única por carga útil. Al descifrar y descompilar el descargador de segunda etapa, se encontró un archivo manifest que indicaba que ejecutaría la clase start al iniciarse.
A partir del método main, la puerta trasera obtiene la variable de entorno query y la repara para convertirla en una cadena base64 válida. Ese valor decodificado contiene la clave AES, un valor único necesario para descargar la etapa 3, y las IPs del servidor de C2 y del host víctima. Luego se envía una solicitud al C2 usando ese valor codificado en base64, añadido con TLS v3.
Una receta de CyberChef que muestra el resultado de esto es la siguiente:
Usando el espacio asignado que hemos renombrado como recvBuf, el dropper continúa aceptando bytes hasta que se ha descargado por completo la carga útil de la etapa 3. Luego, usando la clave AES y el IV fijo, descifra los datos descargados, lo que produce un zip intencionalmente corrupto. Luego reparan el encabezado cortando los primeros dos bytes (cC en el ejemplo que analizamos), y proceden a la extracción y carga.
Una receta de CyberChef que muestra el resultado de esto es la siguiente:
Finalmente, la puerta trasera descomprime el archivo descargado usando el método auxiliar proporcionado, y resuelve dinámicamente las clases que contiene.
Etapa 3: Puerta trasera Java / Marco de post-explotación
La etapa final es un marco de post-explotación modular basado en Java que contiene una cantidad significativa de funcionalidad. Se compone de 9 archivos de clase, siendo el controlador principal la clase Cli cargada por la etapa anterior. El marco es compatible con Linux y Windows, aunque Huntress solo observó su uso en Windows.
Cli
Esto comienza con una clase pública llamada Cli que recibe tres parámetros pasados desde el descargador de la etapa 2, almacenados en las variables host, cliid, y stage1fn.
Los parámetros pasados desde la etapa 2 corresponden a:
- La dirección IP de C2 obtenida del parámetro query
- La dirección IP del sistema explotado y el puerto asociado obtenido del parámetro query (usado como identificador único de víctima)
- El nombre de archivo almacenado en la variable de entorno f del descargador (en este caso cleo.2607)
La clase tiene varias variables estáticas definidas que se usan a lo largo de la carga útil, incluyendo una que identifica si está operando en un sistema operativo Windows o no.
Esto ejecuta el método principal run() y el posterior método runDelFileCmd() para eliminar del disco la carga útil de la primera etapa (el descargador) (cleo.2607) usando PowerShell o Bash según si el sistema fue identificado como Windows o no.
El método run se encarga de usar la clase SrvSlot para encolar conexiones a la dirección IP de C2 obtenida del parámetro query anterior en el puerto 443. Esta clase extiende la clase Slot, lo que le permite acceder a la variable estática st, usada para enviar comandos personalizados al implante desde el servidor de C2 conectado, y forma parte de un protocolo de C2 personalizado. Estas conexiones continuarán hasta que el número de conexiones sea 5 o más, y la variable st se establezca en algo distinto de 200 o 500.
Esta clase también contiene dos métodos auxiliares: l (registro) y dmp (volcado hexadecimal). El primero se usa en todo el implante para registrar varios datos en un búfer de registro. El segundo permite al malware crear un volcado hexadecimal válido a partir de un arreglo de bytes proporcionado, el cual luego se registra usando la función auxiliar de registro. La función de volcado hexadecimal probablemente sea para fines de depuración, ya que no se llama en ninguna parte del implante.
Dwn
Una clase de gestión de archivos zip para subir, empaquetar y manejar archivos zip para que los operadores lean y recopilen archivos o directorios. Se usan varias variables en esta clase, además de un método llamado Dwn encargado de registrar cuánto tiempo ha pasado desde la última actualización y asignar una cola de archivos para subir al servidor de C2, almacenada en un arraylist llamado lvs.
La clase contiene un método tick usado para llevar el control del tiempo transcurrido y enviar una actualización de estado al servidor de C2 si han pasado más de 5 segundos desde la última actualización.
El método setStat, llamado por tick, establece campos del paquete tick recién creado con información sobre el estado actual de la clase Dwn.
Curiosamente, esta clase también tiene una verificación llamada tick2 encargada de rastrear el estado de los archivos que se están exfiltrando, y de gestionar la cola de archivos a exfiltrar.
El método addFile se usa para agregar archivos a la cola para exfiltración.
Esto se complementa con los métodos readFile y write para recuperar el archivo del disco.
Otros métodos zipOpen, zipClose, y getCurrZipSize usan una nueva instancia de la clase Mos, pero aparte de eso son bastante autoexplicativos.
DwnLevel
Una clase simple para gestionar un arreglo de archivos en preparación para el archivado zip y la subida al atacante. Se usa principalmente en la clase Dwn y sirve solo para mantener el estado.
Mos
Una pequeña clase para manejar archivos zip de múltiples partes. Para optimizar el envío de archivos zip, estos se dividen cada 262154 bytes y luego se archivan con ZipIDs durante la exfiltración.
Esto parece equivaler a 2 de las longitudes de paquete de su protocolo personalizado, de 131072 bytes.
Proc
Proc contiene implementaciones de las tareas que el malware puede realizar, así como algunas capacidades de reconocimiento en la máquina de la víctima. Estos comandos se analizan en la clase SlotSrv.
La clase Proc permite a los atacantes remotos principalmente emitir comandos de ejecución y leer archivos de configuración usando la función run. El comando ishell (shell interactiva) utiliza la función pipeMode para manejar shells interactivas, lo que indica que el malware tiene la capacidad de permitir que un atacante actúe de forma manual («hands-on-keyboard») y ejecute comandos interactivamente a través de una terminal.
Esto coincide con la actividad identificada por el Huntress SOC cuando se observó que un actor de amenazas ejecutó interactivamente un comando nltest. La función run también es el método principal usado para obtener la configuración del software Cleo mediante el comando prs-conf.
El método confParser recupera elementos de un archivo de configuración ubicado en conf/Top.xml en disco y obtiene todos los valores de host dentro de este. Para cada host identificado, intentará obtener el archivo xml correspondiente dentro de hosts/<hostname.xml> y analizarlo para usarlo en una función loadUserDirs.
La función loadUserDirs se usa para analizar cualquier nodo Host o Mailbox dentro del archivo xml del host y extraer los alias definidos en este archivo. Además, también analiza el archivo en busca de referencias a varios directorios, que se envían junto con el alias con el que están emparejados.
- Directorio de inicio predeterminado
- Ruta raíz de FTP
- Bandeja de entrada
- Bandeja de enviados
- Bandeja de salida
Esto incluye un método para reemplazar cualquier variable personalizada llamada Homedirectory y Homedirectorytype que pudiera haberse definido en la instancia de la aplicación, con su valor correspondiente.
Esto demuestra que el autor del malware tenía conocimiento de los archivos de configuración específicos usados dentro del software Cleo, y estaba interesado en entender qué tipos de relaciones comerciales se habían configurado en una instancia de Cleo Harmony, VLTrader o LexiCom, además de dónde residían en disco los archivos de carga útil, como archivos enviados o recibidos entre estas empresas.
Curiosamente, esto contenía un método llamado checkDir que parece verificar si un directorio es legible o no; sin embargo, nunca se llamó en el código, por lo que pudo haberse usado con fines de depuración o se trata de una característica aún no implementada.
SFile
Esta clase permite a los atacantes realizar operaciones básicas de lectura y escritura en el sistema de archivos usando las clases FileOutputStream y FileInputStream. Esto se referencia en los procedimientos de manejo de archivos dentro de la clase SvrSlot.
También hay varias funciones auxiliares que ayudan a SFile a leer, escribir y gestionar archivos:
ScSlot
Este es un enlace de comunicación de múltiples canales, con el canal usado para manejar comunicación asíncrona; cada ScSlot actúa como una canalización única de datos usando la clase SvrSlot.
Slot
Esta clase se encarga de establecer y gestionar la conexión con el servidor de C2. Inicializa dos búferes para enviar y recibir datos:
SrvSlot
Esta clase se ocupa principalmente de recibir y enviar respuestas del servidor de C2. Al conectarse a un servidor de C2, esta clase registra el número de paquete usando el método l dentro de la clase Cli con fines de depuración, antes de establecer st en 2, indicando que la conexión se estableció correctamente y estaba lista para recibir un paquete hello. Esto sobrescribe un método con el mismo nombre en Slot usado para determinar si la conexión se ha establecido o no.
El paquete hello enviado al C2 tiene un encabezado de 16 bytes, cuya estructura es la siguiente:
| Desplazamiento de byte | Valor |
|---|---|
| 0 - 9 | Math.random() * 256.0D |
| 10 | 2 |
| 11 | 119 |
| 12 | 33 |
| 13 | -1 |
| 14 | 1 |
| 15 | Cli.fIsWin ? 0 : 1 |
Un detalle interesante a notar es los 10 bytes basura que se añaden al frente del paquete. Esto probablemente sea un intento de evadir técnicas de huella digital de red, aunque los demás bytes del paquete son estáticos, lo que hace que eso sea relativamente trivial.
El primer paquete enviado al C2 por el malware es un paquete “hello” que contiene un identificador emparejado con un nombre de host si este se obtuvo previamente en Cli (cliid + “\t” + hostname), además de la sincronización requerida para cifrar paquetes posteriores.
Después de enviar ese paquete hello, el implante espera una respuesta hello del servidor, que se analiza en el método prsHelloPkt.
SvrSlot también contiene una opción para registrar estadísticas de cada endpoint comprometido, que parece usarse para el seguimiento de salud de su propio C2 y endpoint.
Hay tres opciones de depuración usadas para recopilar información; el comando principal es #dbg#, que recopila información sobre cuántos errores de archivos zip ha habido, cuántas veces se encontró un estado de cola del servidor, y la disponibilidad de memoria libre en el host.
El comando #ll# muestra cuántos archivos se han añadido a los zips que referencian la clase Dwn mencionada anteriormente.
El comando #lsz# recopila información sobre los archivos zip, detallando el tamaño del último archivo zip, el ID del zip, el número de archivo zip y el desplazamiento del archivo zip; esto probablemente se usa para depurar el proceso de archivado multi-zip realizado dentro de la clase Mos mencionada anteriormente.
Análisis del protocolo C2
Malichus utiliza un protocolo de C2 totalmente personalizado. Cada paquete entrante y saliente es procesado por la función en SrvSlot llamada pkt0. Esta toma un paquete completo y calcula su valor CRC32 para confirmar la integridad del paquete. Luego, los bytes 3 y 4 se establecen con los bytes altos y bajos de ese valor CRC32 antes de cifrarlo.
Después de que se actualiza el CRC32 del paquete, pkt0 llama a addEncr, encargada de cifrar el paquete. Hay algunas variables globales usadas para gestionar tanto el estado de cifrado como el de descifrado:
El C2 y el cliente deben mantenerse sincronizados para que el cifrado/descifrado funcione, ya que la mutación de las variables de estado e índice se actualiza según el valor del byte previamente cifrado/descifrado.
Existe un método similar para descifrar paquetes, pero usa un conjunto diferente de variables para llevar el control del estado:
Como mencionamos antes, el método pkt0 es el constructor de paquetes final usado por los otros 6 tipos de paquetes, la mayoría de los cuales son métodos sobrecargados del nombre pkt. Cada uno tiene usos ligeramente distintos pero comparten que el primer byte es el identificador de paquete. Hay 4 tipos de paquete genéricos, y 2 especializados. De los paquetes genéricos:
Los dos paquetes especiales utilizados son helloPkt y zipPkt, con el helloPkt ya detallado anteriormente:
Después de que pkt0 crea los paquetes, se colocan en la cola outbuf y se envían al C2 usando el método evWrite de la clase Slot:
Conclusión
Si bien el alcance completo de quién creó este malware y por qué sigue siendo objeto de especulación, este blog busca arrojar luz sobre una nueva familia de malware que apunta al software Cleo, su funcionalidad, y ofrece una posible visión de lo que el autor del malware esperaba lograr con esta pieza de malware personalizada.
Este artículo de blog se creó de forma independiente junto con otros proveedores de la industria, y coincide en gran medida con el análisis realizado también de forma independiente por:
Identificadores de paquete C2 válidos
| Número de paquete | Valor | Explicación |
|---|---|---|
| 1 | Hello | Paquete hello para inicializar conexiones |
| 2 | Channel | Obtener identificador de canal de C2 |
| 3 | Data | Paquete de datos |
| 4 | Prepare File | Prepara un archivo para la subida |
| 5 | Close Channel | Cierra el canal de C2 y cualquier identificador abierto |
| 6 | Echo | Devuelve los datos recibidos al C2 |
| 7 | Halt | Reinicia los búferes de entrada |
| 8 | Command | Ejecuta el comando recibido |
| 9 | Stop | Detiene el proceso, canal o transferencia de archivo |
| 10 | Resume | Reinicia el proceso, canal o transferencia de archivo |
| 11 | Ack | Confirma la sincronización de los comandos procesados |
| 12 | File I/O Status | Confirma que el implante puede recibir o procesar datos zip |
| 13 | Zipdata Reassembly | Reensambla los fragmentos del zip y los elimina de la cola |
IOCs
| Nombre de archivo | SHA256 |
|---|---|
| cleo.2607 | 6705eea898ef1155417361fa71b1078b7aaab61e7597d2a080aa38df4ad87b1c |
| Cli | 0c57b317b572d071afd8ccdb844dd6f117e20f818c6031d7ba8adcbd32be0617 |
| Dwn | 429d24e3f30c7e999033c91f32b108db48d669fde1c3fa62eff9da2697ed078e |
| DwnLevel | f80634ce187ad4834d8f68ac7c93500d9da69ee0a7c964df1ffc8db1b6fff5a9 |
| Mos | 0b7b1b24f85a0107829781b10d08432db260421a7727230f1d3caa854370cb81 |
| Proc | 1ba95af21bac45db43ebf02f87ecedde802c7de4d472f33e74ee0a5b5015a726 |
| SFile | 57ec6d8891c95a259636380f7d8b8f4f8ac209bc245d602bfa9014a4efd2c740 |
| ScSlot | 87f7627e98c27620dd947e8dd60e5a124fdd3bb7c0f5957f0d8f7da6d0f90dee |
| Slot | 1e351bb7f6e105a3eaa1a0840140ae397e0e79c2bdc69d5e1197393fbeefc29b |
| SrvSlot | f4e5a6027b25ede93b10e132d5f861ed7cca1df7e36402978936019930e52a16 |
Reglas YARA
https://github.com/huntresslabs/threat-intel/tree/main/2024/2024-12/Cleo