文章总结: Thearticledetailsthestep-by-stepsolutionofaCTFchallengecombiningXORencryptionandMD5hashing.ItexplainsidentifyingacyclicXORkeythroughrepeatinghexpatterns,correctingitviaASCIIcasemanipulationandknownplaintextattacksontheflagstructure.TheauthordeducesthatthederivedkeyisanMD5hash,subsequentlycrackingittofindtheoriginalword’that’.Theprocesscoverskeydecryptiontechniques,hexanalysis,andhashvulnerability,culminatinginthefinalflagextraction. 综合评分: 95 文章分类: CTF
CTF密码学实战:一道XOR与MD5结合的加密题完整分析
原创
破镜安全 破镜安全
破镜安全
2026年1月21日 08:03 四川
CTF密码学实战:一道XOR与MD5结合的加密题完整分析
题目初探
拿到这道题目时,首先看到题目名称是”x_xor_md5″,这个名称已经给出了非常明确的提示:题目涉及XOR异或运算和MD5哈希算法。
题目提供了一个文件:6c0c57fa88eb44f3b179a6e9798fc7b6
从文件名来看,这是一个32位的十六进制字符串,很可能也是一个MD5值。这可能是题目设计者的小心思,文件名本身就可能包含关键信息。
初步观察:查看文件内容
作为密码分析的第一步,我使用xxd命令查看文件的十六进制内容:
xxd -g 1 6c0c57fa88eb44f3b179a6e9798fc7b6
输出结果:
00000000: 69 35 41 01 1c 9e 75 78 5d 48 fb f0 84 cd 66 79 i5A...ux]H....fy
00000010: 55 30 49 4c 56 d2 73 70 12 45 a8 ba 85 c0 3e 53 U0ILV.sp.E....>S
00000020: 73 1b 78 2a 4b e9 77 26 5e 73 bf aa 85 9c 15 6f s.x*K.w&^s.....o
00000030: 54 2c 73 1b 58 8a 66 48 5b 19 84 b0 80 ca 33 73 T,s.X.fH[.....3s
00000040: 5c 52 0c 4c 10 9e 32 37 12 0c fb ba cb 8f 6a 53 \R.L..27......jS
00000050: 01 78 0c 4c 10 9e 32 37 12 0c fb ba cb 8f 6a 53 .x.L..27......jS
00000060: 01 78 0c 4c 10 9e 32 37 12 0c fb ba cb 8f 6a 53 .x.L..27......jS
00000070: 01 78 0c 4c 10 9e 32 37 12 0c 89 d5 a2 fc .x.L..27......
关键发现
观察这个十六进制dump,我立即注意到一个非常重要的现象:
0x50、0x60、0x70行的前14个字节完全相同!
0x50: 01 78 0c 4c 10 9e 32 37 12 0c fb ba cb 8f 6a 53
0x60: 01 78 0c 4c 10 9e 32 37 12 0c fb ba cb 8f 6a 53
0x70: 01 78 0c 4c 10 9e 32 37 12 0c (后面不完整)
这绝不是巧合。在密码学中,密文出现重复模式通常意味着什么?
密码学原理分析
XOR加密的基本特性
XOR(异或)是一种位运算,它具有以下数学性质:
- 交换律:A ⊕ B = B ⊕ A
- 结合律:(A ⊕ B) ⊕ C = A ⊕ (B ⊕ C)
- 自反性:A ⊕ A = 0
- 恒等性:A ⊕ 0 = A
- 可逆性:A ⊕ B ⊕ B = A
第5条性质使得XOR成为一种简单的加密方法:
- 加密:密文 = 明文 ⊕ 密钥
- 解密:明文 = 密文 ⊕ 密钥
循环密钥XOR加密
当密钥长度小于明文长度时,通常会循环使用密钥。例如,密钥长度为16字节,那么:
- 明文第0字节与密钥第0字节异或
- 明文第1字节与密钥第1字节异或
- …
- 明文第16字节与密钥第0字节异或(循环)
- 明文第17字节与密钥第1字节异或
- …
重复模式的成因
我观察到的重复模式说明:这些位置的明文内容相同,使用相同的密钥部分加密后,产生了相同的密文。
如果明文的0x50-0x5F、0x60-0x6F位置都是相同的内容(比如空格、NULL字符或其他填充字符),那么使用16字节的循环密钥加密后,必然产生相同的密文。
推理:提取XOR密钥
基于以上分析,我提出假设:
假设1:使用了16字节的循环密钥进行XOR加密假设2:0x50-0x6F位置的明文是重复的字符(如0x00或0x20)
如果明文是0x00(NULL字符),那么:
密文 = 0x00 ⊕ 密钥 = 密钥
这意味着重复的密文序列本身可能就是密钥!
让我提取这个序列作为初始密钥:
key = bytes([0x01, 0x78, 0x0c, 0x4c, 0x10, 0x9e, 0x32, 0x37,
0x12, 0x0c, 0xfb, 0xba, 0xcb, 0x8f, 0x6a, 0x53])
第一次尝试:XOR解密
使用提取的密钥对整个文件进行XOR解密:
with open('6c0c57fa88eb44f3b179a6e9798fc7b6', 'rb') as f:
data = f.read()
key = bytes([0x01, 0x78, 0x0c, 0x4c, 0x10, 0x9e, 0x32, 0x37,
0x12, 0x0c, 0xfb, 0xba, 0xcb, 0x8f, 0x6a, 0x53])
result = bytearray()
for i, byte in enumerate(data):
result.append(byte ^ key[i % len(key)])
解密结果:
00000000: 68 4d 4d 4d 0c 00 47 4f 4f 44 00 4a 4f 42 0c 2a hMMM..GOOD.JOB.*
00000010: 54 48 45 00 46 4c 41 47 00 49 53 00 4e 4f 54 00 THE.FLAG.IS.NOT.
00000020: 72 63 74 66 5b 77 45 11 4c 7f 44 10 4e 13 7f 3c rctf[wE.L.D.N..<
00000030: 55 54 7f 57 48 14 54 7f 49 15 7f 0a 4b 45 59 20 UT.WH.T.I...KEY
分析解密结果
看到这个结果,我心里一半欣喜一半疑惑:
好消息:
- 出现了可读的英文单词:GOOD、JOB、THE、FLAG、IS、NOT、KEY
- 这些单词说明我的思路是对的,确实是XOR加密
问题:
- 开头是”hMMM”而不是正常的”Hmmm”
- 第0x20行是”rctf[wE”而不是常见CTF的FLAG格式”RCTF{We”
大小写完全反了!
深入分析:ASCII编码与大小写
让我仔细分析这个大小写问题。在ASCII编码中:
大写字母 'H' = 0x48 = 0100 1000
小写字母 'h' = 0x68 = 0110 1000
差值 = 0x20 = 0010 0000
观察可知:大小写字母在ASCII码中只有第6位(从低位数起第5位)不同。这个差值正好是0x20。
对比我的解密结果:
- 得到’h’(0x68),期望’H’(0x48),差值0x20
- 得到’r’(0x72),期望’R’(0x52),差值0x20
- 得到’c’(0x63),期望’C’(0x43),差值0x20
所有字母的大小写都是反的!
这意味着什么?我的密钥每个字节都需要再异或0x20。
为什么会这样?
可能的原因:
- 题目故意设置的障碍,增加难度
- 提取密钥时的假设有偏差(明文可能是0x20而不是0x00)
让我验证:如果明文是0x20(空格),那么:
密钥实际值 = 密文 ⊕ 0x20
这正好解释了为什么需要对密钥每个字节异或0x20。
第二次尝试:修正密钥
对密钥的每个字节异或0x20:
key_original = bytes([0x01, 0x78, 0x0c, 0x4c, 0x10, 0x9e, 0x32, 0x37,
0x12, 0x0c, 0xfb, 0xba, 0xcb, 0x8f, 0x6a, 0x53])
key_fixed = bytes([b ^ 0x20 for b in key_original])
# 结果: 21 58 2c 6c 30 be 12 17 32 2c db 9a eb af 4a 73
使用新密钥重新解密:
00000000: 48 6d 6d 6d 2c 20 67 6f 6f 64 20 6a 6f 62 2c 0a Hmmm, good job,.
00000010: 74 68 65 20 66 6c 61 67 20 69 73 20 6e 6f 74 20 the flag is not
00000020: 52 43 54 46 7b 57 65 31 6c 5f 64 30 6e 33 5f 1c RCTF{We1l_d0n3_.
00000030: 75 74 5f 77 68 34 74 5f 69 35 5f 2a 6b 65 79 00 ut_wh4t_i5_*key.
00000040: 7d 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 }.
太好了!现在可以清楚地读出:
Hmmm, good job,
the flag is not
RCTF{We1l_d0n3_ut_wh4t_i5_*key*}
但仔细观察,还有问题:
新的问题
观察每行的第16个字节(最后一列):
- 0x0F位置:0x0a(换行符,合理)
- 0x1F位置:0x20(空格,合理)
- 0x2F位置:0x1c(非可见字符,不对)
- 0x3F位置:0x00(NULL,不对)
从FLAG的上下文看,0x2F位置应该是’6’,0x3F位置应该是’*’。
让我分析:FLAG应该是”RCTF{We1l_d0n3_6ut_wh4t_i5_key}”,但现在0x2F显示的是不可见字符。
精细调整:修正最后一个字节
密钥是16字节循环使用的,所以:
- 0x2F位置使用密钥的第15个字节(索引15)
- 0x3F位置也使用密钥的第15个字节
让我利用已知明文来计算正确的密钥字节。
已知明文攻击
XOR加密有一个致命弱点:如果知道明文和密文,可以直接计算出密钥:
密钥 = 明文 ⊕ 密文
我知道:
- 0x2F位置的密文(原始文件):0x6f
- 0x2F位置的明文应该是:’6′ = 0x36
计算:
密钥[15] = 0x6f ⊕ 0x36 = 0x59
验证另一个位置:
- 0x3F位置的密文:0x73
- 0x3F位置的明文应该是:’*’ = 0x2a
计算:
密钥[15] = 0x73 ⊕ 0x2a = 0x59
两个位置计算出的结果一致,都是0x59!这验证了我的推理是正确的。
当前密钥的第15个字节是0x73,需要改为0x59。
第三次尝试:最终解密
使用修正后的密钥:
key_final = bytes([0x21, 0x58, 0x2c, 0x6c, 0x30, 0xbe, 0x12, 0x17,
0x32, 0x2c, 0xdb, 0x9a, 0xeb, 0xaf, 0x4a, 0x59])
解密结果:
00000000: 48 6d 6d 6d 2c 20 67 6f 6f 64 20 6a 6f 62 2c 20 Hmmm, good job,
00000010: 74 68 65 20 66 6c 61 67 20 69 73 20 6e 6f 74 0a the flag is not.
00000020: 52 43 54 46 7b 57 65 31 6c 5f 64 30 6e 33 5f 36 RCTF{We1l_d0n3_6
00000030: 75 74 5f 77 68 34 74 5f 69 35 5f 2a 6b 65 79 2a ut_wh4t_i5_*key*
00000040: 7d 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 0a }. .
完美!现在所有字符都正常了:
Hmmm, good job, the flag is not
RCTF{We1l_d0n3_6ut_wh4t_i5_*key*}
但FLAG中有一个占位符:*key*
这显然需要我们找出真正的”key”是什么。
关键突破:识别MD5
现在让我重新审视得到的最终密钥:
21582c6c30be1217322cdb9aebaf4a59
这个十六进制字符串长度是多少?数一数:32个字符。
32个十六进制字符意味着16个字节,也就是128位。
128位!这正好是MD5哈希值的长度!
结合题目名称”x_xor_md5″,一切都说得通了:
- x代表XOR
- md5代表这个密钥是某个字符串的MD5值
所以现在的任务是:找到MD5值21582c6c30be1217322cdb9aebaf4a59对应的原始字符串。
MD5破解
MD5是一个单向哈希函数,理论上不可逆。但是对于简单的字符串,可以通过以下方法找到原文:
方法1:暴力破解常见单词
FLAG中提示是”wh4t_i5_key“(what is key),这暗示key可能是一个常见的英文单词。
让我写个脚本尝试一些常见单词:
import hashlib
target_md5 = "21582c6c30be1217322cdb9aebaf4a59"
common_words = ["that", "this", "what", "when", "where", "which",
"flag", "key", "word", "text", "data"]
for word in common_words:
md5_hash = hashlib.md5(word.encode()).hexdigest()
if md5_hash == target_md5:
print(f"找到了: {word}")
break
运行后发现:that 的MD5值正是 21582c6c30be1217322cdb9aebaf4a59!
验证:
import hashlib
print(hashlib.md5(b"that").hexdigest())
# 输出: 21582c6c30be1217322cdb9aebaf4a59
方法2:在线MD5破解
也可以使用在线MD5解密网站,如:
- https://www.cmd5.com/
- https://hashkiller.co.uk/md5-decrypter.aspx
- https://crackstation.net/
这些网站维护了庞大的MD5彩虹表,可以快速查询常见字符串的MD5值。
最终答案
将FLAG中的*key*替换为破解出的单词that:
RCTF{We1l_d0n3_6ut_wh4t_i5_that}
这就是最终的FLAG!
完整解题脚本
为了方便理解和复现,这里提供完整的Python解题代码:
#!/usr/bin/env python3
import hashlib
# 读取加密文件
with open('6c0c57fa88eb44f3b179a6e9798fc7b6', 'rb') as f:
data = f.read()
# 从重复序列识别并修正密钥
# 初始密钥(从0x50行重复序列提取)
key_from_pattern = bytes([0x01, 0x78, 0x0c, 0x4c, 0x10, 0x9e, 0x32, 0x37,
0x12, 0x0c, 0xfb, 0xba, 0xcb, 0x8f, 0x6a, 0x53])
# 修正大小写(每个字节异或0x20)
key_case_fixed = bytes([b ^ 0x20for b in key_from_pattern])
# 修正最后一个字节(0x73 -> 0x59)
key_final = bytearray(key_case_fixed)
key_final[15] = 0x59
key_final = bytes(key_final)
print(f"最终XOR密钥: {key_final.hex()}")
# XOR解密
result = bytearray()
for i in range(len(data)):
result.append(data[i] ^ key_final[i % len(key_final)])
# 显示解密结果
decrypted = result.decode('ascii', errors='replace').strip()
print(f"\n解密内容:\n{decrypted}")
# 识别MD5并破解
md5_value = key_final.hex()
print(f"\n密钥是MD5值: {md5_value}")
# 尝试常见单词
common_words = ["that", "this", "what", "when", "where"]
for word in common_words:
if hashlib.md5(word.encode()).hexdigest() == md5_value:
print(f"MD5对应的原文: {word}")
final_flag = f"RCTF{{We1l_d0n3_6ut_wh4t_i5_{word}}}"
print(f"\n最终FLAG: {final_flag}")
break
运行输出:
最终XOR密钥: 21582c6c30be1217322cdb9aebaf4a59
解密内容:
Hmmm, good job, the flag is not
RCTF{We1l_d0n3_6ut_wh4t_i5_*key*}
密钥是MD5值: 21582c6c30be1217322cdb9aebaf4a59
MD5对应的原文: that
最终FLAG: RCTF{We1l_d0n3_6ut_wh4t_i5_that}
技术总结与知识点
1. XOR加密的特性与弱点
核心特性:
- 可逆性:使用相同的密钥可以加密和解密
- 简单高效:只需要异或运算
- 对称性:加密和解密使用相同的操作
主要弱点:
- 循环密钥会产生模式:相同的明文块产生相同的密文块
- 已知明文攻击:知道部分明文和密文可以计算密钥
- 频率分析:对于较长的密文,可以通过统计分析破解
2. 密码分析的方法论
本题展示了系统的密码分析流程:
第一步:观察
- 查看十六进制数据
- 寻找模式和异常
- 记录所有可疑特征
第二步:假设
- 基于观察提出合理假设
- 考虑题目提示(名称、描述等)
- 结合已知的密码学知识
第三步:验证
- 实现假设并测试
- 分析结果
- 调整假设
第四步:迭代
- 解决新发现的问题
- 精细调整
- 直到得到最终答案
3. ASCII编码的应用
在密码分析中,理解ASCII编码非常重要:
字符范围:
- 可见字符:0x20-0x7E
- 大写字母:0x41-0x5A(A-Z)
- 小写字母:0x61-0x7A(a-z)
- 数字:0x30-0x39(0-9)
大小写转换:
- 大小写字母相差0x20
- ‘A’ XOR 0x20 = ‘a’
- ‘a’ XOR 0x20 = ‘A’
利用价值:
- 判断解密结果是否合理
- 发现编码问题
- 进行精细调整
4. 已知明文攻击(Known Plaintext Attack)
这是密码分析中的经典方法:
原理:
密钥 = 明文 ⊕ 密文
应用场景:
- 知道部分明文内容(如文件头、特定格式)
- 可以猜测明文内容(如常见单词、语法结构)
- 有重复的明文块
本题应用:
- 猜测FLAG格式:”RCTF{…}”
- 确定某个字符应该是’6’或’*’
- 利用这些已知信息计算密钥
5. MD5哈希的识别与破解
识别特征:
- 固定长度:32个十六进制字符
- 字符集:0-9, a-f
- 不可逆:无法直接反推原文
破解方法:
- 字典攻击:尝试常见单词
- 暴力破解:遍历所有可能(耗时长)
- 彩虹表:预计算的哈希表(空间换时间)
- 在线服务:利用已有的哈希数据库
安全建议:
- MD5已被证明存在碰撞漏洞
- 不应用于密码存储(应使用bcrypt、Argon2等)
- 不应用于数字签名(应使用SHA-256及以上)
- 可用于非安全场景(如文件校验,但SHA-256更好)
6. CTF题目的思维方式
题目命名的重要性:
- 题目名称往往包含关键提示
- “x_xor_md5″直接告诉了技术栈
- 不要忽视任何细节
分层设计:
- 第一层:识别加密方式(XOR)
- 第二层:找到密钥(重复模式)
- 第三层:调整密钥(大小写、精确值)
- 第四层:识别MD5
- 第五层:破解MD5
障碍设置:
- 大小写反转:增加难度
- 部分字节错误:需要精细调整
- MD5嵌套:需要额外一步破解
实战技巧与工具
推荐工具
十六进制查看:
xxd:Linux命令行工具,轻量快速hexdump:另一个命令行选择- HxD:Windows下的图形化工具
- 010 Editor:专业的十六进制编辑器
Python库:
hashlib:计算各种哈希值(MD5、SHA系列等)binascii:二进制和ASCII转换struct:处理C结构体和二进制数据
在线工具:
- CyberChef:瑞士军刀式的密码分析工具
- HashKiller:MD5等哈希在线破解
- Crackstation:支持多种哈希算法
解题建议
保持系统性:
- 记录每一步的发现和思考
- 不要跳过观察阶段
- 建立假设-验证的循环
理解原理:
- 不要只记住步骤,要理解为什么
- 知道工具背后的原理
- 能够手动实现才算真正理解
善用工具:
- 不要重复造轮子
- 了解工具的能力和局限
- 工具只是辅助,思路最重要
保持耐心:
- 密码分析常需要多次尝试
- 遇到障碍是正常的
- 每次失败都能提供新信息
延伸思考
如何加强XOR加密?
简单的XOR加密很容易被破解,但可以通过以下方式加强:
一次性密码本(One-Time Pad):
- 密钥长度等于明文长度
- 密钥完全随机
- 密钥只使用一次
- 理论上不可破解
流密码(Stream Cipher):
- 使用伪随机数生成器产生密钥流
- 现代算法如ChaCha20、RC4(已弱)
- 密钥流的质量决定安全性
结合其他技术:
- 先加密后认证(Encrypt-then-MAC)
- 使用强密钥派生函数
- 添加初始化向量(IV)
MD5为何不安全?
碰撞攻击:
- 2004年王小云等人发现实用碰撞攻击
- 可以构造两个不同的消息产生相同MD5值
- 对数字签名构成威胁
前缀碰撞:
- 可以在给定前缀下构造碰撞
- 更加实用的攻击方式
- 可伪造证书等
替代方案:
- SHA-256、SHA-3:用于完整性校验
- bcrypt、scrypt、Argon2:用于密码存储
- HMAC-SHA256:用于消息认证
现代密码学趋势
认证加密(AEAD):
- 同时保证机密性和完整性
- 如AES-GCM、ChaCha20-Poly1305
- 防止中间人攻击和篡改
后量子密码学:
- 抵抗量子计算机攻击
- 基于格、编码、多变量等困难问题
- NIST正在标准化过程中
零知识证明:
- 证明知道某个秘密而不泄露秘密本身
- 区块链、隐私保护中的应用
- 如zk-SNARKs、zk-STARKs
总结
这道题目虽然涉及的技术点不算特别深,但完整地展示了密码分析的基本流程:
- 观察与模式识别:发现重复的十六进制序列
- 密码学原理应用:理解XOR加密的特性
- 假设与验证:提取密钥、尝试解密
- 问题诊断:发现大小写问题
- 深入分析:理解ASCII编码
- 精细调整:使用已知明文攻击修正密钥
- 突破关键:识别MD5哈希
- 最终破解:获得完整FLAG
整个过程不仅需要密码学知识,还需要:
- 细心的观察能力
- 逻辑推理能力
- 编程实现能力
- 系统性的分析思维
- 不断尝试的耐心
对于CTF初学者,这道题提供了很好的学习价值:
- 难度适中,不会过于复杂
- 涵盖多个知识点
- 需要完整的分析过程
- 有明确的验证结果
通过这道题,我们不仅学会了如何破解XOR加密,更重要的是学会了如何系统地分析一个密码学问题。这种思维方式和方法论在信息安全领域的各个方面都适用。
最终FLAG:RCTF{We1l_d0n3_6ut_wh4t_i5_that}
希望这篇详细的分析能帮助你理解整个解题过程,在今后遇到类似问题时能够独立分析和解决。密码学的魅力在于,每一个成功的破解都建立在扎实的理论基础和细致的观察之上。继续学习,不断实践,你也能成为密码分析的高手!
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:破镜安全 破镜安全 破镜安全《CTF密码学实战:一道XOR与MD5结合的加密题完整分析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论