-
-
[原创]2022秋季赛题目提交
-
2022-10-25 15:42
6945
-
战队名称:天外星系
战队创建者:geekfire
题目名称:NoLimit
输出提示:key正确则输出提示good!
题目设计说明
本题注册算法通过两段shellcode实现,分别简称为sc1 sc2
算法的核心步骤是根据生成的很简单的迷宫地图和输入的迷宫路径来验证是否能走出迷宫。
其中sc1负责生成迷宫地图,如果检测到调试信息等会生成错误的伪迷宫。
SC2负责验证迷宫,如果迷宫验证无误,则会修改提示信息为good!否则为no!
如果key正确则输出提示good! 否则输出no!或者不输出任何提示。
另外sc1通过aes 算法加密,私钥的前4个字符需要枚举才能解密,后面的字符被RSA加密。
(1)aes秘钥后部分解密:
给定的rsa算法里面的n可以快速被分解,通过分解n求出rsa私钥,即可解密,得到后部分秘钥为:AllIsNothing。
(2)aes秘钥前四个字符枚举条件 四个字符必须为数字类型
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 | 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()
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' )
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
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 ;
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
(3)解密了sc1后 sc1负责生成迷宫
过了一些调试和一部分条件后生成真实迷宫如下:
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
|
(4)key的组成
1、用来解密的rsa私钥
2、AES秘钥前4个字符
3、路径长度
4、路径坐标
最后key 为:
1 | 1BDF5752B86533B0EF0C488375EBFE389163712709D3FEE35C7679A1AB7A8E697366227CAF168C99DD7F110100F303132334353525161717273748494
|
(5)最后留了一个干扰项
有个求解方程的逻辑,实际上无解,就算解出来了也会输出no!
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2022-12-11 12:03
被kanxue编辑
,原因: