11 diciembre 2012

Cazando Cuckoos


Cuckoo está de moda, en la última NcN se puso de manifiesto. Se habló por encima de él en alguna presentación, algunos de los asistentes lo conocían, lo usaban o incluso contribuían de alguna manera al proyecto. Posiblemente este artículo levante alguna suspicacia porque de lo que voy a hablar no es de las bondades del proyecto, que las tiene y muchas, sino de sus deficiencias. Antes de continuar, quiero que quede patente que no me dedico a hacer malware, al contrario, pero la motivación de este artículo viene más porque si Cuckoo empieza a ser un proyecto de moda, no tardarán en sacar métodos para detectarlo y/o evadirlo.

Funcionamiento

Cuckoo es una sandbox capaz de analizar el comportamiento de una muestra de malware. Para ello, la muestra se ejecuta en una máquina virtual monitorizando ciertas apis. Además de las funciones que ejecuta, también registra el tráfico de red y puede comparar el comportamiento de la muestra con las firmas programables de las que dispone. Esta última parte da una visión de alto nivel de lo que podría hacer la aplicación analizada.  El diagrama siguiente muestra un poco más de detalle sobre su funcionamiento:


Figura 1: Diagrama de los componentes de Cuckoo


La aplicación (cuckoo.py) que corre en el host levanta un snapshot limpio de la máquina virtual y deja la muestra en un directorio compartido entre ambos o lo manda a través del agente (agent.py). A su vez, configura el análisis y ejecuta el analizador (analyzer.py) en la máquina virtual. Éste lanza la muestra en estado suspendido e inyecta una DLL (cuckoomon.dll) cuyo nombre va generando aleatoriamente en el proceso. La DLL se encarga de interponerse entre algunas de las APIs de Windows que se consideran más interesantes. 

La DLL además de cambiar el comportamiento de algunas de las llamadas, también las registra en un fichero. Esta DLL se vuelve a inyectar en cada uno de los procesos que el binario original crea ya que entre el analizador y la DLL se establece una comunicación a través de un “named pipe”. La dll notifica al analizador si la muestra lanza nuevos procesos o ficheros y cuales son para que el analizador pueda inyectar la dll en estos procesos.

Empiezan los problemas

Viendo el diseño de la sandbox, podemos empezar a advertir los primeros problemas:
  • Los ficheros resultantes de los análisis se almacenan en la propia máquina guest por lo que es posible que sufran alteraciones por parte de la muestra.
  • La comunicación entre la DLL y el analizador no lleva ningún tipo de autenticación, así que es posible inyectar datos falsos dentro del análisis (limitados).
  • Del mismo modo, el agente que se ejecuta en la máquina también carece de autenticación.
  • No todas las APIs están hookeadas por lo que se pierde información para generar firmas más específicas.
  • En tanto que las APIs están hookeadas en espacio de usuario, su detección es sencilla.

Detección de hooks

Como comentaba anteriormente,  no todas las apis están monitorizadas, pero existe un número interesante de ellas. Para hacer esto, el analizador inyecta la dll una vez que el proceso está lanzado en estado suspendido. Cuando se ha asegurado de que la dll se ha inyectado correctamente, cambia el estado y empieza la fiesta.
Para hacer el hook de las APIS, Cuckoo tiene 5 métodos de los cuales usa 1:


Figura 2: Extracto de código en cuckoomon.c definiendo el método de interceptación














Como curiosidad, el proyecto malwr.com usa otro distinto. La forma que tiene de hookear las APIS es mediante la inserción de un salto absoluto a otra dirección dentro de la propia DLL que hará las veces de función remplazada. Esto se hace en el punto de entrada de cada API. Como ejemplo, las imágenes muestran dos funciones antes y después de la carga de la DLL:

Figura 3: La función DeleteFileW antes y después de haber sido hookeada

Figura 4: La función ZwCreateProcess antes y después de haber sido hookeada

Fijaos que la instrucción “MOV EDI,EDI” en DeleteFileW ha sido remplazada por un “JMP”, así como las siguientes instrucciones para acomodar el cambio. Muchas de las APIs de Windows tienen como prólogo de las funciones ese “MOV EDI,EDI”. Esto es intencionado, Microsoft lo hizo así para permitir el parcheado en caliente. Raymond Chen en su artículo http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx lo explica un poco más en detalle. Aunque no todas las llamadas tienen el mismo aspecto y dependerá de cada una de ellas. En el caso de ZwCreateProcess, la instrucción empieza asignando a EAX el valor de la llamada y el hook lo reemplaza por un salto a la dll.
La detección de estos hooks se puede realizar comparando los opcodes originales  con los que la muestra puede leer durante su ejecución.  Aunque el propio código de Cuckoo nos propone una alternativa:

Figura 5: El código de cuckoomon nos da pistas sobre cómo detectarlo


Directorios 

Otra de los métodos de detección de Cuckoo es a través de la búsqueda del directorio donde se alojan los ficheros de log que registra la DLL inyectada. Esta ruta está incluida en el código de la DLL por lo que a menos que se haya modificado, siempre estará en c:\cuckoo. 
Usando esta función, podemos detectar la presencia de un directorio y de ahí determinar si al menos el directorio existe:
Figura 6: Función para conocer si el directorio existe

Named Pipes

Cuando se lanza el proceso a través del analizador, se crea un pipe al que acceden ambos para comunicarse eventos relevantes (nuevos procesos y ficheros creados). Este pipe, que siempre tiene el mismo nombre, es  accesible desde \\.\pipe\cuckoo. Su detección puede darse o bien abriendo el pipe o a través del listado de los pipes existentes con la siguiente función: 

Figura 7: La función de la imagen se puede utilizar para detectar el pipe de cuckoo

En este caso opté por listar el contenido y buscar “cuckoo”. El motivo principal fue que en las pruebas encontré firmas que detectaban el acceso a ciertas partes de cuckoo. 
El principal problema de esto, no es sólo el hecho de ser fácilmente detectable. Dado que cualquier proceso puede escribir en ese pipe, el malware puede enviar información errónea al proceso de análisis y alterar el resultado del mismo. 

Detección del agente

El agente de Cuckoo es una novedad en la versión 0.4. Anteriormente, la muestra de malware se dejaba en un directorio compartido entre la máquina host y la guest. Dado que ahora las máquinas virtuales no tienen por qué estar alojadas bajo Virtualbox, se ha optado por instalar un agente que permite comunicarse con el host para ejecutar acciones como subir la muestra u obtener el análisis, haciéndolo independiente del sistema de virtualización. La contrapartida es que ese agente es visible en un simple listado de procesos, que el puerto también está codificado en el código fuente y que carece de autenticación, al igual que el pipe, por lo que la interacción con el agente es libre.

Bypass de la sandbox

Como punto final al análisis me dediqué a hacer pruebas de concepto con todo lo expuesto además de poder comprobar no solo que la detección es posible y trivial, sino que además podría ejecutar un binario sin el conocimiento de la sandbox.

Como comentaba antes, la inyección de la DLL se realiza al lanzar la aplicación en estado suspendido. Durante este proceso, la DLL se carga en el espacio de memoria del proceso y modifica las llamadas a las apis introduciendo saltos hacia las funciones suplantadas. Cuando termina, el proceso recibe la orden de empezar a ejecutarse y el resto ya lo conocéis.

Sin embargo, durante la ejecución, el proceso tiene control casi absoluto sobre sí mismo. En tanto que conocemos algunas de las apis que intercepta y su forma original, es posible volver a revertir los hooks que realiza la sandbox.

El proceso es simple y se resume en este gráfico:
Figura 8: Proceso para revertir el hook de Cuckoo

Para poder hacer el bypass de la dll, primero se tiene que restaurar las funciones ZwCreateProcess, ZwCreateProcessEx y CreateProcessInternalW:


Figura 9: La función VirtualProtect habilita la escritura de los bytes en la API hookeada
Después de obtener la dirección de las tres funciones, alteramos su punto de entrada con los bytes originales. En este caso son para un Windows XP EN SP3, no he tenido la oportunidad de tener acceso a otro sistema para comprobarlo en otras versiones ? Después de estos cambios, las funciones que notifican a Cuckoo que existe un nuevo proceso, quedan en sus estado original por lo que la sandbox es incapaz de inyectar la dll en el proceso nuevo que se ejecutará sin restricciones:

Figura 10: ¿Dónde está la calculadora?
A pesar de que el proceso lanza una calculadora, no aparece en el listado de procesos (Process Tree). El hecho de poder acceder a la dirección de las funciones interceptadas y poder restaurar los bytes originales, provocan que la sandbox pierda el conocimiento de las acciones que se realizan a partir de ese mismo momento.

Conclusiones

Dado que Cuckoo es un proyecto de código abierto, implementar algunas medidas para entorpecer su detección son, en algunos casos, igual de triviales. Desde modificar las rutas de los directorios, pasando por cambiar la forma en la que se comunican cada uno de los componentes.
Por el contrario, implementar un método para evitar restaurar las funciones interceptadas se complica  un poco, aunque sería posible modificar el comportamiento de VirtualProtect para evitar que el proceso pueda modificar los permisos de ciertas zonas que pudieran contener las funciones que nos interesan.  En cualquier caso, esperamos que este proyecto siga adelante porque tiene muchas cosas que aportar y aún le queda por sorprendernos.


Artículo cortesía de Iñaki rodriguez

5 comments :

Adrián dijo...

Muy interesante :)

Iñaki R. dijo...

Me alegro de que así sea y como bonus track, la prueba de concepto : http://pastebin.com/9tyT1vBg

psaneme dijo...

Artículo genial Iñaki

Iñaki R. dijo...

Muchas gracias :)

Iñaki R. dijo...

Me alegro de que así sea!