House of Rabbit

House of Rabbit - article cover image RE & Pwn
Time it takes to read this article 8 minutes.

Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.

Introduction

House of Rabbit is a fastbin technique that abuses malloc_consolidate — the routine glibc runs to merge fastbin chunks back into the unsorted bin — to manufacture a large overlapping free chunk out of a tiny heap. The core trick is to corrupt a *fastbin* chunk’s metadata (either its size or its fd link) and then trigger consolidation. Because the consolidation path takes the corrupted fields largely at face value, the attacker can forge a free chunk whose size spans far beyond its real bounds, or splice a fake chunk into a regular bin, producing an overlap primitive without needing a large allocation or a linear overflow across many chunks.

House of Rabbit’s appeal is economy: it can create a big overlapping chunk even when the program only ever hands out small allocations, because malloc_consolidate is willing to move a fastbin chunk of any (forged) size into the unsorted/large-bin machinery. It has two classic variants — one that enlarges a fastbin chunk’s size so the consolidated result overlaps later allocations, and one that corrupts a fastbin fd to link a fake chunk into a normal bin during consolidation. The technique depends on glibc having fastbins and malloc_consolidate with the relatively light checking that path historically applied; later glibc hardening of the fastbin free path (size sanity, and on newer releases alignment/consistency checks) constrains which forged sizes survive to consolidation, so the version and the exact size you forge matter.

Attack Prerequisites

House of Rabbit needs a write into a fastbin chunk’s metadata and a way to trigger consolidation. Typical requirements:

  • Edit control over a freed fastbin chunk — a UAF or overflow that lets you rewrite the chunk’s size (variant 1) or fd (variant 2) while it sits in a fastbin.
  • A way to trigger malloc_consolidate — allocating a large (small-bin-sized or bigger) chunk, or any operation that calls consolidation, flushes the fastbins and processes your corrupted chunk.
  • A forged size that survives the fastbin/consolidation checks for the target glibc — the value must be plausible enough not to abort before consolidation moves it.
  • A second controlled chunk / fake chunk layout for the fd variant, so the spliced node is consistent when consolidation walks the list.
  • A heap leak if the target addresses (for a fake chunk or the extent of the overlap) must be computed against a randomised heap.

How It Works

Fastbins are singly-linked LIFO lists of small free chunks that glibc keeps *without* setting the following chunk’s PREV_INUSE bit clear — fastbin chunks are treated as ‘in use’ for coalescing purposes, which is why they are fast. Periodically, though, glibc calls malloc_consolidate to empty the fastbins: it walks each fastbin, and for every chunk it merges it with adjacent free chunks and moves the result into the unsorted bin (from where it can reach small/large bins). Consolidation reads the chunk’s size to find the next chunk and to compute the merged extent. House of Rabbit attacks that trust.

Variant 1 (size corruption). The attacker frees a small chunk into a fastbin, then — via a UAF/overflow — overwrites its size field with a much larger, still-plausible value (arranged so the forged ‘next chunk’ at chunk + size presents a valid PREV_INUSE/size so consolidation proceeds). When malloc_consolidate runs, it treats the chunk as that larger size and places a big free chunk into the unsorted bin — one whose span overlaps chunks that are still allocated after the original small chunk. Reallocating that big chunk hands the attacker a pointer aliasing live objects: a clean overlap primitive built from a small heap.

Variant 2 (fd corruption). Instead of enlarging the size, the attacker corrupts the fastbin chunk’s fd to point at a fake chunk they have laid out in controlled memory. During malloc_consolidate, glibc follows the fastbin fd chain, so the fake chunk is processed as if it were a genuine free fastbin chunk and gets inserted into a regular bin. A later allocation of the matching size then returns the fake chunk — an out-of-bin allocation at an attacker-chosen address, similar in effect to a fastbin poison but reached through the consolidation machinery (which historically applied fewer checks than the direct malloc-from-fastbin path). In both variants the payoff is the same family of primitives — chunk overlap or arbitrary allocation — but House of Rabbit reaches them by driving malloc_consolidate rather than the ordinary alloc/free fast path, which is what makes it distinctive.

Vulnerable Code / Setup

The enabling bug is edit-after-free (or an overflow) on a fastbin-sized chunk, combined with a code path that later requests a larger allocation and so triggers malloc_consolidate:

#include <stdlib.h>
#include <string.h>

char *slot[16];

/* edit() writes into a chunk even after it was freed -> lets us rewrite a
   fastbin chunk's size or fd while it sits in the bin. */
void edit(int i, const void *src, size_t n) { memcpy(slot[i], src, n); }

void demo(void) {
    slot[0] = malloc(0x58);      /* fastbin-sized                       */
    slot[1] = malloc(0x58);
    free(slot[0]);               /* slot[0] -> fastbin[0x60]             */

    /* VARIANT 1: enlarge the freed fastbin chunk's size so consolidation
       produces a big chunk overlapping slot[1] and beyond. */
    unsigned long big = 0x1000 | 1;   /* forged size w/ PREV_INUSE       */
    edit(0, &big, 8);            /* BUG: rewrite freed chunk's size word  */

    /* Trigger consolidation: a large request flushes the fastbins. */
    malloc(0x400);               /* calls malloc_consolidate            */
    /* fastbin[0x60] chunk, now 'size 0x1000', lands in the unsorted bin */
}
C

For the fd variant, overwrite the freed chunk’s fd to a fake chunk address instead of its size, then trigger consolidation so the fake node is walked into a bin:

unsigned long fake = (unsigned long)&fake_chunk;   /* controlled memory */
edit(0, &fake, 8);           /* corrupt fastbin fd -> fake chunk        */
malloc(0x400);               /* consolidation follows fd, links fake in  */
C

Triage the target — the technique needs a glibc whose fastbin/consolidation path accepts the forged metadata, so fingerprint the version:

$ ldd ./rabbit | grep libc     # fastbin + malloc_consolidate behaviour
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (2.27)
$ checksec --file=./rabbit
RELRO      STACK CANARY   NX        PIE
Partial    Canary found   NX enab.  No PIE
Bash

Walkthrough / Exploitation

Confirm the corrupted fastbin chunk and watch consolidation move it into the unsorted bin at the forged size. In gdb with pwndbg:

gdb -q ./rabbit
pwndbg> run
pwndbg> fastbins          # the freed chunk before corruption
pwndbg> # after edit() rewrites size and the large malloc:
pwndbg> bins              # the chunk now sits in the unsorted bin at 0x1000
pwndbg> vis_heap_chunks   # confirm the big chunk overlaps slot[1] and later
Bash

The exploit forges the fastbin metadata, triggers consolidation, then reclaims the oversized chunk to obtain the overlap (variant 1) or allocates the fake chunk (variant 2). A pwntools skeleton for the size-corruption variant:

from pwn import *

io = process('./rabbit')

def alloc(i, sz):   ...   # menu wrappers
def free(i):        ...
def edit(i, data):  ...   # edit-after-free capable

# 1) free a fastbin chunk, enlarge its size, trigger consolidation
alloc(0, 0x58); alloc(1, 0x58)
free(0)                              # -> fastbin[0x60]
edit(0, p64(0) + p64(0x1001))        # forge a large size w/ PREV_INUSE
alloc(2, 0x400)                      # malloc_consolidate: big chunk -> unsorted

# 2) reclaim the oversized chunk; it overlaps slot[1] (still live)
big = alloc(3, 0x100)                # aliases slot[1] -> overlap primitive
edit(3, payload_that_overwrites_slot1_metadata)

# 3) from the overlap, stage a tcache/fastbin poison to an arbitrary target
#    (e.g. a function pointer, GOT entry, or FILE struct), then trigger it.
Python

From the overlap, the escalation is the usual sequence: the reclaimed big chunk aliases a live object, so you overwrite its size or a pointer field to set up a follow-on poison, then redirect a later allocation onto a chosen target (a GOT entry on Partial RELRO, a hook on pre-2.34 glibc, or a FILE vtable on modern libc) for arbitrary write and control-flow hijack. The fd variant reaches an arbitrary allocation more directly by having consolidation link the fake chunk into a bin that a subsequent malloc drains.

Note: Consolidation reads size to locate the ‘next’ chunk, so a forged size in variant 1 must leave a plausible chunk header at chunk + size (a sane size with PREV_INUSE set) or the merge logic can abort or misbehave. Lay the forged size so its implied next-chunk boundary lands on memory you control, and verify in gdb that the post-consolidation unsorted chunk has exactly the extent you intended before reclaiming it.

Opsec: House of Rabbit is tightly bound to the fastbin/malloc_consolidate checking of the specific glibc: newer releases added fastbin free size sanity and, on 2.32+, safe-linking of the fastbin fd (so the fd variant must mangle the pointer and needs a heap leak). A forged size or fd that sails through on 2.27 may abort on a later libc. Fingerprint first, and confirm consolidation actually moved your chunk (bins) rather than assuming it did.

Detection and Defense

House of Rabbit is enabled by edit-after-free on fastbin metadata and by the consolidation path trusting forged sizes/links, so defenses target both:

  • Null pointers after free and forbid writes to freed chunks — the edit-after-free that rewrites the fastbin size/fd is the root cause.
  • Keep glibc current — later fastbin free size checks and safe-linking (>= 2.32) of the fd constrain which forgeries survive to consolidation and force a heap leak for the fd variant.
  • Full RELRO + PIE + ASLR — remove static targets and force leaks for the fake-chunk and overlap addresses.
  • AddressSanitizer in CI — detects the underlying use-after-free/overflow deterministically before the corrupted chunk ever reaches consolidation.
  • Hardened allocators — out-of-line metadata and validated chunk extents deny the forged-size overlap and the fd-splice the technique relies on.

Real-World Impact

House of Rabbit is valued in CTF pwn and allocator research as an economical way to obtain overlapping chunks — it can build a large overlap from a heap that only ever serves small allocations, which is exactly the constraint many challenges impose. It highlights malloc_consolidate as an attack surface distinct from the ordinary alloc/free fast path, and it complements overlap-oriented techniques like House of Einherjar and House of Botcake by reaching a similar primitive through a different allocator routine. The bug classes it consumes — use-after-free (CWE-416) and heap overflow (CWE-122) into fastbin metadata — are among the most prevalent and severe memory-safety weaknesses in C/C++ software, and the technique is a reminder that every allocator routine that trusts in-band metadata, not just malloc and free themselves, is part of the attack surface.

Conclusion

House of Rabbit drives malloc_consolidate to turn a corrupted fastbin chunk into a large overlapping free chunk (by forging its size) or into an arbitrary allocation (by forging its fd), producing an overlap or out-of-bin allocation from a small heap. Its reliance on the historically light checking of the consolidation path makes it version-sensitive — newer fastbin size checks and safe-linking constrain the forgeries and demand a heap leak — but the underlying idea endures: consolidation is another routine that trusts chunk metadata, and controlling that metadata controls the heap.

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL