pragyan-ctf-2022

涉及到的题目链接:链接:https://pan.baidu.com/s/1ll1bWhd845tj6JDFkx3i6Q
提取码:fsrp

Web Awesome

打开网页,看到提交按钮的事件

image-20220307223734453

从源代码中定位到这个onclick

image-20220307223841070

这是一个考察wasm的题,将网站中的index.wasm下载下来,进行.wasm->.o的转换,ida中看到转化后的目标文件,运算只有一个异或9

image-20220307224040013

再看到init_memory中的数据,异或一下9即是flag

image-20220307224232136

Oak

给的一个class文件,使用jar命令将这个class文件打包成一个jar包,然后用jd-gui查看jar包。

1
jar -cvf Oak.jar Oak.class

jar包内容如下,剩下就是简单的java代码审计逆向。

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
import java.io.PrintStream;

public class Oak
{
static long[] data = { 28767L, 24418L, 25470L, 29771L, 26355L, 31349L, 13032L, 30456L, 14663L, 27592L, 8916L, 29409L, 7348L, 17474L, 5124L, 3345L, 49357L, 61058L, 65159L, 53773L, 67886L, 72426L, 103728L, 158125L, 179542L, 166504L, 212101L, 282674L, 320873L, 329272L, 400021L, 479881L, 535081L, 599886L, 662294L, 731441L, 831284L, 947032L, 1021482L };

public static int t_helper(int paramInt, int[] paramArrayOfInt)
{
if (paramArrayOfInt[paramInt] != -1) {
return paramArrayOfInt[paramInt];
}
if (paramInt == 0)
{
paramArrayOfInt[0] = 0;
return paramArrayOfInt[0];
}
if (paramInt == 1)
{
paramArrayOfInt[1] = 1;
return paramArrayOfInt[1];
}
if (paramInt == 2)
{
paramArrayOfInt[2] = 3;
return paramArrayOfInt[2];
}
paramArrayOfInt[paramInt] = (3 * t_helper(paramInt - 1, paramArrayOfInt) - 3 * t_helper(paramInt - 2, paramArrayOfInt) + t_helper(paramInt - 3, paramArrayOfInt));

return paramArrayOfInt[paramInt];
}

public static int t(int paramInt)
{
int[] arrayOfInt = new int[paramInt + 1];
for (int i = 0; i < arrayOfInt.length; i++) {
arrayOfInt[i] = -1;
}
return t_helper(paramInt, arrayOfInt);
}

public static void main(String[] paramArrayOfString)
{
if (paramArrayOfString.length != 1)
{
System.out.println("Usage: [flag]");
return;
}
if (check(paramArrayOfString[0])) {
System.out.println("Correct!");
} else {
System.out.println("Incorrect");
}
}

public static long[] conv(String paramString)
{
long[] arrayOfLong = new long[paramString.length()];
for (int i = 0; i < paramString.length(); i++) {
arrayOfLong[i] = ((paramString.charAt(i) << '\b') + paramString.charAt((i + 1) % paramString.length()));
}
return arrayOfLong;
}

public static boolean check(String paramString)
{
long[] arrayOfLong = conv(paramString);
for (int i = 0; i < arrayOfLong.length; i++) {
if (data[i] != (arrayOfLong[i] ^ t(i * i))) {
return false;
}
}
return true;
}
}

Secure-lock

开始就是五个逻辑很明显的check

1、check1

限定了这个数是45位,且给了这个数的后15位与前9位,而check_num2就是验证这个数是否是阿姆斯特朗数。
image-20220307224439985

image-20220307224445820

因此这个checK就是让我们求一个45位,且限定了前9位与后15位的阿姆斯特朗数,我们爆破中间21位即可。

1
2
3
4
5
6
7
8
9
10
11
12
check = lambda N:sum(map(lambda x: int(x)**len(str(N)), str(N))) == N

x = '01'
a = '110011001'
c = '100011001011111'
for i in range(2**21):
b = ''
for j in range(21):
b += x[(i >> j)&1]
ans = int(a+b+c, 2)
if check(ans):
print(ans)

2、check2

一个6字节伪随机数,与shuffle算法,这里要注意一下的就是通过输入8字节数据通过shuffle函数后要变为6字节与伪随机数相等。

image-20220307225304002

image-20220307225316911

解密:

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
>>> v7 = [0]*8
>>> v7[0] = 67
>>> v7[1] = 131
>>> v7[2] = 37
>>> v7[3] = 59
>>> v7[4] = 193
>>> v7[5] = 71
>>> v7[6] = 107
>>> v7[7] = 233
>>> v7
[67, 131, 37, 59, 193, 71, 107, 233]
>>> ans = []
>>> for i in range(8):
... tmp = 0
... for j in range(8):
... if i != j:
... tmp ^= v7[j]
... ans.append(tmp)
...
>>> ans
[153, 89, 255, 225, 27, 157, 177, 51]
>>> enc2 = 0x0E08A6F2D5B19
>>> enc2 = enc2.to_bytes(6, 'big')
>>> enc2
b'\xe0\x8ao-[\x19'
>>> for i in range(2, 8):
... ans[i] ^= enc2[i-2]
...
>>> ans
[153, 89, 31, 107, 116, 176, 234, 42]
>>> ans = bytes(ans)
>>> int.from_bytes(ans, 'little')
3092478108203178393

3、check3

一个base64

4、check4

多个方程:
image-20220307225841720

因为这个check的有一个条件 && ,反过来就是 || 这样就导致了多解,这使得这个程序本地有多个可以反馈正确的输入。

z3解即可。

5、check5

就是一些异或和移位算法:

image-20220307230148992

解密:

先跑出get()函数返回的数据:

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
#include <stdio.h>

int v7[10];


int check_num_1(int a1)
{
unsigned int i; // [rsp+10h] [rbp-8h]

if ( a1 == 1 )
return 0;
if ( (a1 & 1) == 0 )
return a1 == 2;
for ( i = 3LL; a1 >= i * i; i += 2LL )
{
if ( !(a1 % i) )
return 0;
}
return 1;
}


int get(int a1)
{
int v2; // [rsp+8h] [rbp-10h]
int v3; // [rsp+10h] [rbp-8h]

v2 = -1LL;
v3 = 0LL;
while ( v2 != a1 )
{
if ( check_num_1(++v3) )
++v2;
}
return v3;
}

int main(void)
{
v7[0] = 103;
v7[1] = 127;
v7[2] = 157;
v7[3] = 11;
v7[4] = 29;
v7[5] = 211;

for(int i = 0; i < 6; i++)
{
printf("%#x, ", get(v7[i])&0xff);
}

return 0;
}
//0x39, 0xcf, 0xa1, 0x25, 0x71, 0x15

异或和移位解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> x = [0x39, 0xcf, 0xa1, 0x25, 0x71, 0x15]
>>> enc3 = 0xA8F24130A1EFLL
>>> enc3 = enc3.to_bytes(6, 'little')
>>> enc3
b'\xef\xa10A\xf2\xa8'
>>> int.from_bytes(enc3, 'little')
185758429258223
>>> enc3 = list(enc3)
>>> enc3 = [16 * (i & 0xF) + (i >> 4) for i in enc3]
>>> enc3
[254, 26, 3, 20, 47, 138]
>>> hex(254)
'0xfe'
>>> hex(26)
'0x1a'
>>> ans = [enc3[i]^x[i] for i in range(6)]
>>> ans
[199, 213, 162, 49, 94, 159]
>>> ans = bytes(ans)
>>> ans
b'\xc7\xd5\xa21^\x9f'
>>> res3 = int.from_bytes(ans, 'little')
>>> res3
175226908497351

过了5个check后来看到最后的check_flag,就是将前5个check输入的数据作为key和flag进行一些异或运算

image-20220307231112324

总结一下,得到前5个check的输入和最后的密文如下:
image-20220307231152805

解密:

以下涉及到一个python中将一个多元列表转化为一元列表的简单写法:

1
a = [j for i in aa for j in i]
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
>>> v19[0] = 0xB688C7A7BE252CFB
>>> v19[1] = 0x3B31753308242432
>>> v19[2] = 0x23B66D9AC612B9D4
>>> v19[3] = 0xC732FCCF85C78B27
>>> enc = [i.to_bytes(8, 'little') for i in v19]
>>> enc
[b'\xfb,%\xbe\xa7\xc7\x88\xb6', b'2$$\x083u1;', b'\xd4\xb9\x12\xc6\x9am\xb6#', b"'\x8b\xc7\x85\xcf\xfc2\xc7"]
>>> enc = b''.join(enc)
>>> enc
b"\xfb,%\xbe\xa7\xc7\x88\xb62$$\x083u1;\xd4\xb9\x12\xc6\x9am\xb6#'\x8b\xc7\x85\xcf\xfc2\xc7"
>>> enc = list(enc)
>>> enc
[251, 44, 37, 190, 167, 199, 136, 182, 50, 36, 36, 8, 51, 117, 49, 59, 212, 185, 18, 198, 154, 109, 182, 35, 39, 139, 199, 133, 207, 252, 50, 199]
>>> p1 = 28116440335967
>>> p2 = 3092478108203178393
>>> p3 = 'Mc5LyF7YvIxLxeTX'
>>> p4 = 'SIZHCAKC3LxftSDT'
>>> p5 = 175226908497351
>>> p3 = list(p3.encode())
>>> p4 = list(p4.encode())
>>> p3 = [16 * (i & 0xF) + (i >> 4) for i in p3]
>>> p4 = [16 * (i & 0xF) + (i >> 4) for i in p4]
>>> p1 = p1.to_bytes(8, "little")
>>> p2 = p2.to_bytes(8, "little")
>>> p5 = p5.to_bytes(8, "little")
>>> xx = p1+p2+p5
>>> xx
b'_Fp^\x92\x19\x00\x00\x99Y\x1fkt\xb0\xea*\xc7\xd5\xa21^\x9f\x00\x00'
>>> enc
[251, 44, 37, 190, 167, 199, 136, 182, 50, 36, 36, 8, 51, 117, 49, 59, 212, 185, 18, 198, 154, 109, 182, 35, 39, 139, 199, 133, 207, 252, 50, 199]
>>> for i in range(32-8):
... enc[i] ^= xx[i]
...
>>> enc
[164, 106, 85, 224, 53, 222, 136, 182, 171, 125, 59, 99, 71, 197, 219, 17, 19, 108, 176, 247, 196, 242, 182, 35, 39, 139, 199, 133, 207, 252, 50, 199]
>>> for i in range(32-8, 32):
... enc[i] = ~enc[i]
...
>>> enc
[164, 106, 85, 224, 53, 222, 136, 182, 171, 125, 59, 99, 71, 197, 219, 17, 19, 108, 176, 247, 196, 242, 182, 35, -40, -140, -200, -134, -208, -253, -51, -200]
>>> enc = [i&0xff for i in enc]
>>> enc
[164, 106, 85, 224, 53, 222, 136, 182, 171, 125, 59, 99, 71, 197, 219, 17, 19, 108, 176, 247, 196, 242, 182, 35, 216, 116, 56, 122, 48, 3, 205, 56]
>>> pp = zip(p3, p4)
>>> pp = [j for i in pp for j in i]
>>> pp
[212, 53, 54, 148, 83, 165, 196, 132, 151, 52, 100, 20, 115, 180, 149, 52, 103, 51, 148, 196, 135, 135, 196, 102, 135, 71, 86, 53, 69, 68, 133, 69]
>>> enc
[164, 106, 85, 224, 53, 222, 136, 182, 171, 125, 59, 99, 71, 197, 219, 17, 19, 108, 176, 247, 196, 242, 182, 35, 216, 116, 56, 122, 48, 3, 205, 56]
>>> enc = [164, 106, 85, 224, 53, 222, 136, 182, 171, 125, 59, 99, 71, 197, 219, 17, 19, 108, 176, 247, 196, 242, 182, 35, 216, 116, 56, 122, 48, 3, 205, 56]
>>> enc
[164, 106, 85, 224, 53, 222, 136, 182, 171, 125, 59, 99, 71, 197, 219, 17, 19, 108, 176, 247, 196, 242, 182, 35, 216, 116, 56, 122, 48, 3, 205, 56]
>>> flag = [enc[i]^pp[i] for i in range(len(enc))]
>>> flag
[112, 95, 99, 116, 102, 123, 76, 50, 60, 73, 95, 119, 52, 113, 78, 37, 116, 95, 36, 51, 67, 117, 114, 69, 95, 51, 110, 79, 117, 71, 72, 125]
>>> bytes(flag)
b'p_ctf{L2<I_w4qN%t_$3CurE_3nOuGH}'

p_ctf{L2<I_w4qN%t_$3CurE_3nOuGH}

Classic

ida中打开所给elf文件,很明显这不是一个常规的elf文件,从字符串我们能知道这是一个python文件打包成的elf文件。

image-20220308110107864

使用archive_viewer.py对该elf文件进行解包

archive_viewer.py就在我们安装pyinstaller包的子目录下,如下面截图中的路径

pyinstaller的安装:pip3 install pyinstaller

找到archive_viewer.py的位置后将其复制到当前要解包elf文件所在的目录:

image-20220308111145374

解包:

image-20220308111508386

使用 x 命令提取出enc与struct文件:

image-20220308111540002

接着就是常规的修复pyc头,然后使用uncompyl6对修复后的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
# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.5 (default, Jan 27 2021, 15:41:15)
# [GCC 9.3.0]
# Embedded file name: enc.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 257 bytes
a = 'ABCDEFGHIJ'
a1 = 'abcd'
enc_a = 'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJAB'
x = [1, 2, 3]
lst = []

def alg(a, k, lst, enc):
l = len(k)
r = [a[i::l] for i in range(l)]
k1 = ''
for i in k:
k1 += str(ord(i))
else:
for i in range(l):
i1 = 0
e = ''
for c in r[i]:
i1 = ord(c) ^ ord(k[i]) ^ i1 >> 2
e += chr(i1)
else:
r[i] = e

else:
k2 = (k1 * 6)[-3::-1]
for i in range(len(k2)):
v2 = k2[i]
if 48 <= ord(v2) <= 51:
lst.append((ord(enc[i]) ^ 14) - 47 ^ 9)
elif 52 <= ord(v2) <= 54:
lst.append((ord(enc[i]) ^ 15) + 28)
else:
if 55 <= ord(v2) <= 57:
lst.append((ord(enc[i]) ^ 13) - 62)
lr = list(zip(*r))
eflg = ''.join((hex(256 + ord(i))[3:] for i in ''.join((''.join(j) for j in lr))))
if lst == x:
if len(lst) != 0:
return 'Good Job'
return 'Try again'


print(alg(a, a1, lst, enc_a))
# okay decompiling enc.pyc

然后我们看到题目所给的另外一个.enc密文文件:

1
2
3
4
5
25 46 -3 73 4 86 5 52 -2 86 6 48 3 88 91 2 25 53 -2 55 -2 -1 0 53 87 0 6 -2 85 52 0 2 88 89 5 73 3 -3 2 1 -6 25 4 83 0 48 0 89 4 48 25 88 89 4 6 55 -1 7 1 1 25 1 85 53 6 1 87 7 0 3 86 136 6 3 2 42 4 42 -1 50 7 86 2 25 -4 138 3 48 25 136 90 6 25 4 1 3 0 -9 25 6 89 55 2 -6 87 4 0 50 84 137 4 5



encrypted key - 0791151174073560118079115117407356011807911511740735601180791151174073560118079115117407356011807911511740735601

看到所给的.enc密文文件会感觉莫名奇妙,也不清楚他们到底对应原py代码中的那个变量,这也是本题很谜语的地方,还有就是根本不知道原py代码中那个变量是我们要逆的flag。

经过梳理分析,得到正确的考点和逻辑如下:

1.密文文件中所给的数组为py代码中的 x 数组

2.密文文件中的encrypted key为py代码中的 k2

3.py代码中的eflg变量与enc变量是相同的含义(再py代码中根本没体现出来,故意的吗,,,

image-20220308112509250

4.知道以上三点后,可以知道本题目的加密算法是:a变量是flag,经过a1密钥加密最后得到eflg,而eflg也是对应enc,而x变量是enc加密的值。

因此本题就是要我们通过加密flag的加密值和加密的密钥值来先求出加密的flag和密钥,最后通过密钥解密出加密的flag。

解密:

1.利用加密的密钥解密出加密的flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def d1():
for i in range(len(k2)):
v2 = k2[i]
if 48 <= ord(v2) <= 51:
x[i] = ((x[i]^9)+47)^14
#lst.append((ord(enc[i]) ^ 14) - 47 ^ 9)
elif 52 <= ord(v2) <= 54:
x[i] = (x[i]-28)^15
#lst.append((ord(enc[i]) ^ 15) + 28)
else:
if 55 <= ord(v2) <= 57:
x[i] = (x[i]+62)^13
#lst.append((ord(enc[i]) ^ 13) - 62)


x = [25, 46, -3, 73, 4, 86, 5, 52, -2, 86, 6, 48, 3, 88, 91, 2, 25, 53, -2, 55, -2, -1, 0, 53, 87, 0, 6, -2, 85, 52, 0, 2, 88, 89, 5, 73, 3, -3, 2, 1, -6, 25, 4, 83, 0, 48, 0, 89, 4, 48, 25, 88, 89, 4, 6, 55, -1, 7, 1, 1, 25, 1, 85, 53, 6, 1, 87, 7, 0, 3, 86, 136, 6, 3, 2, 42, 4, 42, -1, 50, 7, 86, 2, 25, -4, 138, 3, 48, 25, 136, 90, 6, 25, 4, 1, 3, 0, -9, 25, 6, 89, 55, 2, -6, 87, 4, 0, 50, 84, 137, 4, 5]
k2 = '0791151174073560118079115117407356011807911511740735601180791151174073560118079115117407356011807911511740735601'

d1()
print(bytes(x))

2.根据flag的格式 p_ctf{ 与加密的密钥还原出正确的密钥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> a = 'p_ctf{'
>>> a = list(b'p_ctf{')
>>> a
[112, 95, 99, 116, 102, 123]
>>> key = [a[i]^c_flag[i] for i in range(len(a))]
>>> key
[106, 53, 70, 47, 115, 119]
>>> bytes(key)
b'j5F/sw'
>>> k2 = '0791151174073560118079115117407356011807911511740735601180791151174073560118079115117407356011807911511740735601'
>>> k2 = k2[::-1]
>>> k2
'1065370471151197081106537047115119708110653704711511970811065370471151197081106537047115119708110653704711511970'
>>> key
[106, 53, 70, 47, 115, 119]
>>> key += [70, 81]
>>> bytes(key)
b'j5F/swFQ'

image-20220308113337100

3.使用密钥解密还原出flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
key = list(b'j5F/swFQ')
c_flag = "1a6a255b150c73041e1c106e46016b34325a764251286f322c13220c0322196e0243375c074e2e0d35417a7c1c10122738102c45423d7b25"
c_flag = list(bytes.fromhex(c_flag))
tmp = [c_flag[i::len(key)] for i in range(len(key))]
for i in range(len(key)):
n = len(c_flag)//len(key)
for j in range(n):
if j != n-1:
tmp[i][n-1-j] ^= (tmp[i][n-2-j] >> 2)^key[i]
else:
tmp[i][n-1-j] ^= key[i]

tmp = list(zip(*tmp))
tmp = [j for i in tmp for j in i]
print(bytes(tmp).decode())
#p_ctf{5Ur3_W0u1d_h4v3_3nJ0y3d_D3crypt1nG_d1Dnt_u_5tu6N9}

image-20220308113635828

-------------本文结束感谢您的阅读-------------