xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



Exploit Development

Fundamentals


Antes de pasar a explotar, es necesario aprender algunos conceptos básicos, las explotaciones se realizarán en arquitectura x86 ya que conociendo como se realiza en ella se puede adaptar muy facilmente a x64 con un par de cambios minimos


Memory


Cuando se ejecuta un programa en Windows a este se le asigna memoria, desde la dirección mas baja 0x00000000 hasta la mas alta 0x7fffffff entra dentro del rango de "user-mode" y de la dirección 0x80000000 hasta 0xffffffff en "kernel-mode".

Al crearse un proceso se crean con el las estructuras PEB y TEB:

• PEB: Contiene los parametros de "windows-user" en el proceso actual, como lo son la dirección al ejecutable o el puntero a el loader asi como información sobre el heap.

• TEB: Contiene información sobre el hilo, como lo puede ser la dirección de la estructura PEB, ubicación a la pila del hilo actual o el puntero hacia la estructura SEH.


Stack


Cuando se crea un hilo, este ejecuta codigo desde el programa o librerías, este hilo requiere un área de acceso rápido para funciones, variables e información del programa, esto se conoce como pila, cada hilo crea su propio stack o pila.

El stack trabaja bajo una estructura LIFO (Last-In-First-Out), esto significa que los últimos datos que han sido empujados usando la instrucción push, serán también los primeros en eliminarse cuando se ejecute una instrucción pop para eliminar datos.

Cuando se crea una pila el puntero a ella apunta a la parte superior, esto significa que al introducir información en la pila este puntero disminuye, lo que quiere decir que la pila crece al revés, desde la dirección mas alta hasta la dirección mas baja.


Calling Conventions


Las convenciones de llamada se refieren a la forma en que las funciones reciben los parámetros y el valor de la dirección de retorno en cada arquitectura, en x86 los argumentos se empujan a la pila junto con la dirección de retorno y esta se limpia después de hacer la llamada a la función para poder utilizarse después.

Cuando se llama a una función esta necesita saber a donde apuntar cuando finaliza su ejecución asi que antes de realizar la llamada se guarda la dirección de la siguiente instrucción en el stack y cuando esta llegue al final y ejecute la instrucción ret tomará la dirección guardada en el stack y volverá ahí para ejecutar lo que esta en ella.


Registers


Para ser eficientes al ejecutar un codigo la CPU utiliza 9 registros de 32 bits, los registros son pequeñas ubicaciones donde los datos se pueden leer y/o modificar eficientemente, algo a tener en cuenta es que cada registro se puede dividir en subregistros de 16 y/o 8 bits como se muestra en la siguiente tabla.

En el caso del registro de 32 bits (EAX) se divide en un subregistro de 16 bits (AX) que a su vez se puede dividir en 2 subregistros de 8 bits (AH) y (AL) respectivamente

Varios de los registros se usan como registros de propósito general para almacenar datos temporales, algunos de los propositos de cada uno son:

• EAX (Accumulator): Instrucciones aritméticas y lógicas.
• EBX (Base): Puntero base para direcciones de memoria.
• ECX (Counter): Puntero usado como contador.
• EDX (Data): Direccionamiento, multiplicación y división.
• ESI (Source Index): Puntero a la fuente en operaciones de cadenas.
• EDI (Destination Index): Puntero al destino en operaciones de cadenas.

Otros registros bastante importantes que almacenan punteros son:

• ESP (Stack Pointer): Almacena el puntero a la parte superior del stack.
• EBP (Base Pointer): Puntero a la parte superior de la pila cuando se llama a la función.
• EIP (Instruction Pointer): Apunta a la dirección de la siguiente instrucción a ejecutar.


Endianness


Hay diferentes formas de representar los valores en memoria, las mas comunes son:

• Big Endian: Adoptado por Motorola y otros, consiste en representar los bytes en el orden natural, por lo que el valor hexadecimal 0x01020304 se guardaría en memoria con los bytes ordenados como 01 02 03 04 por lo que no sufriria cambios.

• Little Endian: Adoptado por Intel, el mismo valor 0x01020304 se guardaría en orden inverso con los bytes 04 03 02 01 de manera que se hace más intuitivo el acceso a datos, porque se efectúa fácilmente de forma incremental de menos a más relevante.


Example


Un ejemplo de codigo vulnerable seria el siguiente, este llama a la función vuln() pasándole la string del primer argumento, esta función copia la string hacia la variable dest con solo un buffer de 64 bytes, asi que... ¿que pasa si se envian más bytes?

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

void vuln(char *src) {
    char dest[64];
    strcpy(dest, src);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s <string>\n", argv[0]);  
        return 1;
    }

    vuln(argv[1]);
    return 0;
}

❯ i686-w64-mingw32-gcc code.c -no-pie -o file.exe  

Luego de compilarlo podemos pasarlo a un desensamblador como IDA donde vemos que el puntero donde guardará los datos copiados apunta a ebp - 72

Usaremos como argumento una cadena de 72 A's que se escribiran antes de ebp, 4 B's que sobrescribiran ebp, 4 C's que se escribirán el stack como las 20 D's

❯ python3 -q
>>> ("A" * 72) + ("B" * 4) + ("C" * 4) + ("D" * 20)
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDDDDDDDDDDDDDDDDD'  
>>>

Para analizar el programa necesitamos un debugger, en este caso usaremos WinDbg

Si nos detenemos con un breakpoint en el ret podemos ver que escribimos A's hasta antes de sobrescribir ebp que ahora vale 0x42424242 (valor de BBBB en hexadecimal) y las C's y D's se guardan justo después de esto en el stack

Al ejecutar el ret intentara apuntar a la dirección de memoria guardada en la parte superior del stack pero como esa dirección la hemos sobrescrito con CCCC intentara apuntar a 0x43434343 y al no encontrar esa dirección el programa corrompe

Resumiendo, el Buffer Overflow mas conocido generalmente ocurre cuando no se controla la cantidad de datos que se escriben y al sobrescribir la dirección de retorno esta apuntará a un valor que el atacante introduce por lo que puede tomar control