0%

KnightCTF 2022 wp

这个比赛是一个对新人很友好的比赛,不过也能学到一些知识

The_Flag_Vault

64位elf文件

用ida打开,发现程序将输入的字符串存入s2中,与s1字符串比较,若相同则输出正确的flag

所以我们只需要运行程序并输入s1字符串即可得到flag

The Encoder

64位无壳

用它给出的数据减去1337既得flag

1
2
3
4
5
6
7
8
9
10
encoder = [1412, 1404, 1421, 1407, 1460, 1452, 1386, 1414, 1449, 1445,
1388, 1432, 1388, 1415, 1436, 1385, 1405, 1388, 1451, 1432,
1386, 1388, 1388, 1392, 1462]
v7 = 1337
flag = []
for i in range(len(encoder)):
flag.append(encoder[i] - v7)
print(flag)
for i in range(len(flag)):
print(chr(flag[i]),end='')

Baby Shark

这道题我最开始一直以为是音频隐写,后来发现并不是,用jd-gui打开

将0xflag对应部分用base64解密(base64解密网站)

Flag Checker

将文件用ida打开,找到main函数如下

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[512]; // [rsp+0h] [rbp-240h] BYREF
char v5[51]; // [rsp+200h] [rbp-40h] BYREF
char v6; // [rsp+233h] [rbp-Dh]
int v7; // [rsp+234h] [rbp-Ch]
int j; // [rsp+238h] [rbp-8h]
int i; // [rsp+23Ch] [rbp-4h]

strcpy(v5, "08'5[Z'Y:H3?X2K3V)?D2G3?H,N6?G$R(G]");
printf("Give me a flag : ");
__isoc99_scanf("%s", v4);
for ( i = 0; v4[i]; ++i )
{
if ( v4[i] <= 64 || v4[i] > 90 )
{
if ( v4[i] <= 96 || v4[i] > 122 )
v4[i] = v4[i];
else
v4[i] = -37 - v4[i];
}
else
{
v4[i] = -101 - v4[i];
}
}
for ( j = 0; v4[j]; ++j )
v4[j] -= 32;
v7 = 0;
v6 = 0;
while ( v5[v7] )
{
if ( v5[v7] != v4[v7] )
{
v6 = 0;
break;
}
v6 = 1;
++v7;
}
if ( v6 )
puts("You have entered the right flag.");
else
puts("Sorry ! Its wrong flag.");
return 0;
}

由伪代码可知,程序将flag存入v4中,经过一系列加密后与v5字符串进行比较,所以只需要将已知的v5字符串解密即得flag,注意v4中每个元素的范围都在ascii码的范围内,所以要将运算结果对128取余,脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
v5 = "08'5[Z'Y:H3?X2K3V)?D2G3?H,N6?G$R(G]"
coded_flag = []
for x in v5:
coded_flag.append(ord(x) + 32)
for i in range(len(coded_flag)):
if 90 < coded_flag[i] <= 96 or coded_flag[i] > 122 or coded_flag[i] <= 64:
coded_flag[i] = coded_flag[i]
elif 96 < coded_flag[i] <= 122:
coded_flag[i] = (-37 - coded_flag[i])%128
else:
coded_flag[i] = (-101 - coded_flag[i])%128
for z in coded_flag:
print(chr(z), end='')

运行即得flag

Knight Vault

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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+Bh] [rbp-435h]
int i; // [rsp+Ch] [rbp-434h]
int v6; // [rsp+Ch] [rbp-434h]
char v7[48]; // [rsp+10h] [rbp-430h] BYREF
char v8[1016]; // [rsp+40h] [rbp-400h] BYREF
unsigned __int64 v9; // [rsp+438h] [rbp-8h]

v9 = __readfsqword(0x28u);
strcpy(v7, "*9J<qiEUoEkU]EjUc;U]EEZU`EEXU^7fFoU^7Y*_D]s");
puts("Hello There..\nWelcome to KS Vault.");
printf("Please enter vault password : ");
__isoc99_scanf("%s", v8);
for ( i = 0; v8[i]; ++i )
{
v8[i + 512] = v8[i] - 10;
if ( v8[i + 512] == 65 )
v8[i + 512] = 42;
}
v6 = 0;
v4 = 0;
while ( v7[v6] )
{
if ( v7[v6] != v8[v6 + 512] )
{
v4 = 0;
break;
}
v4 = 1;
++v6;
}
if ( v4 )
win();
else
puts("Wrong password !");
return 0;
}

解密脚本如下

1
2
3
4
5
6
7
8
encoded_flag = '*9J<qiEUoEkU]EjUc;U]EEZU`EEXU^7fFoU^7Y*_D]s'
flag = []
for i in encoded_flag:
if ord(i) == 42:
flag.append(75)
else:flag.append(ord(i) + 10)
for i in range(len(flag)):
print(chr(flag[i]), end='')

运行得flag

Knight Switch Bank

这道题也是给出了加密后的字符串以及加密过程,求出加密前的字符串即得flag

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[512]; // [rsp+0h] [rbp-430h]
char v5[512]; // [rsp+200h] [rbp-230h] BYREF
char v6[32]; // [rsp+400h] [rbp-30h] BYREF
int i; // [rsp+420h] [rbp-10h]
char v8; // [rsp+427h] [rbp-9h]
int v9; // [rsp+428h] [rbp-8h]
int v10; // [rsp+42Ch] [rbp-4h]

strcpy(v6, "ZRIU]HdANdJAGDIAxIAvDDsAyDDq_");
v10 = 0;
v9 = 0;
puts("-------------------------------------");
puts("\tKnight Switch Bank");
puts("--------------------------------------");
puts("Welcome to Knight Switch Bank....");
printf("Please enter your password : ");
__isoc99_scanf("%s", v5);
while ( v5[v10] )
{
if ( v5[v10] <= 64 || v5[v10] > 77 )
{
if ( v5[v10] <= 96 || v5[v10] > 109 )
{
if ( v5[v10] <= 77 || v5[v10] > 90 )
{
if ( v5[v10] <= 109 || v5[v10] > 122 )
v4[v10] = v5[v10] - 32;
else
v4[v10] = v5[v10] - 13;
}
else
{
v4[v10] = v5[v10] - 13;
}
}
else
{
v4[v10] = v5[v10] + 13;
}
}
else
{
v4[v10] = v5[v10] + 13;
}
++v10;
}
while ( v4[v9] )
v4[v9++] += 2;
v8 = 0;
for ( i = 0; v6[i]; ++i )
{
if ( v6[i] != v4[i] )
{
v8 = 0;
break;
}
v8 = 1;
}
if ( v8 )
winner();
else
puts("Oh My God ! You entered a wrong password.");
return 0;
}

可以看到,程序对不同范围的输入字符串进行了不同的加密,所以可以爆破求字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
encoded_flag = 'ZRIU]HdANdJAGDIAxIAvDDsAyDDq_'
flag = []
for j in range(len(encoded_flag)):
for i in range(0, 128):
if i <= 64 or i > 77:
if i <= 96 or i > 109:
if i <= 77 or i > 90:
if i <= 109 or i > 122:
k = i - 32
else:
k = i - 13
else:
k = i - 13
else:
k = i + 13
else:
k = i + 13
if k == ord(encoded_flag[j]) - 2:
flag.append(i)
for i in range(len(flag)):
print(chr(flag[i]), end='')

运行既得flag

Droid Flag

这道题比赛时我没有做出来,原因是不会寻找字符串

方法一,用jeb直接打开DroidFlag.apk,在MainActivity中可以看到,flag即为getS1(),getS3(),getS2(),getS4()几个函数的返回值拼接起来

双击getS1(),可以跳转到定义这几个函数的位置

可以看到,jeb已经将这几个字符串的值注释出来了,易得flag

1
2
3
4
5
6
7
s1="3LpM1s"
s3="D10RdNa"
s2="3Sr3V3r"

a = s1 + "_" +s2+ "_" +s3
b = a[::-1]
print('KCTF{'+b+'}')

另一种方法参考了这篇wp

[Droid Flag Rev] Knight CTF 2022 (github.com)

首先将apktools复制到DroidFlag.apk所在的文件夹,执行如下命令

在values文件夹中找到strings.xml

同样可以查看字符串