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!!

7 comentarios:

  1. Muy interesante. No se me habría ocurrido buscar los valores de las variables usadas en el algoritmo. Aunque pensandolo un momento tiene sentido.

    Por cierto no conocía la función hash Tangle.

    ResponderEliminar
  2. muy bien danielo!!!
    A ver para cuando nos hacemos otro reto en.mi casa!!

    Ciaooo

    ResponderEliminar
  3. Muy bien explicado! Con ganas del siguiente jeje

    ResponderEliminar
    Respuestas
    1. No te quites merito que esto fue a medias eee!! :D

      Eliminar
  4. Buena chavales!, cuando otro ctf?

    ResponderEliminar
  5. ¡Dani que casi me hablas ya en binario!

    Muy interesante el reto!! Sois unos cracks.

    ResponderEliminar
    Respuestas
    1. @Alv, ya me gustaría a mí!! jajajajaj Merci crack!!

      Y a ver si quedamos un día a tomar unas birras y me cuentas qué tal va todo..

      Un Saludo!!

      Eliminar