En la Parte 1 logramos controlar el registro EIP (Instruction Pointer), pero ¿qué hacemos con ese control?
La respuesta está en el stack (pila), una estructura de datos fundamental:
Característica
Descripción
Tipo
LIFO (Last In, First Out)
Función
Almacena datos temporales, direcciones de retorno, variables locales
Crecimiento
Hacia direcciones de memoria más bajas
Límites
Definidos por el SO y la arquitectura
graph TB
subgraph STACK["📚 Stack en x86 (32-bit)"]
TOP["🔴 ESP (Top) - Dirección actual"]
DIR1["Dirección más alta"]
DIR2["... datos locales... "]
DIR3["Datos que escribimos<br/>(shellcode)"]
DIR4["Dirección más baja"]
end
STACK --> CREC["⬇️ Crece hacia direcciones<br/>más bajas"]
📊 Anatomía de nuestro Buffer Overflow
Después del crash del EIP en la Parte 1, nuestro payload se estructura así:
┌─────────────────────────────────────────────┐
│ BUFFER (2606 bytes "A") │ ← Rellena el espacio
├─────────────────────────────────────────────┤
│ EIP (4 bytes "B") = 0x42424242 │ ← Control del flujo
├─────────────────────────────────────────────┤
│ ESPACIO DISPONIBLE (C's, D's, etc.) │ ← 👈 AQUÍ VA EL SHELLCODE
├─────────────────────────────────────────────┤
│ Variables locales / Stack frame anterior │
└─────────────────────────────────────────────┘
⬇️ Cuando EIP salta a ESP
👉 Se ejecuta el shellcode
Stack Overflow vs Buffer Overflow
Buffer Overflow: Escribimos más datos de los permitidos en un buffer
Stack Overflow: Aprovechamos eso para sobrescribir datos en el stack
Cómo funciona el ESP
🎯 El registro ESP (Extended Stack Pointer)
Aspecto
Detalle
Nombre
Extended Stack Pointer
Tamaño
4 bytes (32-bit)
Función
Apunta a la “pila” actual (LIFO)
En nuestro caso
Apunta a los datos que escribimos (nuestro shellcode)
sequenceDiagram
participant CPU
participant EIP as Registro EIP
participant ESP as Registro ESP
participant STACK as Stack Memory
CPU->>EIP: EIP = 0x42424242 (nuestro control)
CPU->>ESP: ESP apunta a 0xC's (nuestro shellcode)
EIP->>STACK: Salta a la dirección del ESP
STACK->>CPU: Ejecuta shellcode en el ESP
CPU->>CPU: RCE (Remote Code Execution) ✓
Estrategia: JMP ESP
🎪 El problema
Si ponemos directamente la dirección del ESP en el EIP, no funcionará porque:
El ESP es dinámico: Su valor cambia en cada ejecución
No es predecible: No sabemos exactamente dónde estará
Necesitamos una referencia fija: Un lugar en memoria que SIEMPRE nos lleve al ESP
✅ La solución: Buscar una instrucción JMP ESP
En lugar de saltar directamente a una dirección desconocida, buscamos una instrucción que ya exista en la memoria del programa:
JMP ESP ; Salta a la dirección apuntada por ESP
Esta instrucción puede encontrarse en:
El código del programa vulnerable (SLMail)
Las librerías que usa (. dll files)
El SO
📍 Cómo funciona
graph TD
A["Función vulnerable<br/>sobrescribida"] -->|RETURN| B["Ejecuta instrucción<br/>JMP ESP"]
B -->|JUMP| C["Código en el ESP<br/>(nuestro shellcode)"]
C -->|Ejecuta| D["Reverse shell / RCE"]
style A fill:#ffcccc
style B fill:#ffff99
style C fill:#99ff99
style D fill:#90EE90
🔎 Pasos para encontrar JMP ESP
1️⃣ Con mona.py (Immunity Debugger)
!mona jmp -r esp
Esto buscará en toda la memoria instrucciones como:
Crea un bytearray excluyendo los badchars que ya conoces.
📊 Resumen visual de la Parte 2
graph TD
A["Tenemos control del EIP<br/>0x42424242"] --> B["¿Cómo ejecutar código?"]
B --> C["Usar el ESP que apunta<br/>a nuestro shellcode"]
C --> D["Problema: ESP es dinámico"]
D --> E["Solución: JMP ESP"]
E --> F["Problema: Bad characters<br/>corrompen el shellcode"]
F --> G["Solución: Analizar e<br/>identificar badchars"]
G --> H["Siguiente: Generar shellcode<br/>sin badchars"]
style H fill:#90EE90
💡 Conceptos clave
Stack Pointer (ESP)
El ESP es un registro que siempre apunta a la “cima” del stack (última posición de datos). Si logramos que el EIP salte al ESP, ejecutaremos código en esa ubicación.
Direcciones con NULL bytes
En lenguaje C, \x00 termina strings. Si la dirección del JMP contiene 0x00, se truncará y no funcionará:
\x00 - Null (termina strings)
\x0A - LF (salto de línea)
\x0D - CR (retorno de carro)
\x20 - Space (algunos lo filtran)
🔗 Siguientes pasos
flowchart LR
A["✓ Entendemos ESP<br/>y JMP ESP"] --> B["Siguiente: <br/>Parte 3"]
B --> C["Generar Bytearray<br/>y detectar Badchars"]
C --> D["Crear shellcode<br/>sin badchars"]
D --> E["Ejecutar exploit<br/>completo"]
📚 Comandos clave para esta fase
Comando
Función
En qué fase
!mona jmp -r esp
Buscar JMP ESP
Immunity Debugger
!mona bytearray
Generar bytearray sin badchars
Immunity Debugger
!mona compare -a <dirección>
Comparar memoria con patrón
Immunity Debugger
python test_badchars.py
Enviar bytearray a la víctima
Máquina atacante
Resumen de Parte 2
✅ El ESP apunta a nuestro shellcode en el stack
✅ Necesitamos JMP ESP para redirigir la ejecución al ESP
✅ Los bad characters pueden corromper el shellcode
✅ Debemos identificar y evitar los bad characters