tutorial solucion seguridad programacion pila overflows desbordamiento contramedidas caracteristicas camara c++ buffer-overflow fortify-source

c++ - solucion - ¿Qué es un desbordamiento de búfer y cómo puedo causar uno?



desbordamiento de pila (11)

He oído sobre un desbordamiento de búfer y me gustaría saber cómo causar uno.

¿Alguien puede mostrarme un pequeño ejemplo de desbordamiento de búfer? Nuevo (¿y para qué se utilizan?)


Con las respuestas correctas: para obtener más información sobre este tema, es posible que desee escuchar el Podcast Security Now. En el Episodio 39 (hace un tiempo) discutieron esto en profundidad. Esta es una forma rápida de obtener una comprensión más profunda sin tener que digerir un libro completo.

(En el enlace encontrará el archivo con versiones de varios tamaños, así como una transcripción, si está orientado visualmente). El audio no es el medio perfecto para este tema, pero Steve está haciendo maravillas para lidiar con esto.


Ejemplo clásico de un desbordamiento de búfer:

// noone will ever have the time to type more than 64 characters... char buf[64]; gets(buf); // let user put his name

El desbordamiento del búfer por sí solo a menudo no ocurre a propósito. Sucede más a menudo debido a un error llamado "uno por uno". Lo que significa que ha calculado mal el tamaño de la matriz en uno, tal vez porque se olvidó de dar cuenta de un carácter nulo de terminación, o porque algunas otras cosas.

Pero también se puede usar para algunas cosas malvadas. De hecho, el usuario conoce este hoyo desde hace mucho tiempo, y las inserciones dicen 70 caracteres, y los últimos contienen algunos bytes especiales que sobrescriben alguna ranura de pila; si el usuario es realmente complicado, golpeará la ranura de la dirección de devolución en la pila , y lo sobrescribe para saltar al búfer recién insertado: porque lo que el usuario ingresó no era su nombre, sino el código de shell que compiló previamente y que arrojó. Ese entonces será ejecutado. Hay algunos problemas. Por ejemplo, tiene que organizar no tener una "/ n" en ese código binario (porque get dejaría de leer allí). Para otras formas que alteran las funciones de cadena peligrosas, el cero binario es problemático porque las funciones de cadena dejan de copiar allí en el búfer. La gente ha usado xor con dos veces el mismo valor para producir un cero también, sin escribir un byte cero explícitamente.

Esa es la forma clásica de hacerlo. Pero hay algunos bloques de seguridad que pueden decir que ocurrieron tales cosas y otras cosas que hacen que la pila no sea ejecutable. Pero supongo que hay trucos mucho mejores de los que acabo de explicar. Algún tipo de ensamblador probablemente podría contarle largas historias sobre eso :)

Cómo evitarlo

Siempre use funciones que tomen también un argumento de longitud máxima, si no está 100% seguro de que un búfer es realmente lo suficientemente grande. No juegues juegos como "oh, el número no excederá los 5 caracteres" - fallará algún día. Recuerda ese cohete donde los científicos dijeron que el número no excederá de alguna magnitud, porque el cohete nunca sería tan rápido. Pero algún día, en realidad fue más rápido, y lo que resultó fue un desbordamiento de enteros y el cohete se estrelló (se trata de un error en Ariane 5 , uno de los errores informáticos más costosos de la historia).

Por ejemplo, en lugar de obtener usa fgets . Y en lugar de sprintf use snprintf donde sea adecuado y esté disponible (o solo las cosas de estilo C ++ como istream y esas cosas)


El ejemplo de desbordamiento de búfer "clásico" es:

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

Eso te permite jugar con los parámetros de desbordamiento del búfer y ajustarlos al contenido de tu corazón. El libro " Hackear - El arte de la explotación " (Enlace va a Amazon) explica en detalle cómo jugar con desbordamientos de búfer (puramente como un ejercicio intelectual, obviamente).


En este contexto, un búfer es una porción de memoria reservada para un propósito particular, y un desbordamiento de búfer es lo que sucede cuando una operación de escritura en el búfer sigue pasando el final (escritura en memoria que tiene un propósito diferente). Esto siempre es un error.

Un ataque de desbordamiento de búfer es uno que utiliza este error para lograr algo que el autor del programa no pretendía que fuera posible.


Esto debería ser suficiente para reproducirlo:

void buffer_overflow() { char * foo = "foo"; char buffer[10]; for(int it = 0; it < 1000; it++) { buffer[it] = ''*''; } char accessViolation = foo[0]; }


Si desea comprobar su programa para desbordamientos de búfer, puede ejecutarlo con herramientas como Valgrind . Encontrarán algunos errores de administración de memoria para usted.


Un desbordamiento de búfer solo está escribiendo más allá del final de un búfer:

int main(int argc, const char* argv[]) { char buf[10]; memset(buf, 0, 11); return 0; }


Además de lo que ya se ha dicho, tenga en cuenta que su programa puede o no "bloquearse" cuando ocurre un desbordamiento del búfer. Debería bloquearse, y debería esperar que lo haga, pero si el desbordamiento del búfer "se desborda" en otra dirección que su aplicación también ha asignado, su aplicación puede parecer funcionar normalmente durante un período de tiempo más largo.

Si está utilizando una edición posterior de Microsoft Visual Studio, sugeriría utilizar las nuevas contrapartes seguras en el archivo stdlib, como sprintf_s inscribed of sprintf, ect ...


En el sistema operativo Linux moderno no se puede aprovechar el desbordamiento de búfer sin algún experimento EXTRA. por qué ? porque serás bloqueado por ASLR (aleatoriedad de la capa de apilamiento de direcciones) y protector de pila en este compilador GNU C moderno. no encontrará la memoria fácilmente porque la memoria caerá en la memoria aleatoria causada por ASLR . y será bloqueado por el protector de pila si intenta desbordar el programa.

Para comenzar necesita poner ASLR en 0 valor predeterminado es 2

root@bt:~# cat /proc/sys/kernel/randomize_va_space 2 root@bt:~# echo 0 > /proc/sys/kernel/randomize_va_space root@bt:~# cat /proc/sys/kernel/randomize_va_space 0 root@bt:~#

en este caso no se trata del tutorial de desbordamiento de búfer de OLD STYLE que puede obtener de internet. o aleph un tutorial ya no funcionará en su sistema.

ahora vamos a hacer una vulnerabilidad de programa al escenario de desbordamiento de búfer

---------------------bof.c-------------------------- #include <stdio.h> #include <string.h> int main(int argc, char** argv) { char buffer[400]; strcpy(buffer, argv[1]); return 0; } ---------------------EOF-----------------------------

La función strcpy es peligrosa sin el protector de pila, ya que funciona sin verificar cuántos bytes ingresaremos. compila con la opción extra -fno-stack-protector dan -mpreferred-stack-boundary = 2 para quitar el protector de pila en tu programa C

root@bt:~# gcc -g -o bof -fno-stack-protector -mpreferred-stack-boundary=2 bof.c root@bt:~# chown root:root bof root@bt:~# chmod 4755 bof

programa C de desbordamiento de búfer con escenario de acceso raíz SUID ahora lo tenemos. ahora permite buscar cuántos bytes tenemos que poner en el búfer para hacer una falla de segmentación del programa

root@bt:~# ./bof `perl -e ''print "A" x 400''` root@bt:~# ./bof `perl -e ''print "A" x 403''` root@bt:~# ./bof `perl -e ''print "A" x 404''` Segmentation fault root@bt:~#

usted ve que necesitamos 404 bytes para la falla de segmentación del programa (bloqueo) ahora ¿cuántos bytes necesitamos para sobrescribir el EIP ? EIP es la instrucción se ejecutará después. por lo que los piratas informáticos sobrescriben EIP a instrucción maligna lo que quieren en el SUID binario en el programa. si el programa está en la raíz SUID, la instrucción se ejecutará en el acceso raíz.

root@bt:~# gdb -q bof (gdb) list 1 #include <stdio.h> 2 #include <string.h> 3 4 int main(int argc, char** argv) 5 { 6 char buffer[400]; 7 strcpy(buffer, argv[1]); 8 9 return 0; 10 } (gdb) run `perl -e ''print "A" x 404''` Starting program: /root/bof `perl -e ''print "A" x 404''` Program received signal SIGSEGV, Segmentation fault. 0xb7e86606 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6 (gdb) run `perl -e ''print "A" x 405''` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/bof `perl -e ''print "A" x 405''` Program received signal SIGSEGV, Segmentation fault. 0xb7e800a9 in ?? () from /lib/tls/i686/cmov/libc.so.6 (gdb)

programa código de retorno de falla de segmentación GOT. ingresemos más bytes y veamos al registro EIP.

(gdb) run `perl -e ''print "A" x 406''` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/bof `perl -e ''print "A" x 406''` Program received signal SIGSEGV, Segmentation fault. 0xb7004141 in ?? () (gdb) (gdb) run `perl -e ''print "A" x 407''` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/bof `perl -e ''print "A" x 407''` Program received signal SIGSEGV, Segmentation fault. 0x00414141 in ?? () (gdb)

poco más

(gdb) run `perl -e ''print "A" x 408''` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/bof `perl -e ''print "A" x 408''` Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) (gdb) i r eax 0x0 0 ecx 0xbffff0b7 -1073745737 edx 0x199 409 ebx 0xb7fc9ff4 -1208180748 esp 0xbffff250 0xbffff250 ebp 0x41414141 0x41414141 esi 0x8048400 134513664 edi 0x8048310 134513424 eip 0x41414141 0x41414141 <-- overwriten !! eflags 0x210246 [ PF ZF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb)

ahora puedes hacer tu siguiente paso ...


Este es un comentario general sobre las respuestas que recibió. Por ejemplo:

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

Y:

int main(int argc, const char* argv[]) { char buf[10]; memset(buf, 0, 11); return 0; }

En las plataformas Linux modernas, esto puede no funcionar como se esperaba o se pretendía. Es posible que no funcione debido a la función de seguridad FORTIFY_SOURCE.

FORTIFY_SOURCE utiliza variantes "más seguras" de funciones de alto riesgo como memcpy y strcpy . El compilador usa las variantes más seguras cuando puede deducir el tamaño del búfer de destino. Si la copia excede el tamaño del búfer de destino, el programa llama a abort() .

Para deshabilitar FORTIFY_SOURCE para su prueba, debe compilar el programa con -U_FORTIFY_SOURCE o -D_FORTIFY_SOURCE=0 .


Un desbordamiento de búfer es básicamente cuando una sección elaborada (o memoria intermedia) de memoria se escribe fuera de sus límites previstos. Si un atacante puede lograr que esto ocurra desde fuera de un programa, puede causar problemas de seguridad, ya que podría potencialmente permitirle manipular ubicaciones de memoria arbitrarias, aunque muchos sistemas operativos modernos protegen contra los peores casos de esto.

Si bien leer y escribir fuera de los límites previstos generalmente se considera una mala idea, el término "desbordamiento de búfer" generalmente se reserva para escribir fuera de los límites, ya que esto puede hacer que un atacante modifique fácilmente la forma en que se ejecuta el código. Hay un buen artículo en Wikipedia sobre desbordamientos de búfer y las diversas formas en que pueden utilizarse para exploits.

En términos de cómo podría programar uno usted mismo, sería una simple cuestión de:

char a[4]; strcpy(a,"a string longer than 4 characters"); // write past end of buffer (buffer overflow) printf("%s/n",a[6]); // read past end of buffer (also not a good idea)

Si eso compila y lo que sucede cuando se ejecuta probablemente dependa de su sistema operativo y compilador.