Buffer Overflow
En seguridad informática y programación, un desbordamiento de buffer (del inglés buffer overflow o buffer overrun) es un error de software que se produce cuando se copia una cantidad de datos sobre un área que no es lo suficientemente grande para contenerlos, sobrescribiendo de esta manera otras zonas de memoria. Esto se debe en general a un fallo de programación. La consecuencia de escribir en una zona de memoria imprevista puede resultar impredecibles. Existen zonas de memoria protegidas por el sistema operativo. Si se produce la escritura fuera de una zona de memoria protegida se producirá una excepción del sistema de acceso a memoria seguido de la terminación del programa. Bajo ciertas condiciones, un usuario obrando con malas intenciones puede aprovecharse de este mal funcionamiento para ejecutar código malicioso.
Ejemplo de un buffer overflow para Linux
En este ejemplo tenemos un programa con una funcion main que llama a una funcion f y esta como como cualquier función normal luego de ejecutarse devuelve el control a quien la llamo.
main -> f -> main
También tenemos una funcion g que nunca es llamada, pero usando un bo(buffer overflow) podemos hacer que lo sea aun cuando esto no se ha hecho explicitamente en el codigo fuente, para ello en f cambiamos la dirección de memoria a la que deberia retornar normalmente la dirección de la funcion main por la de la funcion g
main -> f -> g
overflow.c para 32 bits
#include <string.h>
#include <stdio.h>
void f(char *str)
{
char foo[16];
strcpy(foo, str);
}
void g(){
printf("Esto nunca se imprime\n");
}
int main()
{
char large_one[19];
large_one[0]='A';
large_one[1]='A';
large_one[2]='A';
large_one[3]='A';
large_one[4]='A';
large_one[5]='A';
large_one[6]='A';
large_one[7]='A';
large_one[8]='A';
large_one[9]='A';
large_one[10]='A';
large_one[11]='A';
large_one[12]='A';
large_one[13]='A';
large_one[14]='A';
large_one[15]='A';
large_one[16]='A';
large_one[17]='A';
large_one[18]='A';
large_one[19]='A';
large_one[20]='A';
large_one[21]='A';
large_one[22]='A';
large_one[23]='A';
large_one[24]='A';
large_one[25]='A';
large_one[26]='A';
large_one[27]='A';
//Direccion de memoria: 0x ** 41 41 41
large_one[28]='A';
large_one[29]='A';
large_one[30]='A';
//Direccion de memoria funcion g: 0x 08 04 84 2e
/*
large_one[28]=0x2e;
large_one[29]=0x84;
large_one[30]=0x04;
large_one[31]=0x08;
*/
f(large_one);
return 0;
}
Ejecucion
Kernel
Linux tiene una debil implementacion de ASLR habilitada por defecto desde el kernel version 2.6.12 para desactivarla use el comando:
- sysctl -w kernel.randomize_va_space=0
El conjunto de parches ExecShield provee una implementacion mas completa y en caso de tenerlo puede deshabilitarlo con:
- sysctl -w kernel.exec-shield=0
Compilacion y ejecucion
$ gcc -fno-stack-protector -g overflow.c -o overflow $ ulimit -c unlimited $ ./overflow Segmentation fault $ gdb -q overflow core Leyendo símbolos desde /tmp/overflow...hecho. [Nuevo Thread 2535] El núcleo se generó por «./overflow». El programa terminó con la señal 11, Segmentation fault. #0 0xd6414141 in ?? () (gdb) disas g Dump of assembler code for function g: 0x0804842e <+0>: push %ebp
- La linea: #0 0xd6414141 in ?? () debe tener una direccion del tipo 0x ** 41 41 41 si no se así reduzca el arreglo large_one a 15 elementos y empezando desde alli agrege uno a uno elementos al arreglo hasta que obtenga una direccion adecuada
- Tome la primera dirección de memoria que sale luego de: Dump of assembler code for function g. En este ejemplo: 0x0804842e
- Ponga 0x 08 04 84 2e en el comentario del código que dice: "dirección de memoria función g"
- Descomente las lineas siguientes
- Coloque los hexadecimales de 0x 08 04 84 2e en orden inverso en los últimos 3 campos del arreglo large_one y en uno mas.
$ gcc -g overflow.c -o overflow $ ./overflow Esto nunca se imprime Violación de segmento (`core' generado)
64 bits
En una computadora de 64 bits debe buscar una dirección del tipo: 0x ** 41 41 41 41 41 41 41
