Crypto 题先判断什么
Crypto 题先判断什么
本文适合
看到密文、参数或加密脚本就想直接丢 CyberChef、爆破或套脚本的 Crypto 入门学习者。学完你能:先判断题目属于编码、哈希、对称加密、公钥密码、签名或数学约束,并用最小验证决定下一步攻击路线。
一句话判断
Crypto 题先判断保护类型和已知参数,再动手写解密脚本;不要把编码当加密,也不要在没有前提的情况下暴力搜索。
最小判断链:
题目中常见信号
优先判断:编码
下一步:直接按规则解码,重新观察输出
优先判断:哈希或摘要
下一步:看是否需要碰撞、长度扩展或字典
优先判断:对称加密
下一步:判断 AES/CBC/CTR/GCM 等模式
优先判断:RSA
下一步:检查分解、低指数、共模、CRT 泄露
优先判断:DSA/ECDSA/Schnorr
下一步:检查 nonce 复用或偏差
优先判断:重复密钥、ECB、CTR nonce 复用
下一步:做分块或异或关系检查
优先判断:实现漏洞
下一步:先读随机数、参数生成、padding 和比较
优先判断:oracle
下一步:区分 padding、格式、MAC、长度错误
核心概念
说明:编码只靠规则还原,加密需要秘密
判断动作:看是否存在 key、私钥、nonce 或数学难题
说明:参数完整度决定是否能直接复现
判断动作:列 算法/模式/key/iv/nonce/ciphertext
说明:没有验证条件就不能盲爆
判断动作:用 flag{}、魔数、padding、JSON 格式验证
说明:爆破必须先估算成本
判断动作:计算候选数量和单次验证耗时
说明:CTF 常把漏洞藏在复用关系里
判断动作:比较 n、nonce、IV、密文块、签名 nonce
Crypto 解题的关键不是“知道很多算法名”,而是能把题目材料转换成可验证的攻击前提。
最小分析流程
- 整理材料:密文、明文样本、脚本、参数、服务接口、flag 格式分别列出。
- 判断表现形式:字符集、长度、是否能 Base64/Hex 解码、是否是文件魔数。
- 列参数表:算法、模式、key、iv/nonce、padding、公钥参数、签名参数。
- 读源码优先:有源码时先看随机数、密钥生成、复用、比较和异常处理。
- 判断攻击前提:低指数、共享素因子、nonce 复用、ECB 块重复、padding oracle 等。
- 做最小验证:用最短脚本证明弱点存在,不直接写完整 exploit。
- 解出中间结果后重新观察:可能还是压缩包、图片、二次编码或另一层密文。
- 记录失败分支:每个被排除的算法和理由都写下来,避免循环尝试。
记录模板:
原始材料:
字符集和长度:
已知参数:
缺失参数:
候选攻击前提:
最小验证:
结论:
下一步:最小验证示例
1. 判断编码还是二进制文件
import base64, binascii
data = "UEsDBAoAAAAAA..."
for name, fn in {
"base64": lambda s: base64.b64decode(s, validate=True),
"hex": lambda s: bytes.fromhex(s),
}.items():
try:
out = fn(data)
print(name, len(out), out[:16].hex(), out[:16])
except Exception as e:
print(name, "fail", e)判断:
输出以 504b0304 开头 -> ZIP 文件,转 Misc/压缩包流程
输出以 89504e47 开头 -> PNG 文件,转图片分析
输出仍是可读编码 -> 继续分层观察2. RSA 共享素因子检查
from math import gcd
ns = [n1, n2, n3]
for i in range(len(ns)):
for j in range(i + 1, len(ns)):
g = gcd(ns[i], ns[j])
if 1 < g < ns[i]:
print("shared factor", i, j, g)判断:
gcd(n1, n2) > 1 -> 两个模数共享素因子,可恢复 p/q 和私钥
所有 gcd 都是 1 -> 排除共享素因子路线,继续看低指数、n 大小或 dp/dq 泄露3. CTR/流密码 nonce 复用检查
from binascii import unhexlify
c1 = unhexlify("...")
c2 = unhexlify("...")
x = bytes(a ^ b for a, b in zip(c1, c2))
print(x[:64])判断:
c1 xor c2 出现大量可读结构 -> 可能是同一 keystream 复用
已知 flag{ 或 JSON 头 -> 可用已知明文恢复部分密钥流
完全随机且样本少 -> 该路线证据不足4. 爆破可行性估算
def estimate(space, try_per_sec=100000):
sec = space / try_per_sec
print(f"{space=}, about {sec:.2f}s")
estimate(10**6)
estimate(26**6)
estimate(2**40)判断:
10^6 以内 -> 适合直接脚本验证
10^8 左右 -> 需要优化、剪枝或并行
2^40 以上 -> 没有强验证和剪枝时不应盲爆
2^128 -> 暴力不可行,必须找实现或数学弱点常见利用 / 解题路线
路线总览:
判断完成后怎么走:分层解码 -> 频率/移位/栅栏枚举 -> 重新观察输出
判断完成后怎么走:字典/弱口令 -> 长度扩展 -> 碰撞或格式绕过
判断完成后怎么走:分块重复检查 -> ECB oracle -> 块替换或逐字节恢复
判断完成后怎么走:IV/上一块影响明文 -> bit flipping 或 padding oracle
判断完成后怎么走:nonce 复用 -> keystream 恢复 -> 多密文拖拽
判断完成后怎么走:分解 n -> 低指数/共模/广播 -> dp/dq/CRT -> Coppersmith
判断完成后怎么走:nonce 复用 -> 私钥恢复;nonce 偏差 -> 格攻击
判断完成后怎么走:种子空间估算 -> 输出反推状态 -> 预测后续密钥
常见失败原因
可能原因:中间结果是二进制文件或二层编码
排查动作:看 magic bytes、长度和可打印比例
可能原因:搜索空间未估算或验证条件错误
排查动作:先打印候选命中率和示例输出
可能原因:key/iv/mode/padding 或编码不匹配
排查动作:分别确认字节长度和模式
可能原因:参数名混淆、n/e/c 进制错误
排查动作:打印 bit length,确认十进制/十六进制
可能原因:结果可能是压缩包/图片/JSON/二次密文
排查动作:重新跑文件魔数和 strings
可能原因:没从源码找真实弱点
排查动作:回到随机数、复用、异常和比较逻辑
迷你案例
题目给出两条十六进制密文和同一个 nonce = 0,脚本里使用 AES-CTR。不要先猜 key,而是检查 nonce 复用。
把两条密文转 bytes 后做异或,发现结果中能看出英文空格和 JSON 结构。由于 CTR 满足 c = p xor stream,同一 nonce 导致两条密文使用同一密钥流,所以 c1 xor c2 = p1 xor p2。
如果已知其中一条明文开头是 {"user":"guest",就能恢复对应长度的 keystream,再解出另一条同位置内容。这个题的判断链是:
信号:AES-CTR + nonce 固定
验证:c1 xor c2 出现可读结构
结论:keystream 复用
路线:已知明文恢复 keystream -> 解另一条密文 -> 找 flag