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 funcionamietno para ejecutar codigo malicioso.
Ejemplo de un buffer overflow para Linux
En este ejemplo tenemos un programa con una funcion main que llama a una funcion f, esta deberia normalmente retornar a main pero al causar un bo cambiamos la direccion de memoria a la que deberia retornar f por g y asi conseguimos ejecutar g, pese a que esta instruccion no este en el codigo de la aplicacion
Codigo 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[30];
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';
/* Direccion de memoria funcion g: 0x 08 04 83 e2 */
large_one[20]=0xe2;
large_one[21]=0x83;
large_one[22]=0x04;
large_one[23]=0x08;
f(large_one);
return 0;
}
Codigo para 64 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[30];
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';
/* Direccion de memoria funcion g: 0x 00 00 00 00 00 40 04 e3 */
large_one[24]=0xe3;
large_one[25]=0x04;
large_one[26]=0x40;
large_one[27]=0x00;
large_one[28]=0x00;
large_one[29]=0x00;
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
- Guarde el codigo en un archivo llamado overflow.c
# gcc -g overflow.c -o overflow # ulimit -c unlimited # ./overflow Segmentation fault # gdb -q overflow core (gdb) disas g Dump of assembler code for function g: 0x080483e2 <g+0>: push %ebp
- Tome la direccion de memoria 0x080483e2 y pongala en el comentario del codigo que dice: "direccion de memoria funcion g" y cada uno de sus hexadecimales en los campos 23, 22, 21 y 20 del arreglo large_one.
# gcc -g overflow.c -o overflow # ./overflow Esto nunca se imprime
