¿Cómo funciona este desbordamiento de búfer simple?

3

Tengo este código simple

vuln.c

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

int main(int argc, char** argv)
{
    char buffer[500];
    strcpy(buffer, argv[1]);
    printf("%s", buffer);
    return 0;
}

Estoy intentando realizar un desbordamiento de búfer y

  • Rellene el búfer con un código malicioso
  • Modificar la dirección de retorno para redirigir al código malicioso

Idealmente, creo que cuando sobrescribo mi búfer 500, a continuación sobrescribiré el puntero base, seguido de la dirección de retorno.

Así es como intenté desbordar el búfer

gcc vuln.c
./a.out $(python -c 'print "\x41" * 501')

Ya que estoy poniendo 501 "A" en el búfer, debería desbordarse teóricamente, ¿verdad? Pero esto no está sucediendo. No obtengo una falla de segmentación, en cambio, obtengo la salida de "AAAA .." y el programa sale normalmente. (No conté el número de As ..).

Así que encendí GDB y comencé a jugar con el número de As hasta que do obtengo una falla de segmentación. ¡Y encuentro que me sale una falla de segmentación cuando pongo 520 "A" s! ¿Cómo?

Con 520 A, el info registers de GDB me da

Ycon521A,ejecutandoelmismocomandoGDB,obtengo

Como puede ver, en la imagen 1, el punto base se llena con \ x41 (hexadecimal para A) y en la imagen 2, el BP se llena con \ x41s y la IP también comienza a llenarse.

No entiendo esto. Así que mi pregunta es: ¿por qué 520? ¿Y por qué no se desbordó en 501?

    
pregunta Izy- 08.11.2018 - 12:31
fuente

1 respuesta

4

Eso se debe a una alineación de 16 bytes, que hacen los compiladores en x86 (_64) por compatibilidad con instrucciones SIMD que Operar en 128 bits (16 bytes). Debido a esto, existe un cierto "relleno" entre el búfer y los registros guardados, 12 bytes en su caso.

Técnicamente, ya has desbordado el búfer si pasas 500 caracteres A al programa porque la cadena termina en nulo. Pero ese byte cero solo sobrescribe el primero de los bytes de relleno. Entre estos bytes de relleno y el rip guardado también está el rbp guardado (8 bytes). Así que el diseño es básicamente así (si canaries están en uso - -fstack-protector - entonces el valor canary se coloca entre el Relleno y registros guardados):

buffer [500 bytes] | padding [12 bytes] | saved rbp [8 bytes] | saved rip [8 bytes]

Entonces, con los caracteres de 520 A, sobrescribe primero el relleno y el rbp guardado antes de que el primer byte del rip guardado se sobrescriba con un byte cero.

    
respondido por el ecdsa 08.11.2018 - 15:13
fuente

Lea otras preguntas en las etiquetas