2023-googleCTF-re-oldshool

二木 王者

2023-googleCTF-re-oldshool

本题叫用给出的用户名,逆向,写出注册机,来批量生成密码,50个用户名,50个密码。

刚刚拿到手,还打开不了,一直报错说窗口有问题,后来摸索了好久才知道,需要调整终端大小才能适应。而且这道题也是用Ncurses从零实现了GUI的图形解密,就造成了代码巨多,主函数的代码就接近5000行,还不算其他函数,要准确的找到我们想要的函数是十分的不容易的。这到题如果代码量少一些的话,难度就相对没那么大了。

对于终端大小,在ubuntu的命令行中输入resize命令和就可以查询目前的长宽大小,我们用Ctrl+鼠标滑轮就可以调节大小,大致这么多

Untitled

我们打开oldshool,可以看到如下界面,输入用户名与密码。

Untitled

对于本道题,我们使用动态调试来确定,我们的输入与验证函数。由于代码实在是太多。二分法最快,嘿嘿。

经过调试,我们可以在3856行的伪代码处,找到我们的加密函数。

Untitled

这里我们可以知道每五位就会检测-号,也就是说我们的密码大致格式为22222-aaaaa-33333-bbbbb-44444

Untitled

通过动态调试,追踪我们输入的密码,我们可以得知,这里的操作是移位查表,1→2,2→3,生成了一张表,我们可以归纳出其规律。

Untitled

通过分析下面的的操作,我们可以知道,这些都是通过我们输入name生成一些固定的加密数据

Untitled

这一部分就是将我们的password进行转置生成矩阵,第n列循环右移n字节,并且进行异或加密

Untitled

最后这是运用了两矩阵相乘生成单位矩阵的性质来判断是否相等

其中后面的&0x1f是取其低 5 位,并将其他位设置为零。

Untitled

到此我们算是分析完了,看到的加密。加密的逻辑就是:

对于密码,将我们输入的密码进行转位换表,然后转置生成矩阵,进行异或

对于用户名,将用户名进行一系列复杂的加密,生成一些固定的密文

最后,再通过逆矩阵知识,进行比较验证正确性。

我们逆向写脚本的话,就要通过动态调试,将用户名生成的对比密文提取出来,求其逆矩阵,求得密码异或后的数据,然后我们将其异或回去,再进行查表,找到应该输入的对应用户名的密码。

按理说,我们写出脚本,就能将其解出的,我们使用z3进行爆破求其逆矩阵,但是我们会发现,求出的有些密码在动态调试时输入是正确的,能过验证,但是直接运行程序却不能过验证。甚至有些还不能爆破。但是我们无论如何看,我们的脚本都是没有问题的,这就很怪。

看了官方wp之后才知道,脚本确实没有写错,但是有反调试,检测到我们的调试器之后,就会将我们加密的一下数据进行改变,由于我们的密文是经过动态调试出来的,所以直接中招。

通过几处的查看交叉引用

Untitled

Untitled

Untitled

Untitled

我们可以发现都是这个if的这个函数下进行改变的,我们对这个函数查看交叉引用,我们可以找到所有的反调试,按照官方的这个就是ptrace函数。

Untitled

四处地方,都是改变我们加密过程中的数据,藏得十分隐秘,因为不会有任何的报错与提示,而且代码量巨多,让其难以发现,写脚本没解出来,第一时间都怀疑脚本的正误与分析的正误。

绕过这个反调试也很简单,在动态调试的时候,将ptrace返回的标志,从1改成0即可

一般的来说:

  • 如果 ptrace 执行成功,则返回 0 ,或者其他值
  • 如果 ptrace 执行失败,则返回 -1,或者其他值。

具体值的含义取决于请求的类型。

绕过反调试后,再用我们的脚本进行解密即可。

解密脚本

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
from z3 import *

matx = [[0x12, 0x1D, 0x10, 0x13, 0x1B],
[0x8, 0x1F, 0x8, 0x17, 0x1E],
[0x1D, 0x3, 0x1C, 0xA, 0x15],
[0x12, 0x1D, 0x08, 0x10, 0x1C],
[0x0B, 0x1E, 0x07, 0x14, 0x07],] #动态调试username加密后的密文

#爆破生成逆矩阵

rows = 5
cols = 5

arr = [[z3.BitVec(f'arr_{i}_{j}', 6) for j in range(cols)] for i in range(rows)]
s = Solver()

for k in range(5):
for m in range(5):
v28 = 0
for i in range(5):
v28 = ((arr[i][m]&0xff) * (matx[k][i]&0xff) + (v28&0xff)) & 0x1f
if k == m:
s.add(v28 == 1)
if k != m:
s.add(v28 == 0)

inv = []
if s.check() == sat:
model = s.model()
for i in range(rows):
row = [model.evaluate(arr[i][j]).as_long() for j in range(cols)]
inv.append(row)
else:
print("err")

print("v18的逆矩阵为:")
print(inv)
#转置逆回去

for i in range(len(inv)):
if i == 0:
continue
elif i == 4:
inv[i][0], inv[i][1], inv[i][2], inv[i][3], inv[i][4] = inv[i][1], inv[i][2], inv[i][3], inv[i][4], inv[i][0]
elif i == 3:
inv[i][0], inv[i][1], inv[i][2], inv[i][3], inv[i][4] = inv[i][2], inv[i][3], inv[i][4], inv[i][0], inv[i][1]
elif i == 2:
inv[i][0], inv[i][1], inv[i][2], inv[i][3], inv[i][4] = inv[i][3], inv[i][4], inv[i][0], inv[i][1], inv[i][2]
elif i == 1:
inv[i][0], inv[i][1], inv[i][2], inv[i][3], inv[i][4] = inv[i][4], inv[i][0], inv[i][1], inv[i][2], inv[i][3]

print("调换位置后的逆矩阵:")
print(inv)
#矩阵进行异或
xor_arr = [[0x1B, 0x00, 0x0A, 0x0D, 0x08],
[0x18, 0x0F, 0x1C, 0x06, 0x07],
[0x12, 0x05, 0x0C, 0x02, 0x04],
[0x1D, 0x1F, 0x09, 0x13, 0x01],
[0x1E, 0x11, 0x0B, 0x16, 0x19]]

for k in range(5):
for m in range(5):
inv[k][m] ^= xor_arr[k][m]
print("异或后的矩阵:")
print(inv)

index_arr = [0x00000010, 0x0000000E, 0x0000000D, 0x00000002, 0x0000000B, 0x00000011, 0x00000015, 0x0000001E, 0x00000007, 0x00000018, 0x00000012, 0x0000001C, 0x0000001A, 0x00000001, 0x0000000C, 0x00000006, 0x0000001F, 0x00000019, 0x00000000, 0x00000017, 0x00000014, 0x00000016, 0x00000008, 0x0000001B, 0x00000004, 0x00000003, 0x00000013, 0x00000005, 0x00000009, 0x0000000A, 0x0000001D, 0x0000000F]

for k in range(5):
for m in range(5):
inv[k][m] = index_arr.index(inv[k][m])
print("索引:")
print(inv)
#查表得出flag
chr_arr = [0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A]
for i in range(5):
for j in range(5):
print(chr(chr_arr[inv[i][j]]), end='')
print('-', end='')

参考文章

google-ctf/2023/rev-oldschool/solution at master · google/google-ctf · GitHub

(87条消息) oldschool_fi5hjump的博客-CSDN博客

 评论