文章总结: 这篇文章系统梳理了CTF中密码学题型的分类与解题思路,涵盖古典密码的统计破解方法、现代密码算法的常见实现漏洞利用,以及协议侧信道攻击等,并通过栅栏密码、Vigenère、babyxor和babyaes四个具体题目展示了从密文到明文的完整解题流程,对CTF参赛者具有实用参考价值。 综合评分: 85 文章分类: CTF,漏洞分析,安全工具
CTF Crypto实战:精选题目Writeup与完整复现
原创
小话安全 小话安全
小话安全
2026年9月3日 16:16 山东
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
CTF中的Crypto(密码学)题型花样繁多,但核心都是考察你识别密码体制、发现薄弱环节、逆向还原信息的能力。下面我梳理了CTF中Crypto的常见题型,方便你快速建立知识框架。
🏛️ 古典密码:语言与统计的博弈
古典密码是CTF中的基础题,通常出现率很高,是新手必须拿下的送分题。它们的安全性主要依赖“人看不出规律”,因此解题核心是利用语言统计特征和格式约束进行破解。
-
编码类:本质是格式转换,并非严格加密。典型例子有:
-
Base64:由
A-Z、a-z、0-9、+、/组成,常以=结尾。 -
Hex:仅由
0-9、a-f组成。 -
URL编码:特征为
%后跟两个十六进制字符。 -
此外还有摩斯密码、猪圈密码、培根密码等。
-
替换密码:将明文中的字母按规则替换成其他字母。
-
凯撒密码:最简单的移位密码,尝试所有25种偏移即可爆破。
-
ROT13:凯撒密码的一种特例,偏移量为13。
-
维吉尼亚密码:使用一个重复的关键词进行多表替换。破解关键是先确定密钥长度(可用Kasiski测试或重合指数法),然后对每组进行频率分析。
-
单表替换密码:建立一个一对一的替换表。解法是利用词频分析或利用已知明文(如
flag{)进行约束求解。 -
置换密码:不改变字母本身,只改变字母的位置。
-
栅栏密码:将明文按“Z”字形排列后按行读取。解法是尝试不同的栅栏层数。
-
列移位密码:按列写入,按行读出。解法是尝试不同的列数和排列顺序。
⚙️ 现代密码:算法与实现的缺陷
现代密码算法(如AES、RSA)本身通常是安全的,CTF题目更多是考察算法在具体实现或参数选择上的漏洞。
-
对称加密:加解密使用相同密钥。
-
ECB模式:相同的明文块会生成相同的密文块。漏洞是可以通过替换密文块来篡改明文,且无需知道密钥。
-
CBC模式:常见漏洞包括Padding Oracle攻击,利用服务器对填充错误的反馈来逐字节恢复明文;以及IV比特翻转攻击,通过修改IV来改变第一个明文分组。
-
AES:重点考察其不同工作模式的缺陷。
-
RC4:一种流密码。最大的漏洞是密钥重用,如果两次加密使用了相同的密钥流,可以通过两次密文异或得到明文的异或值。
-
非对称加密:加解密使用不同的公钥和私钥。
-
模数分解:当模数
n较小时,可直接用工具(如factordb)分解得到p和q。 -
低加密指数攻击:当公钥指数
e很小(如e=3)时,若明文m也较小,则c = m^e < n,直接对密文c开e次方即可。 -
低解密指数攻击:当私钥指数
d较小时,可使用Wiener攻击。 -
共模攻击:同一明文
m,使用相同的模数n但不同的公钥指数e1、e2加密。当e1和e2互质时,可通过扩展欧几里得算法恢复明文。 -
素数选取不当:如果
p和q非常接近,可使用费马分解法;如果p-1或q-1只有小质因子,可使用Pollard’s p-1分解法。 -
RSA:CTF中的绝对核心。常见攻击点都围绕着数学上的“偷懒”或“疏忽”:
-
哈希函数:将任意长度的数据映射为固定长度的摘要。
-
常见考点:MD5和SHA-1的碰撞攻击(找到两个不同消息但哈希值相同);以及简单的破解,即查询在线彩虹表或使用字典攻击。
-
随机数:密码学中随机数的质量至关重要。
-
常见考点:LCG(线性同余生成器)和MT19937(梅森旋转算法)等伪随机数生成器(PRNG)的种子恢复或预测。如果随机数种子被重用或可被预测,整个加密体系将崩溃。
🕵️♂️ 协议与实现漏洞:利用“信息泄露”
这类题目不直接考数学或算法,而是考察你能否敏锐地捕捉到系统在交互过程中泄露的“蛛丝马迹”。
-
侧信道攻击:利用密码系统运行时产生的物理或时间信息来破解密钥。
-
时间攻击:通过比较不同输入的解密时间来推断密钥或明文信息。
-
Padding Oracle攻击:属于利用错误回显的侧信道攻击。
-
协议设计缺陷:利用密钥交换、签名验证等流程中的逻辑漏洞。例如,服务端在签名或解密前对用户输入缺乏校验,导致攻击者可以伪造数据。
🧩 综合与新兴题型:边界在不断拓展
随着密码学的发展,CTF的题型也在不断更新。
- 椭圆曲线密码(ECC):基于椭圆曲线数学的一种非对称加密,相比RSA有密钥更短的优势。考点包括无效曲线攻击、ECDSA签名中随机数
k重用等。 - 后量子密码:随着量子计算的发展,一些能抵抗量子计算攻击的算法开始出现,如基于格的密码(LWE问题)。
- 密码学与其它方向结合:Crypto题目常与Misc(杂项)、Web、Reverse等方向结合,例如将flag隐藏在图片中(隐写术)、或通过Web接口进行Padding Oracle攻击。
题目一 栅栏密码
题目二 Vigenère
pqcq{qc_m1kt4_njn_5slp0b_lkyacx_gcdy1ud4_g3nv5x0}
当然可以!下面详细解释如何获得密钥。
1. 密文与格式
密文:
pqcq{qc_m1kt4_njn_5slp0b_lkyacx_gcdy1ud4_g3nv5x0}
通常 CTF flag 的格式为 flag{…},所以明文开头很可能是:
flag{
2. 已知明文攻击(Known Plaintext Attack)
因为 flag{ 是已知的,我们可以用它与密文开头的 pqcq{ 逐字符比较,推导出加密所用的密钥流(Vigenère 密钥的循环)。
Vigenère 加密规则(仅对字母生效):
- 密文字母 = (明文字母 + 密钥字母) mod 26
- 其他字符(数字、下划线、花括号等)保持原样。
于是我们从第一个字母开始:
| 位置 | 明文字母 | 密文字母 | 偏移量(密文 – 明文 mod 26) | 对应密钥字母 | | — | — | — | — | — | | 1 | f (5) | p (15) | 15 – 5 = 10 | k (10) | | 2 | l (11) | q (16) | 16 – 11 = 5 | f (5) | | 3 | a (0) | c (2) | 2 – 0 = 2 | c (2) | | 4 | g (6) | q (16) | 16 – 6 = 10 | k (10) |
得到的偏移序列是10, 5, 2, 10,即字母 k, f, c, k。
因为 Vigenère 密钥是循环使用的,前四个字符对应的密钥应为 k, f, c, k,这意味着密钥周期为 3,实际密钥为 kfc(第 4 个字符回到第一个密钥字母 k)。
3. 验证密钥
用密钥 kfc 对整个密文进行解密(仅作用于字母,其余字符不变):
- 对每个字母
cipher,计算plain = (cipher - key) mod 26。
解密后得到:
flag{la_c1fr4_del_5ign0r_giovan_batt1st4_b3ll5s0}
完全符合语法和含义(“la cifra del signor Giovan Battista Bellaso” 是 Vigenère 密码的历史背景)。
题目三 babyxor
from secret import *
ciphertext = []
for f in flag: ciphertext.append(f ^ key)
print(bytes(ciphertext).hex())# e9e3eee8f4f7bffdd0bebad0fcf6e2e2bcfbfdf6d0eee1ebd0eabbf5f6aeaeaeaeaeaef2
爆破脚本
#from secret import *
#ciphertext = []
#for f in flag: #ciphertext.append(f ^ key)
#print(bytes(ciphertext).hex())# e9e3eee8f4f7bffdd0bebad0fcf6e2e2bcfbfdf6d0eee1ebd0eabbf5f6aeaeaeaeaeaef2
import binascii
def single_byte_xor_bruteforce(hex_str): """ 对十六进制密文进行单字节异或暴力破解。 参数: hex_str - 十六进制字符串(如 "e9e3...") 返回: 所有候选明文的列表,每个元素为 (密钥, 明文) """ # 将十六进制转换为字节 try: cipher = bytes.fromhex(hex_str.strip()) except ValueError: print("无效的十六进制字符串") return []
candidates = []
for key in range(256): # 对每个字节异或 key plain = bytes([b ^ key for b in cipher])
# 检查是否全部为可打印 ASCII(空格 ~ 波浪号)或常见控制字符 # 可以根据需求放宽条件,例如允许换行、回车等 if all(0x20 <= c <= 0x7e or c in (0x0a, 0x0d) for c in plain): candidates.append((key, plain.decode('ascii', errors='ignore')))
return candidates
if __name__ == "__main__": # 示例密文(来自题目) hex_data = "e9e3eee8f4f7bffdd0bebad0fcf6e2e2bcfbfdf6d0eee1ebd0eabbf5f6aeaeaeaeaeaef2"
results = single_byte_xor_bruteforce(hex_data)
if not results: print("未找到完全可打印的候选明文。") else: print(f"找到 {len(results)} 个候选密钥:") for key, text in results: print(f"密钥 0x{key:02X} ({key}) -> {text}")
题目四 babyaes
from Crypto.Cipher import AESimport osiv = os.urandom(16)key = os.urandom(16)my_aes = AES.new(key, AES.MODE_CBC, iv)flag = open('flag.txt', 'rb').read()flag += (16 - len(flag) % 16) * b'\x00'c = my_aes.encrypt(flag)print(c)print(iv)print(key)
'''b'C4:\x86Q$\xb0\xd1\x1b\xa9L\x00\xad\xa3\xff\x96 hJ\x1b~\x1c\xd1y\x87A\xfe0\xe2\xfb\xc7\xb7\x7f^\xc8\x9aP\xdaX\xc6\xdf\x17l=K\x95\xd07'b'\xd1\xdf\x8f)\x08w\xde\xf9yX%\xca[\xcb\x18\x80'b'\xa4\xa6M\xab{\xf6\x97\x94>hK\x9bBe]F''''
解密脚本
from Crypto.Cipher import AES
# 从输出中复制的密文、IV 和密钥(注意转义)c = b'C4:\x86Q$\xb0\xd1\x1b\xa9L\x00\xad\xa3\xff\x96 hJ\x1b~\x1c\xd1y\x87A\xfe0\xe2\xfb\xc7\xb7\x7f^\xc8\x9aP\xdaX\xc6\xdf\x17l=K\x95\xd07'iv = b'\xd1\xdf\x8f)\x08w\xde\xf9yX%\xca[\xcb\x18\x80'key = b'\xa4\xa6M\xab{\xf6\x97\x94>hK\x9bBe]F'
# 创建 AES CBC 解密器aes = AES.new(key, AES.MODE_CBC, iv)
# 解密plaintext_padded = aes.decrypt(c)
# 去掉零填充(加密时末尾补了 \x00)plaintext = plaintext_padded.rstrip(b'\x00')
# 输出结果print(plaintext.decode('utf-8', errors='ignore'))
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:小话安全 小话安全 小话安全《CTF Crypto实战:精选题目Writeup与完整复现》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论