viernes, 24 de octubre de 2014

Resolviendo los retos de io.smashthestack.org con radare2 [1-3]

¡Saludos!

     Esta semana tenía pensado hacer una entrada sobre el proyecto que estoy desarrollando con la Raspberry PI (Architeuthis) pero como @Aetsu hizo una esta semana, prefiero tocar otro palo. Estoy empezando a estudiar exploiting en linux y por el IRC me recomendaron que hiciera los retos de io.smashthestack.org porque va en progresión de dificultad y puedes ir aprendiendo con cada paso que das. He llegado a resolver hasta el reto 9, y actualmente estoy intentado hacer el 10 -los 10 primeros son los "básicos", leyendo "smash the stack for fun and profit", "Format String Technique"  y consultando en google las dudas es suficiente- . En total son 30, por lo que me queda bastante...

      Los retos los he ido resolviendo con gdb, pero ahora los estoy volviendo a hacer usando radare2. Durante el Navaja Negra asistí al taller que impartió @trufae y me moló bastante la herramienta. Alguna vez intenté flirtear con ella, pero la curva de aprendizaje hizo que continuara con gdb. Ahora estoy forzándome a usarla, y fruto de ello es este post. Sobre cómo funcionan estos retos y demás, teneis toda la info en  io.smashthestack.org. Pasemos a hacer los tres primeros retos.


Reto 1

         Si ejecutamos level01 vemos que nos solicita tres dígitos:


     Si escribimos tres al azar termina la ejecución y no nos muestra ningún mensaje ni nada. Veamos que está haciendo por dentro, desensamblandolo. Para hacer ésto con radare2 simplemente ejecutamos
r2 level01
      Una vez dentro de radare2 usamos el comando "aa" para analizar el binario. Para echar un ojo a la función main haremos "pdf@sym.main". En este caso  la "p" significa "print"; la "d", "disassemble"; y la "f", function. Si lo desglosamos, en el fondo recordar los comandos es fácil.  Deberíamos de ver el desensamblado de la función main:





     Si nos fijamos vemos un cmp a un valor fijo:
cmp eax, 0x10f
      Lo pasamos a decimal usando el comando "?" (nos permite realizar operaciones aritméticas)

     Vemos que se trata de 271. Si le pasamos ese valor al programa nos dará una shell con la que poder ver la password del siguiente reto:


   Reto 2

     En este reto sí nos proporcionan el código fuente original (level02.c):


   Para poder alcanzar la porción de código que nos brinda el /bin/sh debemos de disparar un SIGFPE. ¿Cuando se dispara? Pues si googleamos "linux sigfpe" nos topamos con este enlace , después de la Wikipedia, que dice:

Integer division by zero has undefined result.
       On some architectures it will generate a SIGFPE signal.
  (Also dividing the most negative integer by -1 may generate SIGFPE.)


   Bueno, intentar dividir por 0 no podemos porque en el código el denominador correspondía con el segundo argumento, y hace la comprobación if (argc != 3 || !atoi(argv[2])) -es decir que necesita dos argumentos y el segundo no puede ser cero-. Asi que hacemos la prueba con el valor int más negativo posible: −2,147,483,648

Sí, he confunido el mayor positivo con el mayor negativo en el primer intento. Memorizar cosas no es lo mio :(
Y ya tenemos nueva shell para conseguir la pass que nos de acceso al siguiente reto.


Reto 3

      En este reto también nos proporcionan el código fuente (en general suelen darlo en la mayoría)


          Como vemos en el código la función "bad()" es llamada desde un puntero. Si ejecutamos el programa con un string de más de 4 caracteres vemos que nos imprime un texto donde nos muestra la dirección donde está "bad" (0x80484a4) y llama a esta función, la cual nos dice a qué otra dirección deberíamos de haber saltado para poder ejecutar "good()" (0x8040474). Viendo los memcpy y memset huele a overflow por los cuatro costados. Es bastante similar al reto "Extra" que hubo este año en el Navaja Negra.

         Tiramos radare2 en modo debug y le metemos unas cuantas "A" como punto de referencia:
radare2 -d ./level03 AAAAAAAAAAAA
Miramos la función main (usando "aa"  y "pdf" tal  y como en el reto 1). Miramos donde hace el memcpy() 

  Ponemos un breakpoint para buscar nuestras "A"s y ver la distancia que hay hasta el puntero que debería de llamar a bad() (buscamos la dirección 0x80484a4), de tal forma que podremos calcular cuanta basura debemos de meter antes de sobreescribirlo con la dirección de good() -que era 0x8048474-:

db 0x08048530
dc
Con dc ejecutamos el progrma hasta nuestro bp. Ahí miramos qué hay en ESP y calculamos la distancia:


      Si contamos los grupos de 4 bytes hasta llegar a lo resaltado (que era el 0x80484a4 que buscábamos) vemos que son 19. Por lo tanto haciendo el cálculo (es tan facil como 20 x 4 - 4 pero bah, que radare haga el cálculo, a mí me da pereza :P)  
? 19 * 4
76 0x4c 0114 76.0 0000:004c 76 "L" 01001100 76.0 0.000000
   Necesitamos meter 76 y después la dirección que nos sugería, o sea que quedaría como:

level3@io:/levels$ ./level03 `perl -e 'print "A" x 76 . "\x74\x84\x04\x08";'`

  Y nos devuelve un:

This is exciting we're going to 0x8048474
Win.

Junto con una bonita shell que nos llevará a conseguir el pass del nivel 4 :D.

Cuando saque algo de tiempo postearé los siguientes retos usando r2.


Byt3z! 
5 0verl0ad Labs: Resolviendo los retos de io.smashthestack.org con radare2 [1-3] ¡Saludos!      Esta semana tenía pensado hacer una entrada sobre el proyecto que estoy desarrollando con la Raspberry PI ( Architeuthis ) ...

1 comentario:

Anónimo dijo...

Excelente XC3LL!

Exploits en linux son doblemente interesantes, yo me estaba empezando a entusiasmar con exploits cuando por cosas de la vida dejé de tener tiempo para estas cosas. Pero siempre sigo el blog.

Saludos!
jep

< >