Ir al contenido principal

Desbordamiento de enteros (Integer Overflow)

Ya os he hablado en este blog de posibles problemas potenciales que se pueden dar en los programas y que son susceptibles de ser explotados para hacer que dichos programas se comporten de forma diferente a la que deberían. Uno de estos problemas es el del desbordamiento de la pila. Sin embargo, hay otros posibles errores de programación que, aunque menos obvios, son igual de peligrosos. Uno de ellos es el desbordamiento de enteros o integer overflow. Para entender cómo funciona os presento un ejemplo muy sencillo pero didáctico.



Valga como ejemplo el siguiente código en C.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
    int i, j, k, suma;

    if (argc == 4) {
        i = atoi(argv[1]);
        j = atoi(argv[2]);
        k = atoi(argv[3]);

        if (i<=0 || j<=0 || k<=0) {  
            printf("Ningún parametro puede ser <= 0\n");
        } else {
            suma = i + j + k;
            printf("%d + %d + %d = %d\n", i, j, k, suma);
            if (suma == 0) {
                printf("Área privada\n");
            }
        }   
    } else {
        printf("Se necesitan tres parámetros\n");
    }
    return(0);
}

En principio, tal y como está planteado el código no debería ser posible entrar en el área privada debido a que, como nos aseguramos de que las variables i, j y k son positivas, no es posible que su suma tome un valor negativo ¿verdad?. Sin embargo, si nos fijamos, no se establece ningún control sobre la conversión de los parámetros de entrada a números enteros (int). Esto produce un potencial problema conocido como Integer Overflow.

En la arquitectura x86 una variable tipo int permite almacenar valores de 32 bits. Internamente se almacenan en un formato llamado complemento a 2. En esta forma de representación, si el bit más significativo (el de la izquierda) está a 1, el valor se considera negativo, así que sólo tenemos que desbordar el valor de la variable int y poner este bit a 1 para conseguir un valor negativo. Así pues, el mayor valor positivo de 32 bits que puede almacenar una variable entera en complemento a 2 es 0x7fffffff, que en binario es 01111111111111111111111111111111 y en decimal 2147483647. Como el bit más significativo es 0 el valor del entero es positivo.

Si ahora sumamos 1 a este valor, obtendremos 0x80000000 que en binario es 10000000000000000000000000000000. Como tiene el bit más significativo a 1, se interpreta como un valor negativo. Vamos a probar.

alberto@bluestorm$ ./intoverflow 2147483647 1 2
2147483647 + 1 + 2 = -2147483646
Parece que nuestro programa es vulnerable a ataques de tipo integer overflow. Ya de entrada hemos conseguido poner una variable del programa a un valor negativo cuando el programador pretendía expresamente que esto no fuera posible, ya que se había preocupado de comprobar que los parámetros de entrada fueran positivos. A partir de aquí, es relativamente fácil hacer explotar el programa.

Vamos a tratar de dar un paso más y ejecutar el código del área privada, para lo cual nos hace falta conseguir que la suma de las tres variables (suma = i + j + k;) valga 0 y así el flujo de ejecución entre en el área privada. Para conseguir el valor 0 necesitamos desbordar el valor entero de la variable suma, que es de 32 bits. El mayor número representable en 32 bits es 232=4294967295 (11111111111111111111111111111111 en binario). Si sumamos 2147483647 + 2147483647 obtenemos 4294967294 (232-1), que en binario es 11111111111111111111111111111110 (0xFFFFFFFE). Si a este valor le sumamos 1 obtendremos 11111111111111111111111111111111 (0xFFFFFFFF) y si le volvemos a sumar 1 tendremos... ¡¿0?!

En realidad tenemos 100000000000000000000000000000000, que es un valor de 33 bits, pero como sólo podemos almacenar 32 en la variable, el bit más significativo (el 1) se descarta y nos queda 00000000000000000000000000000000. Vamos a verificarlo:

alberto@bluestorm$./intoverflow 2147483647 2147483647 2
2147483647 + 2147483647 + 2 = 0
Área privada
Pues sí, resulta que para nuestro programa 2147483647 + 2147483647 + 2 = 0

Y por lo tanto, hemos conseguido que el flujo de ejecución sea diferente al que el programador pretendía.

Conclusión: No sólo hay que tener cuidado con el desbordamiento de la pila o de los buffers. El desbordamiento de enteros es potencialmente igual de peligroso.

Comentarios

Publicar un comentario

Entradas populares de este blog

Creando firmas de virus para ClamAV

ClamAv es un antivirus opensource y multiplataforma creado por Tomasz Kojm muy utilizado en los servidores de correo Linux. Este antivirus es desarrollado por la comunidad, y su utilidad práctica depende de que su base de datos de firmas sea lo suficientemente grande y actualizado. Para ello es necesario que voluntarios contribuyan activamente aportando firmas. El presente artículo pretende describir de manera sencilla cómo crear firmas de virus para ClamAV y contribuir con ellas a la comunidad.

Manejo de grafos con NetworkX en Python

El aprendizaje computacional es un área de investigación que en los últimos años ha tenido un auge importante, sobre todo gracias al aprendizaje profundo (Deep Learning). Pero no todo son redes neuronales. Paralelamente a estas técnicas, más bien basadas en el aprendizaje de patrones, también hay un auge de otras técnicas, digamos, más basadas en el aprendizaje simbólico. Si echamos la vista algunos años atrás, podemos considerar que quizá, la promesa de la web semántica como gran base de conocimiento ha fracasado, pero no es tan así. Ha ido transmutándose y evolucionando hacia bases de conocimiento basadas en ontologías a partir de las cuales es posible obtener nuevo conocimiento. Es lo que llamamos razonamiento automático y empresas como Google ya lo utilizan para ofrecerte información adicional sobre tus búsquedas. Ellos lo llaman Grafos de Conocimiento o Knowledge Graphs . Gracias a estos grafos de conocimiento, Google puede ofrecerte información adicional sobre tu búsqueda, ad

Scripts en NMAP

Cuando pensamos en NMAP, pensamos en el escaneo de puertos de un host objetivo al que estamos relizando una prueba de intrusión, pero gracias a las posibilidades que nos ofrecen su Scripting Engine , NMAP es mucho más que eso. Antes de continuar, un aviso: algunas de posibilidades que nos ofrecen los scripts de NMAP son bastante intrusivas, por lo que recomiendo hacerlas contra hosts propios, máquinas virtuales como las de Metasploitable, o contrato de pentesting mediante. Para este artículo voy a usar las máquinas de Metasploitable3 . No voy a entrar en los detalles sobre el uso básico de NMAP, ya que hay miles de tutoriales en Internet que hablan sobre ello. Lo cierto es que NMAP tiene algunas opciones que permiten obtener información extra, además de qué puertos están abiertos y cuales no. Por ejemplo, la opción -sV trata de obtener el servicio concreto, e incluso la versión del servicio que está corriendo en cada puerto. Otro ejemplo es la opción -O, que intenta averiguar el