House of Roman

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Información básica

This was a very interesting technique that allowed for RCE without leaks via fake fastbins, the unsorted_bin attack and relative overwrites. However it has been patched.

Aplicabilidad en 2026

  • glibc window: Works reliably on 2.23–2.27 (the how2heap PoC tested 2.23–2.25). Starting 2.28, the “additional checks for unsorted bin integrity” patch makes the unsorted‑bin write unreliable, so success drops sharply. From 2.34 onward __malloc_hook/__free_hook were removed, making the original target unavailable. Use it only on old libc’s (or custom builds that keep the hooks) or for CTF challenges that ship an old libc.
  • Tcache era (≥2.26): Tcache will eat your 0x70 allocations and stop the fastbin/unsorted primitives. Disable it (setenv("GLIBC_TUNABLES","glibc.malloc.tcache_count=0",1);) before any allocation or fill each 0x70 tcache bin with 7 frees to drain it.
  • Safe-linking: It applies to tcache/fastbin in ≥2.32, but House of Roman only needs partial pointer overwrite of a libc address already present in fd/bk, so safe-linking does not help the defender here (the attacker never forges a fresh pointer). The real stopper is the hook removal and the unsorted-bin checks.

Code

Objetivo

  • RCE abusando de punteros relativos

Requisitos

  • Edit fastbin and unsorted bin pointers
  • 12 bits de aleatoriedad deben ser forzados por fuerza bruta (0.02% de probabilidad de éxito)

Pasos del ataque

Part 1: Fastbin Chunk apunta a __malloc_hook

Crear varios chunks:

  • fastbin_victim (0x60, offset 0): UAF chunk later to edit the heap pointer later to point to the LibC value.
  • chunk2 (0x80, offset 0x70): For good alignment
  • main_arena_use (0x80, offset 0x100)
  • relative_offset_heap (0x60, offset 0x190): relative offset on the ‘main_arena_use’ chunk

Then free(main_arena_use) which will place this chunk in the unsorted list and will get a pointer to main_arena + 0x68 in both the fd and bk pointers.

Now it’s allocated a new chunk fake_libc_chunk(0x60) because it’ll contain the pointers to main_arena + 0x68 in fd and bk.

Then relative_offset_heap and fastbin_victim are freed.

/*
Current heap layout:
0x0:   fastbin_victim       - size 0x70
0x70:  alignment_filler     - size 0x90
0x100: fake_libc_chunk      - size 0x70 (contains a fd ptr to main_arena + 0x68)
0x170: leftover_main        - size 0x20
0x190: relative_offset_heap - size 0x70

bin layout:
fastbin:  fastbin_victim -> relative_offset_heap
unsorted: leftover_main
*/
  • fastbin_victim tiene un fd que apunta a relative_offset_heap
  • relative_offset_heap es un offset de distancia desde fake_libc_chunk, el cual contiene un puntero a main_arena + 0x68
  • Cambiar el último byte de fastbin_victim.fd hace que fastbin_victim apunte a main_arena + 0x68.

Para las acciones anteriores, el atacante necesita ser capaz de modificar el puntero fd de fastbin_victim.

Luego, main_arena + 0x68 no es tan interesante, así que modifiquémoslo para que el puntero apunte a __malloc_hook.

Tenga en cuenta que __memalign_hook suele comenzar con 0x7f y ceros antes, entonces es posible falsearlo como un valor en el fast bin de 0x70. Debido a que los últimos 4 bits de la dirección son aleatorios hay 2^4=16 posibilidades para que el valor termine apuntando donde nos interesa. Así que se realiza un BF attack aquí de modo que el chunk queda así: 0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23).

(Para más info sobre el resto de los bytes consulta la explicación en el how2heap example). If the brute force fails the program just crashes (restart until it works).

Luego, se realizan 2 mallocs para eliminar los 2 chunks iniciales del fast bin y se asigna un tercero para obtener un chunk en __malloc_hook.

malloc(0x60);
malloc(0x60);
uint8_t* malloc_hook_chunk = malloc(0x60);

Parte 2: Unsorted_bin attack

For more info you can check:

Unsorted Bin Attack

Pero básicamente permite escribir main_arena + 0x68 en cualquier ubicación especificada en chunk->bk. Para el ataque elegimos __malloc_hook. Luego, después de sobrescribirlo, usaremos una sobrescritura relativa para apuntar a un one_gadget.

Para esto empezamos obteniendo un chunk y colocándolo en el unsorted bin:

uint8_t* unsorted_bin_ptr = malloc(0x80);
malloc(0x30); // Don't want to consolidate

puts("Put chunk into unsorted_bin\n");
// Free the chunk to create the UAF
free(unsorted_bin_ptr);

Usa un UAF en este chunk para apuntar unsorted_bin_ptr->bk a la dirección de __malloc_hook (esto lo forzamos por fuerza bruta previamente).

Caution

Ten en cuenta que este ataque corrompe el unsorted bin (hence small and large too). Así que solo podemos usar allocations del fast bin ahora (un programa más complejo podría hacer otras allocations y crash), y para desencadenarlo debemos alloc del mismo tamaño o el programa crashará.

Así que, para desencadenar la escritura de main_arena + 0x68 en __malloc_hook después de establecer __malloc_hook en unsorted_bin_ptr->bk solo necesitamos hacer: malloc(0x80)

Paso 3: Establecer __malloc_hook a system

En el paso uno controlamos un chunk que contenía __malloc_hook (en la variable malloc_hook_chunk) y en el segundo paso logramos escribir main_arena + 0x68 allí.

Ahora, abusamos de una sobreescritura parcial en malloc_hook_chunk para usar la dirección libc que escribimos ahí (main_arena + 0x68) para apuntar a una dirección one_gadget.

Aquí es donde es necesario bruteforcear 12 bits de aleatoriedad (más info en el ejemplo de how2heap).

Finalmente, una vez que la dirección correcta está sobreescrita, llama a malloc y dispara el one_gadget.

Modern tips & variants

  • Unsorted-bin hardening (2.28+): Las comprobaciones de integridad extra en unsorted chunks (sanidad de tamaños + enlaces de lista) hacen que la clásica escritura en unsorted‑bin sea frágil. Para sobrevivir a _int_malloc, debes mantener los enlaces fd/bk consistentes y los tamaños plausibles, lo que normalmente requiere primitivas más potentes que una simple sobreescritura parcial.
  • Hook removal (2.34+): Con __malloc_hook eliminado, adapta la primitiva para aterrizar en cualquier GOT/global escribible que luego puedas reutilizar (p. ej., sobreescribir exit@GOT en binarios non-PIE) o pivota a un secuestro del top‑chunk al estilo House of Pie para controlar top en lugar de un hook.
  • Any‑address fastbin alloc (romanking98 writeup): La segunda parte muestra cómo reparar la freelist 0x71 y usar la escritura en unsorted‑bin para colocar una allocation de fastbin sobre __free_hook, luego poner system("/bin/sh") y desencadenarlo vía free() en libc‑2.24 (pre-hook removal).

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks