.¿Qué daño causará el desbordamiento del búfer?

En la seguridad actual de redes y sistemas distribuidos, más del 50% de los exploits más utilizados son desbordamientos de búfer. El ejemplo más famoso es el gusano que aprovechó la vulnerabilidad fingerd en 1988. Entre los desbordamientos de búfer, el más peligroso es el desbordamiento de pila, porque un intruso puede utilizar el desbordamiento de pila para cambiar la dirección del programa de retorno cuando la función regresa, permitiéndole saltar a cualquier dirección. Uno de los peligros es que el programa falle y. Provoca el rechazo del servicio, el otro es saltar y ejecutar un fragmento de código malicioso, como obtener un shell, y luego hacer lo que quieras.

En primer lugar, introduzcamos algunos conceptos relacionados con la pila: hay dos tipos de memoria dinámica, pila y montón. La pila está en el extremo superior de la memoria y el montón está en el extremo inferior de la memoria. Cuando se ejecuta el programa, la pila crece hacia abajo hacia el montón y el montón crece hacia arriba hacia la pila. Por lo general, las variables locales, las direcciones de retorno y los parámetros de función se colocan en la pila.

Dirección baja

Variables locales

Puntero base antiguo

Dirección de retorno

Parámetros de función (izquierda)

Parámetros de función (...)

Parámetros de función (derecha)

Dirección alta

Podemos escribir un pequeño programa de prueba :

#include "string.h"

void test(char *a);

int main(int argc, char* argv[] )

{

char a[] = “hola”;

prueba(a);

devuelve 0;

}

prueba nula(char *a)

{

char* j;

char buf[6] ;

strcpy(buf,a);

printf("&main=%p\n",&main);

printf("&buf=%p \n ",&buf);

printf("&a=%p\n",&a);

printf("&test=%p\n",&test);

for ( j=buf-8;j<((char *)&a)+8;j++)

printf("%p: 0x%x\n",j, *( unsigned char *)j);

}

Main define una cadena hola y luego llama a la función de prueba. En la función de prueba, hay una cadena local de longitud 6 Variable buf. Y luego copie el parámetro de copia a en buf. Aquí, debido a que la longitud de no a es menor o igual que la longitud de buf, no hay desbordamiento de buf.

Luego muestre las direcciones de cada función, parámetro, variable local y la dirección entre la variable de cadena local buf y el parámetro a. Vemos:

&main=0040100A

&buf=0012FF14 <. /p>

&a=0012FF28

&test=00401005

0012FF0C: 0xcc

0012FF0D: 0xcc

0012FF0E: 0xcc

0012FF0F: 0xcc

0012FF10: 0xcc

0012FF11: 0xcc

0012FF12: 0xcc

0012FF13: 0xcc

0012FF14: 0x68 h ¡Esto es buf!

0012FF15: 0x65 e

0012FF16: 0x6c l

0012FF17: 0x6c l

0012FF18: 0x6f o

0012FF19: 0x0 \0

0012FF1A: 0xcc

0012FF1B: 0xcc

0012FF1C: 0x1c aquí está

0012FF1D: 0xff dos

0012FF1E: 0x12 antiguo

0012FF1F: puntero base 0x0, ignórelo

0012FF20: 0x80

0012FF21: 0xff

0012FF22: 0x12

0012FF23: 0x0

0012FF24: 0x34 Esta es la dirección del remitente

0012FF25: 0xb8

0012FF26: 0x40 es muy similar a la dirección principal

0012FF27: ¡0x0 está cerca!

0012FF28: 0x78 Esto es

0012FF29: 0xff parámetro a, es decir,

0012FF2A: 0x12 una cadena

0012FF2B: 0x0 Dirección

0012FF2C: 0xe

0012

FF2D: 0x0

0012FF2E: 0x0

0012FF2F: 0x0

Dado que el compilador de c no realiza la verificación de límites por sí solo, si el contenido en buf es lo suficientemente largo en lugar de hola, entonces es muy probable que se sobrescriba la dirección de retorno original y el programa salte a otros lugares. Para fines de prueba, definimos una función simple eco para permitir que la prueba salte a eco cuando regrese.

Utilice printf("&echo=%p\n",&echo); ya sé que la dirección de echo es 0x0040100f Del ejemplo anterior, ya sé cuántos datos deben cubrirse desde 0012ff14. a 0012ff27. Cuéntalo. ¿Solo lo sabes? 8?1 Vuelve a escribirlo de la siguiente manera:

#include "string.h"

void test(char *a);

void echo() ;

int main(int argc, char* argv[])

{

char a[16];

int i ;

for(i=0;i<16;i++) a='x'; //Cubra la parte sin importancia para llegar a la dirección del remitente

a[16]= 0xf; //La dirección del remitente se reescribe aquí

a[17]=0x10;

a[18]=0x40;

a[19]= 0x00; //Por un lado, el byte alto es exactamente 00 y, al mismo tiempo, 00 es el final de la cadena

test(a); /p>

devuelve 0;

}

prueba nula (char *a)

{

char* j;

char buf[6];

strcpy(buf,a); //El búfer asignado es solo 5, pero el resultado es 19, ¡desbordado!

printf("&main=%p\n",&main);

printf("&buf=%p\n",&buf);

printf ("&a=%p\n",&a);

printf("&echo=%p\n",&echo);

printf("&test=%p\n ",&test);

for ( j=buf-8;j<((char *)&a)+8;j++)

printf("%p: 0x%x \n",j, *(unsigned char *)j);

}

void echo()

{

printf ("jaja!\n");

printf("jaja!\n");

printf("jaja!\n");

printf("jaja!\n");

}

Como resultado, vemos que después de mostrar la dirección, ¡aparece jaja en la función de eco! \njaja! \njaja! \njaja! \n, lo que indica que el salto de desbordamiento fue exitoso, pero el programa falló al final porque echo no pudo encontrar su dirección de retorno. Pero hoy hemos logrado nuestro objetivo de utilizar el desbordamiento de pila para ejecutar otros códigos. La próxima vez les diré cómo combinar el desbordamiento de pila y el código de shell.

Gracias a todos, por cierto, yo también soy principiante, tengo algunos malentendidos, espero que los expertos puedan darme consejos,