手把手教你用Python复现BUUCTF signin题:从密文signin.txt到flag的完整逆向
·
手把手教你用Python复现BUUCTF signin题:从密文signin.txt到flag的完整逆向
在CTF竞赛中,密码学题目往往考验选手对加密算法的理解与逆向能力。今天我们将以BUUCTF平台上的"signin"题目为例,通过Python代码完整还原解题过程。这道题来自2020年羊城杯赛事,采用了典型的 Toy Cipher 设计思路,非常适合初学者理解多层加密的逆向分析方法。
1. 题目分析与准备工作
首先我们需要明确题目提供的两个关键要素:
- 密文文件
signin.txt内容为:BCEHACEIBDEIBDEHBDEHADEIACEGACFIBDFHACEGBCEHBCFIBDEGBDEGADFGBDEHBDEGBDFHBCEGACFIBCFGADEIADEIADFH - 参考论文中提供的替换规则表(字典映射关系)
常见踩坑点 :
- 密文读取时注意文件编码(建议使用UTF-8)
- 替换表需要完整准确,任何一个字符映射错误都会导致最终结果偏差
- 分组处理时注意字符串长度是否为4的倍数
准备基础代码框架:
# 初始化环境
cipher_dict = {
'M':'ACEG', 'R':'ADEG', 'K':'BCEG', 'S':'BDEG',
'A':'ACEH', 'B':'ADEH', 'L':'BCEH', 'U':'BDEH',
'D':'ACEI', 'C':'ADEI', 'N':'BCEI', 'V':'BDEI',
'H':'ACFG', 'F':'ADFG', 'O':'BCFG', 'W':'BDFG',
'T':'ACFH', 'G':'ADFH', 'P':'BCFH', 'X':'BDFH',
'E':'ACFI', 'I':'ADFI', 'Q':'BCFI', 'Y':'BDFI'
}
def read_ciphertext(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return f.read().strip()
2. 第一阶段解密:四字母分组替换
原始密文由多个4字母组成的片段构成,我们需要先将其拆解并对照字典进行替换:
def first_stage_decrypt(ciphertext, cipher_dict):
result = []
# 创建反向字典便于查询
reverse_dict = {v: k for k, v in cipher_dict.items()}
for i in range(0, len(ciphertext), 4):
block = ciphertext[i:i+4]
if block in reverse_dict:
result.append(reverse_dict[block])
else:
raise ValueError(f"未知的密文块: {block}")
return ''.join(result)
关键改进点 :
- 使用字典推导式创建反向映射表,提升查询效率
- 添加异常处理机制,遇到无法识别的密文块会立即报错
- 采用列表拼接字符串,性能优于直接字符串相加
执行第一阶段解密:
ciphertext = read_ciphertext('signin.txt')
stage1_result = first_stage_decrypt(ciphertext, cipher_dict)
print("第一阶段解密结果:", stage1_result) # 输出: LDVUUCMEXMLQSSFUSXKEOCCG
3. 第二阶段解密:列表反转替换
根据题目提示,我们需要将原始字母表反转后进行二次替换:
def second_stage_decrypt(ciphertext):
original_list = ['M','R','K','S','A','B','L','U','D','C','N','V',
'H','F','O','W','T','G','P','X','E','I','Q','Y']
reversed_list = original_list[::-1]
result = []
for char in ciphertext:
if char in original_list:
index = original_list.index(char)
result.append(reversed_list[index])
else:
raise ValueError(f"未知的密文字符: {char}")
return ''.join(result)
优化技巧 :
- 使用列表切片
[::-1]快速实现反转 - 再次添加字符验证机制,确保输入合法性
- 提前定义好原始字母表,避免硬编码
执行第二阶段解密:
stage2_result = second_stage_decrypt(stage1_result)
print("第二阶段解密结果:", stage2_result) # 输出: TOYSAYGREENTEAISGWHTCOOL
4. Flag格式处理与最终输出
CTF题目的flag通常有特定格式要求,我们需要进行最后修饰:
def format_flag(raw_flag):
flag = raw_flag.replace('GWHT', 'GWHT{').replace('COOL', 'COOL}')
if not flag.startswith('GWHT{') or not flag.endswith('COOL}'):
raise ValueError("Flag格式异常,请检查解密过程")
return flag
final_flag = format_flag(stage2_result)
print("最终Flag:", final_flag) # 输出: GWHT{TOYSAYGREENTEAISCOOL}
注意事项 :
- 实际比赛中flag格式可能不同,需要根据题目描述调整
- 添加格式验证可以避免因解密错误导致的无效提交
- 建议将GWHT和COOL作为可配置参数
5. 构建通用Toy Cipher解密工具
为了提升代码复用性,我们可以将上述步骤封装成一个完整类:
class ToyCipherDecoder:
def __init__(self):
self.cipher_dict = {
'M':'ACEG', 'R':'ADEG', 'K':'BCEG', 'S':'BDEG',
'A':'ACEH', 'B':'ADEH', 'L':'BCEH', 'U':'BDEH',
'D':'ACEI', 'C':'ADEI', 'N':'BCEI', 'V':'BDEI',
'H':'ACFG', 'F':'ADFG', 'O':'BCFG', 'W':'BDFG',
'T':'ACFH', 'G':'ADFH', 'P':'BCFH', 'X':'BDFH',
'E':'ACFI', 'I':'ADFI', 'Q':'BCFI', 'Y':'BDFI'
}
self.original_list = [
'M','R','K','S','A','B','L','U','D','C','N','V',
'H','F','O','W','T','G','P','X','E','I','Q','Y'
]
def decrypt_file(self, file_path):
ciphertext = self.read_ciphertext(file_path)
stage1 = self.first_stage_decrypt(ciphertext)
stage2 = self.second_stage_decrypt(stage1)
return self.format_flag(stage2)
# 其他方法保持不变,只需改为实例方法
# ...
工具类优势 :
- 封装所有解密逻辑,提供统一接口
- 可轻松扩展支持更多加密变种
- 便于集成到自动化解题脚本中
使用示例:
decoder = ToyCipherDecoder()
flag = decoder.decrypt_file('signin.txt')
print("使用工具类解密结果:", flag)
6. 常见问题与调试技巧
在实际操作中可能会遇到以下问题及解决方案:
问题1:密文长度不是4的倍数
# 解决方案:添加长度检查
if len(ciphertext) % 4 != 0:
print("警告:密文长度异常,可能存在截断或读取错误")
问题2:替换后结果不符合预期
# 调试建议:打印中间结果
print("当前处理的密文块:", block)
print("对应的明文字符:", reverse_dict.get(block, '未知'))
问题3:最终flag格式不正确
# 应对策略:灵活处理格式
prefix = 'GWHT{'
suffix = 'COOL}'
if prefix in raw_flag and suffix in raw_flag:
flag = raw_flag.replace(prefix, '').replace(suffix, '')
flag = f"{prefix}{flag}{suffix}"
性能优化建议 :
- 对于超长密文,可以考虑使用生成器而非列表
- 使用字符串的
translate方法可能比循环替换更高效 - 对反向字典查询添加缓存机制
7. 扩展应用与变种思路
掌握了基础解密方法后,我们可以进一步思考:
变种1:动态替换表
def generate_cipher_dict(seed):
# 基于种子生成动态替换表
random.seed(seed)
chars = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
random.shuffle(chars)
return {c: ''.join(random.sample('ABCDEFGH',4)) for c in chars}
变种2:多层嵌套加密
def multi_layer_encrypt(text, layers=3):
for _ in range(layers):
text = first_stage_encrypt(text)
text = second_stage_encrypt(text)
return text
实战技巧 :
- 遇到类似题目时,先分析加密步骤的组成部分
- 使用中间结果验证每个阶段的正确性
- 保留完整的测试用例便于回归验证
在CTF比赛中,密码学题目往往会在基础算法上增加各种变化。通过这个案例,我们不仅学会了如何逆向分析Toy Cipher,更重要的是掌握了 分层解密 的通用思路。下次遇到类似题目时,可以尝试先将复杂问题拆解为多个简单步骤,然后逐个击破。
更多推荐
所有评论(0)