2021年“春秋杯”新年欢乐赛

这次的赛制是第一次见到的沙漏赛制,对我这种不急不忙佛系做题的不是很友好,最后的名次也还行,前排观赏前面的神仙打架
result2.png

签到

程序是python打包出来的,解包找到主程序checkin,补上pyc头文件,反编译得到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
import cv2, re, sys
from aip import AipOcr
from apii import APP_ID, API_KEY, SECRECT_KEY, flag
client = AipOcr(APP_ID, API_KEY, SECRECT_KEY)
cap = cv2.VideoCapture(0)
i = 0
x = 1
print('\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⣀⣄⣀⢀⣀⣀⡀⠀⠀⠀⢀⣄⣀⣀⣀⣀⡀⠀⠀⠀⢠⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⡤⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⢀⣹⣉⣝⢸⡇⠀⠀⠀⠀⢀⡞⠉⠉⣹⠉⠉⠁⠀⠀⢠⢼⢦⠐⢺⠓⢲⠀⠀⠀⠀⣾⠀⠀⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⡤⣤⠤⢸⡏⢹⠉⠀⠀⠀⢸⡏⠉⢹⠉⠉⠁⠀⠀⠘⢸⠠⠤⢼⡤⠼⠤⠀⠀⠀⠛⡒⠒⡗⢒⠒⠂⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⢀⠇⠀⠰⣸⠀⢸⠀⠀⠀⠈⠉⠉⠉⢻⠉⠉⠉⠀⠀⠀⢸⠀⣠⠏⠱⣄⠀⠀⠀⢀⡴⠁⠀⡇⠈⠳⡄⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠉⠁⠀⠁⠀⠘⠀⠀⠀⠀⠀⠀⠀⠘⠀⠀⠀⠀⠀⠀⠘⠘⠁⠀⠀⠈⠃⠀⠀⠈⠀⠈⠉⠁⠀⠀⠀⠀⠀\n')
while True:
ret, frame = cap.read()
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('capture', frame)
cv2.imwrite('.\\i' + str(i) + '.png', frame)
i = i + 1
if i - 1 > x:
z = open('.\\i' + str(x) + '.png', 'rb')
img = z.read()
message = client.basicGeneral(img)
for j in message.get('words_result'):
words = message['words_result']
num_list = []
for s in words:
num_list.append(s['words'])
final = num_list
final = ''.join(final)
if 'FUN' in final:
print(flag)
f = open('flag.txt', 'w', encoding='utf-8')
f.write(flag)
f.close()
sys.exit(0)
else:
print('识别失败')
sys.exit(0)

else:
x = x + 1

if cv2.waitKey(1) & 255 == ord('q'):
break

cap.release()
cv2.destroyAllWindows()

看了一下代码,有OCR,发现真的和题目描述一样对着电脑摄像头扫“FUN”就可以了,然后放弃继续逆向写了个大大的FUN扫出来得到flagflag{ju5t_f0r_FUN}

evilMem

拿到vmem文件先用volatility查一下imageinfo

evilMem-1.png

Win7SP1x86

然后查一下进程列表

1
vol.py -f image.vmem --profile=Win7SP1x86 pslist

发现进程

1
0x858e1030 EvilImage.exe          1884   3516      1        7      1      0 2021-01-17 16:33:41 UTC+0000

把进程内存dump出来

1
vol.py -f image.vmem --profile=Win7SP1x86 memdump -p 1884 -D evilimage/

dump出来的内存用foremost恢复一下,恢复出来的文件太多,大多是系统进程,没什么太大的研究价值,根据时间找到两个可疑文件

1
2
28:	00000608.exe 	      16 KB 	     311296 	 01/17/2021 16:28:19
29: 00000672.dll 13 KB 344064 01/17/2021 16:28:19

修复一下程序segment的raw offset,反编译一下,发现exe文件时主程序EvilImage.exe,dll文件是调用的evil.dll

evilMem-2.png

没有修复IAT,但是并不影响理解程序,这里没有太多的内容,只是调用一下Evil.dll里面的checkflag函数判断一下输入的flag是否正确就可以了

evilMem-3.png

加密过程中出现了很明显的特征“expand 32-byte k”,很容易判断出来是chacha20,v8-v14进行密钥初始化,iv经过if判断条件的计算也可以很轻松地算出来,最后的加密结果也在内存中可以直接读取,解这个密码没有什么难度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import binascii
from Crypto.Cipher import ChaCha20
import struct

keys = [0xAA0F37A3, 0x214FF178, 0x6FF0CC56, 0x4B65E511, 0x2F60906D, 0xCA638692, 0xA001E464, 0x2BE81780]
key = b''
for i in keys:
key += struct.pack('<I', i)
secret = key

iv = struct.pack('<I', 0x6E7568D7 - 0x74) + struct.pack('<I', (0x217569D6 - 0x65))
print(iv)

msg = 'EE2AC08F12AF33C5D1133E75C88AADBC3D0200246522037E72623311FC838FB6'
msg = binascii.unhexlify(msg)
msg_nonce = iv
ciphertext = msg
cipher = ChaCha20.new(key=secret, nonce=msg_nonce)
plaintext = cipher.decrypt(ciphertext)
print(plaintext)

# b'chunqiu!'
# b'flag{R3im@aging_1ndir3ctly_LoL}\x00'

十二宫的挑衅

密码本身有点难度,但是因为和zodiac密码的解码方法完全相同,所以只要照着步骤做就很容易解出来

先对密文进行一个简单的换位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
s = '''^#@$@#()/>@?==%1(
!)>(*+3<#86@-7$^.
4&)8%#5&6!=%1#$-$
+5&?#!.03!%=@=101
0?(*~#??.+)%&.7^8
=1%*^=$5$7@@8>&*9
9@0185(+7)<%3#@^4
&@@<.)$3*#%%<<*++
.@.?=~**+!==65^@&'''
a = s.split('\n')
# print(a)
i = 0
j = 0
cc = []
for z in range(9 * 17):
cc.append(a[j % 9][i % 17])
if (z + 1) % 17 == 0:
cc.append('\n')
i += 2
j += 1
print("".join(cc))

然后放到AZdecrypt里面跑一下就出来了

1
2
3
4
5
IKILLED A LOT OF PEOPLE AND THE PEOPLE I 
KILLED WILL BECOME SLAVES TO SERVE ME THIS
IS FLAG WUUHUU TAKE OFF I HOPE YOU CAN DECRYPT
IT AS SOON AS POSSIBLE OR I WILL CONTINUE
TO COMMIT THE CRIME

就是flag确实不太好认

snowww

盲水印,binwalk跑出结果有一个matlab脚本,在这里找到讲解和提取水印的脚本

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
clc;clear;close all;
alpha = 1;

im = double(imread('original.jpg'))/255;
FA=fft2(im);
ori=double(imread('attack.jpg'))/255;
FB=fft2(ori);
imsize = size(im);
load('encode.mat');
FAO=ifft2(FB);
RI = FAO-double(im);
xl = 1:imsize(2);
yl = 1:imsize(1);
[xx,yy] = meshgrid(xl,yl);

FA2=fft2(FAO);
G=(FA2-FA)/alpha;
GG=G;
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(M(i),N(j),:)=G(i,j,:);
end
end
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:);
end
end
figure,imshow(GG);title('extracted watermark');

跑出结果

snow-1.png

我当时以为这已经是对我眼睛最大的考验了

SuperBrain

似乎是一个开发板的程序,进行一系列初始化之后运行主程序

superbrain-1.png

首先会进行通信,接收地图数据和答案数据

superbrain-2.png

superbrain-3.png

可以看到一共接收了36组地图和36个答案,然后执行程序判断答案是否正确

superbrain-4.png

每次执行传入一个地图和对应的答案

执行的程序主要算法如下

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
answer = (char)answer;
LABEL_10:
line = &map[7 * answer + 1];
while ( index < 7 && answer >= 0 && answer < 7 )
{
addr = &line[index];
ele = line[index];
if ( answer % 2 )
{
if ( line[index] )
{
if ( ele == 1 )
{
*addr = 0;
v8 = 0;
--answer;
goto LABEL_10;
}
if ( ele != 2 )
{
if ( ele != 3 )
return v8 == 1;
*addr = 2;
v8 = 2;
++answer;
goto LABEL_10;
}
*addr = 1;
v8 = 1;
if ( ++index >= 0 )
goto LABEL_10;
}
else
{
*addr = 3;
v8 = 3;
if ( --index >= 0 )
goto LABEL_10;
}
return v8 == 1;
}
if ( line[index] )
{
if ( ele == 1 )
{
*addr = 2;
v8 = 2;
++answer;
goto LABEL_10;
}
if ( ele != 2 )
{
if ( ele != 3 )
return v8 == 1;
*addr = 0;
v8 = 0;
--answer;
goto LABEL_10;
}
*addr = 3;
v8 = 3;
if ( --index >= 0 )
goto LABEL_10;
return v8 == 1;
}
*addr = 1;
v8 = 1;
if ( ++index < 0 )
return v8 == 1;
}
return v8 == 1;

可以看到类似于走迷宫的算法,分奇偶行执行不同的操作,然后发现答案只传入0-6这7种数字,直接遍历就可以跑出答案

最后一部分输出flag,需要一些数据在内存里都找的到

superbrain-5.png

很容易可以解出来

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
#include <iostream>

int runGame(char (*gamemap)[7], uint8_t startPoint) {
char answer; // r18
int v8; // r17
int v9; // r6
int index; // r19
char *v11; // r5
char *v12; // r4
char *v13; // r4
char *v14; // r5
char *line; // r20
char *addr; // r3
int ele; // r6
char map[61]; // [sp+7h] [-79h] BYREF
index = 0;
answer = startPoint;
v8 = -1;
v9 = 0;
do {
v11 = &map[7 * v9];
v12 = &(*gamemap)[7 * v9 - 1];
do {
*++v11 = *++v12;
++index;
} while (((index - 7) & 1) != 0);
for (; index < 7; index += 2) {
v13 = v12 + 1;
v14 = v11 + 1;
*v14 = *v13;
v12 = v13 + 1;
v11 = v14 + 1;
*v11 = *v12;
}
++v9;
index = 0;
} while (v9 < 7);
answer = (char) answer;
LABEL_10:
line = &map[7 * answer + 1];
while (index < 7 && answer >= 0 && answer < 7) {
addr = &line[index];
ele = line[index];
if (answer % 2) {
if (line[index]) {
if (ele == 1) {
*addr = 0;
v8 = 0;
--answer;
goto LABEL_10;
}
if (ele != 2) {
if (ele != 3)
return v8 == 1;
*addr = 2;
v8 = 2;
++answer;
goto LABEL_10;
}
*addr = 1;
v8 = 1;
if (++index >= 0)
goto LABEL_10;
} else {
*addr = 3;
v8 = 3;
if (--index >= 0)
goto LABEL_10;
}
return v8 == 1;
}
if (line[index]) {
if (ele == 1) {
*addr = 2;
v8 = 2;
++answer;
goto LABEL_10;
}
if (ele != 2) {
if (ele != 3)
return v8 == 1;
*addr = 0;
v8 = 0;
--answer;
goto LABEL_10;
}
*addr = 3;
v8 = 3;
if (--index >= 0)
goto LABEL_10;
return v8 == 1;
}
*addr = 1;
v8 = 1;
if (++index < 0)
return v8 == 1;
}
return v8 == 1;
}


int main() {
char gameMap[36][11][7] = {{
{1, 0, 0, 0, 0, 0, 0,},
{3, 0, 2, 2, 3, 1, 3,},
{1, 3, 2, 1, 2, 2, 1,},
{3, 2, 3, 1, 0, 1, 0,},
{3, 3, 0, 1, 2, 1, 0,},
{0, 3, 2, 2, 2, 1, 3,},
{2, 2, 2, 2, 1, 2, 3,},
},
{
{0, 0, 0, 0, 0, 1, 0,},
{0, 2, 2, 2, 1, 1, 1,},
{0, 2, 3, 2, 2, 0, 1,},
{0, 3, 1, 0, 2, 0, 1,},
{0, 0, 2, 1, 3, 1, 1,},
{3, 0, 1, 2, 2, 0, 2,},
{3, 3, 3, 0, 2, 3, 3,},
},
……………………………………
{
{3, 3, 1, 3, 3, 0, 2,},
{1, 2, 1, 3, 0, 2, 3,},
{0, 1, 2, 3, 1, 1, 2,},
{2, 1, 0, 3, 0, 2, 1,},
{3, 3, 0, 2, 3, 1, 2,},
{2, 2, 3, 3, 2, 3, 1,},
{3, 2, 2, 3, 0, 2, 3,},
},
{
{1, 0, 2, 0, 2, 3, 0,},
{2, 1, 3, 0, 0, 3, 0,},
{2, 3, 0, 0, 1, 2, 0,},
{3, 3, 0, 2, 0, 1, 1,},
{3, 0, 3, 2, 1, 2, 0,},
{0, 3, 2, 1, 0, 2, 2,},
{2, 3, 2, 2, 2, 0, 1,},
},
};
int res[36];
for (int i = 0; i < 36; i++) {
for (int j = 0; j < 7; j++) {
if (runGame(gameMap[i], j)) {
res[i] = j;
break;
}
}
}

std::string charDic = "0123456789abcdef-";
int base[] = {0x17, 0x06, 0x0A, 0x20, 0x2D, 0x0B, 0x39, 0x01, 0x0A, 0x10,
0x5E, 0x01, 0x1A, 0x0F, 0x51, 0x59, 0x06, 0x1F, 0x0E, 0x09,
0x23, 0x47, 0x03, 0x0A, 0x38, 0x05, 0x48, 0x23, 0x02, 0x0A,
0x12, 0x33, 0x1D, 0x1F, 0x48, 0x15};

std::cout << "flag{";
for (int i = 0; i < 36; i++)
std::cout << charDic[(res[i] + base[i]) % 17];
std::cout << "}" << std::endl;
return 0;
}

//flag{ace40f94-1b3d-d97f-f256-bb726e611fa7}

puzzle

每个碎片都加了噪声,还有很多碎片有很大部分重复内容,就算是拼出来图片也看不清楚是什么字符……

但是又太菜了写不出脚本识别,就只能手动拼图+ps识图

1
flag{w9w45my6x8kk4e8gp9nqm6j2c154wad49}

2019-nCoV

有三个文件,mp3,wav和一个压缩包,然后题目新增了一个hint,很容易看出来base32编码

1
2
3
4
5
6
http://www.merrybio.com.cn/blog/SARS-CoV-2-genomic-analysis.html
https://www.ncbi.nlm.nih.gov/orffinder/
http://www.merrybio.com.cn/blog/coronavirus-introduction.html

Please notice The largest structural protein
the password is the md5(it's gene sequence) and do not let the ‘\n’ in md5()

给的文章有新冠病毒的介绍和基因查询页面,根据提示要算S蛋白质的基因序列的md5得到密码,这个密码是mp3隐写的密码,用MP3Stego解出来又一个密码“2019-nCoV”,试了一下是压缩包密码

1
2
3
4
m = hashlib.md5()
m.update("MFLLTTKRTMFVFLVLLPLVSSQCVNLTTRTQLPPAYTNSFTRGVYYPDKVFRSSVLHSTQDLFLPFFSNVTWFHAIHVSGTNGTKRFDNPVLPFNDGVYFASTEKSNIIRGWIFGTTLDSKTQSLLIVNNATNVVIKVCEFQFCNDPFLGVYYHKNNKSWMESEFRVYSSANNCTFEYVSQPFLMDLEGKQGNFKNLREFVFKNIDGYFKIYSKHTPINLVRDLPQGFSALEPLVDLPIGINITRFQTLLALHRSYLTPGDSSSGWTAGAAAYYVGYLQPRTFLLKYNENGTITDAVDCALDPLSETKCTLKSFTVEKGIYQTSNFRVQPTESIVRFPNITNLCPFGEVFNATRFASVYAWNRKRISNCVADYSVLYNSASFSTFKCYGVSPTKLNDLCFTNVYADSFVIRGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAWNSNNLDSKVGGNYNYLYRLFRKSNLKPFERDISTEIYQAGSTPCNGVEGFNCYFPLQSYGFQPTNGVGYQPYRVVVLSFELLHAPATVCGPKKSTNLVKNKCVNFNFNGLTGTGVLTESNKKFLPFQQFGRDIADTTDAVRDPQTLEILDITPCSFGGVSVITPGTNTSNQVAVLYQDVNCTEVPVAIHADQLTPTWRVYSTGSNVFQTRAGCLIGAEHVNNSYECDIPIGAGICASYQTQTNSPRRARSVASQSIIAYTMSLGAENSVAYSNNSIAIPTNFTISVTTEILPVSMTKTSVDCTMYICGDSTECSNLLLQYGSFCTQLNRALTGIAVEQDKNTQEVFAQVKQIYKTPPIKDFGGFNFSQILPDPSKPSKRSFIEDLLFNKVTLADAGFIKQYGDCLGDIAARDLICAQKFNGLTVLPPLLTDEMIAQYTSALLAGTITSGWTFGAGAALQIPFAMQMAYRFNGIGVTQNVLYENQKLIANQFNSAIGKIQDSLSSTASALGKLQDVVNQNAQALNTLVKQLSSNFGAISSVLNDILSRLDKVEAEVQIDRLITGRLQSLQTYVTQQLIRAAEIRASANLAATKMSECVLGQSKRVDFCGKGYHLMSFPQSAPHGVVFLHVTYVPAQEKNFTTAPAICHDGKAHFPREGVFVSNGTHWFVTQRNFYEPQIITTDNTFVSGNCDVVIGIVNNTVYDPLQPELDSFKEELDKYFKNHTSPDVDLGDISGINASVVNIQKEIDRLNEVAKNLNESLIDLQELGKYEQYIKWPWYIWLGFIAGLIAIVMVTIMLCCMTSCCSCLKGCCSCGSCCKFDEDDSEPVLKGVKLHYT".encode())
print(m.hexdigest())
# 98eb1b1760bcc837934c8695a1cee923

wav是lsb隐写,直接用SilentEye解码得到一串没什么意义的字符“priebeijoarkjpxmdkucxwdus”

压缩包得到一张图片和hint2

hint2 16进制转ascii码得到第二个提示

1
2
3
4
you must pay attention to N protein ,How do that get into the viral capsid?
do you know steghide?
the password is encrypt by Vigenère Cipher
the screct key is The top 20 characters with the most occurrences are counted+COMBAT'

图片应该是用steghide做的隐写,隐写的密码是刚刚wav隐写得到的字符经过维吉尼亚密码解码之后的结果,但是维吉尼亚密码的密钥就跟第一句不明不白的话有关系,卡了很久,后来仔细看一下文章,有这么一句话

1
核衣壳蛋白(nucleoproteinN)位于囊膜内部,呈螺旋状,包裹着病毒单股正链的RNA基因组。病毒在进行装配时,N蛋白先和病毒RNA相互作用形成复合体形式,然后再结合M蛋白、E蛋白,最后被包装进入病毒衣壳内。

猜测是对N蛋白质、M蛋白质、E蛋白质的基因序列进行词频统计

1
2
3
4
5
6
7
8
9
10
11
12
dic = {}
N="MSDNGPQNQRNAPRITFGGPSDSTGSNQNGERSGARSKQRRPQGLPNNTASWFTALTQHGKEDLKFPRGQGVPINTNSSPDDQIGYYRRATRRIRGGDGKMKDLSPRWYFYYLGTGPEAGLPYGANKDGIIWVATEGALNTPKDHIGTRNPANNAAIVLQLPQGTTLPKGFYAEGSRGGSQASSRSSSRSRNSSRNSTPGSSRGTSPARMAGNGGDAALALLLLDRLNQLESKMSGKGQQQQGQTVTKKSAAEASKKPRQKRTATKAYNVTQAFGRRGPEQTQGNFGDQELIRQGTDYKHWPQIAQFAPSASAFFGMSRIGMEVTPSGTWLTYTGAIKLDDKDPNFKDQVILLNKHIDAYKTFPPTEPKKDKKKKADETQALPQRQKKQQTVTLLPAADLDDFSKQLQQSMSSADSTQAMFHLVDFQVTIAEILLIIMRTFKVSIWNLDYIINLIIKNLSKSLTENKYSQLDEEQPMEIDMADSNGTITVEELKKLLEQWNLVIGFLFLTWICLLQFAYANRNRFLYIIKLIFLWLLWPVTLACFVLAAVYRINWITGGIAIAMACLVGLMWLSYFIASFRLFARTRSMWSFNPETNILLNVPLHGTILTRPLLESELVIGAVILRGHLRIAGHHLGRCDIKDLPKEITVATSRTLSYYKLGASQRVAGDSGFAAYSRYRIGNYKLNTDHSSSSDNIALLVQ"
for word in N:
if word not in dic:
dic[word] = 1
else:
dic[word] = dic[word] + 1

swd = sorted(dic.items(), key=lambda asd: asd[1], reverse=True)
print(swd)

# LGASTRIQKNDPFEVYMWHC

一共20个字符按照频次从高到低的顺序排列,在后面添加COMBAT得到密钥LGASTRIQKNDPFEVYMWHCCOMBAT

解维吉尼亚密码得到隐写的密码eliminatenovelcoronavirts

用steghide解出flag

1
flag{we_will_over_come_SARS-COV}
2021 ciscn Re部分wp 2020-全国工业互联网安全技术技能大赛

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×