Heap exploitation: Use-After-Free, tcache poisoning y House of X
· 9 min de lectura
El heap es superficie de ataque clave en binarios modernos. Aprende heap internals (ptmalloc2, tcache, fastbins), primitivas clásicas (UAF, double free, heap overflow), técnicas avanzadas (House of Force, House of Spirit, tcache poisoning), y cómo CTFs y bug bounties las explotan.
Heap exploitation es la disciplina de corromper el allocator de memoria dinámica para obtener control de ejecución. Mientras stack buffer overflows se volvieron difíciles gracias a stack canaries, NX, ASLR y CFI, el heap sigue siendo terreno fértil: allocators como ptmalloc2 (glibc) mantienen metadata inline con los datos del usuario, y cualquier write corrupto puede encadenar primitivas hasta arbitrary code execution.
Esta lección cubre la arquitectura interna del heap de glibc (chunks, bins, tcache), las primitivas de corrupción clásicas (Use-After-Free, double free, heap overflow), las técnicas avanzadas de la familia House of X, y el panorama moderno de mitigaciones. Es la décima lección de la Tanda 7.
Heap exploitation requiere fundamentos sólidos de C, punteros, y depuración con GDB. Si no dominas stack overflows y ROP (lección rop-return-oriented), revisar primero.
Arquitectura del heap glibc#

El allocator ptmalloc2 de glibc gestiona memoria dinámica. Conceptos clave:
Chunks#
Cada bloque de memoria asignado por malloc() vive en un chunk. Estructura:
+-------------------+
| prev_size (8B) | ← solo si chunk anterior está libre
+-------------------+
| size + flags (8B) | ← tamaño del chunk + bits P/M/N
+-------------------+
| user data | ← lo que devuelve malloc()
| ... |
+-------------------+
Los 3 bits bajos de size son flags: PREV_INUSE (P), IS_MMAPPED (M), NON_MAIN_ARENA (N). El allocator confía ciegamente en estos valores → corromperlos = controlar el allocator.
Cuando un chunk se libera con free(), el espacio de user data se reutiliza para punteros de la free list:
+-------------------+
| prev_size |
+-------------------+
| size + flags |
+-------------------+
| fd (forward ptr) | ← puntero al siguiente chunk libre
| bk (backward ptr) | ← puntero al anterior chunk libre
+-------------------+
Bins#
Chunks liberados van a bins según tamaño:
- Fast bins: chunks pequeños (≤160 bytes default). LIFO singly-linked list. Sin coalescing. Rápido.
- Tcache (thread-local cache, glibc 2.26+): per-thread cache de 64 bins, 7 entries cada uno. LIFO. Checks mínimos. Target principal moderno.
- Unsorted bin: chunks recién liberados antes de clasificar.
- Small bins (≤1024 bytes): FIFO doubly-linked.
- Large bins: tamaños grandes, sorted by size.
Top chunk#
El top chunk (wilderness) es el chunk al final del heap. Cuando no hay bin adecuado, malloc() corta del top chunk. Si top chunk se corrompe → control de expansión del heap.
Primitivas clásicas#
Use-After-Free (UAF)#
char *ptr = malloc(64);
free(ptr);
// ptr sigue siendo válido en el programa (dangling pointer)
// nuevo malloc(64) reutiliza el mismo chunk
char *ptr2 = malloc(64);
// ptr y ptr2 apuntan al mismo lugar
// escribir via ptr corrompe ptr2 y viceversa
UAF es la vulnerabilidad de heap más común en software real. Browsers (Chrome, Firefox), kernels, database engines la sufren. CVEs por cientos cada año.
Double free#
free(ptr);
free(ptr); // segundo free → chunk en la free list dos veces
Resultado: allocator devuelve el mismo chunk a dos malloc() diferentes. El programa cree que tiene dos buffers independientes pero comparten memoria. Glibc moderno detecta double free en fast bins (abort), pero tcache pre-2.29 no verificaba.
Heap overflow#
char *a = malloc(32);
char *b = malloc(32);
// overflow de a corrompe metadata de b
memcpy(a, user_input, 128); // overflow 96 bytes
Corromper el campo size del chunk siguiente permite controlar qué bin se usa al liberar, o crear overlapping chunks.
Técnicas avanzadas: House of X#
La familia House of X son técnicas nombradas por Phantasmal Phantasmagoria (2005) y extendidas por la comunidad CTF:
House of Force#
Corromper el top chunk size con un valor enorme (ej: 0xffffffffffffffff). Luego un malloc() con offset calculado devuelve un puntero a cualquier dirección. Requiere: heap overflow que alcance top chunk, conocer dirección objetivo.
House of Spirit#
Crear un fake chunk en el stack u otra región controlada. Luego free() ese fake chunk → va al bin. Siguiente malloc() devuelve puntero al stack/región controlada → arbitrary write.
Tcache poisoning (moderno, post-glibc 2.26)#
Tcache tiene checks mínimos. Corromper el puntero fd de un chunk en tcache → siguiente malloc() devuelve dirección arbitraria. Pre-glibc 2.32 no tenía safe-linking → trivial. Post-2.32 requiere leak de heap address.
tcache[64]: A → B → C
↓ (corrupted fd)
tcache[64]: A → TARGET_ADDRESS
malloc(64) → A
malloc(64) → TARGET_ADDRESS ← arbitrary write
House of Lore#
Corromper small bin para devolver puntero a fake chunk en stack. Similar a House of Spirit pero para small bins.
House of Orange#
Corromper top chunk size para triggerar _int_free() del viejo top chunk cuando heap expande. Combinado con unsorted bin attack → overwrite _IO_list_all → fake FILE structure → code execution. Funciona sin free() en el programa.
House of Einherjar#
Corromper prev_size y PREV_INUSE flag para causar backward consolidation con un fake chunk. Resultado: chunk retornado por malloc overlaps con área controlada.
Mitigaciones modernas#

Glibc ha añadido protecciones progresivamente:
- Safe unlinking (glibc 2.3.4+): verifica
FD->bk == P && BK->fd == Pantes de unlink. Bloquea classic unlink attack pero bypass con known addresses. - Double free detection (fast bins): abort si top de fast bin == chunk siendo freed.
- Tcache count check (glibc 2.29+): verifica que count > 0 antes de servir.
- Safe-linking / PROTECT_PTR (glibc 2.32+): XOR del fd pointer con dirección del chunk >> 12. Requiere heap leak para poisoning.
- Pointer guard: protege function pointers en glibc internals.
- ASLR del heap: base address randomizada.
- Hardened allocators: alternativas como mimalloc, PartitionAlloc (Chrome), jemalloc (Firefox), scudo (Android) con diseño security-first.
Cada mitigación ha sido bypasseada en contextos específicos. Investigadores CTF publican bypasses dentro de meses. La carrera armamentista continúa.
Herramientas#
Kit para heap exploitation:
- GDB + pwndbg/GEF: visualización de heap state, chunks, bins, tcache.
- pwntools (Python): framework para escribir exploits.
flat(),p64(),remote(). - how2heap (shellphish): repositorio de ejemplos para cada técnica.
- HeapLAB (Max Kamper): curso dedicado.
- Ghidra/IDA: análisis estático del binario para encontrar la vuln.
Workflow típico:
# GDB con pwndbg
gdb ./vulnerable_binary
pwndbg> heap # ver estado del heap
pwndbg> bins # ver estado de bins
pwndbg> tcachebins # ver tcache
pwndbg> vis_heap_chunks # visualización gráfica
CTF heap challenges#
Heap exploitation es disciplina estrella de CTFs (pwn category). Pattern típico:
- Menu-driven binary: opciones create/edit/delete/show notes/messages/etc.
- Bug: UAF (delete sin nullify pointer), heap overflow (edit sin bounds check), double free.
- Leak: usar UAF para leer fd pointer de unsorted bin → libc leak.
- Write: usar tcache poisoning o technique específica → overwrite
__free_hook(pre-2.34),__malloc_hook, o stack return address. - Shell: hook apunta a
system(), trigger con chunk conteniendo "/bin/sh".
Post glibc 2.34: __malloc_hook y __free_hook eliminados. Exploitation usa FSOP (File Stream Oriented Programming), _IO_list_all overwrite, o ROP.
Plataformas para practicar: pwnable.tw, pwnable.kr, HITCON CTF, PlaidCTF, picoCTF.
FSOP: File Stream Oriented Programming#
Post glibc 2.34, con hooks eliminados, FSOP es la técnica dominante para obtener code execution desde una arbitrary write primitive:
- Overwrite
_IO_list_all(puntero global a la lista de FILE streams). - El puntero apunta a un fake FILE structure controlada por el atacante.
- La fake structure tiene vtable apuntando a funciones controladas.
- Cuando glibc hace flush de streams (al llamar
exit(),abort(), o inclusoputs()), invoca funciones de la vtable fake. - La función vtable ejecuta
system("/bin/sh")u otro payload.
Variantes modernas: House of Apple (corromper _IO_wide_data vtable), House of Banana (abuso de _rtld_global), House of Kiwi (trigger via __malloc_assert). Cada una explota un path diferente del glibc internals para invocar la vtable corrupta.
FSOP requiere entender las estructuras internas de glibc a nivel profundo. El decompilado en Ghidra de las funciones _IO_flush_all_lockp, _IO_str_overflow, y _IO_wstr_overflow es lectura obligatoria para exploitation moderna.
Heap spraying#
En contextos donde ASLR dificulta predecir la dirección del heap, heap spraying llena el heap con datos controlados para aumentar probabilidad de que un puntero corrupto aterrice en territorio controlado. Común en browsers: JavaScript arrays de tamaño específico llenan el heap con NOP sleds o fake objects.
En explotación moderna de Chrome (V8), el spray usa ArrayBuffers de tamaño calibrado para ocupar partitions específicas de PartitionAlloc. En Firefox (SpiderMonkey), TypedArrays y Strings sirven propósito similar con jemalloc.
Heap en kernel#
Linux kernel tiene sus propios allocators: SLUB, SLAB, SLOB. Vulnerabilidades de heap en kernel (UAF en struct cred, pipe_buffer, msg_msg) son vector principal de privilege escalation.
Técnicas: cross-cache attacks, elastic objects, msg_msg spray, pipe_buffer primitive. Explotación kernel heap es frontier actual de offensive security (2024-2026).
Diferencia fundamental con userland: kernel heap no tiene ASLR tan fuerte (KASLR es base-only, offsets internos fijos), pero tiene SMEP/SMAP que impide ejecutar/leer código de usuario desde kernel mode. Mitigaciones como KFENCE (sampling heap guard) y KMSAN (memory sanitizer) detectan corrupción pero tienen coste de performance, así que no siempre están habilitados en producción.
Cross-cache attacks son particularmente interesantes: explotar UAF en un slab cache liberando el page completo de vuelta al page allocator, luego reclamarla desde otro slab cache con objetos de tipo diferente. Esto convierte un UAF en un type-confusion a nivel kernel, bypass de todas las mitigaciones por slab cache isolation. Técnica publicada 2022, aún viable en kernels actuales con configuraciones default.
Del heap a la cadena de ataque#
Heap exploitation rara vez vive aislada. Cadena típica en exploit real:
- Info leak (format string, UAF read, partial overwrite) → leak de libc base address.
- Heap corruption (overflow, UAF write, double free) → arbitrary write primitive.
- Code execution (overwrite function pointer, vtable, GOT entry) → RCE o escalación de privilegios.
- Post-exploitation → shell, persistence, lateral movement.
Bug bounties pagando $10K-$100K+ por heap exploits en Chrome, Firefox, Safari, kernel, VMs. Google paga hasta $250K por Chrome full chain exploit. Security researchers especializados en heap exploitation ganan entre los mejores salarios de toda la industria de infosec, con posiciones senior superando $300K anuales en empresas como Google Project Zero, Microsoft MSRC, o firmas de offensive security.
Preguntas frecuentes#
¿Es necesario memorizar todas las House of X?#
No. Entiende tcache poisoning y UAF profundamente. Las demás son variaciones para contextos específicos. how2heap tiene referencia completa con código de ejemplo para cada técnica, compilable y depurable paso a paso con GDB.
¿Heap exploitation aún es relevante con mitigaciones?#
Sí. Cada año aparecen bypasses nuevos. Chrome paga $250K por full chain exploit. El campo evoluciona pero no desaparece.
¿Qué lenguaje usar para exploits?#
Python con pwntools es estándar. C para payloads compilados. Assembly para shellcode.
¿Dónde practico?#
El laboratorio orion-recon de whoami-labs te introduce a análisis de servicios y hunting de bugs como punto de partida. Para heap específico, combina con plataformas CTF especializadas (pwnable.tw, HITCON) donde practicas binarios vulnerables en sandbox.
Resumen#
- Heap internals: chunks con metadata inline, bins (fast, tcache, unsorted, small, large).
- Primitivas: UAF (puntero colgante), double free (chunk duplicado en free list), heap overflow (corromper metadata vecino).
- Técnicas: tcache poisoning (fd overwrite), House of Force (top chunk), House of Spirit (fake chunk), House of Orange (sin free).
- Mitigaciones: safe-linking (glibc 2.32+), safe unlinking, tcache count checks.
- Herramientas: GDB + pwndbg, pwntools, how2heap, Ghidra.
- Kernel: SLUB/SLAB heap bugs son vector principal de privilege escalation moderna.