Durante el análisis de un código malicioso uno se puede encontrar con diferentes técnicas que utilizan los cibercriminales para ocultar sus amenazas, y que buscan dificultar el trabajo de los especialistas de seguridad que queremos comprender las acciones de un malware sobre un sistema. Una de estas técnicas es conocida como Process Replacement, un método utilizado para sobrescribir el espacio de memoria de un proceso en ejecución por uno con fines maliciosos.
A través de esta técnica, un atacante busca evitar que eventos como la creación de archivos o de nuevos procesos no conocidos por el sistema puedan disparar una detección o la identificación de una acción maliciosa. Además, este método provee al código malicioso los mismos privilegios que el proceso que remplaza, y por eso como procesos objetivos suelen utilizar programas como svchost.exe, algo que tampoco llamaría la atención del usuario en un primer lugar.
Para que esta técnica funcione, el dropper crea un nuevo proceso suspendido y luego reemplaza su contenido. Esto causa que el programa no haga nada hasta que no se resuma su thread principal, dándole tiempo al atacante a reemplazar la imagen del proceso por un nuevo ejecutable. El atacante logra esta acción mediante el uso del CREATE_SUSPENDED (0x4) como el parámetro dwCreationFlags al realizar una llamada a CreateProcess, que por ejemplo en C++ según la documentación de MSDN tiene la siguiente firma:
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
Una vez que ya se creó el proceso en memoria, el siguiente paso que el malware lleva adelante es remplazar la memoria del proceso creado con el código malicioso y resumir el thread principal para comenzar la ejecución. Muchas veces, lo que a un analista le interesa es poder estudiar, debuggear, o volcar la memoria del segundo proceso, pero mientras el mismo está en estado suspendido, no podemos utilizar un debugger como OllyDbg o Immunity Debugger para analizarlo.
Para sortear este tipo de acciones existen diferentes metodologías, y como les comentamos anteriormente en este post, estaremos viendo una que lleva el nombre de EBFE por los valores que debemos reemplazar para poder resumir el segundo proceso y que no ejecute ninguna actividad hasta que lo analicemos con un debugger. Con el fin de demostrar esta técnica utilizaremos el BitCoin Miner que analizó Matías Porolli, luego de que se descifraron los 2 resources con la clave y se invocó a CreateProcess con el parámetro de dwCreationFlags con CREATE_SUSPENDED:
Luego de que se ejecutó la llamada a CreateProcess, podemos ver con Process Explorer que se cargó el proceso vbc.exe y el mismo se encuentra Suspendido. En este punto, se ha creado el nuevo proceso, pero sin embargo, no se ha ejecutado ninguna instrucción; de esta manera ahora la primera función del malware está completada, creó un nuevo proceso que no es malicioso y ahora reemplazará su contenido por el payload que descifró en memoria.
En este punto, lo que necesitamos hacer es esperar a que comience a escribir la imagen del proceso a través de una llamada a WriteProcessMemory. Si buscamos en el código desensamblado del ejecutable podemos ver que esta acción se lleva adelante algunas instrucciones más adelante, luego de llamar algunas otras API de Windows:
Si ahora dejamos ejecutar el proceso y ponemos un breakpoint en la primera llamada a WriteProcessMemory podemos observar toda la información que necesitamos para ver qué es lo que escribirá en el proceso vbc.exe ():
En 1 podemos ver la llamada en el código del ejecutable, en 2 podemos corroborar cuáles son los valores de los parámetros al llamar a la función. En primer lugar hProcess, contiene el handle 0x68 del proceso con el ID 3868. Además, el parámetro Buffer apunta a la dirección de memoria 0x00C4002, que si la seguimos en el DUMP contiene un ejecutable que identificamos por los primeros bytes 4D 5A o su representación en ASCII con “MZ” indicando que se trata de un ejecutable.
Ahora ya sabemos qué es lo que intentará escribir en la imagen del proceso que cargó suspendido. El siguiente paso es clave en esta técnica que se llama EB FE. Debemos modificar los primeros bytes del programa con los bytes EB FE una vez que lo encontremos. En esta etapa existen diferentes acciones que se pueden hacer.
La primera de ellas sería leer la información de la cabecera del PE y encontrar el offset al Entry Point. La otra opción es realizar un dump de la región de memoria y buscar esta dirección de alguna otra manera; tomando esta segunda opción solo volcamos la región de memoria en donde se encuentra alojado el ejecutable, en este caso 0x00c40020 para asegurarnos que comience correctamente el ejecutable, y restándole 0x20 para no leer otra región de memoria y tener problemas:
Una vez que realizamos esta acción podemos abrir el dump con cualquier herramienta que nos permita analizar el ejecutable y así conocer su Entry Point o el offset al mismo, para luego buscarlo dentro de la imagen y editarlo:
Una vez que hemos encontrado el Entry Point en el dump de memoria antes de que se escriba al proceso suspendido y modificamos estos dos bytes por EB FE, se guardan los anteriores que luego vamos a necesitar:
Ahora que ya hemos hecho el reemplazo en memoria podemos dejar la ejecución del proceso que está en nuestro debugger, hasta antes de la llamada a ResumeThread:
A esa llamada se le pasa el handle al thread del proceso creado, cuyo contenido ya había sido remplazado con el miner. Ahora, si tenemos en cuenta que los bytes EB FE serán como un BreakPoint improvisado, que causará que el nuevo proceso haga un bucle infinito en su Entry Point, una vez que ejecutemos la llamada a ResumeThread el nuevo proceso comenzará a consumir casi todo el CPU:
Ahora podemos abrir una nueva instancia del procesador y realizar un attach al proceso; si todo salió bien, veremos que detiene la ejecución en la instrucción que nosotros habíamos modificado:
En este paso lo que nos resta por hacer es reemplazar esos bytes con los originales 55 8B y en este punto ya nos encontramos con el proceso listo para comenzar a analizarlo sin problemas. Este tipo de técnicas son útiles para un código malicioso para reemplazar la imagen de otro proceso, crear un thread y ejecutarlo. Además, si bien algunas herramientas pueden ayudar a realizar este proceso de manera más automática, siempre es útil para un analista de malware o especialista en seguridad conocer cómo hacerlo de manera manual. Así, sabrán en detalle las acciones que implementan los atacantes para poder contrarrestarlas.