Cargando



Vulnerabilidad de Buffer Overflow

En este tutorial se va a mostrar un ejemplo de desbordamiento de buffer para que entiendas por qué sucede y los daños que puede causar.


ago 19 2016 13:40
Avanzado
ago 20 2016 16:51

En este tutorial vamos a hablar del desbordamiento de buffer (Buffer Overflow), un fallo que lleva mucho tiempo existiendo, ocurre cuando no se comprueban de manera correcta los datos que se copian en una zona de memoria (que ha sido reservada previamente), puede ser que la aplicación funcione correctamente si el usuario inserta datos con un tamaño adecuado, pero si nosotros reservamos memoria para 15 caracteres y el usuario nos inserta 20, afectará a otro área de memoria, que puede estar reservada o no.

 

Esto puede hacer que nuestro programa se cuelgue, pero también puede ser mucho peor, un usuario con malas intenciones puede aprovecharse de este error e influir en el funcionamiento de la aplicación o ejecutar código arbitrario en un equipo (normalmente ese código nos abrirá un intérprete de comandos). Además si el programa corre con privilegios elevados tenemos un grave fallo de seguridad. Otro ataque con el que se puede alterar el funcionamiento de una aplicación o inyectar código es XSS.

 

Nota
Las ejecuciones que verás a lo largo en este tutorial han sido realizadas en el sistema operativo Ubuntu 16.04 de 32 bits.

 

Vamos a ver un ejemplo sencillo de código en C que es vulnerable a este ataque, al lanzar el programa debemos pasar un parámetro, la aplicación espera recibir una cadena no superior a 15 caracteres, si es la cadena esperada será un acceso exitoso, si no se le “denegará”. El código es el que se muestra a continuación:

#include <stdlib.h>
#include <stdio.h>


#define password "Test"

void test(char *str){
    char buffer[15];
    int n = 0;

    strcpy(buffer, str);
    if(strcmp(buffer, password) == 0){
        n = 1;
    }

    if(n){
        printf("Success\n");
        exit(0);
    }else{
        printf("Access denied\n");
    }
}

int main(int argc, char *argv[]){
    if (argc < 2){
        printf("The app requires a parameter\n");
        exit(-1);
    }
    
    test(argv[1]);
}
El programa tiene el nombre de desbordar.c, y para compilar se ha usado lo siguiente:
gcc desbordar.c -o desbordar -fno-stack-protector
La última parte: -fno-stack-protector se usa para que el compilador no ponga protección y podamos mostrar el ejemplo. Si el usuario mete datos correctos, que es una cadena de un máximo 15 caracteres, el programa funciona bien, si metemos una “contraseña” incorrecta nos mostrará Access denied, y si metemos “Test” nos pondrá Success. Veamos una captura ejecutando el programa 2 veces, una con acceso incorrecto y otra con el String correcto:

 

buffer-overflow-1.jpg

 

Vemos que todo funciona correctamente. Pero y si insertamos una cadena superior, veamos que pasa:

 

buffer-overflow-2.jpg

 

Hemos lanzado el programa con 20 letras A, y nos muestra Success. En esta aplicación no tenemos nada, simplemente se sale de la aplicación, pero hemos accedido a un área restringida sin conocer la contraseña. Si sustituimos la siguiente función:

strcpy(buffer, str);
Por la siguiente:
strncpy(buffer, str,15);
Y ejecutamos el código con 20 letras A, tenemos la siguiente salida:

 

buffer-overflow-3.jpg

 

También puedes ver que hacemos uso de strcmp, en su lugar deberíamos utilizar strncmp, así controlamos también el tamaño. Hemos controlado que solo se puedan copiar un máximo de 15 caracteres, por lo que no afecta a nuestro programa que inserten más. Si después de mostrar el mensaje Success ejecutamos un comando de sistema (en este caso whoami), obtenemos la información:

 

buffer-overflow-4.jpg

 

Arriba no somos root, pero si lo ejecutamos con sudo, obtenemos lo siguiente:

 

buffer-overflow-5.jpg

 

Lo único que hemos añadido es una línea en el código que vimos arriba, debajo de la línea de código:

printf("Success\n");
Hemos puesto:
system("whoami");
Para entender un poco que ha pasado, voy a modificar el programa para mostrar las 2 variables con las que contamos (buffer y n) tanto si es correcto como no, y a continuación está la salida, la primera insertamos una cadena que será tratada como correcta (“Test”), luego uno incorrecto que no se pasa de longitud y por último las 20 letras A:

 

buffer-overflow-6.jpg

 

Vemos que en la primera ejecución vale 1 la variable n, porque la cadena pasada es la correcta, en la segunda vale 0, porque es errónea, pero en la última vale 1094795585, lo que hace saltarse la condición que ponemos if(n), será cierto siempre que n sea distinto a 0. No es una buena condición, aunque no tendría por qué fallar si estuviera bien el resto del código. Si metemos 16 letras A cómo parámetro veremos que el valor de la variable n es 65:

 

buffer-overflow-7.jpg

 

Si miramos el código ASCII, el número 65 corresponde a la letra A, hemos visto que la memoria de la variable n se ha tocado sin quererlo nosotros, esa letra de más que hemos pasado como parámetro ha ido a parar a la variable n. Tendríamos la memoria como sigue:

 

memoria-desbordada.jpg

 

Si nos pasamos de caracteres puede ser que nos lance un mensaje de violación de segmento (si eliminamos el exit(0) que tenemos en el if(n)), podemos verlo en la siguiente imagen:

 

violacion-de-segmento-8.jpg

 

Esa advertencia se debe a que se ha intentado acceder a una zona de memoria que se encuentra fuera de los límites de la que asigno el sistema operativo a la aplicación. Si compiláramos el ejemplo de la siguiente manera:

gcc desbordar.c -o desbordar -fstack-protector
O simplemente eliminando -fno-stack-protector de la compilación que vimos la primera vez, y ejecutamos el código con desbordamiento obtenemos el siguiente resultado:

 

proteccion-gcc-buffer-overflow-9.jpg

 

Un extra de protección que nos proporciona gcc.

 

Nota
Si quisiéramos ejecutar un código (shellcode) tendríamos que sobrescribir la dirección de retorno con la de nuestra shellcode, es algo más complejo que el ejemplo visto en el tutorial y por lo tanto conlleva más trabajo.

 

Si alguien consigue aprovecharse de esta vulnerabilidad puede causarte mucho daño. Evitar tener este tipo de fallo y que un usuario malintencionado se pueda aprovechar de esto es muy fácil, programar correctamente, se tiene que conocer bien el lenguaje de programación que se está utilizando, saber que funciones usar y que no usar, testear bien la aplicación, no solo con datos correctos, tiene que funcionar también correctamente cuando tratemos con datos imprevistos.

 

Otros ataques que puedes revisar y estar al tanto para que no te afecten o minimizar sus riesgos son: DoS y Fuerza bruta. Y no olvides de revisar la página CVE para estar al tanto de las vulnerabilidades.


¿Te ayudó este Tutorial?


2 Comentarios

Gran tutorial Josué, ¡Gracias! La programación en C es muy peligrosa si no se controla bien, pero es el mejor lenguaje para hacer programas rápidos, no queda otra...


Pablo Ramirez
ago 23 2016 14:00

guay, hacking power!!! Que siga existiendo esta vulnerabilidad es gracioso, xd

No esperes más y entra en Solvetic
Deja tus comentarios y aprovecha las ventajas de la cuenta de usuario ¡Únete!

X