q3rv0

Follow the white rabbit….

Desbordando El Buffer en Linux X86 (I)

Cada vez que corremos un proceso en la maquina, se crea una memoria virtual para dicho proceso, ese segmento se divide en 3 partes, el texto, los datos y por ultimo el stack, que es donde nos vamos a concentrar para realizar la explotacion de este tipo de vulnerabilidad.

Que es el stack?

El stack es un segmento en la memoria que se encarga de almacenar datos y a su vez recuperarlos, administra la informacion en modo LIFO (last in first out), quiere decir que el ultimo dato en entrar es el primero en salir, un ejemplo mas sencillo seria comparar el stack con una pila de cd’s, en el que el primer cd hace de base de la pila y para llegar a el hay que retirar uno por uno los que se encuentran encima. Entonces cada vez que se ingresa un dato al stack se utiliza una instruccion denominada PUSH y cuando se retira un dato se llama a la instruccion POP, algo similar a un array. Los registros del procesador.

El procesador cuenta con varios registros que cumplen diversas tareas en el stack, por el momento solo voy a mencionar a 3 de ellos.

EIP – Registro que almacena la direccion de memoria de la proxima funcion que se va a ejecutar.

ESP – Apunta a la parte superior del stack

EBP – Aputa a la base del stack Ahora que ya se explicaron algunos conceptos basicos sobre el tema, vayamos al nudo del tutorial. Que es un Buffer overflow?

Empezemos por definir que es un buffer, no es mas que un espacio de memoria en donde se vuelcan datos para determinada accion por ejemplo, tengo el siguiente programa en c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

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

    char buffer[10];

    strcpy(buffer, argv[1]);

    printf(buffer);

    return 0;
}

Donde se define un buffer de 10 bytes, luego se lo pasa a la funcion strcpy que almacena en el buffer el argumento insertado por el usuario, esta misma no se encarga de controlar el tamaño limite del buffer por lo tanto si se introduce una mayor cantidad de caracteres, el espacio desbordara y comenzara a sobrescribir los registros de memoria cercanos al buffer.

Antes que nada, vamos a desactivar ASLR

ASLR en criollo es un sistema de seguridad que vuelve aleatoria las direcciones de la memoria virtual.

1
echo 0 > /proc/sys/kernel/randomize_va_space

y compilaremos el prog con los siguientes flag de gcc.

1
gcc -ggdb -fno-stack-protector -mpreferred-stack-boundary=2 -o prog prog.c

Para quitarle algunas protecciones al stack que le agrega gcc al compilar.

Probemos pasandole mas de 10 caracteres al programa.

Como se ve el programa crashea, veamos que pasa por dentro con gdb.

1
gdb prog

Le pasamos los argumentos

1
run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

y una vez que el prog nos patee miramos en los registros a ver que encontramos?

1
info registers

Como se ve en la imagen, se sobrescribieron dos registros como EIP y EBP con x41, que representa el caracter A en hex.

Ahora por que crashea?, como explique anteriormente, el registro EIP contenia la direccion de la proxima funcion a ejecutar, pero al ser sobreescrito con 4 A’s, EIP termina apuntando a una direccion invalida.

Sabiendo que podemos inyectar en EIP, podriamos controlar el flujo del programa y mandarlo a la direccion que se nos ocurra. Controlando el flujo de ejecucion.

Vamos con otro ejemplo pero un poco mas divertido.

Le agregue la funcion owned() al programa anterior, que lo unico que hace es imprimir un texto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void owned(){
    printf("Owned :)");
}


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

    char buffer[10];

    strcpy(buffer, argv[1]);

    printf(buffer);

return 0;
}

Pero no vamos a llamarla en el code, sino que vamos a inyectarle la direccion de memoria de owned() al EIP para asi alterar la salida del prog.

compilamos y abrimos con gdb.

Ahora tenemos que saber el punto exacto donde empieza a sobrescribirse EIP.

Le pasamos 14 A’s – nada

Le pasamos 15 A’s y se puede ver como empieza a infectarse el EIP

Como una direccion de memoria esta compuesta por 4 bytes le sumamos 3 y tenemos el EIP bien pintado con 18 A’s.

Ahora solo nos queda averiguar la direccion donde la funcion owned() comienza a ser pusheada al stack, para esto vamos a desensamblarla

1
disas owned

Se ven las instrucciones del codigo ensamblador, la primera linea comienza a pushear la funcion asi que usaremos esa direccion de memoria: 0x08048408

Ahora que tenemos la direccion con la que editaremos el EIP, solo basta restar 4 A’s e incluirla.

A*14 + 0x08048408

Voy a utlizar python para realizar la inyeccion. como se ve, se utiliza el escape \x para codificar los datos.

Pero si miramos la direccion a donde apunta el EIP vemos que esta al revez, esto pasa por que los procesadores Intel utilizan el sistema little-endian por lo tanto tendremos que invertir la direccion antes de ingresarla.

Y ahora si logramos ver la salida de la funcion owned.

Y si en vez de direccionar a una funcion, lograramos llegar hasta un shellcode alojado en la memoria?, eso se vera en el proximo tutorial, saludos!