algoatson.github.io ~/posts
segfault@algoatson.github.io
back to index

the House of Rabbit

2026-06-142 min readheap pwn glibc

Fastbins are the lazy bin: chunks pile up there without consolidating. But the moment malloc needs a non-fast chunk, malloc_consolidate sweeps the whole fastbin array into the unsorted bin — trusting each chunk's size on the way. The House of Rabbit forges one of those sizes to be enormous, so the sweep produces a giant free chunk sitting on top of memory that's still in use.

target./rabbit
arch
amd64-64-little
libc
2.27
technique
forge fastbin chunk size -> malloc_consolidate -> giant overlapping chunk
needs
edit one freed fastbin chunk + a way to trigger consolidation

the sweep that trusts your size#

malloc_consolidate runs when a request can't be served from the fastbins/tcache and have_fastchunks is set — in practice, allocating a larger chunk triggers it. For each fastbin chunk it merges with neighbours and links the result into the unsorted bin, reading the size field to find where each chunk ends.

So: free a fastbin chunk, edit its size to something huge, and trigger the sweep.

a freed 0x40 fastbin chunk — size forged up to 0x1001hex
00000000  00 00 00 00 00 00 00 00  01 10 00 00 00 00 00 00  |................|
rabbit.cc
1
2
3
4
5
6
7
8
9
char *a = malloc(0x38);          // 0x40 chunk
char *b = malloc(0x38);
free(a); free(b);                // both into the 0x40 fastbin; have_fastchunks set

edit(a, size = 0x1001);          // forge a's size to 0x1000 (+ PREV_INUSE)
//   ...and lay a valid-looking chunk header at a + 0x1000 so the sweep doesn't fault

malloc(0x500);                   // not fast -> malloc_consolidate sweeps the fastbins
// a is now a 0x1000 free chunk in the unsorted bin, overlapping b and beyond

why a giant chunk is a win#

That 0x1000 free chunk overlaps every allocation that lived after a. Allocate it back and you hold a writable window over other live chunks' data and metadata — the same overlap position that unlocks tcache poisoning, fake sizes, and libc-pointer recovery. You manufactured it from a single size edit and a large malloc, with no double-free and no leak.

The second flavour of the technique edits a fastbin chunk's fd instead of its size: point it at a fake chunk, and malloc_consolidate links your fake chunk into the unsorted bin for you — a fastbin-fd poison that survives into the sorted bins.

where it goes

Reclaim the giant chunk, overwrite the overlapped chunk's fd to __free_hook, poison, system:

txt
segfault{c0ns0l1d4t3_th3_wh0l3_h34p}

the sweep is fragile

malloc_consolidate walks chunk-by-chunk using the sizes it reads, so the chunk at a + forged_size must look like a real chunk (sane size, sane PREV_INUSE) or the sweep faults mid-merge. Lay a fake header there first, and single-step the consolidating malloc in pwndbg — this technique crashes loudly when an offset is wrong, which at least makes it easy to debug.

Next, the finale for the cruellest challenges — the House of Corrosion, which builds a libc leak and a shell out of nothing, no info-leak bug required.