0%

DASCTF_7

DASCTF_7月赛

这次逆向没以前那么坐牢了。

隐秘的角落

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
void __cdecl main_main()
{
__int64 v0; // rdi
__int64 v1; // rsi
__int64 v2; // [rsp+8h] [rbp-88h]
_QWORD *v3; // [rsp+8h] [rbp-88h]
_QWORD *v4; // [rsp+50h] [rbp-40h]
_QWORD v5[4]; // [rsp+58h] [rbp-38h] BYREF
__int64 v6[2]; // [rsp+78h] [rbp-18h] BYREF

sync___ptr_WaitGroup__Add((__int64)&main_wg, 1LL);
runtime_newobject((__int64)&unk_4B0DA0, v2);
v4 = v3;
v6[0] = (__int64)&unk_4B0DA0;
v6[1] = (__int64)&off_4E9BB0;
fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v6, 1LL, 1LL);
v5[2] = &unk_4AE9C0;
v5[3] = v4;
fmt_Fscanf(v0, v1, (const char *)&go_itab__os_File_io_Reader);
runtime_newproc(16, (char)&off_4D2310, *v4);
v5[0] = &unk_4B0DA0;
v5[1] = &off_4E9BC0;
fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v5, 1LL, 1LL);
sync___ptr_WaitGroup__Wait((__int64)&main_wg);
}

也是个go,通过动调发现关键函数在off_4D2310,点进去发现有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
void __golang main_checkflag(__int64 a1, __int64 a2)
{
char v2; // al
__int64 v3; // [rsp+18h] [rbp-70h]
char v4; // [rsp+18h] [rbp-70h]
__int64 v5; // [rsp+20h] [rbp-68h]
char v6[32]; // [rsp+40h] [rbp-48h] BYREF
_QWORD v7[2]; // [rsp+60h] [rbp-28h] BYREF
_QWORD v8[2]; // [rsp+70h] [rbp-18h] BYREF

v3 = runtime_stringtoslicebyte((__int64)v6, a1, a2);
main_Myencode(v3);
if ( v5 == qword_55EA78 )
{
runtime_memequal((__int64)main_enc, v3, qword_55EA78, v3);
v2 = v4;
}
else
{
v2 = 0;
}
if ( v2 )
{
v8[0] = &unk_4B0DA0;
v8[1] = &off_4E9B90;
fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v8, 1LL, 1LL);
}
else
{
v7[0] = &unk_4B0DA0;
v7[1] = &off_4E9BA0;
fmt_Fprintln((__int64)&go_itab__os_File_io_Writer, os_Stdout, (__int64)v7, 1LL, 1LL);
}
sync___ptr_WaitGroup__Add((__int64)&main_wg, -1LL);
}

直接看encode,发现是rc4,交叉引用密文和密钥,发现密文在init段被异或了,拿脚本直接解了

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
import base64
def rc4_main(key="init_key", message="init_message"):
print("RC4解密主函数调用成功")
print('\n')
s_box = rc4_init_sbox(key)
crypt = rc4_excrypt(message, s_box)
return crypt
def rc4_init_sbox(key):
s_box = list(range(256))
print("原来的 s 盒:%s" % s_box)
print('\n')
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
print("混乱后的 s 盒:%s" % s_box)
print('\n')
return s_box
def rc4_excrypt(plain, box):
print("调用解密程序成功。")
print('\n')
plain = base64.b64decode(plain.encode('utf-8'))
plain = bytes.decode(plain)
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append((ord(s) ^ k))
print("res用于解密字符串,解密后是:%res" % res)
print('\n')
cipher = "".join(res)
print("解密后的字符串是:%s" % cipher)
print('\n')
return cipher
#a=[0xE0,0xB2,0x5F,0x3D,0x8F,0xFA,0x94,0xB6,0xE7,0x9D,0x6C,0x98,0x66,0xD2,0x0F,0xEA,0x6D,0x6F,0xBE,0xC5,0x71,0x40,0x08,0x1B,0xF6,0xF3,0xBD,0xA8,0x8D,0x09,0x7B,0x7C]
a = [251, 198, 166, 157, 196, 219, 123, 86, 182, 70, 166, 192, 133, 100, 122, 154, 55, 76, 16, 150, 233, 167, 40, 196, 177, 45, 241, 222, 71, 59, 181, 243, 44, 125, 103, 29]
s = ""
for i in a:
s += chr(i)
s = str(base64.b64encode(s.encode('utf-8')), 'utf-8')
rc4_main("thisiskkk", s)

EzGo

这个题又是go,确实不难。难的是解方程。

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
void __cdecl main_main()
{
__int64 v0; // rdi
__int64 v1; // rsi
__int128 v2; // xmm0
__int64 v3; // rcx
__int64 v4; // rax
__int64 i; // rdx
__int64 v6; // rdx
__int64 v7; // [rsp+10h] [rbp-120h]
__int64 v8; // [rsp+10h] [rbp-120h]
int v9; // [rsp+18h] [rbp-118h]
__int64 v10; // [rsp+18h] [rbp-118h]
__int64 v11; // [rsp+18h] [rbp-118h]
__int64 v12; // [rsp+18h] [rbp-118h]
__int128 v13; // [rsp+18h] [rbp-118h]
__int64 v14; // [rsp+18h] [rbp-118h]
char v15; // [rsp+18h] [rbp-118h]
_QWORD *v16; // [rsp+20h] [rbp-110h]
__int64 v17; // [rsp+20h] [rbp-110h]
__int64 v18; // [rsp+20h] [rbp-110h]
__int64 v19; // [rsp+28h] [rbp-108h]
__int64 v20; // [rsp+28h] [rbp-108h]
__int64 v21; // [rsp+30h] [rbp-100h]
__int64 v22; // [rsp+30h] [rbp-100h]
__int64 v23; // [rsp+38h] [rbp-F8h]
__int128 v24; // [rsp+40h] [rbp-F0h]
__int64 v25; // [rsp+50h] [rbp-E0h]
__int64 v26; // [rsp+58h] [rbp-D8h]
__int64 v27; // [rsp+60h] [rbp-D0h]
char v28[24]; // [rsp+68h] [rbp-C8h] BYREF
char v29; // [rsp+80h] [rbp-B0h] BYREF
__int64 v30; // [rsp+88h] [rbp-A8h]
__int64 *v31; // [rsp+90h] [rbp-A0h]
_QWORD v32[2]; // [rsp+98h] [rbp-98h] BYREF
_QWORD v33[2]; // [rsp+A8h] [rbp-88h] BYREF
_QWORD v34[2]; // [rsp+B8h] [rbp-78h] BYREF
char v35[8]; // [rsp+C8h] [rbp-68h] BYREF
__int64 v36; // [rsp+D0h] [rbp-60h]
__int128 v37; // [rsp+D8h] [rbp-58h]
char v38[8]; // [rsp+E8h] [rbp-48h] BYREF
__int64 v39; // [rsp+F0h] [rbp-40h]
__int128 v40; // [rsp+F8h] [rbp-38h]
char v41[8]; // [rsp+108h] [rbp-28h] BYREF
__int64 v42; // [rsp+110h] [rbp-20h]
__int128 v43; // [rsp+118h] [rbp-18h]

while ( (unsigned __int64)&v29 <= *(_QWORD *)(*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer + 16LL) )
runtime_morestack_noctxt();
v31 = (__int64 *)runtime_newobject((__int64)&unk_4D3020);
v34[0] = "\b";
v34[1] = v31;
v9 = 2;
v16 = v34;
v19 = 1LL;
v21 = 1LL;
fmt_Fscanf(v0, v1, (const char *)&off_515980);
if ( v31[1] != 40 )
goto LABEL_10;
v41[0] = 0;
v42 = 0LL;
v2 = 0LL;
v43 = 0LL;
v3 = *v31;
v30 = *v31;
v4 = v31[1];
v27 = v4;
for ( i = 0LL; i < v4; i = v26 )
{
if ( *(unsigned __int8 *)(v3 + i) >= 0x80u )
{
runtime_decoderune(v3, v4, i, v9, (__int64)v16);
v6 = v17;
}
else
{
v6 = i + 1;
}
v26 = v6;
v25 = math_big_nat_shl(
v42,
v43,
*((__int64 *)&v43 + 1),
v42,
v43,
*((__int64 *)&v43 + 1),
8LL,
v23,
v24,
*((__int64 *)&v24 + 1));
v43 = v24;
v42 = v23;
v38[0] = 0;
v39 = 0LL;
v40 = 0LL;
v10 = math_big___ptr_Int__SetInt64((__int64)v38, SHIDWORD(v25), v7);
v16 = (_QWORD *)math_big___ptr_Int__Add((__int64)v41, (__int64)v41, v8, v10);
v4 = v27;
v3 = v30;
v2 = 0LL;
}
v35[0] = 0;
v36 = 0LL;
v37 = v2;
v22 = math_big___ptr_Int__SetString(
(__int64)v35,
(__int64)"13145309456454850877228433642468099885703532627357198144609408341691751453534987676043709654743561019"
"0391556347148927592380050533193934285571983556924577144473815598516557161",
174LL,
10LL,
(__int64)v16,
v19);
math_big___ptr_Int__Mul((__int64)v41, (__int64)v41, (__int64)v41, v11);
math_big___ptr_Int__Mod((__int64)v41, (__int64)v41, (__int64)v35, v12);
math_big_nat_itoa(v42, v43, DWORD2(v43), v41[0], 10LL, v19, v22, v23);
v19 = runtime_slicebytetostring((__int64)v28, v20, v21, v13);
if ( v18 != 173 )
goto LABEL_10;
runtime_memequal(
v14,
(__int64)"33529281532734294938614341047870321616766628114182320093600990983456360122704185955921012051918080449587733"
"939007294096845300395098833835443815283246602601870001850089370636",
173LL,
v14);
if ( v15 )
{
v33[0] = &unk_4D3020;
v33[1] = &off_514208;
fmt_Fprintln((__int64)&off_5159A0, qword_59D910, (__int64)v33, 1LL, 1LL, v19, v21, v23);
}
else
{
LABEL_10:
v32[0] = &unk_4D3020;
v32[1] = &off_514218;
fmt_Fprintln((__int64)&off_5159A0, qword_59D910, (__int64)v32, 1LL, 1LL, v19, v21, v23);
}
}

v14是我们通过输入计算出的值,来看程序,首先是输入,然后是一个for循环,for里嵌套一个decode程序,不知道是干什么的,刚开始还以为是解密程序,准备试试能不能解密,发现好像不行,那就慢慢跟程序,else里的是把输入的flag转换成ascii然后拼在一块,接下来是一个Mul函数,是将其乘方,然后是一个Mod函数,也就是mod这个大数,最后是equal函数,与大数对比。我们通过输入和输出就可以判断出自己的想法是否正确。

1
2
3
4
5
输入 '1' * 40
得到 44940666680036190507667229736237320843945547872118244516233712832889597565447002558460113570518306630271439743085954562346590579900861429645286911145287866527699481049284543

猜测:0x31313131313131313131313131313131313131313131313131313131313131313131313131313131 * 0x31313131313131313131313131313131313131313131313131313131313131313131313131313131 % 131453094564548508772284336424680998857035326273571981446094083416917514535349876760437096547435610190391556347148927592380050533193934285571983556924577144473815598516557161 = 44940666680036190507667229736237320843945547872118244516233712832889597565447002558460113570518306630271439743085954562346590579900861429645286911145287866527699481049284543
所以我们可以知道,我们要解一个方程 x^2 % big_num = result

这玩意是个rabin,纯密码学了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import libnum
import gmpy2
#导入公钥
n=131453094564548508772284336424680998857035326273571981446094083416917514535349876760437096547435610190391556347148927592380050533193934285571983556924577144473815598516557161
e = 2
c=33529281532734294938614341047870321616766628114182320093600990983456360122704185955921012051918080449587733939007294096845300395098833835443815283246602601870001850089370636
#n 在线分解
p=17489158711316178659
q=7516261744453902635364442762653073356746063224482072262455102025715350278471780391042196223686233375846890331396948280463168691132631674699134296333350979
inv_p = gmpy2.invert(p, q)
inv_q = gmpy2.invert(q, p)
mp = pow(c, (p + 1) // 4, p)
mq = pow(c, (q + 1) // 4, q)
a = (inv_p * p * mq + inv_q * q * mp) % n
b = n - int(a)
c = (inv_p * p * mq - inv_q * q * mp) % n
d = n - int(c)
#因为rabin 加密有四种结果,全部列出。
aa=[a,b,c,d]
for i in aa:
# print(i)
print(libnum.n2s(int(i)))

#DASCTF{48fa8aa2b489e9adac1750ea16ddc7b5}

fantansic maze

这题刚开始比赛的时候就在看这个,首先是与靶机交互,先爆破四位sha256,然后得到base64加密的ELF文件,创建新文件发现迷宫,就是要走1000次然后到达func1000。

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
.text:0000000000001358                 public function_0
.text:0000000000001358 function_0 proc near ; CODE XREF: function_156+85↓p
.text:0000000000001358 ; function_328+C1↓p ...
.text:0000000000001358
.text:0000000000001358 var_4 = dword ptr -4
.text:0000000000001358
.text:0000000000001358 ; __unwind {
.text:0000000000001358 endbr64
.text:000000000000135C push rbp
.text:000000000000135D mov rbp, rsp
.text:0000000000001360 sub rsp, 10h
.text:0000000000001364 mov eax, cs:tmp
.text:000000000000136A add eax, 1
.text:000000000000136D mov cs:tmp, eax
.text:0000000000001373 lea rdi, s ; "step1:"
.text:000000000000137A call _puts
.text:000000000000137F mov eax, 0
.text:0000000000001384 call read_num
.text:0000000000001389 mov [rbp+var_4], eax
.text:000000000000138C cmp [rbp+var_4], 0Ah
.text:0000000000001390 ja loc_141E
.text:0000000000001396 mov eax, [rbp+var_4]
.text:0000000000001399 lea rdx, ds:0[rax*4]
.text:00000000000013A1 lea rax, unk_3501C
.text:00000000000013A8 mov eax, [rdx+rax]
.text:00000000000013AB cdqe
.text:00000000000013AD lea rdx, unk_3501C
.text:00000000000013B4 add rax, rdx
.text:00000000000013B7 db 3Eh
.text:00000000000013B7 jmp rax
.text:00000000000013BA ; ---------------------------------------------------------------------------
.text:00000000000013BA mov eax, 0
.text:00000000000013BF call function_163
.text:00000000000013C4 mov eax, 0
.text:00000000000013C9 call function_451
.text:00000000000013CE mov eax, 0
.text:00000000000013D3 call function_771
.text:00000000000013D8 mov eax, 0
.text:00000000000013DD call function_602
.text:00000000000013E2 mov eax, 0
.text:00000000000013E7 call function_859
.text:00000000000013EC mov eax, 0
.text:00000000000013F1 call function_998
.text:00000000000013F6 mov eax, 0
.text:00000000000013FB call function_958
.text:0000000000001400 mov eax, 0
.text:0000000000001405 call function_437
.text:000000000000140A mov eax, 0
.text:000000000000140F call function_582
.text:0000000000001414 mov eax, 0
.text:0000000000001419 call function_474
.text:000000000000141E
.text:000000000000141E loc_141E: ; CODE XREF: function_0+38↑j
.text:000000000000141E mov eax, 0
.text:0000000000001423 call main
.text:0000000000001428 nop
.text:0000000000001429 leave
.text:000000000000142A retn
.text:000000000000142A ; } // starts at 1358
.text:000000000000142A function_0 endp

直接看汇编,也就是一个switch,输入1就跳转到function_163这样的。这种switch操作是通过rax寄存器进行跳转,并不是找表来跳转。如果我们每次switch都选择default,其实就可以一直填充计数器,所以只需找一条路直达func1000就行,我们只能从传过来的16进制来分析call指令的地址,call指令占5个字节如00000000000013BF call function_541 在 Hex View 中为\x00\x01\xbd\x7b\xe8(这里为大端序), \xe8可以理解为标志位,\x00\x01\xbd\x7b为地址到跳转函数的00000000000013BF偏移,而这里每一个函数的偏移为0xd3,这里就可以通过偏移来算出call指令所指向的函数,不会写交互脚本,所以只能借鉴出题师傅的脚本来写一遍:

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
from pwn import*
import random
from hashlib import sha256
import sys

p = remote("127.0.0.1",1447)

def pass_proof():
dir = string.ascii_letters + string.digits
p.recvuntil('[+] sha256(XXXX+')
#salt是已知的后面几位
salt = p.recv(16).strip().decode()
p.recvuntil(') == ')
#hash就是要等于的值
hash = p.recv(64).strip().decode()
while True:
rand_str = (''.join([random.choice(dir) for _ in range(4)])) + salt
if sha256(rand_str.encode()).hexdigest() == hash:
print(rand_str[:4])
p.sendlineafter('[+] Plz Tell Me XXXX :', rand_str[:4])
break

def get_elf():
p.recvuntil("map :\n")
data = p.recvuntil('That\'s all\n',drop=True)[:-1]
data = base64.b64decode(data)
fd = open('./1','wb')
fd.write(data)
fd.close()

pass_proof()
get_elf()

fd = open("./1","rb")
#fucn0地址
offset = 0x13c0

def get_map(fd, offset):
Map = []
for i in range(1000):
addr = offset
for j in range(10):
fd.seek(addr)
fc = u64(fd.read(4).ljust(8,b'\x00'))
if fc <= 0x33765:
fc = fc // 0xd3 + i + 1
else:
fc = fc - 0x100000000
if fc > -0xd3:
fc = i
else:
if addr + fc < 0x134a:
fc = 1000
else:
fc = fc // 0xd3
fc = fc + i + 1
Map.append(fc)
addr += 0xa
offset += 0xd3

return Map
Map = get_map(fd, offset)

def do_bfs(Map):
values = []
keys = []
for y in range(1000):
for x in range(10):
if x == 0:
values.append([])
values[y].append(Map[x + y * 10])
for i in range(1000):
keys.append(i)
result = dict(zip(keys, values))
result[1000] = ""
#print(result)
result1 = None
q = [(0, "")]
check_map = {}
while len(q):
f = q[0]
q = q[1:]
if(f[0] == 1000):
result1 = f[1]
break
if f[0] not in check_map:
check_map[f[0]] = f[1]
for i in range(10):
if result[f[0]][i] not in check_map:
q.append((result[f[0]][i],f[1]+str(i)))
return result1

result = do_bfs(Map)
print(result)
result = str(result)
times = 999 - len(result)

#default
for ii in range(times):
p.sendline("11")
for i in range(len(result)):
p.sendline(str(int(result[i])+1))