Skip to content

Latest commit

 

History

History
348 lines (241 loc) · 14.5 KB

File metadata and controls

348 lines (241 loc) · 14.5 KB

0x600 — Countermeasures

Este capítulo invierte la perspectiva: en lugar de explotar, defender. Erickson cubre todo lo que el sistema hace para evitar que un overflow termine en RCE, y luego enseña a saltarse cada protección. La lección clave: las mitigaciones no eliminan los bugs, suben el coste.

Índice


0x610 — Detección

Las defensas se agrupan en dos familias:

Tipo Qué hace Ejemplos
Detectivo Avisa cuando algo va mal logs, IDS, EDR, file integrity monitors
Preventivo Impide que algo vaya mal NX, ASLR, canary, capabilities, seccomp

Un atacante competente trata las dos por separado: rompo la prevención y evado la detección. El libro se centra en la prevención técnica; aquí añado contexto moderno (EDR, telemetría, SIEM).


0x620 — Daemons del sistema

Un daemon es un proceso de fondo que escucha y reacciona a eventos. Históricamente son el punto blando de las máquinas Linux: corren como root, escuchan en red, y cualquier overflow en uno de ellos es un RCE remoto sin autenticación.

Daemons clásicos: sshd, httpd, bind, cups, postfix. Modernos: systemd-*, polkit, dbus-broker, avahi.

Reducir la superficie de ataque

# qué escucha en mi máquina
ss -tlnp                  # TCP listening con PID
ss -ulnp                  # UDP listening
systemctl list-units --type=service --state=running

# desactivar lo que no uso
sudo systemctl disable --now cups avahi-daemon

Aislamiento

Cada daemon debería correr con el menor privilegio posible:

  • User dedicado (no root) si la función no necesita privilegios.
  • Capabilities en lugar de SUID. cap_net_bind_service en lugar de root para escuchar en puerto <1024.
  • systemd sandboxing: PrivateTmp=, ProtectSystem=full, NoNewPrivileges=, CapabilityBoundingSet=, SystemCallFilter= (seccomp).
  • chroot/pivot_root o namespaces (lo que hacen Docker / Podman / nsjail).
  • seccomp filtrando syscalls que el daemon nunca usa.

Ver /etc/systemd/system/ y systemd-analyze security <unit> para ver el score de aislamiento.


0x630 — Herramientas defensivas

Herramienta Para qué Detecta
tripwire / aide File integrity monitoring cambios en /bin, /etc
auditd Auditoría de syscalls acceso a archivos, ejecución
fail2ban Bloquea IPs por fallos repetidos brute force en SSH/HTTP
selinux / apparmor MAC (Mandatory Access Control) un proceso compromise no puede salir de su perfil
iptables / nftables Firewall tráfico no esperado
osquery SQL sobre estado del sistema hunting reactivo
falco / tetragon Detección runtime con eBPF actividad anómala en kernel
lynis Auditoría manual configuración floja

Para un Linux moderno bien defendido: AppArmor + auditd + fail2ban + nftables restrictivo + systemd hardening en cada daemon.


0x640 — Logs

/var/log/ es el primer sitio que mira el defensor y el primero que limpia el atacante. Logs típicos:

Archivo Qué contiene
/var/log/auth.log (Debian) o /var/log/secure (RHEL) Logins, sudo, SSH
/var/log/syslog o /var/log/messages Mensajes generales del kernel y daemons
/var/log/kern.log Kernel
/var/log/btmp Logins fallidos (binario, ver con lastb)
/var/log/wtmp Logins exitosos (binario, ver con last)
/var/log/utmp Sesiones activas (who)
/var/log/audit/audit.log auditd
/var/log/journal/ systemd-journald (binario, ver con journalctl)

Reglas para defensores

  • Logs lejos del host. Un atacante con root borra logs locales en segundos. Forwardear a un servidor externo (rsyslog, journald-remote, vector, fluentbit) en cuanto se generan.
  • Hash chain o append-only. Logs en una particion chattr +a, o mejor, en un sistema externo donde el host no tiene permisos de borrado.
  • Correlación. Un sudo solo no dice nada; un sudo + cambio en /etc/passwd + nueva sesión SSH desde IP nueva = chain de actividad sospechosa. Eso es el trabajo de un SIEM.

Trucos del atacante

# borrar entradas wtmp/btmp del propio user
utmpdump /var/log/wtmp > /tmp/wtmp.txt
sed -i '/atacante/d' /tmp/wtmp.txt
utmpdump -r < /tmp/wtmp.txt > /var/log/wtmp

# anti-forensics: alterar timestamps con touch
touch -r /etc/hostname /tmp/payload      # mismo mtime que /etc/hostname

Por eso el hardening estándar incluye logs externos, FIM y monitoreo de borrados.


0x650 — Lo obvio que se pasa por alto

Erickson dedica una sección a errores de configuración: la mayor parte de los compromises reales no son 0-days, son detalles humanos. Ejemplos modernos:

  • Buckets S3 públicos con backup de la base de datos.
  • .env o config.php accesibles vía web (/.env returns 200).
  • Tokens de API en commits de Git público (busca con gitleaks, trufflehog).
  • SSH con autenticación por password permitida y root accesible.
  • Default credentials (admin:admin) en routers, paneles web, IPMI.
  • Servicios internos expuestos a Internet (Redis sin auth, MongoDB sin auth, Elasticsearch sin auth).
  • DNS rebinding en panels de admin que asumen "solo localhost".

La regla: asume que el atacante vio la diapositiva con la contraseña por defecto antes que tú.


0x660 — Camuflaje avanzado

Para el atacante:

  • Process hiding: rootkits LKM ocultan procesos al /proc. Detectables con unhide o chkrootkit.
  • Library injection: LD_PRELOAD para sobrescribir funciones de libc en un proceso.
  • In-memory only payloads: no tocan disco. Detectables con EDR que vigila mmap+exec.
  • Living off the land (LOLBAS / LOLBins): usar binarios firmados por la distro (bash, python, awk, find -exec) en lugar de meter binarios propios. Más sigiloso, evita signature-based detection.

Process injection en Linux (educativo)

ptrace(PTRACE_ATTACH, pid) + PTRACE_POKETEXT + PTRACE_SETREGS permite inyectar código en otro proceso. /proc/<pid>/mem permite leer/escribir memoria de procesos del mismo UID.

Mitigaciones:

  • /proc/sys/kernel/yama/ptrace_scope = 1 (default): solo el padre puede attach.
  • /proc/sys/kernel/yama/ptrace_scope = 2: solo con CAP_SYS_PTRACE.
  • /proc/sys/kernel/yama/ptrace_scope = 3: ptrace deshabilitado.

0x670 — La infraestructura completa

Defender un servidor solo no sirve si la red está abierta. La defensa moderna es defense in depth:

   Internet
      │
   ┌──┴───┐
   │ WAF  │           filtrado L7 (Cloudflare, ModSec)
   └──┬───┘
      │
   ┌──┴───┐
   │  LB  │           terminación TLS, rate limiting
   └──┬───┘
      │
   ┌──┴───┐  ───►  segmentación VPC: solo el LB habla con app
   │ APP  │
   └──┬───┘
      │
   ┌──┴───┐  ───►  DB en subnet privada, sin Internet egress
   │  DB  │
   └──────┘

Cada capa falla cerrada: si una se compromete, las siguientes deberían contener el daño.


0x680 — Smuggling y restricciones de buffer

A veces el buffer es muy pequeño y no cabe shellcode tradicional. Soluciones:

Staged shellcode

El shellcode etapa 1 (pequeño) hace read(0, buf, n) para leer una etapa 2 mucho más grande del mismo socket o fd. Pwntools shellcraft.staged hace esto.

Egghunter

Si no controlas dónde aterrizas pero hay un payload tuyo en algún lado del proceso (env, otro buffer, heap chunk), puedes incluir un egghunter: un mini-shellcode que escanea memoria buscando un marker (EGG! EGG!) y salta al payload que viene después. ~30 bytes.

Encoding alfanumérico

Si el filtro acepta solo [A-Za-z0-9], usar msfvenom -e x86/alpha_mixed. La idea: codificar bytes binarios usando solo instrucciones x86 cuyos opcodes caen en ese rango (push, pop, varios inc/dec).

Restricciones modernas

strncpy, snprintf, _FORTIFY_SOURCE cortan tu payload. Soluciones:

  • Sobrescribir solo el byte bajo de un puntero (partial overwrite) — solo modifica los últimos 8/16 bits, evita los nulls del medio.
  • Encadenar varias escrituras pequeñas (típico en format string %hhn).
  • Usar bugs lógicos del programa para introducir el payload por una ruta que no pasa por el filtro.

0x6a0 — Stack canary

Compilado con -fstack-protector-strong (default en gcc moderno), antes de la dirección de retorno se guarda un valor random de 8 bytes (el canary). Antes del ret, el código comprueba que el canary sigue intacto. Si no, llama a __stack_chk_fail y el programa muere con *** stack smashing detected ***.

Cómo se ve en assembler:

function:
    push rbp
    mov  rbp, rsp
    sub  rsp, 0x40
    mov  rax, qword ptr fs:[0x28]    ; lee canary del TLS
    mov  qword ptr [rbp - 8], rax    ; lo guarda justo antes del saved RBP

    ; ... código ...

    mov  rax, qword ptr [rbp - 8]
    sub  rax, qword ptr fs:[0x28]    ; canary == el original?
    je   .ok
    call __stack_chk_fail            ; no → abort
.ok:
    leave
    ret

Cómo se evade

  • Leak. Una format string vulnerability lee el canary y lo reescribimos con el mismo valor.
  • Brute-force. Solo en fork servers donde el canary se hereda y los crashes no rotan: bytewise (256·8 = 2048 intentos máx). En cualquier otro caso (proceso fresco), el canary cambia → no se puede.
  • Bypass por estructura. Si el bug sobrescribe variables locales antes del canary y el programa hace algo malo con esas variables antes del ret, no importa romper el canary.
  • Sobrescribir punteros a estructuras de datos (vtables, function pointers en heap, exception handler) — no pasa por la pila, no tropieza con el canary.

codigo/0x6b0/canary_test.c compila el mismo overflow con y sin canary y muestra la diferencia.


0x6b0 — Stack no ejecutable (NX)

NX (No-eXecute), también llamado XD o DEP, marca páginas de memoria como no ejecutables. La pila y el heap son no-ejecutables por defecto.

Comprobar:

$ checksec --file=binario
RELRO     STACK CANARY   NX   PIE   ...
Full      Canary found   NX   PIE   ...

Con NX, meter shellcode en la pila y saltar ahí da SIGSEGV en lugar de ejecutar.

ROP (Return-Oriented Programming)

La técnica que ganó NX. En vez de meter código nuevo, encadenamos gadgets (secuencias de pocas instrucciones que terminan en ret) que ya están en el binario o en libc. Cada gadget hace una micro-operación, y la cadena completa equivale a un programa.

Ejemplo: para llamar a system("/bin/sh") en x86-64 SysV:

gadget1: pop rdi ; ret    ← carga rdi
data1  : addr de "/bin/sh" en libc
gadget2: ret              ← alineamiento (movaps en system necesita rsp%16==0)
target : addr de system en libc

Cuando ret ejecuta, salta a gadget1, que hace pop rdi (toma data1 de la pila), luego ret (salta a gadget2), que hace ret (alineamiento), que salta a system. system ve rdi apuntando a /bin/sh.

Encontrar gadgets: ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6.

mprotect ROP

Si no te apetece escribir todo en gadgets, llama a mprotect(stack_addr, size, PROT_READ|PROT_WRITE|PROT_EXEC) con ROP, lo que vuelve a hacer la pila ejecutable. Luego salta a tu shellcode metido en la propia pila.


0x6c0 — ASLR y PIE

ASLR (Address Space Layout Randomization) aleatoriza las bases de:

  • Stack
  • Heap
  • Bibliotecas dinámicas (libc, ld.so)
  • Mapas anónimos (mmap)
  • Y, si está PIE, el propio binario.

Cada ejecución del proceso tiene direcciones distintas. Esto rompe los exploits que tenían direcciones hardcodeadas.

Niveles en Linux

$ cat /proc/sys/kernel/randomize_va_space
2     # 2 = ASLR completo (stack+heap+libs+brk), default
      # 1 = stack+libs aleatorios, brk no
      # 0 = OFF

Para reproducir el libro:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space   # off
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space   # on

PIE (Position Independent Executable)

Compilar con -pie -fPIE hace que el propio binario también sea aleatorio. Sin PIE, _start, main, printf@plt están en direcciones fijas (0x400xxx típico) — ROP es trivial. Con PIE, también la base del binario se aleatoriza, así que necesitas un leak previo.

Verificar:

$ file binario | grep "PIE"
binario: ELF 64-bit LSB pie executable ...

Cómo se evade

  1. Info leak. Un format string, un read excesivo, o cualquier vector que filtre direcciones del proceso. Una vez tienes un puntero a libc, puedes calcular la base y de ahí cualquier función.
  2. Brute force. Solo en 32-bit (16 bits aleatorios → 65536 intentos en un fork-server). En 64-bit hay 28+ bits aleatorios → impracticable.
  3. Partial overwrite. Sobrescribir solo los bytes bajos de un puntero. Si la página de la libc tiene 4 KB (12 bits), los 12 bits bajos están fijos; los siguientes son aleatorios pero pequeños. Saltar dentro de la misma página no necesita conocer la base.
  4. Heap spray + ret-into-heap. Ya casi no funciona (NX en heap), pero variantes con JIT spray sí en navegadores.

Comprobando todo de un vistazo

$ checksec --file=./binario
RELRO          STACK CANARY      NX            PIE
Full RELRO     Canary found      NX enabled    PIE enabled

Tabla de protecciones modernas en distros default:

Distro Stack canary NX ASLR PIE RELRO
Debian/Ubuntu (gcc default) Full
Kali Full
Alpine (musl) Full

Para reproducir el libro tienes que apagar todas explícitamente, como hace nuestro Makefile en las secciones inseguras.


Siguiente: 0x700 — Cryptology.