miércoles, 13 de noviembre de 2013

NcN PreQuals 2013: Reto 3 (b)

Hace unos días, tras la solución del Reto 1 y el Reto 2 de las quals del CTF de la NcN de este año, os comentaba una posible solución al Reto 3, y os dejaba pendiente una segunda solución "tirando de debugger", que  es la que veremos hoy.

Yo voy a utilizar radare2 para hacerlo, pero podríais utilizar gdb, IDA, o vuestro debugger favorito.
Para los que se pierdan como yo mirando el puro ensamblador, radare2 tiene una función con la que podemos pintar un poco los bloques de las funciones, lo cual nos ayudará a visualizar un poco mejor lo que pasa:

$ r2 -d level.elf
[0x0040101f]> af@main
[0x0040101f]> ag > foo.dot
foo.dot created

Hemos abierto el binario y nos hemos colocado en el main, y desde ahí generamos la gráfica que comentábamos anteriormente. Ahora solo tenemos que convertirla a otro formato más visible, por ejemplo PNG:

 $ dot -Tpng foo.dot > foo.png


No voy a subir el PNG a alta resolución (podéis generarlo vosotros mismos), pero creo que con esta captura será suficiente para que veáis el proceso.

¿Os acordáis que nos habíamos dado cuenta que la aplicación comparaba carácter a carácter? Quizá buscar bucles sea un buen punto de partida. En este caso, se puede ver fácilmente en la gráfica que existen únicamente DOS bucles (los identificamos porque hay flechas que vuelven a bloques anteriores). Si miramos ambos detenidamente veremos que el que nos interesa es el de la izquierda de los que tenéis marcados. Las dos flechas señalan donde están los dos puntos donde voy a hacer zoom a continuación, para que no os perdáis.

Pegando un vistazo al bucle, observamos una bifurcación curiosa:


Como podéis ver, llega un momento en el que se realiza una comparación y el flujo de la ejecución se va a una rama que llama a la función game_over(), o a otra rama que llama a las funciones success() y después a no_me_jodas_manolo() (vaya nombre xDDD). Está claro que lo que nos interesa es forzar a que la ejecución del programa vaya a esta última rama, pero nos interesa hacerlo desde algún sitio en el que el contenido cifrado ya haya sido descifrado, o sino seguramente no vamos a conseguir nada ¿Donde podemos estar seguros que estará el contenido cifrado pero todavía no hemos tenido que introducir caracteres? Pues al comienzo del bucle que estábamos mirando.


Donde tenéis la fecha roja es el bloque a donde vuelve el bucle, con lo que... ¿qué pasará si sobre escribimos el contenido y metemos un salto al bloque que nos da la solución? ¿funcionará?

Os he marcado también con un recuadro rojo la que supuestamente es la comparación entre lo que introduce el usuario por teclado y lo que hay en memoria. Poniendo un breakpoint aquí y yendo a la zona de memoria a la que referencia seguramente también seríamos capaces de obtener la clave, pero de momento nosotros vamos a intentar hacer el bypass directo que comentábamos antes, a ver si funciona.

$ cp level.elf foolevel.elf
$ r2 -w foolevel.elf
[0x00400690]> s 0x0040114e
[0x0040114e]> wa jmp dword 0x0040117b
Written 5 bytes ( jmp dword 0x0040117b)=wx e928000000
[0x0040114e]> pd
      ,=< 0x0040114e     e928000000       jmp dword 0x40117b

Hemos abierto una copia de level.elf a la que hemos llamado foolevel.elf y la hemos abierto en modo de escritura. Nos hemos ido a la posición 0x0040114e, que es la del inicio del bucle, y hemos sobre escrito un salto a la posición 0x0040117b, que es la del bloque que suponemos que nos mostrará el flag. Ahora solo nos queda salir de radare y ejecutar el binario a ver que pasa:

$ ./foolevel.elf
|  >  Type to win, only what I want to read...
|  >  |
|  -> Congratulations! The key is:
|  9e0d399e83e7c50c615361506a294eca22dc49bfddd90eb7a831e90e9e1bf2fb

Bingo! Ya tenemos el flag de nuevo, pero esta vez no hemos necesitado sacar la clave. En otras situaciones no habríamos podido hacer esto (o hubiera resultado mucho más complicado), porque depende mucho de como esté implementado, pero en esta ocasión ha funcionado a las mil maravillas. Por supuesto, existen bastantes maneras de solucionar este mismo reto. Si lo habéis probado de otra manera y queréis dejar un comentario... será bien recibido :)

No hay comentarios :