Buffer Overflow para negados. I-Parte.

Clásico de los clásicos donde los haya, y todavía totalmente en vigencia.

Mediante esta vulnerabilidad alguien puede llegar a ejecutar el código que quiera insertando en ciertas partes de la memoria. Vaaaleeee, esto es complicado! ¿Quién narices va a hacer esto? ¿Mr. Robot? Pues no. Dejando la teoría aparte vamos a ver un ejemplo que nos va a poner los pelos de punta, porque vamos empezar a pensar que no tenemos ni idea de cuántos de estos agujeros podemos tener.

#include <stdio.h>
#include <strings.h>
int main(int argc, char *argv[]){
int puede_pasar = 0;
char password[12];
char passwordGuardado[12] = "SIoNOoES";
gets (password);
if (strncmp(password, passwordGuardado, 6) == 0){
puede_pasar = 1;
}
if (puede_pasar == 0){
printf("Ehhhh!! ¿que haces tu aqui?");
} else {
printf("Yeah!!! Hola megasuperUsuario!");
}

Estudiemos un poco el código muy simple, escrito en C. Se trata de una función, típica que recibe parámetros, compara el password recibido con el password que tiene guardado en el codigo, si coinciden saluda al usuario.

Nos puede parece que el mayor error es el guardar el password a hierro en el codigo, y la verdad es que no es muy elegante, pero no es el mayor problema. El problema más grande y el agujero que se usa para ejecutar código malicioso en el sistema es que NO COMPROBAMOS LA LONGITUD DE LA VARIABLE INTRODUCIDA.

Ahora vamos a ver porque: 

Para empezar, diremos que el valor de todas las variables que aparecen se guardan en una zona de la memoria llamada Stack.

Lo primero que hacemos es guardar el valor “SIoNOoES” en la variable passwordGuardado, es decir, en el Stack.

La función gets, lee una cadena que entra el usuario y guarda el valor en la variable password, tambíen en el Stack.

la función strncmp compara dos strings, en un principio el password que tenemos guardado con el password que ha entrado el usuario, pero realmente compara el contenido de las dos zonas de memoria del stack que ocupan las variables password y passwordGuardado. Si el contenido es el mismo nos devolverá un 0.

Si las variables son iguales, asignamos un 1 a la variables que nos indica que podemos pasar y saludamos el usuario. Como no podía ser de otra manera la variable puede_pasar, también está en el stack.

¿Que problema hay?

Bueno, dejando de lado los obvios, como guardar el password (que no se hace nunca) y encima en el codigo, (que eso si que no se hace nunca), hay otro problema un poco mas escondido (aunque tampoco demasiado) un problema de Overflow:

Casi cualquier password que demos con una longitud mayor de 12 será válido. El programa intenta guardar el valor de lo introducido en el usuario en la variable password, pero en esta variable no cabe todo lo que ponemos, así que ni corto ni perezoso continúa ocupando memoria que no le pertenece, y en este caso la memoria de la variable puede_pasar. Es decir, la variable puede_pasar tiene asignada una dirección de memoria en el stack, a la que le hemos puesto un 0 al inicio del programa. Pero este valor es sustituido por los caracteres que le sobran a password. Como el valor de puede_pasar ya no será un 0, el programa le da la bienvenida al megaSuperUsuario.

representacion irónica de un buffer overflow
Los Overflow puede ser muy pero que muy elegantes.

Resumiendo. 

Los ataques de este tipo, que son los más simples de overflow, solo pueden producirse en algún programa que permita ser llamado, es decir nos ofrece un API, y que encima no este muy bíen codificado, ya que comprobando la longitud de todas las entradas este problema no tendría que darse. Pero nadie nos dice que sea un descuido.

Muchas de las puertas traseras tienen una forma parecida a este error, el programador las puede introducir sin levantar demasiadas sospechas y seguramente pasen desapercibidas. No hace falta guardar passwords secretos, ni ocultar funciones que nadie conoce. Tan solo codificas mal cualquier función que pueda ser llamada externamente y ya tienes una bonita puerta trasera.

En el ejemplo hemos visto que se puede modificar el contenido de una variable, pero eso no es todo, no es ni tan solo lo más peligroso. En el stack se almacena tambien la dirección de memoria que contiene el codigo a ejecutar al volver de la función llamada…. y si! Lo pueden cambiar, y eso ya es más peligroso.

Ahora solo tenemos que pensar en la cantidad de código de terceros que tenemos en nuestros sistemas, cualquiera de ellos puede haber sido víctima de un programador que ha dejado una puerta trasera que mediante un Buffer Overflow puede ejecutar cualquier código.

¿Como? Pues lo vemos en otro post, que es tarde y este ya es bastante largo.

Comentarios.