CTF密码学实战:一道XOR与MD5结合的加密题完整分析
嗯,用户让我总结一下这篇文章的内容,控制在100字以内,而且不需要用“文章内容总结”这样的开头。首先,我得通读一下这篇文章,了解它的主要内容。 这篇文章讲的是一个CTF题目,题目名称是"x_xor_md5",提示涉及到XOR加密和MD5哈希。作者拿到一个文件,通过查看十六进制内容发现了重复的模式,推测使用了XOR加密,并且密钥是循环使用的。接着,作者提取了密钥并尝试解密,发现解密结果中的大小写不对劲,于是调整了密钥。最终通过已知明文攻击修正了密钥,并发现密钥本身是一个MD5哈希值。破解MD5后得到了最终的FLAG。 所以,总结起来就是:文章详细描述了如何通过分析XOR加密和MD5哈希来解决CTF题目,最终找到了正确的密钥和FLAG。 现在要控制在100字以内,我得精简一下语言。确保涵盖主要步骤:XOR加密、重复模式、提取密钥、调整密钥、MD5破解、得到FLAG。 可能的结构是:文章描述了一个CTF题目,涉及XOR加密和MD5哈希。通过分析文件中的重复模式提取密钥,并调整大小写错误。利用已知明文攻击修正密钥后,发现其为MD5值并成功破解,最终获得FLAG。 这样应该在100字左右。 </think> 文章描述了一个CTF题目"x_xor_md5"的解题过程。通过对文件的十六进制内容分析发现重复模式,并推测使用了循环XOR加密。提取密钥后尝试解密发现大小写反转问题,在调整密钥后成功解密部分内容。进一步分析发现密钥为某个字符串的MD5值,并通过暴力破解找到原文"that",最终获得FLAG:RCTF{We1l_d0n3_6ut_wh4t_i5_that}。 2026-1-21 07:38:43 Author: www.freebuf.com(查看原文) 阅读量:0 收藏

题目初探

拿到这道题目时,首先看到题目名称是"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(异或)是一种位运算,它具有以下数学性质:

  1. 交换律:A ⊕ B = B ⊕ A

  2. 结合律:(A ⊕ B) ⊕ C = A ⊕ (B ⊕ C)

  3. 自反性:A ⊕ A = 0

  4. 恒等性:A ⊕ 0 = A

  5. 可逆性: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。

为什么会这样?

可能的原因:

  1. 题目故意设置的障碍,增加难度

  2. 提取密钥时的假设有偏差(明文可能是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 ^ 0x20 for 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

总结

这道题目虽然涉及的技术点不算特别深,但完整地展示了密码分析的基本流程:

  1. 观察与模式识别:发现重复的十六进制序列

  2. 密码学原理应用:理解XOR加密的特性

  3. 假设与验证:提取密钥、尝试解密

  4. 问题诊断:发现大小写问题

  5. 深入分析:理解ASCII编码

  6. 精细调整:使用已知明文攻击修正密钥

  7. 突破关键:识别MD5哈希

  8. 最终破解:获得完整FLAG

整个过程不仅需要密码学知识,还需要:

  • 细心的观察能力

  • 逻辑推理能力

  • 编程实现能力

  • 系统性的分析思维

  • 不断尝试的耐心

对于CTF初学者,这道题提供了很好的学习价值:

  • 难度适中,不会过于复杂

  • 涵盖多个知识点

  • 需要完整的分析过程

  • 有明确的验证结果

通过这道题,我们不仅学会了如何破解XOR加密,更重要的是学会了如何系统地分析一个密码学问题。这种思维方式和方法论在信息安全领域的各个方面都适用。

最终FLAG:RCTF{We1l_d0n3_6ut_wh4t_i5_that}

希望这篇详细的分析能帮助你理解整个解题过程,在今后遇到类似问题时能够独立分析和解决。密码学的魅力在于,每一个成功的破解都建立在扎实的理论基础和细致的观察之上。继续学习,不断实践,你也能成为密码分析的高手!


文章来源: https://www.freebuf.com/articles/others-articles/467499.html
如有侵权请联系:admin#unsafe.sh