Skip to main content
Análisis forense con volatility

Cazando malware con Volatility

Volatility es una herramienta forense de código abierto para la respuesta a incidentes y el análisis de malware. Está escrito en Python y es compatible con Microsoft Windows, Mac OS X y Linux.

Bienvenidos a un nuevo post en ByteMind. En el caso de hoy vamos a explicar diferentes formas de identificar y cazar malware con volatility a partir de un dump previamente creado. Esta entrada será la continuación del anterior post Análisis forense con volatility donde explicamos qué es Volatility, cómo instalarlo, su funcionamiento, así como diferentes comandos con los que identificar una imagen, procesos, etc.

Aunque todos los comandos de Volatility pueden ayudar en la búsqueda y caza de malware de una forma u otra, hay algunos específicos para según que queremos buscar o que pueden hacer más fácil la búsqueda de determinados elementos como pueden ser por ejemplo rootkits o códigos maliciosos. Para más información se puede consultar la web oficial de volatility.

A continuación vamos a ver una serie de comandos que nos ayudarán en la búsqueda de malware.

 

malfind

El comando malfind ayuda en la búsqueda de códigos/DLLs ocultos o inyectados en la memoria del usuario, en función de caracterísitcas como la etiqueta VAD y los permisos de página.

Cabe destacar que malfind no detecta DLLs inyectadas en un proceso mediante CreateRemoteThread-> LoadLibrary. Las DLLs inyectadas con dicha técnica no están ocultas y, por lo tanto, pueden verse con el comand dlllist. El propósito de malfind es localizar archivos DLLs que los métodos o herramientas estándar no son capaces de obtener.

A continuación veremos un ejemplo de como utilizarlo para detectar en este caso la presencia de Zeus, para quien no lo sepa, Zeus es un malware troyano que se ejecuta en sistemas windows con la intención de obtener datos bancarios mediante el registro de teclas y de formularios del navegador.

A continuación el ejemplo y la posterior explicación:

En el ejemplo anterior podemos observar varias partes.

El primer segmento de memoria (que comienza en 0x01600000) se detecta porque es ejecutable, marcado como privado, es decir, que no está compartido entre procesos, y dispone de una etiqueta VadS, lo que significa que no hay ningún archivo de memoria mapeado que esté utilizando dicho espacio. Basándonos en el desensamblado de los datos encontrados parece contener algunos trozos de API hook trampoline.

El segundo segmento de memoria (0x015D0000) se detecta porque contiene un ejecutable que no figura en las listas de módulos del PEB.

Además de la propia salida del comando, si se quieren almacenar los segmentos de memoria identificados se puede proporcionar un directorio de salida, previamente creado, con la opción -D o –dump-dir. En este caso, una copia sin empaquetar del binario malicioso Zeus será almacenada en el directorio especificado.

 

yarascan

El comando yarascan puede ayudar en búsquedas complejas y específicas. Puede ayudar en la búsqueda, mediante expresiones regulares, de cualquier secuencia de bytes, cadenas ASCII o cadenas Unicode en modo de usuario o en la memoria del Kernel.

Puede crear un fichero de reglas YARA y especificarlo con la opción –yara-file o, si sólo está buscando algo simple, es posible especificar los criterios como –yara-rules=RULESTEXT.

Para buscar firmas definidas en el archivo rules.yar y mostrar los resultados en pantalla podemos utilizar el siguiente comando:

En el caso de querer buscar una cadena simple en cualquier proceso sería con:

Y en caso de querer volcar los datos obtenidos a fichero utilizaríamos la opción -D folder o –dump-dir=folder

Es posible también realizar la búsqueda de un patrón de bytes dado para un proceso en particular:

O mediante el uso de expresiones regulares:

Para realizar una búsqueda en la memoria del kernel añadiríamos la opción -K al mismo como en el siguiente ejemplo:

 

svcscan

Para ver que servicios están registrados en la imagen de memoria proporcionada existe el comando svcscan. El resultado muestra el ID del proceso de cada servicio (si está activo y si pertenece a un proceso del usuario), el nombre original del servicio, el nombre para mostrar del servicio, el tipo y el estado actual del mismo. Muestra también la ruta binaria para el servicio registrado, que será un EXE para servicios de usuario y el nombre del controlador para aquellos servicios que se ejecutan desde el kernel.

Podemos ver un ejemplo a continuación:

Desde la versión 2.3 de Volatility, existe la opción verbose que verifica la clave de registro de ServiceDLL e informa del DLL que aloja el servicio. Esta capacidad es crítica ya que el malware comúnmente se instala utilizando svchost.exe (proceso de windows de servicio de host compartido) e implementa el código malicioso real en una DLL.

Podemos ver un ejemplo a continuación:

 

ldrmodules

El comando ldrmodules es muy útil a la hora de buscar DLLs ocultas. Por ejemplo, la desvinculación de una DLL de las listas vinculadas en el PEB (Process Environment Block), sin embargo todavía existe información contenida dentro del VAD (Virtual Address Descriptor) que identifica la dirección base y la ruta completa en el disco. Dado el caso es posible hacer una búsqueda con las listas PEB y hacer una referencia cruzada (conocida como archivos mapeados en memoria) con este módulo.

Para cada archivo PE mapeado en memoria, el comando ldrmodules imprime Verdadero o Falso si el PE existe en las listas PEB.

A continuación podemos verlo con un ejemplo:

Dado que estas listas existen también para el usuario, es posible que el malware oculta DLL simplemente modificando la ruta que muestran. En este caso la herramienta sólo buscaría entradas no vinculadas y pueden pasar por alto el hecho de que pudiese haber sido sobreescrita su ruta, por ejemplo C:\malware.dll y mostrar C:\\windows\system32\kernel32.dll. Para analizar más a fondo la imagen también puede pasar la opción -v o –verbose a ldrmodules para ver la ruta completa de estas entradas.

 

impscan

En el caso de necesitar invertir por completo el código que se encuentra en los volcados de memoria es necesario ver que funciones importa el código, en otras palabras, a que funciones llama de la API. Cuando se realiza un volcado de binarios con dlldump, moddump o procdump, es posible que la IAT (Import Address Table) no se reconstruya correctamente debido a la probabilidad de que una o más páginas del encabezado PE o IAT no residan en la memoria (paginado). Para esto esta el módulo impscan, el cual identifica las llamadas a la API sin analizar el IAT de un PE, y podría funcionar incluso si el malware borrase por completo el encabezado PE.

A continuación veremos un ejemplo con el malware Coreflood, que elimina su encabezado PE una vez que se carga en el proceso destino.

Podemos utilizar malfind para detectar la presencia de este malware según los criterios típicos. Observa en el siguiente ejemplo como la dirección base del PE no contiene el encabezado MZ habitual:

Supongamos ahora que se quiere extraer la copia desempaquetada de Coreflood y ver las APIs importadas. Utilizamos impscan especificando la dirección base que se obtuvo con malfind. En este caso añadimos la dirección base en 0x7ff81000 para tener en cuenta la página que falta:

Si no se especifica una dirección base con la opción -b o –base, acabará escaneando el módulo principal del proceso para las funciones importadas. También es posible especificar la dirección base de un controlador del kernel para escanear dicho controlados en busca de funciones importadas en el mismo.

En el caso del malware laqma carga un controlador del kernel llamado lanmandrv.sys. Es posible extraerlo con moddump, pero el IAT estará dañado. Utilizaremos impscan entonces para poder reconstruirlo:

 

apihooks

El módulo apihooks es muy útil para buscar enlaces API para el usuario o el kernel. Esto encuentra IAT, EAT, hooks en línea y varios tipos especiales de hooks. Para los hooks en línea detecta CALL y JMP en ubicaciones tando directas como indirectas, y detecta secuencias de instrucciones PUSH/RET. También es posible detectar CALL o JMP en los registros posteriores a que un valor inmediato se traslade al registro.

Los tipos de hooks especiales que detecta incluyen el hook syscall en ntdll.dll y llamas a páginas de código desconocidas en la memoria del kernel.

A continuación vamos a ver un ejemplo de detección de hooks IAT instalados por el malware Coreflood. El módulo de hooks es desconocido porque no hay ningún DLL asociado con la memoria en la que existe el código del rootkit. Para extrar el código que contiene los hookes hay varias opciones:

  • Comprobar con el módulo malfind si podemos encontrarlo y extraerlo
  • Utilizar los comandos volshell dd/db para escanear hacia atrás y buscar un encabezado MZ. Posteriormente pasar esa dirección con el valor base a dlldump para extraerlo
  • Utilizar vaddump para extraer todos los segmentos de código en fichero individuales y posteriormente buscar en el archivo los rangos sospechosos.

 

idt

Utilizaremos el comando idt para imprimir el IDT (Interrupt Descriptor Table) del sistema. Si existen varios procesadores se mostrará el IDT para cada CPU individual. El módulo mostrará el número de CPU, el selector GDT, la dirección actual, el módulo propietario y el nombre de la sección PE en la que reside la función IDT. Es posible añadir el parámetro -v o –verbose para mostrar más información acerca de la función IDT.

Algunos rootkits enganchan la entrada IDT para KiSystemService, pero la apuntan a una rutina dentro del módulo NT (donde debería apuntar KiSystemService). Sin embargo, en dicha dirección, hay un enlace al malware. A continuación se muestra un ejemplo de como identificar esto.

Si observamos la entrada 0x2E de KiSystemService está en la sección .rsrc en lugar de .text como el resto.

Podemos ver también más información utilizando la opción verbose:

 

gdt

Utilizaremos el comando gdt para imprimir el GDT (Global Descriptor Table) del sistema. Esto es muy útil para detectar ciertos rootkits que instalan una puerta de llamada para que los programas del usuario puedan llamar directamente al kernel utilizando una instrucción CALL FAR. Si el sistema consta de varias CPUs mostrará el GDT de cada procesador.

A continuación podemos ver un ejemplo en el que el selector 0x3e0 ha sido infectado y utilizado con el propósito de crear una puerta de 32 bits. La dirección de la puerta es 0x8003f000, que es donde continúa la ejecución:

Si se quiere investigar más a fondo la infección, se puede dividir en una volshell como se muestra a continuación que desmontará el código en la dirección de la puerta de llamada.

 

threads

El comando threads brinda detalles acerca de los subprocesos, incluido el contenido de los registros de cada uno de ellos (si está disponible), un desmontaje del código en la dirección de inicio del mismo y varios campos que pueden ser relevantes en una investigación. Dado que cualquier sistema dispone de cientos de hilos, este comando asocia etiquetas descriptivas a cada hilo que encuentra, con los que posteriormente se podrá filtrar mediante el parámetro -F o –filter.

Puede verse una lista de las etiquetas/filtros disponibles con la opción -L como vemos en el siguiente ejemplo:

Si no se especifica ningún filtro, el comando generará información de todos los hilos existentes. Pero puede especificar uno o varios filtros separados por comas.

A continuación se aprecia un ejemplo de búsqueda de subprocesos que se están ejecutando actualmente en el contexto de un proceso diferente del que posee el subproceso:

De primeras, se ve la dirección virtual del objeto ETHREAD junto con el ID del proceso y del hilo. Posteriormente se verán todas las etiquetas asociadas al hilo (SystemThread, AttachedProcess, HookedSSDT), los tiempos de creación/salida, estado, prioridad, etc. Se muestra la base SSDT junto con la dirección de cada tabla de servicio y cualquier función enganchada a las mismas. Finalmente se verá el desensamblado de la dirección de inicio del hilo.

 

callbacks

Volatility es una plataforma forense con la capacidad de imprimir una variedad de rutinas de notificación importantes y devoluciones de llamadas del núcleo. Es capaz de detectar por ejemplo, rootkits, conjuntos de antivirus, herramientas de análisis dinámico (como SysInternals Process Monitor o TcpView) y muchos componentes del uso del kernel de windows para monitorizar y/o reaccionar ante diferentes eventos.

Es capaz de detectar lo siguiente:

  • PsSetCreateProcessNotifyRoutine (creación del proceso).
  • PsSetCreateThreadNotifyRoutine (creación de subprocesos).
  • PsSetImageLoadNotifyRoutine (DLL/carga de imagen).
  • IoRegisterFsRegistrationChange (registro del sistema de archivos).
  • KeRegisterBugCheck y KeRegisterBugCheckReasonCallback.
  • CmRegisterCallback (devoluciones de llamada de registro en XP).
  • CmRegisterCallbackEx (devoluciones de llamadas de registro en Vista y 7).
  • IoRegisterShutdownNotification (devoluciones de llamada de apagado).
  • DbgSetDebugPrintCallback (debug print callbacks en Vista y 7).
  • DbgkLkmdRegisterCallback (depurar devoluciones de llamada en 7).

A continuación se muestra un ejemplo de como detectar la llamada de creación de subprocesos por parte del malware BlackEnergy2. Es posible detectar la devolución de dicha llamada maliciosa porque el propietario es 00004A2A y BlackEnergy2 utiliza un nombre de módulo compuesto por ocho caracteres hexadecimales:

A continuación vemos otro ejemplo en el que se detecta la devolución de llamada de la creación de procesos maliciosos por el rootkit Rustock, la cual apunta a la memoria de \Driver\pe386.

 

driverirp

Utilizaremos el comando driveirp para imprimir la tabla IRP de un controlador. Este comando hereda de driverscan para poder localizar DRIVER_OBJECTS. Posteriormente se desplaza por la tabla de funciones, imprimiendo el propósito de cada una, la dirección y el módulo propietario de la dirección.

En el caso de volatility se imprime toda la información existente, debido a que muchos controladores envían sus funciones IRP a otro controladores con fines legítimos y es el investigador quien tiene que decidir si existe alguna función maliciosa o no. El comando verifica también los hooks en la línea de funciones IRP y, opcionalmente, imprime un desensamblado de las instrucciones en la dirección IRP con la opción -v o –verbose.

A menos que se especifique una expresión regular para el filtrado, el comando mostrará la información existente de los controladores:

En la salida anterior, no es aparente que el driver vmscsi.sys haya sido infectado por el rootkit TDL3. Aunque todos los IRP apuntan de nuevo a vmscsi.sys, apuntan a un trozo organizado en esa misma región por TDL3 con el propósito de evitar a las herramientas de detección de rootkits. Para más información, utilizamos la opción verbose:

Ahora sí se puede ver claramente que TDL3 redirige todos los IRP a su propio código auxiliar en el controlador vmscsi.sys. Dicho código salta a cualquier dirección señalada por 0xffdf0308, que se trata de una ubicación en la región KUSER_SHARED_DATA.

 

devicetree

El módulo devicetree muestra la relación de un objeto controlador con sus dispositivos y cualquier dispositivo conectado. Los rootkits a menudo insertan controladores con fines de filtrado.

A continuación se muestra un ejemplo, donde Stuxnet ha infectado \\FileSystem\\NTFS al conectar un dispositivo malicioso que carece de nombre. Aunque en sí no disponga dle nombre, el objeto del dispositivo identifica a su controlador \\Driver\\MRxNet

El módulo devicetree utiliza DRV para indicar los controladores, DEV para indicar a los dispositivos y ATT para indicar los dispositivos conectados.

En sitemas x64 observamos una salida del comando muy similar a la anterior:

 

psxview

El módulo psxview ayuda en la detección de procesos ocultos comparando con el contenido de PsActiveProcessHead. Entre sus comparaciones observamos las siguientes:

  • Lista vinculada PsActiveProcessHead
  • Escaneo de grupo EPROCESS
  • Escaneo de grupo ETHREAD (luego hace referencia al EPROCESS propietario)
  • PspCidTable
  • Tabla de identificadores de csrss.exe
  • Lista enlazada interna csrss.exe

A partir de Windows Vista, la lista interna de procesos de csrss.exe no está disponible.

A continuación vemos un ejemplo de detección de malware Prolaco con psxview. Un valor False en cualquier columna indicaría que falta el proceso respectivo:

A continuación vemos lo mismo en sistemas x64 donde la salida es muy similar:

 

timers

El comando timers imprime los temporizados instalados del kernel (KTIMER) y cualquier DPC (Deferred procedure calls) asociado. Algunos rootkits como Zero Acess, Stuxnet o Rustock registran temporizadores con un DPC. Aunque el malware intenta ser sigiloso y esconderse en un espacio del kernel de diferentes formas, encontrando los KTIMER y mirando la dirección del DPC es posible encontrar los rangos del código malicioso.

A continuación veremos un ejemplo donde uno de los temporizadores tiene un módulo desconocido, es decir, el DPC apunta a una región desconocida de la memoria del núcleo, y donde se esconde el rootkit:

Cabe destacar que los temporizadores se enumeran de distinta forma según el sistema operativo. En el caso de Windows XP, 2003, 2008 y Vista almacena los temporales en variables globales, mientras que desde Windows 7, se almacenan en regiones específicas del procesador fuera de KPCR (Kernel Processor Control Region). Además, si hay varias CPU, este módulo encontrará todos los KPCR e imprimirá los temporizadores asociados con cada CPU.

 

Plugins extras

En esta sección se indicarán plugins extra que por defecto no vienen en volatility y que son muy útiles en la búsqueda de malware.

Para instalar un plugin nuevo, una vez descargado el mismo, podemos cargarlo de la siguiente forma:

 

volatility-autoruns

Volatility-Autoruns es un módulo extra que por defecto no viene incluido y cuyo principal fin es encontrar puntos de persistencia de malware. Autoruns automatiza la mayoría de las tareas que se necesitarían ejecutar para averiguar cual es el método de persistencia del malware. Uno de los métodos de persistencia más comunes y para el que es muy útil este plugin es la carga del malware en el arranque del sistema.

Para instalar autorun deberemos de descargarnos el código desde github en el siguiente enlace.

O podemos descargar el repositorio con el comando:

 

Una vez descargado deberemos referenciarlo con la opción –plugins para cargar el mismo como vemos en el siguiente ejemplo de un ctf que hice hace algún tiempo:

 

 

Esto ha sido todo por el momento, espero les sea de utilidad en su día a día o si realizan algún ctf relacionado con la búsqueda de malware. Nos vemos en el siguiente post!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *