文章总结: 本文详细解析SM3哈希算法基于Merkle-Damgård结构的自定义IV机制及其在长度扩展攻击中的应用。通过具体攻击场景演示,说明攻击者仅需知晓密钥长度和原始token即可伪造消息认证,并推荐使用HMAC-SM3作为防御方案。文档强调所有基于Merkle-Damgård的哈希函数都存在此特性,切勿使用裸哈希进行消息认证。 综合评分: 85 文章分类: 漏洞分析,WEB安全,安全开发,密码学,应用安全
SM3 自定义IV计算过程:从哈希算法到长度扩展攻击
原创
利刃信安 利刃信安
利刃信安
2026年6月23日 18:00 北京
在小说阅读器读本章
去阅读
SM3 自定义IV计算过程:从哈希算法到长度扩展攻击
摘要:SM3 密码杂凑算法(GB/T 32905-2016)采用 Merkle-Damgård 迭代结构,其初始向量 IV 是固定的 8 个 32 比特常量。但如果将 IV 替换为任意 256 比特值会发生什么?
一、SM3 的迭代结构:Merkle-Damgård
SM3 的核心结构是 Merkle-Damgård 迭代压缩。
消息 M → [填充至 512 比特的倍数] → B₁ || B₂ || ... || Bₙ
V₀ = IV (标准常量)
V₁ = CF(V₀, B₁)
V₂ = CF(V₁, B₂)
...
Vₙ = CF(Vₙ₋₁, Bₙ) = SM3(M)
每一步的 CF 压缩函数接受两个输入:
- • Vᵢ:当前 256 比特链接变量(第一轮用标准 IV)
- • Bᵢ:当前 512 比特消息分组
输出是下一轮的 Vᵢ₊₁。最后一轮的输出就是 SM3 的杂凑值(token)。
关键洞察:每一轮的 CF 输出完全由当前 V 和当前分组 B 决定。这意味着——如果攻击者能控制 V,他就能控制后续所有压缩结果。
二、标准 IV 与自定义 IV
标准 IV 由 GB/T 32905-2016 §5.3.1 定义,是 8 个固定 32 比特字:
| 寄存器 | 值 |
| — | — |
| V₀[0] | 0x7380166F |
| V₀[1] | 0x4914B2B9 |
| V₀[2] | 0x172442D7 |
| V₀[3] | 0xDA8A0600 |
| V₀[4] | 0xA96F30BC |
| V₀[5] | 0x163138AA |
| V₀[6] | 0xE38DEE4D |
| V₀[7] | 0xB0FB0E4E |
自定义 IV 就是将这 8 个字替换为任意用户指定的值。从数学上看,这完全合法——CF 压缩函数不关心 V 的来源,只关心它的数值。于是:
SM3_custom(M, IV_custom) = 以 IV_custom 为起点的 Merkle-Damgård 迭代结果
这个「合法但非常规」的操作,正是长度扩展攻击的核心武器。
三、长度扩展攻击:自定义 IV 的实战应用
3.1 攻击场景
某服务端使用「裸哈希拼接」做消息认证:
token = SM3(secret || message)
其中 secret 是 16 字节密钥(攻击者不知道),message 是用户可控的明文。攻击者截获到:
| 参数 | 值 |
| — | — |
| message | user=alice&role=user (20 字节) |
| token | 984C2580ECB81F4945A4FCC1BE399115558627DAB8062FD519C43CD3819FEF33 |
攻击者的目标:在不知道密钥的情况下,构造 message' 和 token',使得服务端验证通过。
3.2 数学推导
设原始消息总长 L = 16 + 20 = 36 字节。SM3 对它的填充:
M = secret || message
padding₁ = 0x80 || 0x00... || 0x0000000000000120 (共 28 字节)
B₁ = M || padding₁ = 64 字节 (恰好 1 个 512 比特分组)
于是:
token = CF(IV, B₁) ← 标准 IV 下的单分组压缩
攻击者想附加 &admin=true(11 字节)。如果他能以 token 为 IV,对扩展块再执行一次 CF 压缩:
forged = CF(token, extension || padding₂)
其中 padding₂ 基于新总长 = 36 + 28 + 11 = 75 字节计算,长度字段为 75 × 8 = 600 = 0x0258:
padding₂ = 0x80 || 0x00 × 44 || 0x0000000000000258 (共 53 字节)
extension || padding₂ = 11 + 53 = 64 字节 (恰好 1 个分组)
3.3 为什么等价于合法 token?
根据 Merkle-Damgård 结构:
SM3(secret || message || padding₁ || extension)
= CF(CF(IV, B₁), extension || padding₂)
= CF(token, extension || padding₂)
= forged
攻击者不需要知道密钥! 只需要两个条件:
- • 知道密钥的长度(16 字节)——用于计算 padding₁ 和 padding₂
- • 截获的 token ——用作自定义 IV
四、计算示例演示
以下使用 sm3_custom_iv_gui.py 工具对上述场景进行完整计算。
步骤 1:输入参数
密钥长度: 16 字节 (未知, 仅需长度)
原始消息: user=alice&role=user (20 字节)
原始总长度: 16 + 20 = 36 字节
已知 token: 984C2580ECB81F4945A4FCC1BE399115558627DAB8062FD519C43CD3819FEF33
扩展消息: &admin=true (11 字节)
步骤 2:计算 padding₁
SM3 对 36 字节消息的填充:
36 × 8 = 288 比特
追加 0x80 → 37 字节
补零至 56 (mod 64): 37 + 19 = 56 字节
长度字段: 288 = 0x0120 (大端序, 8 字节)
padding₁ = 80000000000000000000000000000000000000000000000000000120
验证:36 + 28 = 64 ≡ 0 (mod 64) ✓
步骤 3:构造扩展块
新总长度: 36 + 28 + 11 = 75 字节
75 × 8 = 600 比特 = 0x0258
对 75 字节消息的填充:
追加 0x80 → 76,补零至 56: (56 - 76%64) % 64 = 44
padding₂ = 0x80 + 0x00×44 + 0x0000000000000258 (共 53 字节)
扩展块 = extension(11B) || padding₂(53B) = 64 字节 = 1 个分组
步骤 4:以 token 为 IV 执行 CF 压缩
将 token 拆分为 8 个 32 比特字作为新 IV:
V₀ = [984C2580, ECB81F49, 45A4FCC1, BE399115,
558627DA, B8062FD5, 19C43CD3, 819FEF33]
对 64 字节扩展块执行标准 CF 压缩(消息扩展 → 64 轮迭代 → 异或)。输出:
V₁ = [EF5BD588, 7E59CB85, A6E870EE, 0D4A2EA5,
E73150A9, B1A5108C, 5E58B970, A6BC411F]
步骤 5:攻击结果
伪造 token = EF5BD5887E59CB85A6E870EE0D4A2EA5E73150A9B1A5108C5E58B970A6BC411F
伪造消息 = known_msg || padding₁ || extension
= 757365723D616C69636526726F6C653D75736572 ← user=alice&role=user
80000000000000000000000000000000000000000000000000000120 ← padding₁
261646D696E3D74727565 ← &admin=true
攻击者发送:
message' = 757365723D...00000120261646D696E3D74727565
token' = EF5BD5887E59CB85A6E870EE0D4A2EA5E73150A9B1A5108C5E58B970A6BC411F
服务端计算 SM3(secret || message') → 结果等于 token' → 验证通过! ✓
五、攻击的条件与局限
攻击者需要
| 条件 | 说明 | | — | — | | 密钥长度 | 用于计算 padding₁ 的位置和 padding₂ 的长度字段 | | 原始消息内容 | 截获的明文,用于构造完整的 padding₁ | | 合法 token | 截获的 SM3(secret || message) 值 |
攻击者不需要
- • 密钥内容 —— 整个攻击不涉及密钥
- • 离线字典攻击 —— 不需要暴力破解
局限性
- • 只能追加数据到消息末尾,不能修改前面的内容
- • 追加的数据前面会夹杂
padding₁的不可打印字节 - • 如果服务器对消息格式做严格校验(如 URL 解码),可能失败
六、防御方案
| 方案 | 原理 | 推荐度 |
| — | — | — |
| HMAC-SM3 | HMAC(K, M) = SM3(opad ⊕ K || SM3(ipad ⊕ K || M)) ,两次杂凑打破可扩展性 | ★★★★★ |
| SM3(K || M || K) | 密钥同时放在首尾,攻击者无法控制尾部 | ★★★ |
| SM3(SM3(K || M)) | 外层 SM3 只接收固定长度输入,无法利用内部状态 | ★★★★ |
| SM3 带盐值 | 每次通信使用不同随机值 | ★★★ |
最佳实践:使用 HMAC-SM3(GM/T 0046 标准),而非裸哈希拼接。
七、概念类比
想象一台搅拌机(CF 压缩函数):
- • 标准模式:你放入 8 种固定原料(标准 IV)+ 64 份食材 → 搅拌 → 得到 8 种混合糊
- • 自定义 IV 模式:你把上一步的混合糊直接倒入搅拌机,再加 64 份新食材 → 再次搅拌
攻击者的核心操作就是截获别人搅拌好的糊(token),然后把它当做初始原料倒入自己的搅拌机,再加上自己选的食材(extension + padding₂),就能假装整个料理都是同一个人做的。
八、参考标准
| 标准 | 内容 | | — | — | | GB/T 32905-2016 | SM3 密码杂凑算法 | | GB/T 32918.2-2016 | SM2 椭圆曲线公钥密码算法(数字签名) | | GM/T 0046 | HMAC-SM3 消息认证码 | | NIST SP 800-185 | SHA-3 派生函数(含 KMAC,HMAC 的现代替代) | | RFC 2104 | HMAC: Keyed-Hashing for Message Authentication |
结语:SM3 的 Merkle-Damgård 结构赋予了「自定义 IV」完全合法的数学地位。长度扩展攻击不是 SM3 的漏洞,而是所有基于 Merkle-Damgård 结构的哈希函数(MD5、SHA-1、SHA-256、SM3)的固有特性。理解了自定义 IV 的计算过程,就理解了为什么 「绝不要用裸哈希做消息认证」。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:利刃信安 利刃信安 利刃信安《SM3 自定义IV计算过程:从哈希算法到长度扩展攻击》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论