El objetivo de la prueba es sobreescribir parte de la memoria (stack), en este caso, la variable buffer puede sobreescribirse pasando la cantidad de bytes permitidos.
https://exploit.education/phoenix/stack-four/
struct {
char buffer[64];
void *ret;
gets(buffer);
ret = __builtin_return_address(0);
printf("and will be returning to %p\n", ret);
Esta prueba tiene algo diferente, ahora es saved return pointer overwrite y al parecer cada vez nos van complicando más las cosas, vamos a revisar el código con GDB.
Voy a explicar en que consiste el return overwrite.
Esté método no es difícil de entender, pero sí de explicar, intentaré ser los más detallista posible.
RET es un puntero hacía la siguiente instrucción que la maquina debe ejecutar, a veces puede haber restricciones o ser reutilizados para llegar a una instrucción nueva o diferente a la que está escrito en el código original.
Este tipo de exploit aprovecha el sistema LIFO (Last In, First Out), para poner un ejemplo, imaginen que tienen hojas de papel, normalmente las personas podrían una hoja sobre una superficie, encima pondrían otra y encima otra, pero en el lenguaje máquina es al revés.
La máquina, primero pone la hoja, y debajo de esa hoja pone la siguiente, y debajo la siguiente, por eso se llama el último entra y primero sale.
Cuando logramos sobreescribir ret, se crea un fallo de segmento, pero si intentamos colocar directamente la dirección a usar, la protección no lo permite y te manda directamente a donde se salvó ret anteriormente, o simplemente no te permite la ejecución del programa.
Entonces utilizamos LIFO a nuestro favor, sobreescribimos RET, y no permitimos que la aplicación te mande al destino normal, sino que sobreescribimos ese RET, con otro RET que no esté protegido, en otras palabras, usamos un RET para brincar a otro RET
LIFO mete el primer RET a la pila, después, al brincar al segundo RET, saca de la pila el primer RET y mete a la pila el segundo RET y de ahí podemos brincar al inicio de alguna instrucción que nos interese.
Incluso si ustedes inyectaran una shell a una aplicación, podrían utilizar un ret para ejecutar esa shell o en otros casos un jmp eax. Pero eso no es lo que estamos haciendo ahorita. xD
Bueno, después de esa explicación, vamos a desamblar la función que nos aparece ahí... "start_level".
Metemos un break ahí para ver que esta haciendo realmente.
Podemos ver como hay un printf que nos menciona sobre un return y al continuar la ejecución nos muestra una dirección (0x0804855c), que va hacia main nuevamente, seguro está protegido.
Vamos a poner a prueba lo anterior mencionado sobre modificar ret, esta vez hay un cambio en ebp, ahora es (ebp-0x4), y (ebp-0x4c) lo que da 72, pero no olvidemos que hay que sobreescribir la variable ret y ebp, entonces ahora serían 80 bytes + 1 para overflow.
Borremos los breaks.
Coloquemos los bytes para ver si funciona el overflow.
Bueno, hagamos una segunda prueba, escribamos "BBBB" para ver si recibimos 0x42424242.
Hagamos una tercera prueba, escribamos "CCCC" para ver si recibimos 0x43434343, ya que si funcionó y se sobreescribe el primer ret, entonces el segundo ret tiene que sobreescribirse, pero hay que recordar que tenemos que colocar un ret que no esté protegido, y el programa nos mostró el ret de main (0x08048568).