复现一下2020CISCN

reverse

z3

ida64打开之后,查看主函数

跟进byte_404020,以为就是前43个数据呢,后来构建z3的时候发现不对

动调了一下,发现最后对比的v47数组元素是2byte,word类型

上脚本

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
from z3 import *

# lis ==\
# [\
# 0x17, 0x4F, 0x00, 0x00, 0xF6, 0x9C, 0x00, 0x00, 0xDB, 0x8D, \
# 0x00, 0x00, 0xA6, 0x8E, 0x00, 0x00, 0x29, 0x69, 0x00, 0x00, \
# 0x11, 0x99, 0x00, 0x00, 0xA2, 0x40, 0x00, 0x00, 0x3E, 0x2F, \
# 0x00, 0x00, 0xB6, 0x62, 0x00, 0x00, 0x82, 0x4B, 0x00, 0x00, \
# 0x6C, 0x48, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0xD7, 0x52, \
# 0x00, 0x00, 0xEF, 0x2D, 0x00, 0x00, 0xDC, 0x28, 0x00, 0x00, \
# 0x0D, 0x64, 0x00, 0x00, 0x8F, 0x52, 0x00, 0x00, 0x3B, 0x61, \
# 0x00, 0x00, 0x81, 0x47, 0x00, 0x00, 0x17, 0x6B, 0x00, 0x00, \
# 0x37, 0x32, 0x00, 0x00, 0x93, 0x2A, 0x00, 0x00, 0x5F, 0x61, \
# 0x00, 0x00, 0xBE, 0x50, 0x00, 0x00, 0x8E, 0x59, 0x00, 0x00, \
# 0x56, 0x46, 0x00, 0x00, 0x31, 0x5B, 0x00, 0x00, 0x3A, 0x31, \
# 0x00, 0x00, 0x10, 0x30, 0x00, 0x00, 0xFE, 0x67, 0x00, 0x00, \
# 0x5F, 0x4D, 0x00, 0x00, 0xDB, 0x58, 0x00, 0x00, 0x99, 0x37, \
# 0x00, 0x00, 0xA0, 0x60, 0x00, 0x00, 0x50, 0x27, 0x00, 0x00, \
# 0x59, 0x37, 0x00, 0x00, 0x53, 0x89, 0x00, 0x00, 0x22, 0x71, \
# 0x00, 0x00, 0xF9, 0x81, 0x00, 0x00, 0x24, 0x55, 0x00, 0x00, \
# 0x71, 0x89, 0x00, 0x00, 0x1D, 0x3A\
# ]
#lis = [23, 79, 0, 0, 246, 156, 0, 0, 219, 141, 0, 0, 166, 142, 0, 0, 41, 105, 0, 0, 17, 153, 0, 0, 162, 64, 0, 0, 62, 47, 0, 0, 182, 98, 0, 0, 130, 75, 0, 0, 108, 72, 0]
lis = [0x4f17,0x9cf6,0x8ddb,0x8ea6,0x6929,0x9911,0x40a2,0x2f3e,0x62b6,0x4b82,0x486c,0x4002,0x52d7,0x2def,0x28dc,0x640d,0x528f,0x613b,0x4781,0x6b17,0x3237,0x2a93,0x615f,0x50be,0x598e,0x4656,0x5b31,0x313a,0x3010,0x67fe,0x4d5f,0x58db,0x3799,0x60a0,0x2750,0x3759,0x8953,0x7122,0x81f9,0x5524,0x8971,0x3a1d]

print(lis[:43])
v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18,v19,v20,v21,v22,v23,v24,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,v35,v36,v37,v38,v39,v40,v41,v42,v43,v44,v45,v46\
= Ints("v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v33 v34 v35 v36 v37 v38 v39 v40 v41 v42 v43 v44 v45 v46")
s = Solver()
s.add(34 * v8 + 12 * v5 + 53 * v6 + 6 * v7 + 58 * v9 + 36 * v10 + v11 == lis[0])
s.add(27 * v9 + 73 * v8 + 12 * v7 + 83 * v5 + 85 * v6 + 96 * v10 + 52 * v11 == lis[1])
s.add(24 * v7 + 78 * v5 + 53 * v6 + 36 * v8 + 86 * v9 + 25 * v10 + 46 * v11 == lis[2])
s.add(78 * v6 + 39 * v5 + 52 * v7 + 9 * v8 + 62 * v9 + 37 * v10 + 84 * v11 == lis[3])
s.add(48 * v9 + 14 * v7 + 23 * v5 + 6 * v6 + 74 * v8 + 12 * v10 + 83 * v11 == lis[4])
s.add(15 * v10 + 48 * v9 + 92 * v7 + 85 * v6 + 27 * v5 + 42 * v8 + 72 * v11 == lis[5])
s.add(26 * v10 + 67 * v8 + 6 * v6 + 4 * v5 + 3 * v7 + 68 * v11 == lis[6])
s.add(34 * v15 + 12 * v12 + 53 * v13 + 6 * v14 + 58 * v16 + 36 * v17 + v18 == lis[7])
s.add(27 * v16 + 73 * v15 + 12 * v14 + 83 * v12 + 85 * v13 + 96 * v17 + 52 * v18 == lis[8])
s.add(24 * v14 + 78 * v12 + 53 * v13 + 36 * v15 + 86 * v16 + 25 * v17 + 46 * v18 == lis[9])
s.add(78 * v13 + 39 * v12 + 52 * v14 + 9 * v15 + 62 * v16 + 37 * v17 + 84 * v18 == lis[10])
s.add(48 * v16 + 14 * v14 + 23 * v12 + 6 * v13 + 74 * v15 + 12 * v17 + 83 * v18 == lis[11])
s.add(15 * v17 + 48 * v16 + 92 * v14 + 85 * v13 + 27 * v12 + 42 * v15 + 72 * v18 == lis[12])
s.add(26 * v17 + 67 * v15 + 6 * v13 + 4 * v12 + 3 * v14 + 68 * v18 == lis[13])
s.add(34 * v22 + 12 * v19 + 53 * v20 + 6 * v21 + 58 * v23 + 36 * v24 + v25 == lis[14])
s.add(27 * v23 + 73 * v22 + 12 * v21 + 83 * v19 + 85 * v20 + 96 * v24 + 52 * v25 == lis[15])
s.add(24 * v21 + 78 * v19 + 53 * v20 + 36 * v22 + 86 * v23 + 25 * v24 + 46 * v25 == lis[16])
s.add(78 * v20 + 39 * v19 + 52 * v21 + 9 * v22 + 62 * v23 + 37 * v24 + 84 * v25 == lis[17])
s.add(48 * v23 + 14 * v21 + 23 * v19 + 6 * v20 + 74 * v22 + 12 * v24 + 83 * v25 == lis[18])
s.add(15 * v24 + 48 * v23 + 92 * v21 + 85 * v20 + 27 * v19 + 42 * v22 + 72 * v25 == lis[19])
s.add(26 * v24 + 67 * v22 + 6 * v20 + 4 * v19 + 3 * v21 + 68 * v25 == lis[20])
s.add(34 * v29 + 12 * v26 + 53 * v27 + 6 * v28 + 58 * v30 + 36 * v31 + v32 == lis[21])
s.add(27 * v30 + 73 * v29 + 12 * v28 + 83 * v26 + 85 * v27 + 96 * v31 + 52 * v32 == lis[22])
s.add(24 * v28 + 78 * v26 + 53 * v27 + 36 * v29 + 86 * v30 + 25 * v31 + 46 * v32 == lis[23])
s.add(78 * v27 + 39 * v26 + 52 * v28 + 9 * v29 + 62 * v30 + 37 * v31 + 84 * v32 == lis[24])
s.add(48 * v30 + 14 * v28 + 23 * v26 + 6 * v27 + 74 * v29 + 12 * v31 + 83 * v32 == lis[25])
s.add(15 * v31 + 48 * v30 + 92 * v28 + 85 * v27 + 27 * v26 + 42 * v29 + 72 * v32 == lis[26])
s.add(26 * v31 + 67 * v29 + 6 * v27 + 4 * v26 + 3 * v28 + 68 * v32 == lis[27])
s.add(34 * v36 + 12 * v33 + 53 * v34 + 6 * v35 + 58 * v37 + 36 * v38 + v39 == lis[28])
s.add(27 * v37 + 73 * v36 + 12 * v35 + 83 * v33 + 85 * v34 + 96 * v38 + 52 * v39 == lis[29])
s.add(24 * v35 + 78 * v33 + 53 * v34 + 36 * v36 + 86 * v37 + 25 * v38 + 46 * v39 == lis[30])
s.add(78 * v34 + 39 * v33 + 52 * v35 + 9 * v36 + 62 * v37 + 37 * v38 + 84 * v39 == lis[31])
s.add(48 * v37 + 14 * v35 + 23 * v33 + 6 * v34 + 74 * v36 + 12 * v38 + 83 * v39 == lis[32])
s.add(15 * v38 + 48 * v37 + 92 * v35 + 85 * v34 + 27 * v33 + 42 * v36 + 72 * v39 == lis[33])
s.add(26 * v38 + 67 * v36 + 6 * v34 + 4 * v33 + 3 * v35 + 68 * v39 == lis[34])
s.add(34 * v43 + 12 * v40 + 53 * v41 + 6 * v42 + 58 * v44 + 36 * v45 + v46 == lis[35])
s.add(27 * v44 + 73 * v43 + 12 * v42 + 83 * v40 + 85 * v41 + 96 * v45 + 52 * v46 == lis[36])
s.add(24 * v42 + 78 * v40 + 53 * v41 + 36 * v43 + 86 * v44 + 25 * v45 + 46 * v46 == lis[37])
s.add(78 * v41 + 39 * v40 + 52 * v42 + 9 * v43 + 62 * v44 + 37 * v45 + 84 * v46 == lis[38])
s.add(48 * v44 + 14 * v42 + 23 * v40 + 6 * v41 + 74 * v43 + 12 * v45 + 83 * v46 == lis[39])
s.add(15 * v45 + 48 * v44 + 92 * v42 + 85 * v41 + 27 * v40 + 42 * v43 + 72 * v46 == lis[40])
s.add(26 * v45 + 67 * v43 + 6 * v41 + 4 * v40 + 3 * v42 + 68 * v46 == lis[41])
s.check()
print(s.model())
1
2
3
4
5
6
arr = [102,108,97,103,123,55,101,49,55,49,100,52,51,45,54,51,98,57,45,52,101,49,56,45,57,57,48,101,45,54,101,49,52,99,50,97,102,101,54,52,56,125]
flag = ""
for i in arr:
flag += chr(i)
print(flag)
# flag{7e171d43-63b9-4e18-990e-6e14c2afe648}

hyperthreading

用ida打开查看,可以可以看到最后的加密函数,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // eax
char v5; // [esp+0h] [ebp-14h]
HANDLE Handles[2]; // [esp+8h] [ebp-Ch] BYREF

sub_401020("plz input your flag:", v5);
sub_401050("%42s", (char)byte_40336C);
Handles[0] = CreateThread(0, 0, StartAddress, 0, 0, 0);
Handles[1] = CreateThread(0, 0, loc_401200, 0, 0, 0);
CreateThread(0, 0, sub_401240, 0, 0, 0);
WaitForMultipleObjects(2u, Handles, 1, 0xFFFFFFFF);
for ( i = 0; i < 42; ++i )
{
if ( byte_40336C[i] != byte_402150[i] )
{
sub_401020("error", (char)Handles[0]);
exit(0);
}
}
sub_401020("win", (char)Handles[0]);
getchar();
return 0;
}

一共创建了三个线程,第一个线程的函数就是加密函数了,本人目前水平只能做到这样的修复了。

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 __userpurge __noreturn StartAddress(_BYTE *a2@<edi>, LPVOID lpThreadParameter)
{
int *v2; // ebp
int v3; // ecx
int v4; // [esp+4h] [ebp-14h]
DWORD v5; // [esp+Ch] [ebp-Ch]
HANDLE Thread; // [esp+10h] [ebp-8h]
int savedregs; // [esp+18h] [ebp+0h] BYREF

v2 = &savedregs;
Thread = CreateThread(0, 0, hHandle, 0, 0, 0);
v5 = WaitForSingleObject(hHandle, 0xFFFFFFFF);
*(v5 - 57) = __ROR1__(*(v5 - 57), 69);
while ( 1 )
{
LOBYTE(v3) = v3 - *a2;
v2 = (v2 - 1);
byte_40336C[*(v2 - 1)] = (byte_40336C[*(v2 - 1)] << 6) ^ (byte_40336C[v3] >> 2);
byte_40336C[*(v2 - 1)] ^= 0x23u;
Sleep(6u);
v4 += NtCurrentPeb()->BeingDebugged + 9;
*(v4 - 117) = __ROR1__(*(v4 - 117), 77);
byte_40336C[*(v2 - 1)] = byte_40336C[v3] + 0x23;
}
}

查看第二个线程中的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DWORD __thiscall sub_401200(char *this, LPVOID lpThreadParameter)
{
struct _PEB *v2; // eax
int v3; // eax
char v4; // bl

v2 = NtCurrentPeb();
HIBYTE(v2->Ldr) = __ROR1__(HIBYTE(v2->Ldr), 182);
v3 = 0;
v4 = 100 * (*(this - 1240466363) + (_BYTE)this);
do
byte_40336C[v3++] += v4;
while ( v3 < 42 );
return 0;
}

第三个线程中的函数

1
2
3
4
5
6
7
8
9
void __stdcall __noreturn sub_401240(LPVOID lpThreadParameter)
{
char v1; // [esp+0h] [ebp-4h]

while ( !IsDebuggerPresent() )
;
sub_401020("debug!\n", v1);
exit(0);
}

后面跟着一个函数WaitForMultipleObjects函数,中间存放的函数是一二线程:

贴上大佬的讲解

网上关于这题的wp挺少,据我统计共有两种做法。

方法一

一种是直接读汇编,或者修补程序,把加密函数逆出来。加密函数逻辑大概就是这个样子。

1
(((((input[i]>>2)^(input[i]<<6))&0xff)^0x23)+0x23)&0xff

最后把对比数组提取出来:

1
2
3
4
5
6
7
8
9
10
import string
ans=[0xDD,0x5B,0x9E,0x1D,0x20,0x9E,0x90,0x91,0x90,0x90,0x91,0x92,0xDE,0x8B,0x11,0xD1,0x1E,0x9E,0x8B,0x51,0x11,0x50,0x51,0x8B,0x9E,0x5D,0x5D,0x11,0x8B,0x90,0x12,0x91,0x50,0x12,0xD2,0x91,0x92,0x1E,0x9E,0x90,0xD2,0x9F]
flag=''

for i in range(len(ans)):
for c in string.printable:
if (((((ord(c)>>2)^(ord(c)<<6))&0xff)^0x23)+0x23)&0xff==ans[i]:
flag+=c
break
print (flag)

不得不说,这段爆破代码实在太强了,又学到了……

代码来源

方法二

读懂程序的逻辑,巧妙绕过第三个线程的isdebugged函数

用od动调,输入字符串之后,动调到WaitForMultipleObjects的时候,程序会自动卡在这个位置上,因为前面判断了程序处于调试模式,所以线程会一直占用,出不来。我们只需要手动暂停调试的程序,等待上面线程没有占用资源之后,再继续执行,之后就会绕过WaitForMultipleObjects函数了。从而到达关键判断处了

因为加密方式未知,这种做法有赌的成分……

这题是简单的异或之类的字符加密,每个字符加密结果固定,所以可以统计每个常见字符对应的加密结果,然后通过对比数组,来推断出flag.

详细看这里

Crypto

rsa

1
2
3
4
5
# 小明经过研究,发现RSA加密算法可以推广,也就是它的模不仅仅是只能为两个素数的乘积,只要是2个以上不同的素数相乘都可以。

n = 50142032499469550407421604706771611415193641755639270667473328045908799316205905505167271138079522738272643811917325451177986948493659090203974349370248583120022500722759239305447823602875823849366662503027591858371125301505134216095903796534740686834236150999
e = 65537
c = 45005399504992587510006608300548120810512973768886391125598523343330913326304417790989607300367232977960116381108873363343598357102136548218343380795022179607741940866191404186680657699739176842869814452906110393321567314747096161480003824583613027819960172221

分解出了三个大素数??

1
2
3
4
5
6
7
8
9
10
11
12
import gmpy2
from Crypto.Util.number import *
n = 50142032499469550407421604706771611415193641755639270667473328045908799316205905505167271138079522738272643811917325451177986948493659090203974349370248583120022500722759239305447823602875823849366662503027591858371125301505134216095903796534740686834236150999
p1 = 368751654879714877087975516875168751974879716873087714877516875971487715687516888458327
p2 = 368751654879714877087975516875168751974879716873087714877516875971487715687518277898523
p3 = 368751654879714877087975516875168751974879716873087714877516875971487715687518438089619
c = 45005399504992587510006608300548120810512973768886391125598523343330913326304417790989607300367232977960116381108873363343598357102136548218343380795022179607741940866191404186680657699739176842869814452906110393321567314747096161480003824583613027819960172221
e = 65537
d = gmpy2.invert(e,(p1-1)*(p2-1)*(p3-1))

m = pow(c,d,n)
print(long_to_bytes(m))

bd (rsa-wiener-attack)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from secret import flag
from Crypto.Util.number import *

m = bytes_to_long(flag)

p = getPrime(512)
q = getPrime(512)
N = p * q
phi = (p-1) * (q-1)
while True:
d = getRandomNBitInteger(200)
if GCD(d, phi) == 1:
e = inverse(d, phi)
break

c = pow(m, e, N)

print(c, e, N, sep='\n')

# 37625098109081701774571613785279343908814425141123915351527903477451570893536663171806089364574293449414561630485312247061686191366669404389142347972565020570877175992098033759403318443705791866939363061966538210758611679849037990315161035649389943256526167843576617469134413191950908582922902210791377220066
# 46867417013414476511855705167486515292101865210840925173161828985833867821644239088991107524584028941183216735115986313719966458608881689802377181633111389920813814350964315420422257050287517851213109465823444767895817372377616723406116946259672358254060231210263961445286931270444042869857616609048537240249
# 86966590627372918010571457840724456774194080910694231109811773050866217415975647358784246153710824794652840306389428729923771431340699346354646708396564203957270393882105042714920060055401541794748437242707186192941546185666953574082803056612193004258064074902605834799171191314001030749992715155125694272289

这题给出的e很大,d是随机生成的,位数为200bit,且满足与phi互质的条件,我目前只能知道的条件为ed = 1 MOD phi

–正解–

这题使用的方式是rsa-wiener-attack,有关连分数攻击方法

使用的脚本https://github.com/pablocelayes/rsa-wiener-attack

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
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import ContinuedFractions, Arithmetic
c=37625098109081701774571613785279343908814425141123915351527903477451570893536663171806089364574293449414561630485312247061686191366669404389142347972565020570877175992098033759403318443705791866939363061966538210758611679849037990315161035649389943256526167843576617469134413191950908582922902210791377220066
e=46867417013414476511855705167486515292101865210840925173161828985833867821644239088991107524584028941183216735115986313719966458608881689802377181633111389920813814350964315420422257050287517851213109465823444767895817372377616723406116946259672358254060231210263961445286931270444042869857616609048537240249
n=86966590627372918010571457840724456774194080910694231109811773050866217415975647358784246153710824794652840306389428729923771431340699346354646708396564203957270393882105042714920060055401541794748437242707186192941546185666953574082803056612193004258064074902605834799171191314001030749992715155125694272289
# firstly git clone https://github.com/pablocelayes/rsa-wiener-attack.git !
def wiener_hack(e, n):
frac = ContinuedFractions.rational_to_contfrac(e, n)
convergents = ContinuedFractions.convergents_from_contfrac(frac)
for (k, d) in convergents:
if k != 0 and (e * d - 1) % k == 0:
phi = (e * d - 1) // k
s = n - phi + 1
discr = s * s - 4 * n
if (discr >= 0):
t = Arithmetic.is_perfect_square(discr)
if t != -1 and (s + t) % 2 == 0:
print("Hacked!")
return d
return False
d = wiener_hack(e, n)
print(d)
m=pow(c,d,n)
flag=long_to_bytes(m)
print(flag)

这边仅限于了解rsa-wiener-attack攻击的使用