复现一下2022DefCampCTF

Reverse

cup_of_tea (xxtea+小端)

python实现tea/xtea/xxtea算法传送门:https://qianfei11.github.io/2019/08/22/Python%E5%AE%9E%E7%8E%B0TEA%E3%80%81XTEA%E3%80%81XXTEA%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86%E7%AE%97%E6%B3%95/

v7字符串经过加密之后是运行后的C7……字符串

1
2
3
4
We are under attack.
We need a group of hacker to decrypt this weird message.
And Betaflash is not slow :)).
Decrypt me if you can: C7E4C81E20EBFB67A4977BA91C9C312FFF81669CA85798D475F0D9081DF7017CC8D009ADF02F67DD41D1F781EF56F8EA2225502AF957D6844084CEE3BB7D2350DBF05DCD8B0AD33CD52C5E0171E4

我们需要做的是通过上面的加密去寻找解密方法,然后把下面的字符串破译出来

ida查看一下:

encrypt函数看了一眼是tea类的加密,sprintf,发送格式化输出到v13字符串,%02x,两位十六进制数,左侧补零。

放C里面调试了一下……

这边就是主要的算法了。

在网上找了一段有关xxtea加密解密的python2版本的代码,然后转成了python3的格式

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
def shift(z, y, x, k, p, e):
return ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((x ^ y) + (k[(p & 3) ^ e] ^ z)))

def decrypt(v, k):
delta = 0x9E3779B9
n = len(v)
rounds = 6 + 52 / n
x = (int(rounds) * delta) & 0xFFFFFFFFFFFFFFFF
y = v[0]
for i in range(int(rounds)):
e = (x >> 2) & 3
for p in range(n - 1, 0, -1):
z = v[p - 1]
v[p] = (v[p] - shift(z, y, x, k, p, e)) & 0xFFFFFFFFFFFFFFFF
y = v[p]
p -= 1
z = v[n - 1]
v[0] = (v[0] - shift(z, y, x, k, p, e)) & 0xFFFFFFFFFFFFFFFF
y = v[0]
x = (x - delta) & 0xFFFFFFFFFFFFFFFF
return v

def encrypt(v, k):
delta = 0x9E3779B9
n = len(v)
rounds = 6 + 52 / n
x = 0
z = v[n - 1]
for i in range(int(rounds)):
x = (x + delta) & 0xFFFFFFFFFFFFFFFF
e = (x >> 2) & 3
for p in range(n - 1):
y = v[p + 1]
v[p] = (v[p] + shift(z, y, x, k, p, e)) & 0xFFFFFFFFFFFFFFFF
z = v[p]
p += 1
y = v[0]
v[n - 1] = (v[n - 1] + shift(z, y, x, k, p, e)) & 0xFFFFFFFFFFFFFFFF
z = v[n - 1]
return v

if __name__ == '__main__':

plain = [1, 2]
key = [2, 2, 3, 4]
encrypted = encrypt(plain, key)
print (encrypted)
decrypted = decrypt(encrypted, key)
print (decrypted)

关键部分做了替换。其中,最为重要的是,题目给出的字符串因为小端模式需要转换

1
2
encrypted  = [0x68E399CC4DAEA4D0,0x81b8676d9966bdba,0x78adb022d0e2ab59,0xf31ce8b16e9ad6f1,0xd6054099FE3E9B58,0xB7443CFDB29DDEA9,0x2922AC6A31D22876,0x2022E42B93EC38E9,0x0A825546911D6DDD,0x8D3A9936229EC043]
key = [73,32,108,111,118,101,32,112,97,110,99,97,107,101,115]

注意这边的6+52/n换成了6+52/10

1
[6859953592044237650, 5070864542475440945, 6436263400528900656, 3370575, 0, 0, 0, 0, 0, 0]

然后将所得的列表,转换成16进制,按每两位一组,拼接成flag

1
string = '5f33737245763352465f546f4e5f533159523376455f7230336e4f'
1
2
3
4
5
6
x = [0x5f,0x33,0x73,0x72,0x45,0x76,0x33,0x52,0x46,0x5f,0x54,0x6f,\
0x4e,0x5f,0x53,0x31,0x59,0x52,0x33,0x6e,0x4f]
flag = ""
for i in range(len(x)):
flag+=chr(x[i])
print(flag)

_3srEv3RF_ToN_S1YR3nO

CTF{3e6a586b00305bc4e74243c66d91525a}

algorithm (dfs plus+暴破 plus)

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
flag = ' [test]'
hflag = flag.encode('hex')
iflag = int(hflag[2:], 16)

def polinom(n, m):
i = 0
z = []
s = 0
while n > 0:
if n % 2 != 0:
z.append(2 - (n % 4))
else:
z.append(0)
n = (n - z[i])/2
i = i + 1
z = z[::-1]
l = len(z)
for i in range(0, l):
s += z[i] * m ** (l - 1 - i)
return s

i = 0
r = ''
while i < len(str(iflag)):
d = str(iflag)[i:i+2]
nf = polinom(int(d), 3)
r += str(nf)
i += 2

print r

放linux运行了一下,仔细分析之后,得出正向加密的步骤:

  1. 将flag字符串中每一个字符都转换为16进制数拼凑起来的数,赋值给hflag,

  2. 将hflag开头两位去除后转换的10进制数,赋值为iflag。

  3. 依次iflag每两位拆分为n,将n和3分别作为一参和二参放入polinom中进行加密:将n通过while循环根据奇偶转换为z表,对z表中的每个元素*m^i后累加为s,返回值为s。

  4. 加密结束后,把最后的结果赋值给nf,转换成str,拼在一起就是加密后的字符。

本人用 “ [hello]”进行了模拟运算

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
def polinom(n, m):
i = 0
z = []
s = 0
while n > 0:
if n % 2 != 0:
z.append(2 - (n % 4))
else:
z.append(0)
n = (n - z[i])/2
i = i + 1
print "z:",
print (z)
l = len(z)
for i in range(0, l):
s += z[i] * m ** i
return s

flag = ' [hello]'
hflag = flag.encode('hex')
print(hflag)
iflag = int(hflag[2:],16)
print(iflag)
i = 0
r = ''
while i < len(str(iflag)):
d = (str(iflag)[i:i+2])
print(d)
nf = polinom(int(d),3)
print "nf:",
print(nf)
r += str(nf)
i += 2
print(r)

若m为99(最大的两位数),while循环后,累加出来的s为1952,所以两位数最多可以被加密成4位数(见以上步骤3)

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

def polinom(n, m):
i = 0
z = []
s = 0
while n > 0:
if n % 2 != 0:
z.append(2 - (n % 4))
else:
z.append(0)
n = (n - z[i]) // 2
i = i + 1
z = z[::-1]
l = len(z)
for i in range(0, l):
s += z[i] * m**(l - 1 - i)
return s

def print_b(b):
s = str(b[0])
for i in b[1:-1]:
if i < 10:
s = s + "0" + str(i)
print(s)
else:
s = s + str(i)
res = s + str(b[-1])
print(res)

b = []
a = '242712673639869973827786401934639193473972235217215301'
def dfs(n):
global b
if (n > len(a)):
return
if (n == len(a)):
m = print_b(b)
return
for i in range(1, 5):
s = a[n:n + i]
for j in range(100):
if (polinom(j, 3) == int(s)):
b.append(j)
dfs(n + i)
b.pop()

dfs(0)

通过dfs和爆破,求出了列表b,其应当为加密函数polinom(n, m) 返回值s组成的列表:

1
[31, 11, 38, 2, 44, 3, 54, 68, 8, 14, 45, 91, 44, 91, 69, 12, 29, 25, 23, 10, 1]

然后通过print_b的填零操作,将列表b转换成iflag

1
31113802440354680814459144916912292523101

注意,b列表的最后一位不需要在前面加上“0”,

最后通过long_to_bytes将iflag转换为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
from Crypto.Util.number import *

def polinom(n, m):
i = 0
z = []
s = 0
while n > 0:
if n % 2 != 0:
z.append(2 - (n % 4))
else:
z.append(0)
n = (n - z[i]) // 2
i = i + 1
z = z[::-1]
l = len(z)
for i in range(0, l):
s += z[i] * m**(l - 1 - i)
return s

def print_b(b):
s = str(b[0])
for i in b[1:-1]:
if i < 10:
s = s + "0" + str(i)
print(s)
else:
s = s + str(i)
res = s + str(b[-1])
print(res) #将还原出的b列表转换为一个十进制数

b = []
a = '242712673639869973827786401934639193473972235217215301'
#通过dfs和循环将拼接的 nf 还原出来
def dfs(n):
global b
if (n > len(a)):
return
if (n == len(a)):
m = print_b(b)
return
for i in range(1, 5):
s = a[n:n + i]
for j in range(100):
if (polinom(j, 3) == int(s)):
b.append(j)
dfs(n + i)
b.pop()

dfs(0)
nf=[31, 11, 38, 2, 44, 3, 54, 68, 8, 14, 45, 91, 44, 91, 69, 12, 29, 25, 23, 10, 1]
iflag = 31113802440354680814459144916912292523101
flag = long_to_bytes(iflag)
print(flag)

关于python Crypto.util 模块的byte_to_long和long_to_bytes的使用:

[ola_th1s_1s_p0l]