Shellcode
El shellcode es un trozo de código máquina —escrito directamente en ensamblador/bytes— que realiza una acción concreta al ejecutarse, clásicamente lanzar una shell (execve("/bin/sh")). Es el “payload” que se ejecuta una vez que tu exploit consigue redirigir el flujo a una zona de memoria que controlas y que es ejecutable. Hoy, con NX, el shellcode directo es menos común (se recurre a ROP), pero sigue siendo fundamental cuando hay regiones ejecutables (sin NX, mmap RWX, JIT).
Qué hace un shellcode típico
Sección titulada «Qué hace un shellcode típico»# el objetivo clásico: execve("/bin/sh", NULL, NULL)# en x86-64, vía syscall execve (número 59):RAX = 59 ; número de syscall execveRDI = "/bin/sh" ; primer argumento (ruta)RSI = 0 ; argvRDX = 0 ; envpsyscallObtener shellcode (no reinventarlo)
Sección titulada «Obtener shellcode (no reinventarlo)»# pwntools genera shellcode para muchas arquitecturasfrom pwn import *context.arch = 'amd64'sc = asm(shellcraft.sh()) # shellcode que lanza /bin/sh# o ensamblar el tuyosc = asm('mov rax, 59; ...')# bases de datosshell-storm.org/shellcode/ # shellcodes listos por arquitectura/objetivomsfvenom -p linux/x64/exec CMD=/bin/sh -f python # MetasploitEntregar y ejecutar el shellcode
Sección titulada «Entregar y ejecutar el shellcode»# requisito: una región de memoria EJECUTABLE donde puedas escribir y a la que saltes# sin NX: meter el shellcode en el stack y saltar a él (ver pwn-stack)payload = nop_sled + shellcode + relleno + p64(direccion_al_sled)# NOP sled (\x90...): aterriza en cualquier punto del sled y "desliza" hasta el shellcode# con NX: no hay stack ejecutable -> usar ROP para mmap/mprotect RWX y luego saltar,# o directamente ROP a execve (ret2syscall, ver pwn-rop)El NOP sled (\x90 repetido) relaja la precisión: no necesitas acertar la dirección exacta del shellcode, basta con caer en cualquier punto del colchón de NOPs.
Restricciones comunes (bad chars)
Sección titulada «Restricciones comunes (bad chars)»Muchos exploits tienen caracteres prohibidos (el \x00 casi siempre, porque corta strings; a veces \x0a, \x20…). El shellcode debe evitarlos:
# shellcode "null-free" / alfanumérico según la restricción# codificadores (encoders) que evitan bad chars (msfvenom -b '\x00\x0a')# shellcode auto-decodificante (se descifra en runtime)# identificar bad chars: probar byte a byte qué se corrompe en memoriaShellcode en otros contextos
Sección titulada «Shellcode en otros contextos»# Windows: shellcode distinto (llamadas a WinAPI, resolución de kernel32...)# staged vs stageless: pequeño stager que descarga el resto, o todo en uno# egghunter: cuando el espacio es pequeño, busca en memoria un marcador + payload# en explotación remota se combina con reverse shell (ver fund-cli)Para la defensa
Sección titulada «Para la defensa»- NX/DEP: la mitigación directa — el stack/heap no ejecutable impide ejecutar shellcode inyectado.
- ASLR/PIE: dificultan saltar a una dirección fija.
- W^X (write XOR execute): ninguna región escribible es ejecutable (evitar mmap RWX).
- CFI / shadow stack, detección de regiones RWX anómalas (EDR), control de JIT.
- Las mismas mitigaciones que frenan el control de RIP (canaries, etc.) impiden llegar a ejecutar el shellcode.
Checklist de prueba
Sección titulada «Checklist de prueba»- Definir qué debe hacer el shellcode (shell/execve/reverse)
- Generarlo (pwntools shellcraft / msfvenom / shell-storm)
- Identificar bad chars y evitarlos (encoder/null-free)
- ¿Hay región ejecutable? (sin NX, mmap RWX, mprotect)
- NOP sled + shellcode + salto a la región
- Si NX: ROP a mprotect/execve en vez de shellcode directo
- Ejecución confirmada (shell)
- Blue: ¿NX, W^X, ASLR activos?