Random IRC quote :      <ash> me odio por lo que estoy haciendo

Soluciones: wob#1 y reto x-ray

Hola,

Después de un tiempo, publicamos las soluciones a estas dos mini-pruebas, si encontraís algún error responded en los comentarios 🙂

Empezamos con wob#1, se trataba de encontrar un fallo en el siguiente programa …

typedef struct _tag
{
        int id;
        int initialized;
}tag,*ptag;ptag    

gettag( int n_alloc )
{
        ptag p=NULL;
        if ( n_alloc &amp;&amp; ( p = (ptag) malloc ( n_alloc * sizeof(tag) ) ) )
        {
                memset(p,0, n_alloc *sizeof(tag));
        }
        return p;
}    

int main (int argc, char **argv)
{
        ptag p;
        int n_alloc,n_init,id,i;
        if (argc < 4) return 0;
        n_alloc   = atoi(argv[1]);
        n_init     = atoi(argv[2]);
        id          = atoi(argv[3]);    

        p = gettag(n_alloc);    

        if (p)
        {
                if ( n_init <= n_alloc )
                {
                        for (i=0;i<n_init></n_init>
                        {
                                p[i].id           = id;
                                p[i].initialized  = 1;
                        }
                }
                free(p);
        }
}

Bueno, lo primero que vemos en el código es que podemos jugar con tres valores que se obtienen
desde la linea de comandos : n_alloc, n_init y id.

n_alloc se pasa como parametro a gettag(), esta función devuelve un puntero a un array de estructuras
de tipo tag, reservando n_alloc estructuras e inicializandolas (rellenandolas de 0’s), si no se puede realizar
la reserva se devuelve un puntero nulo.

Posteriormente se comprueba que gettag haya devuelto un valor no nulo, en ese caso si el número de
estructuras a inicializar (n_init) que hemos pasado por la línea de comandos es menor o igual que el
número de reservas que se han solicitado (n_alloc) se pasa a inicializarlas, la inicialización consiste
en escribir en el campo tag.id de cada una de las estructuras el valor (id) que se ha proporcionado.

Bien, ¿dónde está el truco?, ¿es explotable?, ¿bajo que condiciones? :

El problema en este caso está en cómo se calcula la memoria que es necesario reservar para el
array de estructuras:

if ( n_alloc &amp;&amp; ( p = (ptag) malloc ( n_alloc * sizeof(tag) ) ) )

De momento vamos a suponer que estamos en una arquitectura de 32 bits, más adelante hablaremos
sobre que pasaría en 64bits.

n_alloc es un entero con signo, que es multiplicado por un size_t -> sizeof(tag), size_t a su vez está
definido como entero sin signo, por lo tanto tenemos una multiplicación entre dos valores de 32 bits.

La estructura tag, si está alineada correctamente debería tener un tamaño de 4+4 bytes = 8 bytes,
el truco está en conseguir con un n_alloc muy alto un resultado de multiplicación bajo debido a que
se produce un overflow en el registro, esto es conocido como integer overflow, pensemos en
que valores harían falta :

Para provocar el overflow necesitamos un resultado de multiplicacion igual a 0x100000000, para
expresar este número se necesitan 33 bits :

0x100000000 / 0x8 = 0x20000000

por lo tanto 0x8 * 0x20000000 = 0 en 32 bits, si le sumamos una pequeña cantidad podemos
conseguir un número bajo en vez de un cero, aunque un malloc(0) podría devolver un puntero
igualmente :

0x20000001 * 0x8 = 0x8 , este mismo podría valer, lo pasamos a decimal, que es tal y como
recibe la entrada el programa y obtenemos : 536870913, apuntadlo por ahí :-).

Ahora bien, ¿cuales son las consecuencias de obtener una reserva de memoria baja?, si
nos fijamos en el programa vemos que una vez hecha la reserva en gettag(), desde la
función principal se asume que tenemos espacio necesario para la cantidad de elementos
que hemos solicitado, al pasar un número elevado y obtener una reserva pequeña conseguimos
que el bloque que el programa espera encontrarse en el heap sea menor de lo que debería.

A la hora de inicializar el array de elementos nos pasaremos escribiendo más allá de nuestro
bloque en el heap, esto es conocido como un heap overflow, existe además en este programa
la particular ventaja de que podemos controlar parte de los valores que escribimos en el
heap (el campo id) y de que podemos parar en la posición controlada por el númerro de
estructuras que se desea inicializar (n_init). ¿Es esto explotable?, sí, se podría llegar a explotar
sobreescribiendo estructuras del propio heap, aunque en este caso no hay una manera de
inyectar una shellcode en el proceso ni nada, en una situación similar en la realidad podría
llegar a ser posible la ejecución de código arbitrario.

La demostración práctica :

ns@spinlock:~$ ./wob_1 536870913 20000 3
Violación de segmento

🙂

Quedaba pendiente el tema de hablar de 64 bits, ¿es esto explotable en 64 bits?, pues no,
si volvemos a mirar la multiplicación :

if ( n_alloc &amp;&amp; ( p = (ptag) malloc ( n_alloc * sizeof(tag) ) ) )

En 64 bits los enteros siguen conservando de tamaño de 32 bits, pero el tipo size_t está definido
como unsigned long , que tiene un tamaño de 64 bits, por lo tanto no es posible conseguir explotarlo
en esta arquitectura, ya que no llegaremos al overflow del número de 64bits con ningun valor
que podamos pasar.

Bueno, dicho esto pasamos al reto de xray, este si que no tiene mucha complicación, básicamente
se trataba de escribir un programilla que utilizara las funciones de xaray, intuyendo que cadenas
podían estar por debajo del cifrado, en el post se comentaba que se trataba de una imagen, el
siguiente programa os puede valer de ejemplo :

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

int  lpcallback ( px_ray_info xinfo )
{
        unsigned char *lpdst;
        FILE *fh;
        if ( lpdst = malloc(xinfo->buffsize) )
        {
                decrypt_buffer(lpdst,xinfo);
                if ( fh = fopen("out.x","wb+") )
                {
                        fwrite(lpdst,xinfo->buffsize,1,fh);
                        fclose(fh);
                        printf ("nxaray winsn"
                                 "—n"
                                 " Match at offset  : %dn"
                                 " Cypher used      : %sn"
                                 " Key used         : %dn"
                                 " Key size         : %dn",
                                 xinfo->offset,
                                 get_method_desc(xinfo->method_id),
                                 *(char *) xinfo->xkey.key,
                                xinfo->xkey.keysize);
                }
                free(lpdst);
        }
        return X_BREAK;
}    

int main (int argc, char **argv)
{
        FILE *fh;
        size_t fsize;
        unsigned char *buffer;    

        if ( fh = fopen("newfsh","rb") )
        {
                fseek(fh,0,SEEK_END);
                fsize = ftell(fh);
                fseek(fh,0,SEEK_SET);    

                if ( buffer = malloc(fsize) )
                {
                        fread(buffer,fsize,1,fh);
                        add_string("Exif",strlen("Exif"));
                        add_string("JPEG",strlen("JPEG"));
                        set_xcallback(lpcallback);
                        x_ray(buffer,fsize);
                        free(buffer);
                        free_strings();
                }
                fclose(fh);
        }
}

Si lo ejecutamos ….

xaray wins!

Match at offset : 6
Cypher used : Incremental xor – byte
Key used : 40
Key size : 1

El algoritmo usado es un xor en el cual la clave inicial se va incrementando según se avanza por el fichero, la foto que obtenemos es la que se ve a continuación, cortesía de Eduardo Mc Bregon, artista Palentino de gran renombre, gracias por brindarnos esta maravillosa obra de arte 🙂

La venganza de la tortilla asesina ( o la tortilla contraataca)

Un saludo … mañana más y peor 😉

Se han cerrado los comentarios