最好的语言 - Writeup
题目: 最好的语言 (Best Language)
类型: REVERSE (Python Bytecode)
平台: Python 2.7, 32-bit
题目分析
文件识别
题目给出一个文本文件re,内容为 XML 格式的 Python 2.7 字节码:
- Magic number:
03f30d0a→ Python 2.7 bytecode - 包含 3 个 code object:主模块、函数
_、函数____
算法还原
反编译字节码后还原出以下算法:
# 变量定义
f = 'flag{*******}' # flag 模板
key1 = ''.join(random.sample(string.digits, 4)) # 4位随机数字密钥1
key2 = ''.join(random.sample(string.digits, 4)) # 4位随机数字密钥2
def _(b):
"""XOR 加密:每字节与4位密钥循环异或"""
return ''.join(chr(ord(b[i]) ^ ord(key[i % 4])) for i in range(len(b)))
def ____(a):
"""MD5 摘要(raw bytes)"""
return hashlib.md5(a).digest()
# 编码过程
e = _(f[:12]) + ____(f[12:19]) + _(f[19:])
# 结果 Base64 编码输出:
# U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU=编码分三段:
- f[:12]→ XOR 加密 →12 字节
- f[12:19]→ MD5 原始摘要 →16 字节
- f[19:]→ XOR 加密 →10 字节
Total:12 + 16 + 10 = 38 字节→ Base64 → 最终输出。
Flag 总长度:12 + 7 + 10 = 29 字符。
解题思路
1. Base64 解码
U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU=
↓ base64 decode
e[0:12] = 535550534e6948776a08426b (12 bytes)
e[12:28] = 42eec54bb567824d4e4d0e7caaa8215e (16 bytes, MD5)
e[28:38] = 6b7601476b7050415045 (10 bytes)2. 恢复 key1(已知前缀攻击)
已知 flag 前缀为flag{PyC_1s_(12 字符),XOR 特性:
key1 = XOR(e[:12], 'flag{PyC_1s_')
= XOR([83,85,80,83,78,105,72,119,106,8,66,107],
[102,108,97,103,123,80,121,67,95,49,115,95])
= [53, 57, 49, 52, 53, 57, 49, 52, 53, 57, 49, 52]
= '591459145914' → key1 = '5914'3. 恢复 key2(已知后缀攻击)
已知 flag 后缀为}(f[28] = '}'),且 key2 是 4 位不重复数字:
# f[28] = '}' = 125
# e[37] XOR key2[1] = f[28]
# 69 XOR key2[1] = 125 → key2[1] = 56 = '8'枚举所有可能的 key2(504 种,4 位不重复数字,已知第 2 位 = '8'):
唯一产生可读结果的组合:key2 = '4813'→ f[19:] =_N0t_Hard}
4. 破解 MD5
已知 f[12:19](7 字符)的 MD5 =42eec54bb567824d4e4d0e7caaa8215e。
7 字符 + 字母数字混合 → 36^7 ≈ 780 亿,无法暴力破解。
关键操作:在cmd5.com查询该 MD5 哈希,直接获得原值:613u21i
5. 合成 Flag
f[:12] = 'flag{PyC_1s_'
f[12:19] = '613u21i'
f[19:] = '_N0t_Hard}'Flag:flag{PyC_1s_613u21i_N0t_Hard}
解题脚本
import hashlib, base64
# 题目给的 Base64
encoded = 'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
raw = base64.b64decode(encoded)
# 已知前缀
known_prefix = b'flag{PyC_1s_'
# 恢复 key1
key1 = bytes([raw[i] ^ known_prefix[i] for i in range(12)])
print(f'key1 = {key1.decode()}') # 5914
# key2[1] 由 '}' 推出
key2_byte1 = raw[37] ^ ord('}') # 56 = '8'
# 枚举 key2,找到可读的尾部
key2 = b'4813'
tail = bytes([raw[28 + i] ^ key2[i % 4] for i in range(10)])
print(f'tail = {tail.decode()}') # _N0t_Hard}
# MD5 中间段(查 cmd5.com)
middle = '613u21i'
# 拼接 flag
flag = 'flag{PyC_1s_613u21i_N0t_Hard}'
# 验证
def xor_encode(s, key):
return bytes([ord(s[i]) ^ key[i % 4] for i in range(len(s))])
f = flag
verify = xor_encode(f[:12], key1) + \
hashlib.md5(f[12:19].encode()).digest() + \
xor_encode(f[19:], key2)
assert base64.b64encode(verify).decode() == encoded
print(f'Flag verified: {flag}')验证结果
| 检查项 | 结果 |
|---|---|
| MD5('613u21i') = 42eec54... | 匹配 |
| Base64 往返编码验证 | 匹配 |
| Flag 长度 = 29 | 匹配 |
考点总结
- Python 字节码分析:
.pyc文件结构、dis模块使用、code object 重建 - 已知明文 XOR 攻击: 已知前缀/后缀直接恢复 XOR 密钥
- MD5 在线查询: cmd5.com 可查询常见弱密码的 MD5 原值 — CTF 中经常会遇到
- Base64 编码识别: 末尾
=填充是 Base64 特征 - 约束搜索: key2 搜索时利用"不重复数字"+"已知一位"+"可打印字符"将搜索空间从 10000 降到 504