03 septiembre 2014

Programando plugins para Immunity Debugger




Muchas veces, cuando hemos de hacer ingeniería inversa a algún tipo de software o intentar explotarlo, nos damos cuenta de que las herramientas que están a nuestro alcance se ven limitadas debido a uno u otro motivo. 

Es en estos casos en los que el hecho de saber programar y trabajar con herramientas que se puedan extender fácilmente nos permitirá seguir haciendo nuestro trabajo, a la vez que podemos aportar nuestro granito de arena a la comunidad liberando aquello que desarrollemos. 

En esta situación me encontré el otro día cuando estaba preparando una de las prácticas para el nuevo curso de "Reversing y Exploiting avanzado en Windows y Linux", en el que daré el módulo de "Exploiting en Windows".

El problema que tenía era bastante trivial. Sin embargo, fue una buena oportunidad para volver a echarle un ojo a la documentación de los plugins de Immunity Debugger. La situación era la siguiente: había encontrado unos ROP Gadgets en cierta DLL que, en principio, me iban a permitir explotar la aplicación. Sin embargo, esta DLL no se cargaba al iniciar la aplicación, con lo que no podía establecer un breakpoint en la dirección que me interesaba al iniciarse la aplicación. Estoy seguro que hay muchas otras maneras de aproximar este problema como, por ejemplo, seguir el flujo de ejecución del programa hasta la carga de la DLL o, muy posiblemente, ya exista, por defecto, un modo de establecer este tipo de breakpoints. Pero como ya he dicho, no necesito muchas excusas para ponerme a programar algo que me parezca interesante y que me pueda ayudar en otras situaciones en las que sí sea necesario acabar programándose un plugin específico para tareas más complejas. Sobre el tema de seguir el flujo de ejecución del programa, era algo que quería evitar ya que en mi módulo de exploiting en Windows no será necesario tener (casi) ningún tipo de conocimiento sobre reversing, de modo que nos abstraeremos de este tipo de tareas para centrarnos específicamente en técnicas de exploiting. El tema de reversing se lo dejamos a Sergi y Ricardo con sus respectivos módulos, que son ellos los expertos :)

Lo primero que necesitamos es Immunity Debugger. Este software nos permite depurar los programas que vamos a investigar. Está basado en el clásico OllyDBG. Lo cierto es que cualquier de los dos debuggers os puede servir, sin embargo, yo acostumbro a utilizar Immunity ya que soporta una grandísima herramienta llamada mona.py

En el curso veréis alguna de sus funcionalidades que, seguro, os sorprenderán! Al menos, a mí me dejaron bastante impresionado explicadas de boca del propio Peter, cuando nos las enseñó en el training que dio en Madrid para la RootedCon de este año.

Una vez tengamos Immunity instalado (instalad también la versión de Python que os pide), nos podemos hacer una idea de como funciona cargando cualquier programa que tengáis por ahí. En mi caso, el problema lo tenía con la versión de Winamp que explotaremos en el curso. La podéis descargar (aquí). Instaladla.

Para depurar un programa, abrid Immunity (ejecutadlo como administrador si estáis en Windows mayores a XP) y clicad F3 o id a "File->Open". Id al directorio en el que se ha instalado Winamp (en Windows 7, "C:\Program Files (x86)\Winamp") y abrid el ejecutable "winamp.exe". Deberíais ver algo parecido a lo siguiente:



 En mi caso, he reordenado algunas de las "Views" para tener siempre a la vista aquello que es interesante. Podéis jugar con ello.

¿Dónde estaba mi problema? Pues que quería poner un breakpoint en la DLL in_mod.dll en la dirección 0x12f02bc3 pero ésta no está mapeada al iniciarse la aplicación, así que si intentamos poner un breakpoint en esa dirección nos dará un error.



Visto el "problema", vayamos a la solución. 

La idea es tener un trigger cada vez que una DLL nueva se cargue y si es la que buscamos, añadir el breakpoint en la dirección que nos interesa. Para ello programaremos un plugin (llamados PyCommands en Immunity Debugger). Lo primero que necesitamos es obtener la documentación necesaria para saber qué funcionalidades tenemos disponibles. 

Para ello, en el directorio del depurador ("C:\Program Files (x87)\Immunity Inc\Immunity Debugger\Documentation") tenemos la "documentación". Si estáis en Windows 7, abrid cualquier de esos archivos HLP y disfrutad de los resultados:


lol.

Bueno, siempre podremos ver la documentación online, no? Upps, 404. lol.

Tiremos entonces de la bendita WayBack Machine! Ahora ya tenemos algo parecido a documentación. Aunque sólo sea una API Reference. A partir de esta guía y de la API Reference posiblemente podréis hacer todo aquello que os propongáis. A pesar de la poca profesionalidad por parte de Immunity Inc, lo cierto es que las funcionalidades de la API son muy completas y, además, tenéis muchos ejemplos de plugins en el directorio de PyCommands de Immunity. Ya os aseguro que los vais a utilizar como guia si queréis programar cualquier cosa :)

Lo primero que necesitamos es un modo de controlar que DLL se cargan. Para ello, utilizaremos la clase LoadDLLHook. Para utilizar esta clase tendremos que crearnos una clase propia que herede de ésta, en la que sobrescribiremos ciertos métodos. Quedaría algo tal que así:




Este es nuestro esqueleto. Tenemos nuestra clase DLLHook que hereda de LoadDLLHook, una clase propia de la API de Immunity, para la que sobrescribiremos el método run(). En el método __init__() de nuestra clase (el constructor) llamamos al constructor de la clase de la que heredamos.

El método main() es el principal método de cualquier PyCommand. Es lo primero que se ejecutará al cargarse el plugin. Como veis, retornamos una cadena. Esta cadena se mostrará en la UI de Immunity, en el marco inferior. Lo importante es que instanciamos nuestra clase DLLHook y llamamos al método heredado add(). Este método nos permite crear un hook. Este hook es de tipo LoadDLLHook, así que lo que nos permite es interactuar programáticamente con el depurador cada vez que ocurra un evento LoadDLL, o sea, cada vez que se cargue una DLL. 

Algo que parecía un poco complejo, se ha convertido en algo trivial, verdad? Ahora sólo necesitamos comprobar qué DLLs se van cargando y establecer el breakpoint en la dirección que queramos cuando nuestra DLL se cargue. 

El método run(), a pesar de que la documentación no nos diga absolutamente nada, es el que se ejecuta siempre que una DLL se cargue. Esto lo podemos saber si nos miramos el código fuente de la API (desde la propia documentación online), veríamos el siguiente comentario: "# function that will be runned once the hook is triggered". No les hubiera costado mucho añadir esta línea a la documentación, no? En fin... 

Sin embargo, tenemos un problema. Cuando ejecutamos este método no se nos pasa el nombre de la DLL que se ha cargado, sólo el estado de los registros en el momento de cargarse (los podéis ver a través del parámetro "regs"). Así pues, lo que hice fue obtener el nombre de las DLLs cargadas en el momento en el que el hook se ejecuta. Para ello, tendremos que utilizar las funciones de la clase Debugger. Con esta clase podremos obtener los módulos cargados (método getAllModules()), establecer breakpoints (método setBreakpoint()) y muchas otras cosas.

El código quedaría así:



En este paso hemos añadido unas cuantas cosas. La primera es importar la clase Debugger y crear una instancia (imm) como variable interna de la clase DLLHook. A continuación, se ha modificado el método run().
Primero obtenemos todos los nombres de los módulos cargados a través del método getAllModules(), que retorna un diccionario en el que las claves son los nombres de los módulos. Si nuestra DLL ya se ha cargado, añadimos el breakpoint en la dirección que queramos con el método setBreakpoint(), que recibe la dirección en la que añadir el breakpoint como un entero (0x12f02bc3 en hexadecimal es 317729731 en decimal).

Recordad que el método run() se ejecuta siempre que se cargue una DLL con lo que esta comprobación se realizará por cada DLL cargada. Este proceso ralentiza un poco el proceso de carga del binario, así que a la que se añade el breakpoint, lo que hacemos es desactivar el hook (primero lo desactivamos vía el método disable() de la clase LoadDLLHook y después lo eliminamos con UnHook()) para que la carga de DLL se haga sin que nosotros interceptemos las llamadas y agilicemos el proceso significativamente.

Como véis, ha sido bastante fácil, no? A continuación tenéis el código un poco más bonito y genérico. También lo podéis encontrar aquí.



Ahora sólo tenemos que guardar este código en un archivo .py y ponerlo en el directorio PyCommands dentro del directorio de instalación de Immunity. Ahora, una vez abráis el depurador, cuando salte el breakpoint del entry-point del programa, podéis ejecutar el PyCommand. Por ejemplo, si lo habéis llamado "break_on_load.py", sólo tenéis que escribir "!break_on_load" en la consola de Immunity (el marco inferior) y listo!

Este ejemplo ha sido muy simple. Se pueden hacer cosas bastante más complejas. Por ejemplo, aquí hice un PyCommand que se encarga de buscar ciertas instrucciones siguiendo diferentes flujos de ejecución una vez el programa ha cascado. Este PyCommand puede servir para encontrar "trampolines" que nos permitan saltar a direcciones de memoria que controlemos, dependiendo de los registros que controlemos, una vez hayamos explotado.


Ya sabéis, si queréis aprender estas y otras cosas con algo más de profundidad, no dudéis en apuntaros al curso de reversing y exploiting ;)

Un saludo,
newlog!


Contribución por cortesía de Albert López

5 comments :

Santiago M. Vicente dijo...

Buenas! ante todo enhorabuena por el post, muy bien explicado y muy buena excusa para darle al scripting de immunity :)

Solo comentar, rollo pedante, que el método que existe por defecto en Olly e Immunity para eso mismo está en Options->Debugging Options->Events->Break on new module (DLL).

Pero vamos, que insisto en que entiendo el objetivo del post. Es sólo para dejar el método rápido también por si alguien quiere usarlo, aunque si hay tiempo, siempre se aprende más con tu método y puede servirnos cuando nos encontremos en otras situaciones en las que verdaderamente no quede otra opción :D

Saludos!

newlog dijo...

Jajajaja, qué bueno! Estaba seguro que tenía que existir algo por defecto que hiciera una cosa tan simple. Pero estaba empecinado en hacer otro plugin de Immunity para jugar con los hooks, así que no lo busqué :)

Gracias por comentarlo! Le echaré un ojo.

newlog dijo...

Jajajaja, qué bueno! Estaba seguro que tenía que existir algo por defecto que hiciera una cosa tan simple. Pero estaba empecinado en hacer otro plugin de Immunity para jugar con los hooks, así que no lo busqué :)

Gracias por comentarlo! Le echaré un ojo!

Marian Lucy dijo...

Buen Cliente Día,

En nombre de esta organización Bienestar Finanzas queremos certificar que se trata de una compañía de licencia de préstamo, estamos certificados compañía de préstamos, así como un inversionista ángel hemos estado en este préstamos comerciales como Ángel y tenemos una buena cabeza para los necesitados no robar .

Estamos al servicio de nuestros clientes, ayudamos a los pobres Este préstamo viene de la Hacienda Ayuda Bienestar (Common Wealth) En mis préstamos del programa, las tasas de interés son bajas, pero en el corto plazo en efectivo, Interés requerido para el préstamo es del 2% con una duración de no más de 360 meses (52 años), pero puede ser menos.

La duración mínima para este préstamo es de 24 meses, que es de 2 años, en estos 24 meses se espera que el prestatario para hacer pagos de préstamos que hace que sea aún más fácil de pagar. Email: welfarefinancehelp@hotmail.com a respuesta, me envía su número de teléfono,

esperamos su respuesta lo antes posible. Para cualquier consulta, no dude en preguntar.

atentamente

Jose dijo...

Tentei Instalar o Programa Mais Quando Executo Logo Ele Abre e Fecha Novamente!
O que Faco?