STACK-FIVE amd64
Última actualización
Última actualización
Este nivel presenta el uso de los shellcode para ejecutar código arbitrario.
Por ahora no vamos a crear nuestro propio shellcode, existen muchos ejemplos de shellcode en la red.
Si queremos debuggear nuestro shellcode, tendremos que añadir breakpoints a nuestro código ya que no lo podremos hacer desde el depurador.
Estos breakpoint se añaden incluyendo 0xcc
a nuestro código.
Asegúrate de eliminarlos cuando termines con ellos.
Como en todos los anteriores, lo primero es utilizar rabin2
para obtener más información del binario:
Después lo podemos abrir en nuestro depurador, analizar todo y comprobar las funciones para ver qué estamos buscando:
Vemos que el binario utiliza las funciones gets, puts. Las conocemos de los ejemplos anteriores así que ya sabemos que gets es explotable.
Además de eso, vemos que hay llamadas a dos funciones que conocemos pero no hay una función complete_level que nos permita vencer el nivel. En este caso, tendremos que abrir una shell remota para obtener la victoria.
Vamos a analizar el desensamblado de ambas:
sym.start_level:
Podemos observar que la llamada a la función vulnerable gets() se encuentra en esta función.
sym.main:
ATENCIÓN
Un apunte importante es ver que la ejecución de la función main no termina con exit(), de hecho, la funcion exit() no se utiliza en ninguna parte del binario. Esto se debe a que en la función main() es opcional indicar el retorno.
Si no existe un entero de retorno definido para la función main(), se tomará como 0.
Ya hemos analizado lo más importante de éste binario, a continuación vamos a preparar el exploit:
Como ya hemos mencionado, nuestro objetivo es ejecutar una shell en nuestro objetivo, en este caso, en la máquina phoenix. Los pasos a seguir para conseguir que el shellcode se ejecute son los siguientes:
Comprobar si el stack es ejecutable.
Identificar el tamaño del Buffer.
Colocar nuestra shellcode en el Buffer.
Añadir un tobogán de NOPS para facilitar la ejecución.
Sobrescribir la dirección de retorno para que retorne a la posición de nuestra shellcode.
Obtener una shell interactiva.
Para identificar los permisos que tenemos sobre los diferentes segmentos del binario podemos utilizar la aplicación readelf con el argumento -l para ver las secciones del binario:
Vemos que en el Stack tenemos permisos de lectura, escritura y ejecución.
Para identificar el tamaño del Buffer podemos calcular cuantos bytes tenemos desde la dirección de memoria en la que se almacena el primer carácter del buffer hasta la dirección de retorno:
Vamos a marcar un Breakpoint justo después de la llamada a la función gets() e introducimos "AAAA" por stdin en dicha función.
Del desensamblado de la función main() podemos averiguar que la dirección de retorno es 0x004005c7
Si calculamos el número de bytes que existen entre los dos datos obtenemos que son 136 bytes. Ese es el espacio que tenemos para nuestra shellcode y nuestro tobogan de nops.
Para este paso necesitamos un shellcode, en este caso vamos a utilzar el siguiente shellcode predefinido de shell-storm:
CONSEJO
La instrucción NOP (0x90 en hexadecimal), indica al procesador que no haga nada.
Utilizamos un "tobogán de NOPs" que nos permite asegurarnos de que la ejecución del programa caerá en él y llegará al inicio de nuestra shellcode y no a mitad.
Vamos a empezar a preparar nuestro exploit en python con todo lo que hemos visto anteriormente y nuestro tobogán listo:
Por ultimo necesito una dirección de retorno dentro del tobogán de NOPs que permita que nuestro shellcode se ejecute. Por ejemplo: 0x7fffffffe600
(Lo podemos sacar de la imagen del stack.
Por tanto, nuestro exploit quedaría de la siguiente manera:
Para introducir el input por stdin vamos a utilizar rarun2. La plantilla quedaría como sigue: