c buffer-overflow

Desbordamiento de búfer en C



buffer-overflow (5)

Desmonte la function() y vea cómo se ve.

La compensación debe ser negativa positiva, quizás 64 + 8, ya que es una dirección de 64 bits. Además, debes hacer el ''+7'' en un objeto del tamaño de un puntero, no en un char. De lo contrario, si las dos direcciones cruzan un límite de 256 bytes, habrás explotado tu vulnerabilidad ...

Estoy intentando escribir un desbordamiento de búfer simple utilizando C en Mac OS X 10.6 de 64 bits. Aquí está el concepto:

void function() { char buffer[64]; buffer[offset] += 7; // i''m not sure how large offset needs to be, or if // 7 is correct. } int main() { int x = 0; function(); x += 1; printf("%d/n", x); // the idea is to modify the return address so that // the x += 1 expression is not executed and 0 gets // printed return 0; }

Aquí hay parte del volcado del ensamblador principal:

... 0x0000000100000ebe <main+30>: callq 0x100000e30 <function> 0x0000000100000ec3 <main+35>: movl $0x1,-0x8(%rbp) 0x0000000100000eca <main+42>: mov -0x8(%rbp),%esi 0x0000000100000ecd <main+45>: xor %al,%al 0x0000000100000ecf <main+47>: lea 0x56(%rip),%rdi # 0x100000f2c 0x0000000100000ed6 <main+54>: callq 0x100000ef4 <dyld_stub_printf> ...

Quiero saltar sobre la instrucción movl , lo que significaría que necesitaría incrementar la dirección de retorno en 42 - 35 = 7 (¿correcto?). Ahora necesito saber dónde está almacenada la dirección de retorno para poder calcular el desplazamiento correcto.

He intentado buscar el valor correcto de forma manual, pero se imprime 1 o se abort trap . ¿Hay algún tipo de protección contra desbordamiento del búfer en marcha?

Utilizando un offset de 88 trabajos en mi máquina. Utilicé el enfoque de Nemo para averiguar la dirección de retorno.


Este ejemplo de 32 bits ilustra cómo puede resolverlo, vea a continuación para 64 bits:

#include <stdio.h> void function() { char buffer[64]; char *p; asm("lea 4(%%ebp),%0" : "=r" (p)); // loads address of return address printf("%d/n", p - buffer); // computes offset buffer[p - buffer] += 9; // 9 from disassembling main } int main() { volatile int x = 7; function(); x++; printf("x = %d/n", x); // prints 7, not 8 }

En mi sistema, el desplazamiento es de 76. Esos son los 64 bytes del búfer (recuerde, la pila crece hacia abajo, por lo que el inicio del búfer está lejos de la dirección de retorno) más cualquier otro detritus entre ellos.

Obviamente, si está atacando un programa existente, no puede esperar que calcule la respuesta por usted, pero creo que esto ilustra el principio.

(Además, tenemos la suerte de que +9 no se realice en otro byte. De lo contrario, el incremento de un solo byte no establecería la dirección de retorno como esperábamos. Este ejemplo puede fallar si no tiene suerte con la dirección de retorno dentro de main )

Pasé por alto de alguna manera lo de 64 bits de la pregunta original. El equivalente para x86-64 es 8(%rbp) porque los punteros tienen una longitud de 8 bytes. En ese caso, mi compilación de prueba produce un desplazamiento de 104. En el código anterior, sustituya 8(%%rbp) utilizando el doble %% para obtener un solo % en el ensamblaje de salida. Esto se describe en este documento ABI . Búsqueda de 8(%rbp) .

Hay una queja en los comentarios de que 4(%ebp) es tan mágico como 76 o cualquier otro número arbitrario. De hecho, el significado del registro %ebp (también denominado "puntero de marco") y su relación con la ubicación de la dirección de retorno en la pila está estandarizado. Una ilustración que rápidamente busqué en Google está here . Ese artículo utiliza la terminología "puntero base". Si quisiera explotar los desbordamientos de búfer en otras arquitecturas, se requeriría un conocimiento detallado similar de las convenciones de llamada de esa CPU.


Puede intentar ejecutar su código en un depurador, pasar cada línea de ensamblaje a la vez y examinar el espacio de memoria de la pila, así como los registros.


Roddy tiene razón en que necesitas operar con valores de tamaño de puntero.

Comenzaría por leer valores en su función de explotar (e imprimirlos) en lugar de escribirlos . A medida que se arrastra más allá del final de su matriz, debe comenzar a ver los valores de la pila. En poco tiempo debería encontrar la dirección de retorno y poder alinearse con su volcado de desensamblador.


Siempre me gusta operar con buenos tipos de datos, como este:

struct stackframe { char *sf_bp; char *sf_return_address; }; void function() { /* the following code is dirty. */ char *dummy; dummy = (char *)&dummy; struct stackframe *stackframe = dummy + 24; /* try multiples of 4 here. */ /* here starts the beautiful code. */ stackframe->sf_return_address += 7; }

Con este código, puede verificar fácilmente con el depurador si el valor en stackframe->sf_return_address coincide con sus expectativas.