Monitoreo y análisis con Procmon y rundll32.exe
Relacionado: Forense de memoria de sistema completo. IDOR. Scanning. biblio. Puntero.
Estamos utilizando Procmon (Process Monitor) para examinar la actividad de los procesos en el sistema. En este caso, queremos monitorizar el comportamiento de rundll32.exe, un proceso de Windows comúnmente utilizado para ejecutar funciones desde bibliotecas DLL.
Rundll32 permite ejecutar funciones específicas dentro de una DLL en lugar de tener que invocar un archivo ejecutable completo. Esto es útil para el malware, ya que permite cargar código malicioso en memoria sin necesidad de archivos ejecutables adicionales.
-
Ejecutar el proceso con
rundll32.exe: Para iniciar una función específica de una DLL, utilizamos el comando:Copiar
rundll32.exe nombre_de_programa, nombre_de_funcion -
Filtrar en Procmon: En Procmon, aplicamos un filtro para capturar solo los eventos relacionados con rundll32.exe. Esto nos permite ver todas las interacciones del proceso con el sistema, incluyendo accesos a archivos y cambios en el registro.
-
Quitar el filtro: Si queremos analizar todo el contexto de ejecución de rundll32, podemos eliminar el filtro y ver todas las operaciones relacionadas, como la carga de la DLL o la ejecución de funciones específicas dentro de esta.
-
Filtrar por PID: Si sospechamos que rundll32 puede estar ejecutando otros procesos maliciosos en segundo plano, podemos filtrar por PID (Process ID). Esto ayuda a identificar si el malware está utilizando subprocesos o cargando otros procesos durante su ejecución.
Ganando Persistencia: Modificación de Archivos y Sysinternals Autoruns
El malware a menudo busca garantizar su persistencia en el sistema, es decir, mantener su ejecución incluso después de reinicios o cierres de sesión.
Una de las técnicas utilizadas es modificar archivos en el sistema para que el malware se ejecute automáticamente. En este caso, se accede a un archivo denominado msscevsvre, y el malware comienza a modificarse a sí mismo, lo que puede incluir cambios en el registro de Windows o en los archivos del sistema.
-
Uso de
GetResourceAPI: El malware puede utilizar la API de WindowsGetResourcepara crear un handle a recursos del sistema y así acceder o modificar archivos ejecutables. Al abrir un archivo y escribirlo en otro lugar del sistema, el malware se asegura de que se ejecute en el futuro sin necesidad de intervención del usuario. -
**Uso de Autoruns: Para garantizar que el malware se ejecute cada vez que el sistema inicie o que el usuario se loguee, se puede utilizar la herramienta Autoruns de Sysinternals. Autoruns permite ver todos los puntos de inicio automático, incluidas las tareas programadas y las entradas en el registro de Windows. Es crucial para identificar y eliminar los archivos maliciosos que se configuran para ejecutarse al iniciar el sistema.
Tareas Programadas y Persistencia a Nivel de Kernel
Las tareas programadas en Windows son otro vector importante para la persistencia del malware. Estas tareas pueden configurarse para ejecutarse en momentos específicos, como al iniciar el sistema o cuando se inicia sesión. Además, las tareas programadas se ejecutan a nivel de kernel, lo que proporciona una mayor evasión ante herramientas de monitoreo de nivel de usuario.
-
Ejecución a nivel de kernel: Las tareas programadas tienen la ventaja de ejecutarse a un nivel bajo, lo que hace que sean menos detectables por herramientas estándar de monitoreo. Esto permite que el malware permanezca oculto incluso después de reinicios del sistema.
-
Monitorización de servicios: Para analizar cómo interactúan los servicios del sistema con el malware, utilizamos Procmon. A través de una snapshot (instantánea), podemos comparar el estado del sistema antes y después de un reinicio para identificar cualquier comportamiento anómalo. Además, en Procmon, los servicios sospechosos a menudo aparecen resaltados en rojo, lo que indica que se está llevando a cabo una acción inusual.
Análisis de Comunicación de Red
Para monitorear las comunicaciones que el malware realiza con el servidor de comando y control (C2), se utilizan herramientas especializadas.
-
Wireshark: Esta herramienta nos permite capturar y analizar paquetes de datos de red, lo que es crucial para detectar exfiltración de datos o conexiones hacia servidores de C2. Al capturar el tráfico, podemos identificar qué datos están siendo enviados y hacia qué IPs.
-
Remmuxx y VPN: Para asegurarnos de que nuestras actividades de monitoreo no sean detectadas, utilizamos Remmuxx para redirigir el tráfico de red y un VPN para ocultar nuestra dirección IP y proteger nuestra red de posibles filtraciones.
-
Port scanning: En el caso de que el malware utilice puertos específicos (como el puerto 445 de Samba), podemos realizar un escaneo de puertos para identificar cualquier tráfico inusual.
Evasión mediante Fakenet-ng
Una técnica de evasión común utilizada por los atacantes es la simulación de una red utilizando herramientas como Fakenet-ng. Esta herramienta engaña al malware haciéndole creer que está en una red legítima, cuando en realidad está siendo monitoreado.
-
**Configuración de Fakenet-ng: Para usar Fakenet-ng, primero debemos instalarla y luego modificar su archivo de configuración default.ini para especificar las IPs y servicios simulados. Esto permite que el malware interactúe con una red controlada en lugar de con una red real.
-
**Generación de archivo PCAP: Fakenet-ng también permite generar archivos de captura de paquetes (PCAP) que podemos analizar posteriormente con Wireshark para examinar cómo el malware interactúa con la red simulada.
Uso de Drivers para Persistencia a Nivel de Kernel
El malware también puede utilizar drivers para ganar persistencia. Los drivers maliciosos se ejecutan a nivel de kernel, lo que les da un control total sobre el sistema operativo, lo que facilita la evasión de las herramientas de monitoreo.
-
Drivers Firmados: Para evadir la detección, los drivers maliciosos deben estar firmados digitalmente. Si el malware utiliza drivers legítimos del sistema o explota vulnerabilidades en ellos, puede ejecutarse de forma indetectable.
-
**Registro en Task Scheduler: Además de los drivers, el malware puede registrarse en el Task Scheduler de Windows para garantizar que se ejecute en momentos específicos, incluso cuando el sistema se reinicia.
Análisis de Código y Técnicas de Inyección
Cuando tenemos acceso al código malicioso, el desensamblaje es una técnica crítica para entender su funcionamiento. Usamos herramientas como IDA Pro, OllyDbg o Ghidra para examinar el código del malware.
-
Desensamblaje de Código: Desensamblar el código nos permite ver cómo se comporta el malware, cómo interactúa con los recursos del sistema y qué técnicas utiliza para permanecer oculto. Analizamos instrucciones como MOV, CALL y JMP para entender el flujo de ejecución del malware.
-
Filtros en Procmon para Eliminar Ruido: En Procmon, podemos aplicar filtros para eliminar la “información ruidosa”, como eventos de carga de imágenes y cambios en el registro que no son relevantes para el análisis. Esto nos permite centrarnos en las interacciones más importantes del malware.
Identificación de los procesos que llaman a otro proceso
Cuando estamos analizando un malware o investigando la ejecución de un programa en un sistema, a veces necesitamos identificar qué proceso está invocando a otro. Esto se puede hacer con el PID (Process ID), ya que cada proceso en el sistema tiene un identificador único. En Procmon o en otras herramientas, podemos rastrear este identificador para ver las interacciones entre procesos.
Desensamblaje y análisis de código de malware
Cuando estamos trabajando con malware, el desensamblaje es una de las técnicas más importantes para entender cómo funciona el código malicioso.
-
Descompilación de código en .NET: El código en .NET es relativamente fácil de descompilar utilizando herramientas como ILSpy o dnSpy. Estas herramientas permiten extraer el código fuente a partir de ensamblado .NET, lo que facilita la comprensión de las funciones y el comportamiento del malware.
-
Funciones en C (como
putsyprintf): Al trabajar con código en C, hay funciones comunes comoputsoprintf. Estas funciones son muy utilizadas para imprimir cadenas o valores en consola. En el caso deputs(string), internamente se podría llamar aprintfpara formatear la salida, y es necesario tener en cuenta cómo se manejan los argumentos de estas funciones cuando se examinan a nivel de ensamblador.
Trabajar con direcciones de memoria y punteros
Cuando estamos analizando el código ensamblador, trabajar con direcciones de memoria y punteros es fundamental. Las direcciones efectivas de memoria nos permiten identificar qué partes de la memoria se están utilizando y cómo se accede a ellas.
-
Dirección efectiva de carga (Effective Address): Para trabajar con punteros, necesitamos comprender cómo se cargan y usan las direcciones de memoria en el proceso de ejecución. En ensamblador, la dirección efectiva es la ubicación de la memoria a la que se hace referencia por un puntero.
-
Operaciones con punteros: Para manipular valores a través de punteros, se debe utilizar la dirección de memoria que se obtiene mediante un puntero y se puede hacer operaciones matemáticas con esa dirección. En este contexto, las operaciones que impliquen punteros pueden incluir la adición, sustracción, o la modificación directa de los valores almacenados en memoria.
-
Registros en ensamblador:
-
EAX, EBX, ECX, EDX: Estos registros son los registros generales en un procesador x86 y se utilizan para almacenar valores temporales, resultados de operaciones, y argumentos en llamadas a funciones.
-
EBP (Base Pointer) y ESP (Stack Pointer): Son registros muy importantes al trabajar con la pila.
-
EBP se utiliza para apuntar a la base del marco de pila, es decir, la parte superior de la pila, donde se almacenan las variables locales y los parámetros de las funciones.
-
ESP apunta a la cima de la pila, donde se almacenan los valores que se empujan (push) o sacan (pop) en la pila durante la ejecución de las funciones.
-
Estos registros son cruciales cuando estamos trabajando con estructuras de datos en la memoria o rastreando cómo las funciones manipulan la pila.
-
Divisiones, Restos y el Registro EDX
Cuando realizamos divisiones en ensamblador, se deben tener en cuenta los registros de destino y los valores que se modifican. Al dividir dos números, el resto de la división se guarda en el registro EDX.
- Operaciones de división: En ensamblador, cuando realizamos una división de dos números, el cociente se guarda en el registro EAX y el resto en EDX. Si estamos trabajando con números grandes o valores que requieren múltiples ciclos de operación, estos registros son esenciales.
Saltos Condicionales y Control del Flujo
En ensamblador, controlar el flujo de ejecución es esencial para entender cómo el código se mueve de una instrucción a otra. Esto se hace utilizando saltos condicionales.
-
Saltos condicionales (Jumps):
-
JZ/JNZ:
JZ(Jump if Zero) yJNZ(Jump if Not Zero) son instrucciones que permiten saltar a una dirección específica si el valor en el registro EAX (o cualquier otro registro utilizado) es cero o no es cero, respectivamente. -
JE/JNE:
JE(Jump if Equal) yJNE(Jump if Not Equal) permiten saltar dependiendo de si dos valores son iguales o no, por lo general, se usan después de una comparación de registros. -
JGE/JLE:
JGE(Jump if Greater or Equal) yJLE(Jump if Less or Equal) son instrucciones que realizan saltos si la comparación entre dos valores es mayor o igual, o menor o igual.
-
-
CALLyRET: Las instruccionesCALLyRETson fundamentales cuando se trata de llamar y retornar desde funciones.-
CALL crea un nuevo marco de pila, guardando el valor de la dirección de retorno (la instrucción que se debe ejecutar cuando la función termine).
-
RET indica el final de una función y devuelve el control de la ejecución a la dirección de retorno previamente guardada.
-
-
Manipulación de la pila:
-
Cuando se realiza un
pop, los valores que estaban en la pila se recuperan y el puntero de pila (ESP) se ajusta para apuntar a la nueva cima de la pila. -
Crear un nuevo marco de pila puede implicar ajustar el EBP para apuntar al nuevo marco, lo que ayuda a manejar las variables locales y los parámetros de las funciones.
-
Identificación de los procesos que llaman a otro proceso
Cuando estamos analizando un malware o investigando la ejecución de un programa en un sistema, a veces necesitamos identificar qué proceso está invocando a otro. Esto se puede hacer con el PID (Process ID), ya que cada proceso en el sistema tiene un identificador único. En Procmon o en otras herramientas, podemos rastrear este identificador para ver las interacciones entre procesos.
Desensamblaje y análisis de código de malware
Cuando estamos trabajando con malware, el desensamblaje es una de las técnicas más importantes para entender cómo funciona el código malicioso.
-
Descompilación de código en .NET: El código en .NET es relativamente fácil de descompilar utilizando herramientas como ILSpy o dnSpy. Estas herramientas permiten extraer el código fuente a partir de ensamblado .NET, lo que facilita la comprensión de las funciones y el comportamiento del malware.
-
Funciones en C (como
putsyprintf): Al trabajar con código en C, hay funciones comunes comoputsoprintf. Estas funciones son muy utilizadas para imprimir cadenas o valores en consola. En el caso deputs(string), internamente se podría llamar aprintfpara formatear la salida, y es necesario tener en cuenta cómo se manejan los argumentos de estas funciones cuando se examinan a nivel de ensamblador.
Trabajar con direcciones de memoria y punteros
Cuando estamos analizando el código ensamblador, trabajar con direcciones de memoria y punteros es fundamental. Las direcciones efectivas de memoria nos permiten identificar qué partes de la memoria se están utilizando y cómo se accede a ellas.
-
Dirección efectiva de carga (Effective Address): Para trabajar con punteros, necesitamos comprender cómo se cargan y usan las direcciones de memoria en el proceso de ejecución. En ensamblador, la dirección efectiva es la ubicación de la memoria a la que se hace referencia por un puntero.
-
Operaciones con punteros: Para manipular valores a través de punteros, se debe utilizar la dirección de memoria que se obtiene mediante un puntero y se puede hacer operaciones matemáticas con esa dirección. En este contexto, las operaciones que impliquen punteros pueden incluir la adición, sustracción, o la modificación directa de los valores almacenados en memoria.
-
Registros en ensamblador:
-
EAX, EBX, ECX, EDX: Estos registros son los registros generales en un procesador x86 y se utilizan para almacenar valores temporales, resultados de operaciones, y argumentos en llamadas a funciones.
-
EBP (Base Pointer) y ESP (Stack Pointer): Son registros muy importantes al trabajar con la pila.
-
EBP se utiliza para apuntar a la base del marco de pila, es decir, la parte superior de la pila, donde se almacenan las variables locales y los parámetros de las funciones.
-
ESP apunta a la cima de la pila, donde se almacenan los valores que se empujan (push) o sacan (pop) en la pila durante la ejecución de las funciones.
-
Estos registros son cruciales cuando estamos trabajando con estructuras de datos en la memoria o rastreando cómo las funciones manipulan la pila.
-
Divisiones, Restos y el Registro EDX
Cuando realizamos divisiones en ensamblador, se deben tener en cuenta los registros de destino y los valores que se modifican. Al dividir dos números, el resto de la división se guarda en el registro EDX.
- Operaciones de división: En ensamblador, cuando realizamos una división de dos números, el cociente se guarda en el registro EAX y el resto en EDX. Si estamos trabajando con números grandes o valores que requieren múltiples ciclos de operación, estos registros son esenciales.
Saltos Condicionales y Control del Flujo
En ensamblador, controlar el flujo de ejecución es esencial para entender cómo el código se mueve de una instrucción a otra. Esto se hace utilizando saltos condicionales.
-
Saltos condicionales (Jumps):
-
JZ/JNZ:
JZ(Jump if Zero) yJNZ(Jump if Not Zero) son instrucciones que permiten saltar a una dirección específica si el valor en el registro EAX (o cualquier otro registro utilizado) es cero o no es cero, respectivamente. -
JE/JNE:
JE(Jump if Equal) yJNE(Jump if Not Equal) permiten saltar dependiendo de si dos valores son iguales o no, por lo general, se usan después de una comparación de registros. -
JGE/JLE:
JGE(Jump if Greater or Equal) yJLE(Jump if Less or Equal) son instrucciones que realizan saltos si la comparación entre dos valores es mayor o igual, o menor o igual.
-
-
CALLyRET: Las instruccionesCALLyRETson fundamentales cuando se trata de llamar y retornar desde funciones.-
CALL crea un nuevo marco de pila, guardando el valor de la dirección de retorno (la instrucción que se debe ejecutar cuando la función termine).
-
RET indica el final de una función y devuelve el control de la ejecución a la dirección de retorno previamente guardada.
-
-
Manipulación de la pila:
-
Cuando se realiza un
pop, los valores que estaban en la pila se recuperan y el puntero de pila (ESP) se ajusta para apuntar a la nueva cima de la pila. -
Crear un nuevo marco de pila puede implicar ajustar el EBP para apuntar al nuevo marco, lo que ayuda a manejar las variables locales y los parámetros de las funciones.
-