avatar

Catalog
KCTF2022春季赛第二题

前言

2022春季赛的第二题,成功在规定时间内完成,写完wp后首发在看雪论坛,还混到了一个优秀,现在补档到博客里。

分析流程

Part 0x0

  1. sub_40100C用来显示文本内容
  2. sub_40103A用来读取输入字符串
  3. IDA分析出的 v9 >= 0,这里的v9是单字节有符号数,因此实际运算时表现为 v9 <= 0x80(用C写wp就没这么多事了)
  4. 部分操作是单字节运算,因此运算过后,与上0xFF
  5. 最终得到一个单字节的数,保存在v72中,后面会用到

Part 0x1

  1. 这部分是一个单走的函数sub_40106C,它用来在指定地址初始化一个大小为4096字节的缓冲区,里面包含了1024个生成的随机数
  2. 这里需要注意的是右移运算,实际用的是算术右移,因此需要注意判断操作数的正负情况,来进行对应的补符号位

Part 0x2

  1. 这里是对输入的字符串进行一个运算,还是算术右移需要注意,由于是右移8位,对于负数右移需要或上0xFF000000
  2. 这里会计算出一个值,并最终赋给变量v67,这个v67在最后一关前会进行判断,如果v67的值为0xf52e0765,则能成功闯关,得到唯一解

Part 0x3

  1. 这是一个操作字符串的函数,直接F5即可。减法运算需要注意只保留单字节结果

Part 0x4

  1. 这部分在栈中生成一个长度为200的数组,变量v66指向首地址
  2. 在Part 0x0中计算出来的值会赋值给变量v72,这里会将v72进行符号扩展后,赋值给变量v13(见红框)
  3. 在运算时,乘法运算依旧是带符号的,所以需要分情况讨论一下(见橙框)

Part 0x5

  1. 这里是一个判断,要求字符串前3个数异或后的结果,并与v66数组中靠后的3个数进行比较,若相同,才能进入下一关
  2. 经过测试,如果v72的值(在Part 0x0计算出)的小于0x80,即为正的情况下,那么v66中3个数的值异或的结果一定为0x7;否则将是一个接近-1(0xFFFFFFFF)的大数。
  3. 字符串输入的字符只能在(0-9a-zA-Z)中取,因此只要保证在v72小于0x80的情况下,凑出一个异或结果为0x7的数即可,这里我们选择”007”,刚好可以过掉这一关

Part 0x6/0x7

  1. 这部分判断字符串的第4~7位,这里的字符串是在Part 0x3中经过处理后的字符串的值。这里只要简单凑一下,就可以得到这4个字符为”KCTF”
  2. 这里是v74[0]实际指向的,Arglist[6]的位置,可以通过汇编得出

Part 0x8

  1. 这部分运算直接F5就可以,这里只对最后9位字符串进行判断,要求这4位字符串依次读取,按照此算法,均可被整除
  2. 此时还无法得到正确的字符串序列(图中为了演示此算法已给出),需要放到后面去计算

Part 0x9

  1. 这是一个简单的排序算法,负责将后9位数按照大小进行排序

Part 0xA

  1. 这里会调用sub_4010B7,来操纵一个内置的字符串,然后将排序后的字符串后9位与进行操作后的字符串中的前9位依次进行比较,若相同才可进入之后的判断
  2. 可以发现,这里表明了,后9位字符串的取值正是”123456789”以某一种顺序排列后的结果

Part 0xB

  1. 这里做一个简单的fuzz,找到后9位数的排列顺序,以满足(除了v67的值等于0xf52e0765)外的所有条件
  2. 最终可以找到一个字符串”381654729”

Part 0xC

  1. 此时只差前3位字符串的值未确定了,还是一样,通过一个fuzz,遍历(000~ZZZ)的所有情况,找到符合要求的前3位(3个字符异或结果为7,这个条件也可以加上)
  2. 最终得到字符串”421”
  3. 拼接起来就是”421KCTF381654729”

完整代码

python
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
import os
import itertools

def func(s):

# Part 0x0
v70 = 0
v4 = len(s)
v5 = 0
v71 = v4
v6 = v4
v72 = 0
if v4 != 0:
v7 = s
for i in range(v6):
v5 = ord(v7[i]) ^ v5
v5 = v5 & 0xFF
v8 = 8
for j in range(8):
v9 = 2 * v5
v9 = v9 & 0xFF
v10 = v9 ^ 7
if v9 < 0x80:
v10 = v9
v5 = v10
v4 = v71
v72 = v10
print('Part 0x0 Calculate v72 For Part 0x4: ', hex(v72))


# Part 0x1
list_5B20 = sub_40106C()

for x in list_5B20:
#print(hex(x))
pass


# Part 0x2
v11 = 0xFFFFFFFF
for i in range(v4):
tmp = list_5B20[0xFF & (v11 ^ ord(s[i]))]
if v11 >= 0x80000000:
v11 = ((v11 >> 8) | 0xFF000000) ^ tmp
else:
v11 = (v11 >> 8) ^ tmp
v67 = v11 ^ 0xFFFFFFFF
print('Part 0x2, This Value Should Be 0xF52E0765: ', hex(v67))
if v67 == 0xf52e0765:
return 1


# Part 0x3
s = sub_4010B7(s, v4)
print('Part 0x3 Manipulat Original String, Used For Xor: ', s)


# Part 0x4
v13 = v72
v69 = 1
v14 = v72 + 1
v66 = []
while(v13 < v14):
if v13 > 0x80:
v15 = 0xFFFFFF00 | v13
for j in range(200):
if (v15 & 1) != 0:
v15_abs = 0xFFFFFFFF - v15 + 1
v15_abs = 3 * v15_abs - 1
v15 = 0xFFFFFFFF - (v15_abs - 1)

else:
if v15 > 0x80000000:
v15 = (v15 >> 1) | 0x80000000
else:
v15 = v15 >> 1
v66.append(hex(v15))
#print(j, ' ', hex(v15))

else:
v15 = v13 & 0xFF
for j in range(200):
if (v15 & 1) != 0:
v15 = 3 * v15 + 1
else:
if v15 > 0x80000000:
v15 = (v15 >> 1) | 0x80000000
else:
v15 = v15 >> 1
v66.append((v15))
#print(j, ' ', hex(v15))
v13 = v13 + 1


# Part 0x5
if v72 < 0x80:
print('Part 0x5, Calculate With Or: ', (v66[195]), (v66[196]), (v66[197]))
v17 = (v66[195]) | (v66[196]) | (v66[197])
v18 = v71
cmp_a = (s[0])^(s[1])^(s[2])
print('Part 0x5, Or Result: ', hex(v17))
print('Part 0x5, Xor Result: ', hex(cmp_a))
else:
print('Part 0x5, Calculate With Or: ', hex(int(v66[195], 16)), hex(int(v66[196], 16)), hex(int(v66[197], 16)))
v17 = int(v66[195], 16) | int(v66[196], 16) | int(v66[197], 16)
v18 = v71
cmp_a = (s[0])^(s[1])^(s[2])
print('Part 0x5, Or Result: ', hex(v17))
print('Part 0x5, Xor Result: ', hex(cmp_a))


# Part 0x6
v19 = v17 + 2
v20 = v18 - v19 - 7
print('Part 0x6, This Value Should Be Less Than Or Equal 0: ' , v20)


# Part 0x7 ['007KCTF' can pass here]
print('Part 0x7, s[3] should be 0x14/20: ', s[3])
print('Part 0x7, s[4] should be 0xC/12: ', s[4])
print('Part 0x7, s[5] should be 0x1D/29: ', s[5])
print('Part 0x7, s[5] should be 0xF/15: ', s[6])



# Part 0x8
# v69 = 1, define in Part 0x4
# v74[0] == s[6]
# v74[1] ~ v74[
# v19, Rest Length Of String
# v70 = 0, define in Part 0x0

v74 = s[6:]
print('Part 0x8, Rest Length Of String: ', v19)
v21 = 0
v71 = 0
if v19 > 0:
v22 = 1
for i in range(v19):
v23 = v74[v22] + 10 * v70
print('Part 0x8, (' ,v23, '=', v74[v22], '+', '10 *', v70, ')%', v69, '==', v23%v69)
v24 = v23 - 0x37373737
if v23 <= 0x4B435445:
v24 = v23
v70 = v24
if (v24 % v69):
print('Failed! ')
return 0
#break
v69 = v69 + 1
v22 = v69


# Part 0x9 Just A Sort
v25 = v19 - 1
if (v19 - 1) > 0:
v26 = v19 - 1
for i in range(v26):
v27 = 0
if v25 > 0:
for i in range(v25):
v28 = v74[v27 + 1]
v29 = v74[v27 + 2]
if v28 > v29:
v74[v27 + 1] = v29
v74[v27 + 2] = v28
v27 = v27 + 1
v25 = v25 - 1
print('Part 0x9, Last 9 Elements After Sort: ', v74[1:])


# Part 0xA
a_str = '1234567890_ABCDEFGHIJKLMNOPQRSTUVWXYZ'
a_str = sub_4010B7(a_str, v19)
print('Part 0xA, a_str: ', a_str)
v30 = 0
if v19 > 0:
for i in range(v19):
print('Part 0xA, Compare 2: ', a_str[v30], v74[v30+1])
if a_str[v30] != v74[v30+1]:
print('Failed!')
return 0
v30 = v30 + 1

return 0


def sub_4010B7(s, length):

res = []
for i in range(length):
v4 = ord(s[i])
v5 = 48
if v4 >= 58:
v5 = 55
res.append((v4 - v5) & 0xFF)
#res.append(chr((v4 - v5) & 0xFF))
#print(''.join(res))

return res


def sub_40106C():

v1 = []

for i in range(1024):
v2 = i
v3 = 8
for j in range(8):
if (v2 & 1) != 0:
tmp = 0xEDB88320
else:
tmp = 0
if v2 >= 0x80000000:
v2 = ((v2 >> 1) | 0x80000000) ^ tmp
else:
v2 = (v2 >> 1) ^ tmp
#print(hex(v2))
v1.append(v2)
v1.append(1)

return v1


if __name__ == '__main__':

s = 'A7AKCTF381654729'
res = func(s)
'''
# Fuzz One For Last 9 Bytes
prefix = 'A7AKCTF'
maybe_result = []

example = '123456789'
result = itertools.permutations(example)
for x in result:
suffix = ''.join(x)
s = prefix + suffix
#print(s)
res = func(s)
if res == 1:
print(res, s)
break

print(maybe_result)
'''

'''
# Fuzz Two For First 3 Bytes
suffix = 'KCTF381654729'
text = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVMXYZ'
result = itertools.permutations(text, 3)
for x in result:
prefix = ''.join(x)
s = prefix + suffix
#print(s)
res = func(s)
if res == 1:
print(res, s)
break
'''

参考链接:

  1. 简书:用Python进行排列组合
  2. CSDN:Python使用itertools模块实现排列组合
  3. CSDN:32位汇编语言学习笔记(11)–条件传送指令
Author: cataLoc
Link: http://cata1oc.github.io/2022/05/13/KCTF2022%E6%98%A5%E5%AD%A3%E8%B5%9B%E7%AC%AC%E4%BA%8C%E9%A2%98/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶