Vous êtes sur la page 1sur 7

En el primer paper hablbamos de debuggers, desensambladores... y otras utilidades para la caza de vulnerabilidades.

Antes de usar estas herramientas vamos a necesitar aprender un poquito de ensamblador. As que.. voy a empezar con.. La divertida charla de Assembler

Procedemos a explicar ASM (ensamblador) para procesadores x86 bajo sistemas operativos Linux. Lo primero que debemos tener claro es que el procesador es nuestro amigo, nos quiere un montn y adems opera con registros de la siguiente manera: instruccin destino, fuente instruccin Es la instruccin a ejecutar. Ejemplo: sumar, restar, comparar, mover... fuente Es el operando sobre el que opera la instruccin. (y s, soy un to redundante). Destino A donde va a parar el resultado de la instruccin. Registros mas importantes y otras cosas importantes: EIP Apunta a la siguiente instruccin a ser ejecutada. Pila: La pila, en la pila se apilan datos. Podemos imaginarnos una pila de platos, ponemos o quitamos un plato pero siempre de la cima porque por debajo no se puede. (a la derecha un esquemita de la pila xD). ESP Apunta a la cima de la pila. CALL Guarda el estado del programa en la pila, la direccin de la siguiente instruccin a ejecutar para despus seguir, ejecuta la funcin y retorna. Segmentos de memoria: La memoria de los ejecutables se divide en varias partes o segmentos (cada uno con unos permisos): Texto (o cdigo): Almacena el cdigo ensamblado de nuestro programa. Es de tamao fijo. Y los permisos son de solo lectura. Datos: Almacena variables globales y estticas inicializadas. Es tambien de tamao fijo. Y los permisos aqu son de escritura. Bss: Almacena variables globales y estticas sin inicializar. Su tamao es fijo y los permisos son de escritura. Heap: Su tamao es variable (el programador puede reservar fragmentos de memoria aqu) Stack (pila): Su tamao es variable. Almacena variables locales de las funciones y procedimientos. Almacena tambien el contexto en las llamadas a funciones.

Ahora vamos a ver cmo corrijo y plagio descaradamente un cdigo en C del tutorial Smashing Stack and Profit para comprender la esencia de la vulnerabilidad del desbordamiento del buffer:
void function(int a, int b, int c) { EXPLICACIN DEL CDIGO char buffer1[5]; Tenemos una funcin llamada function que hace lo siguiente: char buffer2[10]; Recibe tres variables enteras (a, b y c) como parmetros. int *ret; ret = buffer1 + 13; (*ret) += 8; } void main() { int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); }

La funcin contiene un buffer de 5 bytes y otro buffer de 10 bytes. Asimismo tenemos un puntero llamdo ret con el que hace algunas cosas. El programa principal hace lo siguiente: Declara una variable entera x. Inicializa la variable x a cero. Llama a la funcion function dando los valores a las variables locales de la funcin: a=1, b=2, c=3 A la vuelta de la llamada a la funcion, x vuelve a valer 1. Al imprimir la variable x por pantalla, x no vale 1 como debera... sin que vale misteriosa y congratulantemente 0.

El ancho de palabra de nuestro procesador de 32 bits, por aquello de 8 bits es 1 byte, hablamos de un procesador de 4 bytes (hablamos tambin de un ancho de palabra de 4 bytes). Por tanto: Buffer1 que se declara de 5 bytes Ocupa en memoria realmente 2 palabras, es decir, 8 bytes. Buffer2 que se declara de 10 bytes Ocupa en memoria realmente 3 palabras, es decir, 12 bytes. Entonces, el cdigo que hemos visto anteriormente lo que hace realmente es lo siguiente (observad el cdigo anterior mientras vais leyendo esta parte): 1- En el main comienza declarando la variable x. 2- Acto seguido inicializa la variable x a cero. 3- Llama a la funcin, guardando en la pila la direccin de la siguiente lnea de cdigo a ejecutar despus de la funcin (llamada ret o direccin de retorno), en nuestro caso la lnea x=1; 4- La intencin del cdigo de la funcin es modificar la direccin de retorno (ret) de la pila para que, al terminar la funcin, en lugar de volver a la lnea x=1; salte a la la lnea printf(%d\n,x); De forma que x no valga 1 sin 0. Vamos a ver en detalle cmo sobreescribe nuestro cdigo la direccin de retorno: Hemos dicho que buffer1[ ] ocupa 2 palabras, es decir, 8 Bytes. El spf son 4 bytes. El ret que hemos declarado (puntero a entero) ocupa 1 Byte. Por tanto desde buffer 1 necesitamos 8 Bytes para llegar a nuestro puntero ret, otro byte para pasar nuestro ret y llegar a spf, y otros 4 bytes para llegar al ret verdadero y poder asignarle un valor con una direccin de retorno vlida (Nosotros buscaremos la direccin de la instruccin printf("%d\n",x); para de esta manera saltarnos la asignacin x=1;) ...Y a eso nos dedicaremos en la siguiente pgina:

Comienza la prctica: 1.- Compilar nuestro ejecutable bof.c (de momento) sin protecciones: snakingmax$taller2:~$ gcc -z execstack -fno-stack-protector bof.c -o bof 4.- Debuggear nuestro ejecutable bof: snakingmax$taller2:~$ gdb bof 5.- Desensamblar (muestra en ASM) la funcin main una vez dentro del debugger: (gdb) dissasemble main

function(1,2,3); Como vemos la llamada a la funcin se encuentra en la direccin de memoria

0x080484a6 Sabemos esto porque aparece una instruccin CALL function.


x = 1; La siguiente instruccin que ser ejecutada se encuentra en la direccin de memoria

0x080484aB
printf("%d\n",x);Y la que queremos que ejecute (es la que est inmediatamente a continuacin)

0x080484b3 Entonces vamos a ver a qu distancia est en el main: _ <main+45> <main+53> -------------------8 Bytes de distancia Est a 8 en relacin a nuestra direccin de retorno.De ah que hagamos esto para saltar a la instruccin del printf sin pasar por x=1: (*ret) += 8;

Por tanto si compilamos y ejecutamos ./bof deberamos obtener el valor 0 a pesar de la asignacin x=1.

Hemos visto cmo se puede modificar la direccin de retorno de una funcin. Entiendo que todo esto es un poco lioso y hay que tener claro punteros en C o C++, nadie dijo que fuese sencillo, por eso voy a hacer unos dibujitos que van a explicar bien el funcionamiento del cdigo en la siguiente pgina.

Explicacin del cdigo en 3 pasos:

Por ltimo explicar el sentido de esta prctica: Esta prctica ha consistido en observar cmo se modifica la direccin de retorno de una funcin en la pila, y manejarnos con el debugger y el desensamblado. En la prxima entrega veremos cmo aprovechar el desbordamiento del buffer con un programa vulnerable que interacte con el usuario. Problemillas y dudas en el taller del foro de underc0de :P

Hasta entonces... Un saludo, SnakingMax