点击蓝字 关注我们
日期: 2022-10-31 作者: Mr-hello 介绍: 写一下最近比赛遇到的逆向题目WP。
0x00 前言
上半年线上 CTF
比赛少的可怜,但是最近的比赛数量真的是密集到爆炸,之前和战队的成员聊天,还说上半年的比赛逆向题目基本一个都没出,是不是打 CTF
比赛的黄金时期真的只有六年。谁知道说完之后接下来的几场比赛就对逆向选手眷顾了起来。刚好要写文章,就整理一下最近比赛的逆向 WP
吧。
0x01 第五空间 Re2
本道题目是2022
年第五空间一道逆向题目,难度并不大,是一个典型的迷宫类型问题。拿到题目之后使用工具进行反编译发现是 MIPS
架构,于是选用 Ghidra
进行反编译,可获得清晰代码逻辑。
可以发现通过 AWSD
控制上下左右进行移动,直接去找地图即可。
在后续分析过程中发现,该地图是以 15
个为一行。从 3
开始,顺着 1
走迷宫,但是这个题目设计的是类似贪吃蛇,拿着 3
去走,每走一步,如果是 1
,就会把 3
赋值给当前位置,之前位置的 3
被重置为 1
。
最终获取到的地图如下。
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 3, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 3, 1, 1, 1, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0,
由于本题设计了三个难度,把三个难度的路径拼接起来计算 MD5
即为最终 flag
。
dddddssdsdddsssaassssdddsdddsssdddsssdssddssddwddssssssdddssssdddss
flag{f77feb47f7ff4f9e6e94f297b18652e0}
本题目难度不大,但是设计为贪吃蛇走迷宫的形式,即拿着 3
进行赋值改值,在之前做过的迷宫类型题目中未出现过,这个设计点比较清奇。然后就是 MIPS
架构,需要使用 Ghidra
进行反编译而已。
0x02 强网杯 GameMaster
本道题目,是一个小游戏,经典21
点,刚开始的时候,一直以为需要赢到一定分数之后,就会触发某种机制,所以一直在更改分数,但是发现好像触发不了。研究了一下代码发现这个逻辑是错误的。
因为是 .NET
语言写的,所以直接使用 dnSpy
进行反编译即可。经过一通分析之后,发现在捕获到 Escape
信号之后会触发 verifyCode
函数,跟进去之后发现,它是一种类似作弊代码的逻辑。
里面会根据我们输入的代码来控制用户分数的变化,关键点如下。
使用工具进行动态调试,依次输入对应的 作弊代码
,可以实现,将 gamemessage
文件先异或34
,再对其进行 AES or DES
解密,最后在对其进行反序列化操作。最后我们将内存中的数据取出后,可得到如下文件内容。
得到最终的 flag
验证函数。研究了一下发现我们需要从游戏程序中得到三个正确的数字,进入该处代码的 Check1
函数,当验证通过之后,就会利用上述三个数字生成最终的 flag
。重点应该放在如何得到这三个正确的数字,看了一下代码逻辑,涉及位运算和异或等复杂运算逻辑,直接使用 z3
解出这三个正确数字。
from z3 import *
KeyStream = [0] * 40
data = [101,5,80,213,163,26,59,38,19,6,173,189,198,166,140,183,42,247,223,24,106,20,145,37,24,7,22,191,110,179,227,5,62,9,13,17,65,22,37,5]
s = Solver()
x = BitVec('x',64)
y = BitVec('y',64)
z = BitVec('z',64)
num = -1
for i in range(320):
x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1) | x << 1)
y = (((y >> 30 ^ y >> 27) & 1) | y << 1)
z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1) | z << 1)
if i % 8 == 0:
num = num + 1
KeyStream[num] = ((KeyStream[num] << 1) | ((((z >> 32 & 1 & (x >> 30 & 1)) ^ (((z >> 32 & 1) ^ 1) & (y >> 31 & 1))))))
s.add(data[0] == KeyStream[0])
s.add(data[1] == KeyStream[1])
s.add(data[2] == KeyStream[2])
s.add(data[3] == KeyStream[3])
s.add(data[4] == KeyStream[4])
s.add(data[5] == KeyStream[5])
s.add(data[6] == KeyStream[6])
s.add(data[7] == KeyStream[7])
s.add(data[8] == KeyStream[8])
s.add(data[9] == KeyStream[9])
s.add(data[10] == KeyStream[10])
s.add(data[11] == KeyStream[11])
s.add(data[12] == KeyStream[12])
s.add(data[13] == KeyStream[13])
s.add(data[14] == KeyStream[14])
s.add(data[15] == KeyStream[15])
s.add(data[16] == KeyStream[16])
s.add(data[17] == KeyStream[17])
s.add(data[18] == KeyStream[18])
s.add(data[19] == KeyStream[19])
s.add(data[20] == KeyStream[20])
s.add(data[21] == KeyStream[21])
s.add(data[22] == KeyStream[22])
s.add(data[23] == KeyStream[23])
s.add(data[24] == KeyStream[24])
s.add(data[25] == KeyStream[25])
s.add(data[26] == KeyStream[26])
s.add(data[27] == KeyStream[27])
s.add(data[28] == KeyStream[28])
s.add(data[29] == KeyStream[29])
s.add(data[30] == KeyStream[30])
s.add(data[31] == KeyStream[31])
s.add(data[32] == KeyStream[32])
s.add(data[33] == KeyStream[33])
s.add(data[34] == KeyStream[34])
s.add(data[35] == KeyStream[35])
s.add(data[36] == KeyStream[36])
s.add(data[37] == KeyStream[37])
s.add(data[38] == KeyStream[38])
s.add(data[39] == KeyStream[39])
if s.check() == sat:
print s.model()
最后使用解出的三个数字,进行 flag
生成即可。
key = [156324965,868387187,3131229747]
key2 = [0] * 12
for i in range(3):
for j in range(4):
key2[i * 4 + j] = (key[i] j * 8 & 255)
key3 = [60,100,36,86,51,251,167,108,116,245,207,223,40,103,34,62,22,251,227]
flag = ''
for k in range(len(key3)):
key3[k] = key3[k] ^ key2[k % len(key2)]
for l in key3:
flag = flag + chr(l)
print flag
#key2 = [101, 84, 81, 9, 115, 137, 194, 51, 51, 198, 162, 186]
#flag{Y0u_@re_G3meM3s7er!}
PS:
输入的作弊代码如下:
AQLMP6579:MF3K
Z5M0G6P16:U3N1
Z5M0G6P16:EEPW
D253Y5J0Y:6VD6
0x03 总结
上述两个题目论难度,个人感觉强网杯的题目难度相对大一点,它涉及到 .NET
语言的反序列化操作,对作者而言是一个比较新的知识点,当时那道题目研究了好几个小时,最终才理清楚了逻辑。但是当时做出来之后觉得还是挺值得的,毕竟学到了新知识。
相比较第五空间的迷宫类型题目,题目新颖在设置了走迷宫的 实体
,在之前比赛中没遇到过这种情况,最常见的是用户输入之后直接判断是否是墙即可,但是这个题目,是判断不是墙之后,会对该位置上的数据重新赋值。
免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。
宸极实验室
宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。
团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。
对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。
原文始发于微信公众号(宸极实验室):『CTF』两道逆向题目
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论