Desbordamiento de bufer

De Wikipedia, la enciclopedia libre

En seguridad informatica y programacion , un desbordamiento de bufer (del ingles buffer overflow o buffer overrun ) es un error de software que se produce cuando un programa no controla adecuadamente la cantidad de datos que se copian sobre un area de memoria reservada a tal efecto (buffer): Si dicha cantidad es superior a la capacidad preasignada, los bytes sobrantes se almacenan en zonas de memoria adyacentes, sobrescribiendo su contenido original, que probablemente pertenecian a datos o codigo almacenados en memoria. Esto constituye un fallo de programacion. [ 1 ]

En las arquitecturas comunes de computadoras no existe separacion entre las zonas de memoria dedicadas a datos y las dedicadas a programa, por lo que los bytes que desbordan el buffer podrian grabarse donde antes habia instrucciones, lo que implicaria la posibilidad de alterar el flujo del programa, llevandole a realizar operaciones imprevistas por el programador original. Esto es lo que se conoce como una vulnerabilidad.

Una vulnerabilidad puede ser aprovechada por un usuario malintencionado para influir en el funcionamiento del sistema. En algunos casos el resultado es la capacidad de conseguir cierto nivel de control saltandose las limitaciones de seguridad habituales. Si el programa con el error en cuestion tiene privilegios especiales constituye en un fallo grave de seguridad.

Se denomina shellcode al codigo ejecutable especialmente preparado que se copia al host objeto del ataque para obtener los privilegios del programa vulnerable.

La capacidad de los procesadores modernos para marcar zonas de memoria como protegidas puede usarse para aminorar el problema. Si se produce un intento de escritura en una zona de memoria protegida se genera una excepcion del sistema de acceso a memoria ( segfault ), seguido de la terminacion del programa. Por desgracia para que esta tecnica sea efectiva los programadores han de indicar al sistema operativo las zonas que se necesita proteger, programa a programa y rutina a rutina, lo que supone un problema para todo el codigo heredado.

Existen diferentes tipos de proteccion contra ataques de desbordamiento de bufer. Estos incluyen sistemas como StackGuard que identifica el comportamiento de los ataques y los verifica de manera eficiente. [ 2 ]

Descripcion tecnica [ editar ]

Un desbordamiento de bufer ocurre cuando los datos que se escriben en un bufer corrompen aquellos datos en direcciones de memoria adyacentes a los destinados para el bufer, debido a una falta de validacion de los datos de entrada. Esto se da comunmente al copiar cadenas de caracteres de un bufer a otro.

Ejemplo basico [ editar ]

En este ejemplo, un programa tiene definidos dos elementos de datos continuos en memoria: un buffer de 8 bytes tipo string , A, y otro de dos bytes tipo entero , B. Al comienzo, A contiene bytes nulos y B contiene el numero 3 (cada caracter se representa mediante un byte).

0 0 0 0 0 0 0 0 0 3
Buffer A Buffer B

A continuacion, el programa intenta almacenar la cadena de caracteres "demasiado" en el buffer A, seguido de bytes nulos para marcar el fin de string. Al no validarse la longitud de la cadena, se sobrescribe el valor de B:

'd' 'e' 'm' 'a' 's' 'i' 'a' 'd' 'o' 0
Buffer A Buffer B

A pesar de que el programador no queria cambiar el contenido del bufer B, el valor de este ha sido reemplazado por un numero equivalente a parte de la cadena de caracteres. Para este ejemplo, en un sistema big-endian que use ASCII , el caracter 'o' seguido del byte nulo equivale al numero 28416.

Si B fuese la unica variable aparte de A definida en el programa, la escritura de datos que sobrepasen los limites de B generarian un error como segmentation fault , concluyendo asi el programa.


Codigo fuente de ejemplo [ editar ]

En el siguiente ejemplo se presenta un codigo fuente en C con un error de programacion. Una vez compilado, el programa generara un desbordamiento de buffer si se lo invoca desde la linea de comandos con un argumento lo suficientemente grande, pues este argumento se usa para llenar un buffer, sin validar previamente su longitud. [ 3 ]

/* overflow.c - demuestra un desbordamiento de buffer */


#include
 <stdio.h>

#include
 <string.h>


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

{

  char
 buffer
[
10
];

  if
 (
argc
 <
 2
)

  {

    fprintf
(
stderr
,
 "MODO DE USO: %s string
\n
"
,
 argv
[
0
]);

    return
 1
;

  }

  strcpy
(
buffer
,
 argv
[
1
]);

  return
 0
;

}

Strings de 9 caracteres o menos no provocaran desbordamiento de buffer. Por el contrario, strings de 10 caracteres o mas, si: Esto siempre es incorrecto, aunque no siempre resultara en un error del programa o segmentation fault .

Este programa puede reescribirse en forma mas segura usando la funcion strncpy de la siguiente manera: [ 3 ]

/* mejor.c - demuestra un metodo de resolver el problema */


#include
 <stdio.h>

#include
 <string.h>


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

{

  char
 buffer
[
10
];

  if
 (
argc
 <
 2
)

  {

    fprintf
(
stderr
,
 "MODO DE USO: %s string
\n
"
,
 argv
[
0
]);

    return
 1
;

  }

  strncpy
(
buffer
,
 argv
[
1
],
 sizeof
(
buffer
));

  buffer
[
sizeof
(
buffer
)
 -
 1
]
 =
 '\0'
;

  return
 0
;

}

Historia de abusos [ editar ]

Uno de los primeros aprovechamientos de los que hay registro de desbordamiento de buferes fue en 1988. Fue uno de los muchos que uso el gusano Morris para propagarse en Internet. El programa abusado fue un servicio de Unix llamado fingerd . [ 4 ]

Mas tarde, en 1995, Thomas Lopatic redescubrio en forma independiente el desbordamiento de bufer y publico sus descubrimientos en la lista de correo sobre seguridad Bugtraq . [ 5 ] ​ Un ano despues, en 1996, Elias Levy (conocido tambien como Aleph One) publico en la revista Phrack su articulo "Smashing the Stack for Fun and Profit", [ 6 ] ​ una introduccion paso a paso para aprovecharse de vulnerabilidades de desbordamientos de bufer basados en la pila .

Desde entonces, por lo menos dos de los gusanos mas importantes de Internet se han aprovechado de los desbordamientos de bufer para comprometer un gran numero de sistemas. En 2001, el gusano Code Red se aprovecho de un desbordamiento de bufer en el Internet Information Services (IIS) 5.0 de Microsoft [ 7 ] ​ y en 2003 el gusano SQL Slammer comprometio maquinas corriendo Microsoft SQL Server 2000 . [ 8 ]

Stack smashing [ editar ]

El pisado de pila o stack smashing es un tipo de desbordamiento de buffer que es aprovechado por algunos virus y otros programas maliciosos para tomar control sobre una aplicacion, o provocar su terminacion. Esto sucede cuando, por algun error imprevisto, se ingresa a la pila de la aplicacion mas datos que los que esta puede contener, lo que provoca que esta se "desborde" y algunos datos se sobreescriban. Para evitar que suceda esto, los compiladores se mejoran cada dia dejandole tiempo al programador para pensar en lo que realmente importa.

La siguiente seria una explicacion de que es, como se hace y como se previene el desbordamiento de pila, con el valor agregado de posibilitar una nueva herramienta que ayudara a explicar errores comunes de programacion. El siguiente ejemplo esta inspirado en la funcionalidad incluida en el nuevo compilador por defecto de la distribucion Debian (en su version inestable):

Este es un ejemplo comun de un programa C vulnerable:

#include
 <stdio.h>

#include
 <string.h>

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

{

  // Buffer estatico en la pila.

  char
 buffer
[
1024
];

  if
 (
 argc
 !=
 2
 )

  {

    printf
(
"Uso: %s argumento
\n
"
,
 argv
[
0
]
 );

    return
(
 -1
 );

  }

  // Copiado de cadenas sin control.

  strcpy
(
 buffer
,
 argv
[
1
]);

  printf
(
 "Argumento copiado
\n
"
 );

  return
(
0
);

}

Este programa simple acepta un argumento y lo copia a un buffer estatico. Este es un error de programacion clasico, si este programa fuese compilado a un ejecutable setuid/setgid (ejecutable por cualquiera como si fuese el dueno) permitiria a un atacante ganar permisos facilmente.

Como se van a demostrar las nuevas funciones del nuevo compilador, hay que asegurarse de compilar el ejemplo anterior con gcc-3.3 de la siguiente forma:

user@pc:/tmp$ gcc-3.3 -o buggy buggy.c

Antes de determinar si se puede romper, se deben ejecutar dos pruebas:

user@pc:/tmp$ /tmp/buggy
Uso: /tmp/buggy argumento
user@pc:/tmp$ /tmp/buggy test
Argumento copiado 

Ambas funcionan como se espera. Ahora, se ejecuta la prueba pasando un argumento mas largo para determinar si se desborda el buffer estatico:

user@pc:/tmp$ ./buggy `perl -e 'print "X"x2048'`
Argumento copiado
Violacion de segmento

Como se observa, la prueba fue exitosa: el buffer se desbordo con un argumento de mas de 2000 caracteres, resultando en una violacion de segmento. Ahora, un archivo de nucleo puede permitir depurarlo para identificar las direcciones de memoria utilizadas por el proceso:

user@pc:/tmp$ ulimit -c 09999999
user@pc:/tmp$ ./buggy `perl -e 'print "X"x3333'`
Argumento copiado
Violacion de segmento (con archivo de nucleo)

Al correr el depurador gdb se puede ver el programa:

user@pc:/tmp$ gdb ./buggy core
GNU gdb 6.4.90-debian
...
El programa finalizo con senal 11, Violacion de segmento
#0 0x58585858 in ?? ()
(gdb) info registers eip
eip 0x58585858 0x58585858

Se puede observar que EIP muestra la direccion de memoria de la siguiente instruccion que sera ejecutada (0×58585858). Esto significa que efectivamente se tomo el control del ejecutable con el script malicioso.

El explotar el ejecutable para correr una linea de comandos habiendo hecho esto es trivial y, por lo general, puede ser automatizado:

user@pc:~/cmd-overflow$ make
gcc-3.3 -o cmd-overflow -Wall -ggdb cmd-overflow.c
gcc-3.3 -o cmd-vuln -Wall -ggdb cmd-vuln.c
user@pc:~/cmd-overflow$ ./cmd-overflow --target=/tmp/buggy --args='%' --size=2048
Argumento copiado
shell-3.1$ id
uid=1000(user) gid=1000(user) groups=29(audio), 44(video), 46(plugdev), 100(users), 1000(user)
shell-3.1$ exit
exit

Se utiliza aqui un programa simple para crear un argumento de 2048 bytes de longitud que contiene el codigo requerido para correr una linea de comandos, y luego se ejecuta el programa defectuoso con este argumento construido a medida.

Se produjo el desbordamiento del buffer al correr este codigo, lo cual resulto en la ejecucion de una linea de comandos. Si el programa se hubiese sido ejecutado en Linux con el flag setuid por un super-usuario se hubieran ganado los privilegios del super-usuario.

Referencias [ editar ]

  1. ≪Buffer Overflow | OWASP≫ . owasp.org (en ingles) . Consultado el 13 de septiembre de 2021 .  
  2. Lhee, Kyung?Suk; Chapin, Steve J. (25 de abril de 2003). ≪Buffer overflow and format string overflow vulnerabilities≫ . Software: Practice and Experience (en ingles) 33 (5): 423-460. ISSN   0038-0644 . doi : 10.1002/spe.515 . Consultado el 16 de febrero de 2024 .  
  3. a b Safer C: Developing Software for High-integrity and Safety-critical Systems ( ISBN 0-07-707640-0 )
  4. "Tour del gusano" por Donn Seeley, Universidad de Utah [1]
  5. Bugtraq security mailing list [2] Archivado el 1 de septiembre de 2007 en Wayback Machine .
  6. "Smashing the Stack for Fun and Profit" by Aleph One [3]
  7. eEye Digital Security [4]
  8. Microsft Technet Security Bulletin MS02-039 [5]

Vease tambien [ editar ]