战队名称:天外星系
战队创建者:geekfire
题目名称:NoLimit
输出提示:key正确则输出提示good!
本题注册算法通过两段shellcode实现,分别简称为sc1 sc2
算法的核心步骤是根据生成的很简单的迷宫地图和输入的迷宫路径来验证是否能走出迷宫。
其中sc1负责生成迷宫地图,如果检测到调试信息等会生成错误的伪迷宫。
SC2负责验证迷宫,如果迷宫验证无误,则会修改提示信息为good!否则为no!
如果key正确则输出提示good! 否则输出no!或者不输出任何提示。
另外sc1通过aes 算法加密,私钥的前4个字符需要枚举才能解密,后面的字符被RSA加密。
给定的rsa算法里面的n可以快速被分解,通过分解n求出rsa私钥,即可解密,得到后部分秘钥为:AllIsNothing。
1
2
|
1
0
0
1
|
1、把前4个字符任意排列,只要其中一组排列满足:按上表所示,横向 和 纵向 两个字符相加结果要全部相等
2、通过aes秘钥尝试解密sc1,使得sc1解密后执行时不产生异常
枚举通过python + vc代码实现
python负责枚举 vc负责解密sc1并执行
其中vc 部分代码如下:
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
|
SetUnhandledExceptionFilter(callback);
int
ScSize
=
0
;
string FindScDec;
string AesKeyPreffix;
if
(argc >
1
)
AesKeyPreffix
=
argv[
1
];
string AesKeySuffix
=
"AllIsNothing"
;
string AesKey;
string aesIV
=
"ABCDEF0123456789"
;
/
/
128
bits
AES aes;
int
size
=
strlen(FindScEnc)
/
2
;
TextToHex(FindScEnc, size);
AesKey
=
AesKeyPreffix
+
AesKeySuffix;
FindScDec
=
(char
*
)aes.DecryptCBC((unsigned char
*
)FindScEnc, size, (unsigned char
*
)AesKey.c_str(), (unsigned char
*
)aesIV.c_str());
/
/
CBC
ScSize
=
FindScDec.size();
void
*
ptr
=
NULL;
ptr
=
VirtualAlloc(
NULL,
ScSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if
(ptr
=
=
NULL) {
/
/
printf(
"Failed to allocate memory: error=%u\n"
, GetLastError());
return
1
;
}
memcpy(ptr, FindScDec.c_str(), ScSize);
__try{
((void(
*
)())ptr)();
printf(
"success!\r\n"
);
}
__except (filterException(GetExceptionCode(), GetExceptionInformation()))
{
printf(
"the aes preffix %s is error\r\n"
, AesKeyPreffix.c_str());
}
return
0
;
|
把上述代码编译为NoLimit.exe,然后用脚本调用枚举
python 枚举部分代码为:
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
|
# -*- coding: UTF8 -*-
import
os
import
threading
import
datetime
chrs
=
'0123456789'
AesPreffix
=
''
a
=
b
=
c
=
d
=
''
AesPreffixS
=
[]
starttime
=
datetime.datetime.now()
threads
=
[]
class
Getoutofloop(Exception):
pass
def
runsc(AesPreffix):
cmd
=
os.popen(
'NoLimit.exe '
+
AesPreffix)
result
=
cmd.read()
print
(
"result:"
+
result)
if
'success'
in
result:
print
(
'AesPreffix is '
+
AesPreffix)
AesPreffixS.append(AesPreffix)
for
a
in
chrs:
AesPreffix
+
=
a
for
b
in
chrs:
AesPreffix
+
=
b
for
c
in
chrs:
AesPreffix
+
=
c
for
d
in
chrs:
AesPreffix
+
=
d
t
=
threading.Thread(target
=
runsc, args
=
(AesPreffix,))
threads.append(t)
t.start()
#os._exit(0)
AesPreffix
=
AesPreffix[:
-
1
]
AesPreffix
=
AesPreffix[:
-
1
]
AesPreffix
=
AesPreffix[:
-
1
]
AesPreffix
=
AesPreffix[:
-
1
]
for
t
in
threads:
t.join(
2
)
os.system(
'taskkill /f /im %s'
%
'NoLimit.exe'
)
#print(AesPreffixS)
str
=
''
lastresult
=
[]
try
:
for
p
in
AesPreffixS:
for
i
in
range
(
0
,
4
):
a
=
p[i]
for
j
in
range
(
0
,
4
):
if
j
=
=
i :
continue
b
=
p[j]
for
k
in
range
(
0
,
4
):
if
k
=
=
j
or
k
=
=
i:
continue
c
=
p[k]
for
l
in
range
(
0
,
4
):
if
l
=
=
k
or
l
=
=
j
or
l
=
=
i:
continue
d
=
p[l]
str
=
a
+
b
+
c
+
d
#print(str)
num
=
ord
(
str
[
0
])
+
ord
(
str
[
1
])
if
num
=
=
ord
(
str
[
2
])
+
ord
(
str
[
3
])
and
num
=
=
ord
(
str
[
0
])
+
ord
(
str
[
2
])
and
num
=
=
ord
(
str
[
1
])
+
ord
(
str
[
3
]):
if
p
not
in
lastresult:
lastresult.append(p)
break
;
#raise Getoutofloop()
except
Getoutofloop:
pass
print
(
'AesPreffix:'
)
print
(lastresult)
endtime
=
datetime.datetime.now()
print
(
'time:%ds'
%
(endtime
-
starttime).seconds )
|
枚举完成大概不到3分钟,结果如下:
result:
AesPreffix:
['1441', '1010', '2222', '2424', '3773', '4646']
time:133s
得到6个结果,6个前缀对应的解密后的sc1分别为:
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
|
1441
test edi,ebx
jl
1C8D88FFFDD
/
/
该跳转不执行
fdiv st(
0
),st(
1
)
ret
2222
ret
2424
xor cl,byte ptr ds:[rbx
+
19
]
ret
3773
ret
4646
adc ch,al
ret
1010
call
1D18DAF0004
接着:
1D18DAF0004
: inc eax
pop rdi
mov ecx,
10107B1
xor ecx,
1010101
add rdi,
1E
xor esi,esi
cld
mov al,byte ptr ds:[rdi]
cmp
al,
11
cmove eax,esi
stosb
loop
1D18DAF0019
push rbx
push rsi
push rdi
push r12
push r13
... ...
|
除了1010外 其他都都是执行两三条汇编指令后就返回了
这时可以判断Aes秘钥为1010AllIsNothing
过了一些调试和一部分条件后生成真实迷宫如下:
1
2
3
4
5
6
7
8
9
10
|
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
00
00
00
00
01
01
01
01
01
01
01
01
01
00
01
01
01
01
01
01
01
00
00
00
00
00
00
01
01
01
01
00
01
01
01
01
00
01
01
01
01
00
00
00
00
01
00
00
00
01
01
01
01
01
00
01
00
01
01
01
01
01
01
01
00
01
01
01
01
01
|
其中00 表示可以通过的路径
然后sc2 负责验证迷宫路径是否正确
路径的坐标为:
1
|
30
31
32
33
43
53
52
51
61
71
72
73
74
84
94
|
1、用来解密的rsa私钥
2、AES秘钥前4个字符
3、路径长度
4、路径坐标
最后key 为:
1
|
1BDF5752B86533B0EF0C488375EBFE389163712709D3FEE35C7679A1AB7A8E697366227CAF168C99DD7F110100F303132334353525161717273748494
|
有个求解方程的逻辑,实际上无解,就算解出来了也会输出no!
更多【2022秋季赛题目提交】相关视频教程:www.yxfzedu.com