Ir al contenido principal

Desarrollo de Malware Básico en Windows (parte 2)

Explotacion usando el controlador de Excepciones SEH con r2 y x32dbg


Buenas noches a todos, (00:57)

Este viernes me he puesto al día con el tute de @RicardoNarvaja de CrackLatinos, el cual planteaba un ejercicio que para resolverlo hay que hacer uso del manejador de excepciones y ver como podemos explotar un programa haciendo uso del mismo.


El binario que vamos a analizar es ABOS2 (el antiguo), con MD5 = 1efa6d3f257f9a98ac65529edc0820b4, ya que Ricardo lo ha actualizado para que al alcanzar los símbolos coincidan con el binario y sea mas fácil su análisis.

Para comprobarlo podemos hacer uso de la herramienta (rahash2) del set de tools que vienen con radare2.



  • rahash2 -a md5  ABO2_VS_2017.exe

Bueno como siempre hacemos debemos de tener una idea general acerca del binario, para ello ejecutamos el comando:

  • rabin2 -I ABO2_VS_2017.exe
                              

Vemos que es un binario de x86, como la mayoría de los que hemos ido analizando en este blog.

Abrimos el programa con Radare2 y analizamos un poco el mismo para ver que tiene:


  • r2  ABO2_VS_2017.exe
  • aaa --> analizamos el programa 
  • afl --> listamos las funciones del binario
  • idp ABO2_VS_2017.pdb --> Cargamos los símbolos del binario
  • s main --> nos vamos a la dirección de la memoria virtual del main()
De todas maneras en una entrada del blog anterior (comandos básicos con Radare2), podéis echarle un vistazo mejor a la explicaciones de los comandos.

Tras situarnos en la primera instrucción del main(), nos vamos al modo PANELS con:

  • V!


                                  


Como vemos tenemos una llamada a messageboxA(), una llamada a gets() y una llamada a exit()
De las cuales la función vulnerable es gets() , ya que lee la cadena hasta que encuentre un final de cadena.

Esto puede provocar que se produzca un Buffer Overflow, ya que nuestro compilador reserva un espacio determinado para la variable (el que le indicamos + extra(opcional)) cuando se compila y genera el ejecutable, y si nosotros guardamos en esa variable un tamaño mayor de datos del reservado, se produce el "overflow".

Para que sea mas fácil su entendimiento hacemos lo siguiente:

  • Re-nombramos la variable "s" por "buffer"
  • Mostramos el desensamblado de la función main() con la variable cambiada
  • Miramos el "stack frame" de la función main() y nos damos cuenta que la variable buffer se encuentra a 0x400 del horizonte (en radare EBP)
                               



Vale sabemos que si machacamos ese tamaño reservado por le compilador para la variable Buffer, mas el tamaño del EBP guardado (4 bytes), lo siguiente que modificaríamos sería el "return address", por lo tanto podríamos saltar a la dirección donde queremos del programa.

Todo esto dando por supuesto que no tenemos ni NX canary, ni DEP. (que ya lo hemos comprobado con la herramienta rabin2).

Un detalle a destacar es que, tras desensamblar la función main(), vemos que hay una llamada a exit(), por lo tanto si producimos un buffer overflow en el gets() no llegaremos a redirigir el flujo ya que nunca llegaremos a la instrucción ret de la función.

Por lo que hay que buscar una alternativa, en este caso será hacer uso del controlador de excepciones SEH, el cual sobrescribiremos y saltaremos produciendo una excepción en la función gets(). Para ello la protección Safe-SEH debe de estar deshabilitada, podemos comprobarlo con este powershell incrustado en el tute.

El SEH es una estructura que esta compuesta por los siguientes campos:
Donde el primer campo apunta al siguiente controlador de la lista, y el segundo apunta a la dirección encargada de gestionar esa excepción.La posición del controlador de excepciones en el stack es la siguiente:

Por lo tanto, hemos de introducir un valor bastante grande en con el gets() para poder machacar el valor de SEH. Para ello nos vamos a la primera instrucción de nuestra función donde se ejecuta el gets() y vemos el controlador de excepciones donde se encuentra:



Vemos que tenemos el primero en la dirección 0x18FF78 y que el código que se encarga de gestionar la excepción comienza en la dirección 0x401B70.

Tal y como hemos visto en teoría, en nuestro stack debe de aparecer en la dirección 0x18FF78 la dirección del siguiente controlador de excepciones (en este caso 0x18FFC4) y justo debajo en la dirección 0x18FF7C, debe de estar la dirección 0x401B70, encargada de gestionar la excepción.



Para ver en que momento machacamos el puntero SEH, podemos hacer lo que recomendó el maestro Ricardo, introducir una input aleatoria y detectar la longitud necesaria para sobrescribir la dirección 0x18FF7C , aunque si sabemos que el buffer empieza en EBP-0x400 = 0x18FB40.

La diferencia es 0x43C = 1084, por lo que vamos a introducir una entrada con python con 1084 "A" y sobrescribiendo la dirección que gestiona la excepción con "BBBB", tal y como muestra la siguiente imagen:

Vemos que se ha sobrescrito, aun así para que se produzca la excepción debemos de concatenar posteriormente un mayor número de caracteres, hasta que se produzca la excepción, y esta haga al programa saltar a la dirección 0x42424242.

Tal y como vemos cuando salta la excepción si nos fijamos en el stack, podemos fijarnos que en la tercera posición del stack se encuentra la dirección que apuntaba al siguiente elemento en la lista de SEH, (definido en la posición 0x18FF78). Esto es una característica del controlador de excepciones sobre x86.

Por lo tanto si encontramos en nuestro binario alguna dirección en la que tengamos esta secuencia de comandos (POP X, POP X , RET). Podemos controlar el flujo de nuestro programa, si en vez de sobrescribir el SEH con "BBBB"  apuntamos a la dirección donde se ejecutan esas instrucciones, ya que saltaríamos a la dirección que indiquemos cuando sobrescribimos en el stack la direccion 0x18FF78 (next SEH pointer).

Por ejemplo en la dirección 0x402A1D tenemos ese grupo de instrucciones, por lo que podemos probar que ocurre con nuestro programa cuando introducimos el siguiente input:


 1080* "A" (relleno) + "\x90\x90\x90\x90" (puntero next SEH que es el código que se ejecutará tras el POP, POP, ret) + "\x1D\x2A\x40\x00" (dirección de las instrucciones POP, POP ,ret en nuestro binario)

Para introducir este input, es necesario hacer uso de un lenguaje de scripting como python y Popen/Pcommunicate para introducir comandos no imprimirles.

Tras ejecutar el programa y "attachearnos" a este usando x32dbg, podemos ver como ser produce la excepción y saltamos a la dirección de nuestro next SEH pointer, ejecutando el código en hexadecimal que hemos introducido 0x90909090 (nop, nop.nop,nop). Junto con el resto de caracteres añadidos posteriormente que serían (\x1D\x2A\x40\x00 + \x43\x43....)


Vale una vez tenemos el flujo controlado, ya podemos hacer lo que queramos, en el ejercicio nos pide que ejecutemos una calculadora, por lo tanto introducimos la shellcode de una de las entradas anteriores, y  añadimos a la misma un exitprocess() para que salgamos del controlador de excepciones. Ya que sino, al terminar nuetra shellcode nuestro binario "peta" y esto provoca una excepción, que llamaría a su vez al controlador de excepciones entrando en un bucle infinito... (vamos que pasaríamos mas tiempo aquí que un político en un club de alterne, y ejecutaríamos infinitas calculadoras :) :P)

Nuestro código en python quedaría de la siguiente manera:




En este último código, hemos sustituido las 4 instrucciones nop que teníamos, por un salto de 6 instrucciones (\xeb\x06) + 2 nop para que este alienado con 4 (\x90\x90). Así saltamos a la primera instrucción justo después de la dirección del (pop, pop ,ret).



Un saludo

Krilin4






Comentarios

Entradas populares de este blog

Radare2- Set de comandos básicos

Buenas a todos y Feliz navidad lo primero. Entre mantecado y mantecado, y motivado por el curso de Ricardo Narvaja (CrackLatinos), me he decidido a publicar una entrada que es mas para mí que para el resto. Pero supongo que puede ser útil para otras personas, de ahí que lo ponga lo mas bonito posible en la web. Vamos a ver un "cheatsheet" con comandos útiles de Radare2 y su funcionalidad, todo esto puede extraerse del --help de las herramientas, pero para los que como yo le gusta ir al grano y ahorrar el mayor tiempo posible, tienen esta entrada. RABIN2: rabin2 -I file_.exe --> Nos proporciona información básica del binario, como arquitectura, y las protecciones del binario como (nx,dep,aslr...) rabin2 -i file_.exe --> Nos muestra los imports del binario. rabin2 -e file_.exe --> Dirección del Entry point. (Dirección virtual y raw offset en el executable) rabin2 -zz file_.exe --> Muestra las strings rabin2 -g file_.exe --> Este comando nos muestr

CTF WriteUp sencillos con Radare2

Para comenzar el blog y así repasar yo de paso, ahora que llevo un tiempo sin hacer ningún tipo de reto en reversing voy a comenzar con algunos CTF que ya se han cerrado y que intentaré resolverlos haciendo uso de Radare2 para los retos que estén sobre la plataforma Linux y IDA para los resto sobre la plataforma Windows. Iré comenzando de dificultad baja y poco a poco subiremos la dificultad conforme vaya sintiéndome mas cómodo yo con las herramientas utilizadas. El  primer reto es un de  Sharif_University_CTF_2016 y es llamado dMd   cuyo binario os podéis descargar de el Github que os dejo a continuación  https://github.com/N4NU/Reversing-Challenges-List/tree/master/Baby/Sharif_University_CTF_2016_dMd . Los voy a encarar de la forma mas sencilla posible para poder seguir un patrón: 1) Mirar el tipo de binario que es: Tras ver el tipo de binario que es, seguidamente lo que hacemos es intentar sacar información del binario como algunas " S trings interesantes "