CBC-MAC 知识点常见问题解答¶
本文档是 Echoes of the Serpent Writeup 的补充知识点,以 Q&A 形式解答 CBC-MAC 长度扩展攻击中的常见疑问。
如何复用本文作为 FAQ 模板¶
- 标题改为:
[知识点] 知识点常见问题解答 - 第一段写明:这是哪个 Writeup 的知识点补充,并给出反向链接
- 至少保留 4 个核心问题:为什么成立 / 关键参数作用 / 公式符号含义 / 常见误区
- 每个问题都用「一句话结论 + 关键推导或示例」的结构,避免只给结论
Q1:不知道密钥,为什么还能伪造 MAC?¶
CBC-MAC 长度扩展攻击完全不依赖密钥。
攻击只需要服务器返回的两个已知 MAC 值:
- 服务器给你 \(T_1 = \text{MAC}(\text{"hello\_world"})\) 和 \(T_2 = \text{MAC}(\text{"get\_flag"})\)
- 构造
forged_msg = pad("hello_world") || (T₁ ⊕ pad("get_flag")) - 提交
forged_msg+ \(T_2\) 即可
XOR 的数学性质保证 MAC 验证一定通过——\(T_1\) 在 CBC 链式计算中与自身异或消去,无需知道密钥。
Q2:如何看出 IV 全零?IV 有什么作用?¶
IV 在哪里看出来的
源码中:
IV = bytes(16) # 16 个 \x00 字节
Python 中 bytes(16) 生成 16 字节全零 \x00\x00...\x00,不是数字 16。
IV 的作用
IV(Initialization Vector,初始化向量)是 CBC 模式中第一个明文块加密前的 XOR 输入:
它的目的是让相同明文在不同 IV 下产生不同密文,防止攻击者识别重复模式。
对本题的影响
IV = 0 时,第一块简化为 \(C_1 = E_K(M_1 \oplus 0) = E_K(M_1)\),XOR 0 等于什么都没做。不过对长度扩展攻击来说,IV 是否全零不影响攻击成立——攻击利用的是 CBC 链式结构,IV 全零只是让分析更直观。
Q3:pad() 是什么意思?¶
pad() 就是源码里的 zero_pad 函数——把数据用 \x00 补齐到 16 字节的倍数。
"hello_world" → 11 字节 + 5 个 \x00 = 16 字节
"get_flag" → 8 字节 + 8 个 \x00 = 16 字节
AES 是分组密码,每块固定 16 字节,不够就必须补齐。Writeup 中的 pad(M₁) 就是指补零后的完整块。
Q4:为什么 MAC(M₁ || M₂) = C₂?¶
因为 CBC-MAC 的定义就是取 CBC 加密后的最后一个密文块:
def cbc_mac(data: bytes) -> bytes:
cipher = AES.new(KEY, AES.MODE_CBC, IV)
ct = cipher.encrypt(zero_pad(data))
return ct[-16:] # 取最后 16 字节 = 最后一个密文块
对于两块消息 \(M_1 \| M_2\),CBC 加密产生 \(C_1, C_2\),ct[-16:] 取的就是 \(C_2\)。
直观理解 CBC 链式过程:
- 1 块消息:MAC = \(C_1\)
- 2 块消息:MAC = \(C_2\)
- N 块消息:MAC = \(C_N\)
永远取最后一个密文块作为 MAC 值。
Q5:什么叫做"两块消息",是如何拼接的?¶
"块"就是 AES 的固定大小:16 字节。 AES 一次只能处理 16 字节,所以消息被切成 16 字节一块。
单块消息
[h e l l o _ w o r l d \0 \0 \0 \0 \0]
|<------------- 16 字节 ------------->|
M₁
两块消息(伪造的 32 字节消息)
块1: [h e l l o _ w o r l d \0 \0 \0 \0 \0] ← pad("hello_world")
块2: [10 89 7b 80 7f 77 71 76 b9 74 86 4b ...] ← T₁ ⊕ pad("get_flag")
|| 就是首尾拼接,两块直接接在一起变成 32 字节。CBC 处理时:
第一块:C₁ = E_K(M₁ ⊕ IV) ← 加密第一块
第二块:C₂ = E_K(M₂ ⊕ C₁) ← 链式:上一块密文 XOR 后再加密
MAC = C₂ ← 取最后一块
每一块都把前一块的加密结果 XOR 进来再加密,这就是"链式"(Chain)的含义。
Q6:\(E_K\) 是什么操作?¶
\(E_K\) 是 AES 加密,不是 XOR。CBC 每一块有两步操作:
- XOR(异或) — 把明文和前一块密文混合
- \(E_K\)(AES 加密) — 用密钥 \(K\) 加密混合后的结果
第一块:
临时值 = M₁ ⊕ IV ← 第一步:XOR
C₁ = E_K(临时值) ← 第二步:AES 加密
第二块:
临时值 = M₂ ⊕ C₁ ← 第一步:XOR
C₂ = E_K(临时值) ← 第二步:AES 加密
\(E_K\) 就是 AES.new(KEY, ...) 做的事——接收 16 字节输入,通过密钥 \(K\) 做一系列置换和替换运算,输出 16 字节密文。没有密钥无法逆推。
但攻击不需要知道 \(E_K\) 内部做了什么,只需要利用链式结构:已知 \(T_1 = E_K(M_1)\),构造下一块输入时用 XOR 把 \(T_1\) 消掉就行。
Q7:MAC(M₁) 就是对 M₁ 进行 AES 加密吗?¶
在本题中是的——因为 \(M_1\) 只有一块且 IV 全零:
但 MAC 和 \(E_K\) 不是同一个概念:
- \(E_K\) — AES 加密一个 16 字节块(底层操作)
- MAC — 对整条消息(可能多块)做 CBC 链式加密后取最后一块(上层协议)
当消息有两块时:
这就不是简单的"一次 AES 加密"了,而是两次 \(E_K\) 加上 XOR。
伪造的数学原理:
\(T_1\) 与自身异或消去,剩下 \(\text{pad}(M_2)\) 送进 \(E_K\) 得到 \(T_2\),伪造成功。
可复制模板(空白版)¶
# [知识点] 知识点常见问题解答
> 本文档是 [对应题目 Writeup](./[writeup_file].md) 的补充知识点,以 Q&A 形式回答常见问题。
---
## Q1:为什么 [攻击/机制] 可以成立?
[一句话结论 + 最关键公式/推导]
## Q2:[关键参数] 在这里起什么作用?
[参数定义 + 对结果的实际影响 + 边界条件]
## Q3:[符号/函数] 具体表示什么?
[拆分步骤解释,避免只给符号]
## Q4:这类题最常见的误区是什么?
[误区 + 正确理解 + 一个反例(可选)]