0%

SUSCTF2022

SUSCTF2022

简述:应该是第一次和syclover的师傅们一块参与到比赛中(tql就出一道签到,不算),本次做出了4道题,两道逆向,两道misc,只交了两道misc,逆向师傅太强了!!!

师傅们都太强了!!!!!

re

DigitalCircuits

给了一个exe的包,包上图片是python。在极客大挑战中就遇到过这种问题,我们首先将exe转成pyc文件:

1
python pyinstxtractor.py DigitalCircuits.exe

然后取出生成文件夹的struct和同名pyc文件,将struct文件前16字节覆盖到pyc文件上,然后反编译pyc文件。

1
uncompyle6 DigitalCircuits.pyc > DigitalCircuits.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
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
import time


def f1(a, b):
if a == '1':
if b == '1':
return '1'
return '0'

def f2(a, b):
if a == '0':
if b == '0':
return '0'
return '1'

def f3(a):
if a == '1':
return '0'
if a == '0':
return '1'

def f4(a, b):
return f2(f1(a, f3(b)), f1(f3(a), b))

def f5(x, y, z):
s = f4(f4(x, y), z)
c = f2(f1(x, y), f1(z, f2(x, y)))
return (s, c)

def f6(a, b):
ans = ''
z = '0'
a = a[::-1]
b = b[::-1]
for i in range(32):
ans += f5(a[i], b[i], z)[0]
z = f5(a[i], b[i], z)[1]
return ans[::-1]

def f7(a, n):
return a[n:] + '0' * n

def f8(a, n):
return n * '0' + a[:-n]

def f9(a, b):
ans = ''
for i in range(32):
ans += f4(a[i], b[i])
return ans

def f10(v0, v1, k0, k1, k2, k3):
s = '00000000000000000000000000000000'
d = '10011110001101110111100110111001'#这是delta的值,老朋友了
for i in range(32):
s = f6(s, d)
v0 = f6(v0, f9(f9(f6(f7(v1, 4), k0), f6(v1, s)), f6(f8(v1, 5), k1))) #从常数猜测是tea加密,并没有&3,不是xtea
v1 = f6(v1, f9(f9(f6(f7(v0, 4), k2), f6(v0, s)), f6(f8(v0, 5), k3)))
return v0 + v1


k0 = '0100010001000101'.zfill(32) #密钥,zfill是将前面的数补成后面长度,前面加0
k1 = '0100000101000100'.zfill(32) #密钥
k2 = '0100001001000101'.zfill(32) #密钥
k3 = '0100010101000110'.zfill(32) #密钥
flag = input('please input flag:')
if flag[0:7] != 'SUSCTF{' or flag[(-1)] != '}':
print('Error!!!The formate of flag is SUSCTF{XXX}')
time.sleep(5)
exit(0)
flagstr = flag[7:-1]
if len(flagstr) != 24:
print('Error!!!The length of flag 24')
time.sleep(5)
exit(0)
else:
res = ''
for i in range(0, len(flagstr), 8):
v0 = flagstr[i:i + 4]
v0 = bin(ord(flagstr[i]))[2:].zfill(8) + bin(ord(flagstr[(i + 1)]))[2:].zfill(8) + bin(ord(flagstr[(i + 2)]))[2:].zfill(8) + bin(ord(flagstr[(i + 3)]))[2:].zfill(8)
v1 = bin(ord(flagstr[(i + 4)]))[2:].zfill(8) + bin(ord(flagstr[(i + 5)]))[2:].zfill(8) + bin(ord(flagstr[(i + 6)]))[2:].zfill(8) + bin(ord(flagstr[(i + 7)]))[2:].zfill(8)
res += f10(v0, v1, k0, k1, k2, k3)
if res == '00111110100010010100011111001011 11001100100101000100011000111001 00110001001101011000001110001000 00111011000010110110100010010011 11011010011000100111001101100001 00111011001011100110010000100111':
print('True')
else:
print('False')
time.sleep(5)
# okay decompiling DigitalCircuits.pyc

很显然从f10能看出这就是个tea加密,并且没有魔改:

但从res能看出是3组数组,不是4组,所以给出脚本:

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
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea (uint32_t* v,int n, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}


int main()
{
uint32_t v[2]= {0x3e8947cb,0xcc944639};
uint32_t w[2]= {0x31358388,0x3b0b6893};
uint32_t x[2]= {0xda627361,0x3b2e6427};
uint32_t k[4]= {17477,16708,16965,17734};
int n = 2; //n的绝对值表示v的长度,取正表示加密,取负表示解密
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
btea(v, -n, k);
printf("%x %x ",v[0],v[1]);
btea(w, -n, k);
printf("%x %x ",w[0],w[1]);
btea(x, -n, k);
printf("%x %x",x[0],x[1]);
return 0;
}

解出来,转换成字符串,然后套上壳。

hell_world

这个题听师傅说与西湖论剑的题是一样的。怪不得出那么快,我们来好好看一下这个题,有点像hgame里面那道虚拟机的题

这个题运行顺序是:0 – 1 – 2 – 3,在3出进行一次字符串判断,长度是44才能到4去,如果不是到5,然后输出wrong。

看内存发现输入数据存入了a1数组

然后转到9 – 10 – 11

其中case9是将输入转化为二进制,只是0和1使用2和3来替换,接着执行的case10是使用异或数组与输入进行异或操作且转化为二进制。

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
case 9:
v17 = sub_7FF6554DEB56();
v41 = *(_QWORD *)(a1 + 344);
v42 = 0;
sub_7FF6554A3F60((__int64)&v41);
*(_QWORD *)(a1 + 344) = v41;
*(_BYTE *)(a1 + 368) = v42;
sub_7FF6554DEBD4(v17);
*(_DWORD *)(a1 + 372) = *(unsigned __int8 *)(a1 + 368);
v18 = sub_7FF6554DEB56();
v19 = *(_DWORD *)(a1 + 372);
if ( v19 < 0 )
sub_7FF6554F88D2("hello.vhdl", 48i64);
sub_7FF6554D0180(&v77, (unsigned int)v19, 8i64);
v55 = v77;
v56 = v38;
v38[0] = *(_DWORD *)v78;
v38[1] = *(_DWORD *)(v78 + 4);
v39 = *(_BYTE *)(v78 + 8);
v40 = *(_DWORD *)(v78 + 12);
if ( v40 != 8 )
sub_7FF6554F88D2("hello.vhdl", 48i64);
v23 = v55;
for ( i = 0i64; (unsigned int)i <= 7; ++i )
{
v25 = *(_BYTE **)(a1 + 8 * i + 16);
*(_BYTE *)(a1 + i + 392) = *(_BYTE *)(v23 + i);
v26 = 1;
if ( !v25[42] )
v26 = *v25 != *(_BYTE *)(a1 + i + 392);
if ( v26 )
sub_7FF6554E5B00();
}
sub_7FF6554DEBD4(v18);
result = sub_7FF6554DF80C(1000000i64, (__int64)"hello.vhdl", 0x31u);
*(_DWORD *)(a1 + 380) = 10;
return result;

我们看到case11处,我们能动调出比较函数,那么一定存在加密后的值,v71–》v6,所以找到了密文,我们输入经过加密就跟密文对比。

那么我们很容易就写出脚本了:

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
data1 = [86,
218,
205,
58,
126,
134,
19,
181,
29,
157,
252,
151,
140,
49,
107,
201,
251,
26,
226,
45,
220,
211,
241,
244,
54,
9,
32,
66,
4,
106,
113,
83,
120,
164,
151,
143,
122,
114,
57,
232,
61,
250,
64,
61,
408,
0,
0,
0]
data2 = [5,
143,
158,
121,
42,
192,
104,
129,
45,
252,
207,
164,
181,
85,
95,
228,
157,
35,
214,
29,
241,
231,
151,
145,
6,
36,
66,
113,
60,
88,
92,
48,
25,
198,
245,
188,
75,
66,
93,
218,
88,
155,
36,
64]
flag = ''
for i in range(len(data2)):
flag += chr(data1[i] ^ data2[i])
print(flag)
#SUSCTF{40a339d4-f940-4fe0-b382-cabb310d2ead}

K.O

tttree

刚看的时候感觉是个二叉树,但无法反汇编,所以只能审代码。有点难度,复现一下:

misc

本次misc出了很快,这种感觉很不错,但我很烦题目都是英文,翻译根本翻不了。。。

ra2

红警游戏,让找旗子,说什么降落到了月球,设定是晚上很危险,白天安全,那直接把白天时间拉满不就行了。

找到目录\Romanovs-Vengeance\mods\rv\maps\ctf-01,找到ctf-01.lua文件,改几个数据

把时间无限延长,然后进去放几个狗,乱跑找flag

这个体验不错,就我在图书馆玩游戏,笑死。。

Tanner

这个题其实我第一天就有思路,而且差不多出来了,就是他这个英文翻译很不准确。。真服了,导致我虚拟机没写完呢,今天还要赶虚拟机。。

这个图,应该是lpdc的另一种表示,可以表示成矩阵,checknodes当成行,bitnodes当成列,得到(10,5)矩阵,题目给了这样一段提示

1
THE FLAG IS the sha256 of the sum ofthe proper codewords(binary plus)which satisfy the condition.(note: with no zeros front)

我纯纯文盲,应该是让求码字,note的意思应该是最后sum的和前面不能加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
import numpy as np
N=10
K=5 #这个值是列数-行数,而不是行数!!!这题比较巧

H=np.array([[ 1,1,1,1,0,0,0,0,0,0],
[1,0,0,0,1,1,1,0,0,0],
[0,1,0,0,1,0,0,1,1,0],
[0,0,1,0,0,1,0,1,0,1],
[0,0,0,1,0,0,1,0,1,1]])

b=[]
for i in range(2**N):
a=format(i,'b')
b.append("{:0>10s}".format(a))

v=np.zeros((2**N,N))
for i in range(2**N):
v[i]=b[i]
for j in range(N):
v[i][j]=b[i][j] #v是0000000~1111111

w=np.zeros((1,N-K))
for o in range(2**N):
if np.all(np.dot(v[o],H.T)%2==w):
print(v[o])

得到许多二进制,我们求和

1
2
3
4
5
a =
0b0000000000+0b0000000111+0b0000011001+0b0000011110+0b0000101010+0b0000101101+0b0000110011+0b0000110100+0b0011000001+0b0011000110+0b0011011000+0b0011011111+0b0011101011+0b0011101100+0b0011110010+0b0011110101+0b0101000010+0b0101000101+0b0101011011+0b0101011100+0b0101101000+0b0101101111+0b0101110001+0b0101110110+0b0110000011+0b0110000100+0b0110011010+0b0110011101+0b0110101001+0b0110101110+0b0110110000+0b0110110111+0b1001001000+0b1001001111+0b1001010001+0b1001010110+0b1001100010+0b1001100101+0b1001111011+0b1001111100+0b1010001001+0b1010001110+0b1010010000+0b1010010111+0b1010100011+0b1010100100+0b1010111010+0b1010111101+0b1100001010+0b1100001101+0b1100010011+0b1100010100+0b1100100000+0b1100100111+0b1100111001+0b1100111110+0b1111001011+0b1111001100+0b1111010010+0b1111010101+0b1111100001+0b1111100110+0b1111111000+0b1111111111
print(a)
#111111111100000
#c17019990bf57492cddf24f3cc3be588507b2d567934a101d4de2fa6d606b5c1

求得的值再sha256,用小写形式交了!!大写不行!!!

audio

如果给出了原音频和加密过的音频,要进行音频相消!

把两段音频同时拖进audacity里面,然后分别对音频进行标准化,然后分别截去静音,然后对fromfriends.wav进行反相操作,而后把两段音频叠起来即可

image-20220301172357554

读出摩斯密码!

misound

频谱图中有AnEWmuLTiPLyis_etimes_wiLLbEcomE_B,本以为是前一段flag,没想到是提示。

然后silenteye搞出来base64,翻出来是207 359 220 224 352 315 359 374 290 310 277 507 391 513 423 392 508 383 440 322 420 427 503 460 295 318 245 302 407 414 410 130 369 317

但这个最牛逼的是听起来像个摩斯密码,用sstv分析

image-20220301185258769

image-20220301185427312

用该网站https://www.dcode.fr/dotsies-writing解出来

我们用第一组数乘第二组数,然后除以26就基本确定flag长度了,26这个值可以试出来

或者我们观察第一组乘第二组除以第三组就是26的近似值!