本文最后更新于13 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
1.2048
代码审计,每轮会将获得分数为输入的sorce,上限为当前得分,初次上限为1w,那么每次成功分数会翻倍,玩个几次即可到达100w分
RCTF{you_are_2048_master}
2.bloker_vm
一开始我看到爆红以为有花,后面up以后找到了sub_411A10
3个case:rc4,xor,移位
因为反调试所以就懒得去找顺序,3个case不算多直接测就行
最后顺序是rc4移位xor
注意rc4的密钥长度是18,但是给的密钥是19位
def KSA(key):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
i, j = 0, 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K
def RC4Decrypt(key, text):
S = KSA(key)
keystream = PRGA(S)
res = []
for char in text:
res.append(char ^ next(keystream))
return bytes(res)
#示例
key = b'thisisyoursecretke'
plaintext = [0x80,0x5,0xE3,0x2F,0x18,0x2F,0xC5,0x8C,0x25,0x70,0xBC,0x5,0x1C,0x4F,0xF2,0x2,0xE5,0x3E,0x2,0x2F,0xE5,0x11,0xA3,0xC0]
Rc4encrypt = RC4Decrypt(key, plaintext)
# print(Rc4encrypt)
xor_data = bytearray(Rc4encrypt)
for i in range(len(xor_data)):
xor_data[i] = ((xor_data[i] >> 6) | (xor_data[i] << 2)) & 0xFF
print(chr(xor_data[i] ^ 0x7D),end='')
RCTF{a_baby_debug_bloker}
3.PPTT
首先观察这两个函数 他们先对我们的输入进行处理 我们通过动调查看返回值
先序遍历: 0137fg8hi49jkalm25bnc6de
中序遍历: f7g3h8i1j9k4lam0nb5c2d6e
后序遍历: fg7hi83jk9lma41nbc5de620
可以看到这里是对输入进行先序和中序遍历
下面这一段是对数组进行交换 这里动调会死 应该是加了反调试 我们使用python模拟一下(这里的v29数组对应的是中序遍历的返回值)
from collections import deque
# 定义二叉树节点
class TreeNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
# 层序方式建立二叉树
def build_tree_level_order(data):
if not data:
return None
root = TreeNode(data[0])
queue = deque([root])
i = 1
while queue and i < len(data):
node = queue.popleft()
if i < len(data):
node.left = TreeNode(data[i])
queue.append(node.left)
i += 1
if i < len(data):
node.right = TreeNode(data[i])
queue.append(node.right)
i += 1
return root
# 先序遍历
def preorder(node, result):
if node:
result.append(node.val)
preorder(node.left, result)
preorder(node.right, result)
# 中序遍历
def inorder(node, result):
if node:
inorder(node.left, result)
result.append(node.val)
inorder(node.right, result)
# 后序遍历
def postorder(node, result):
if node:
postorder(node.left, result)
postorder(node.right, result)
result.append(node.val)
# 主程序
if __name__ == "__main__":
chars = '0123456789abcdefghijklmn'
tree = build_tree_level_order(chars)
pre_result, in_result, post_result = [], [], []
preorder(tree, pre_result)
inorder(tree, in_result)
postorder(tree, post_result)
print("先序遍历:", ''.join(pre_result))
print("中序遍历:", ''.join(in_result))
print("后序遍历:", ''.join(post_result))
def swap_data(data):
v29 = list(data)
swaps = [
(15, 16),
(7, 17),
(18, 8),
(3, 19),
(20, 9),
(21, 22),
(10, 4),
(1, 23),
(11, 12),
(5, 13),
(14, 6),
(2, 0),
]
for a, b in swaps:
v29[a], v29[b] = v29[b], v29[a]
return ''.join(v29)
data = "f7g3h8i1j9k4lam0nb5c2d6e"
result = swap_data(data)
print("After swap:", result)
# 先序遍历: 0137fg8hi49jkalm25bnc6de
# 中序遍历: f7g3h8i1j9k4lam0nb5c2d6e
# 后序遍历: fg7hi83jk9lma41nbc5de620
# After swap: gefckamb52hl48in01j396d7
根据约束z3求解看看结果,是一个多解题,我的思路是查看返回值缩小答案范围
通过返回值可知v24里面有字符‘T’,‘{’,v25里面有‘C’,‘F’,‘}’这几个flag的标志字符
我们给flag加上束缚再次跑一下
from Crypto.Util.number import *
from z3 import *
v17 = BitVec('v17', 64)
v18 = BitVec('v18', 64)
v19 = BitVec('v19', 64)
v20 = BitVec('v20', 64)
v23 = BitVec('v23', 64)
v24 = BitVec('v24', 64)
v25 = BitVec('v25', 64)
s = Solver()
s.add(v20 == v24 & v23)
s.add(v19 == (v24 & v23 | v25 & v23) + 65670)
s.add(v18 == (v25 & v23 ^ v25 & v24) - 1131796)
s.add(v17 == v24 & v23 ^ v25 & v23)
# s.add((v24 & v23 & (~(v24 | v23) | v25 & v23 | v25 & v24 & ~v18) | v25 & v24 & v18) != 0x67437616)
s.add((v23 ^ (v20 & ~v18 | v20 & ~v19 | v17 & v19 | v25 & v23 & ~v18)) == 0x400010000622000)
s.add((v18 ^ (v19 - v20)) == 0x2100A0203EFBB8B)
s.add((v17 ^ v19 ^ v20) == 0x4083102108E)
s.add((v19 ^ v17) - v18 == 0x1551566F3C6485ED)
s.add((v18 ^ v19 ^ v25 & v24) == 0x40836ECAB9A)
s.add((v17 ^ v20) - v18 == 0x3E51566F3C718563)
s.add(v23 - v24 == 0x1AEFF6FDFC121BF1)
s.add((v25 + v24 + v23) % 10 == 8)
while s.check() == sat:
model = s.model()
tmp = long_to_bytes(model[v25].as_long())
tmp1 = long_to_bytes(model[v24].as_long())
s.add(v25 != model[v25])
if tmp.find(b'C') == -1 or tmp.find(b'F') == -1 or tmp.find(b'}') == -1:
continue
if tmp1.find(b'T') == -1 or tmp1.find(b'{') == -1 == -1:
continue
mid1 = long_to_bytes(model[v23].as_long())
mid2 = long_to_bytes(model[v24].as_long())
mid3 = long_to_bytes(model[v25].as_long())
# print(mid1[::-1])
# print(mid2[::-1])
# print(mid3[::-1])
str1 = '0123456789abcdefghijklmn'
str2 = 'gefckamb52hl48i0n1j396d7'
mid = mid1[::-1] + mid2[::-1] + mid3[::-1]
flag = ''
for i in range(24):
flag += chr(mid[str2.find(str1[i])])
if all(32 <= ord(c) <= 126 for c in flag):
print(flag)
注意本题还有一个地方卡了我很久,也是后面看了大佬wp才知道原因
看这里的str1,看似可以通过它减小flag的范围,但是得出的结果是错误的,我使用正确的flag模拟str2的生成值以后比较发现这里的值是被修改过的
先序遍历: RCFksdnaq{wtyeuaTsm}qjsp
这里倒数第2位正确的值转换过后应该是‘s’,但是str1里的是‘r’
这个藏起来的全局初始化函数中,会会对第13个执行一个++的操作
导致这里str1的值变了