文章总结: 本文详细解析SM3哈希算法的长度扩展攻击漏洞,攻击者仅凭截获的hash值和密钥长度即可伪造消息认证。通过图解展示攻击者如何利用SM3的Merkle-Damgård结构特性,在不知道密钥的情况下成功添加&admin=true权限提升字段。文档给出具体攻击步骤演示,并推荐使用HMAC-SM3、密钥后置等五种防御方案,强调避免直接使用裸哈希进行消息认证。 综合评分: 85 文章分类: 漏洞分析,WEB安全,应用安全,解决方案,安全开发
你拿到一个 SM3 Hash,就等于拿到了认证权 —— 图解长度扩展攻击
原创
利刃信安 利刃信安
利刃信安
2026年6月23日 14:30 北京
在小说阅读器读本章
去阅读
你拿到一个 SM3 Hash,就等于拿到了认证权 —— 图解长度扩展攻击
摘要: SM3 是我国商用密码体系中的核心杂凑算法,广泛应用于数字认证、消息认证等场景。然而,许多人并不知道:当你用 SM3(secret || message) 做消息认证时,拿到 hash 值的人,即使不知道密钥,也能伪造任意消息的合法认证。
一、问题场景:一个看似安全的认证方案
某服务端使用 SM3 做消息认证,token 的计算方式为:
token = SM3(secret_key || message)
其中 secret_key 是 16 字节随机密钥,攻击者不知道密钥内容,但已知密钥长度为 16 字节。
攻击者截获了一条合法请求:
| 字段 | 值 |
| — | — |
| message | "user=alice&role=user" (20 字节) |
| token | 984c2580ecb81f4945a4fcc1be399115558627dab8062fd519c43cd3819fef33 |
攻击者想附加 &admin=true 来提升权限。他能在不知道密钥的情况下,伪造出一个合法的认证请求吗?
答案是:能。 这就是 SM3 长度扩展攻击。
二、原理:为什么拿到 Hash 就等于拿到认证权?
要理解这个漏洞,先看 SM3 的内部结构。
SM3 采用 Merkle-Damgård 结构——消息被分为 512-bit(64 字节)的分组,逐块压缩:
SM3(M) 的计算流程:
IV(256bit) → [CF] → V₁ → [CF] → V₂ → ... → Vₙ = SM3(M)
↑M₁ ↑M₂ ↑Mₙ
压缩函数 CF 的输出(256-bit)直接作为下一块的 IV,最终输出的 hash 值就是最后一个 CF 的输出——换句话说,hash 值就是压缩机的“内部状态”本身。
这导致了一个致命后果:拿到 H = SM3(secret || message) 的人,可以把它当新的 IV,继续压缩自己构造的附加数据块:
攻击者伪造:
H = SM3(M) → [CF] → V' = SM3(M || padding || evil_extension)
↑evil_extension (含新填充)
攻击者根本不需要知道 secret 的内容——只需要 H 和密钥的长度。 这就是长度扩展攻击(Length Extension Attack)的核心。
三、答案:一步步计算
设
secret_key = 0123456789ABCDEF0123456789ABCDEF(实际攻击不需要密钥,仅用于验证)。
1. padding₁ 的计算
SM3 填充规则 (GB/T 32905-2016 §5.1):
| 步骤 | 操作 | 结果 |
| — | — | — |
| 原始长度 | secret(16B) + message(20B) | 36 字节 = 288 比特 |
| 追加 0x80 | 36 + 1 | 37 字节 |
| 补 0x00 | 补至 56 mod 64,即 56 − 37 = 19 个 | 56 字节 |
| 填长度 | 8 字节大端序,288 = 0x0000000000000120 | 64 字节 |
padding₁ (28 字节, hex):
80000000000000000000000000000000000000000000000000000120
验证:36 + 28 = 64 字节,恰好一个 512-bit 分组。
2. 伪造 token 的计算
攻击者以 H = 984c2580...819fef33 作为新的 IV,对 "&admin=true" 进行 SM3 压缩:
| 步骤 | 操作 |
| — | — |
| ① 拆分 H | V₀ = 0x984C2580, V₁ = 0xECB81F49, V₂ = 0x45A4FCC1, V₃ = 0xBE399115, V₄ = 0x558627DA, V₅ = 0xB8062FD5, V₆ = 0x19C43CD3, V₇ = 0x819FEF33 |
| ② 新总长 | 36 + 28 + 11 = 75 字节 = 600 比特 |
| ③ padding₂ | 0x80 + 44×0x00 + 0x0000000000000258 (53 字节) |
| ④ 扩展块 | `”&admin=true”(11B) |
| ⑤ CF 压缩 | 以 H 为 IV 执行 64 轮 CF 压缩 |
伪造 token:
ef5bd5887e59cb85a6e870ee0d4a2ea5e73150a9b1a5108c5e58b970a6bc411f
3. 构造合法认证请求
攻击者发送:
message = "user=alice&role=user" ← 原消息
+ padding₁_bytes ← 28 字节原始二进制填充
+ "&admin=true" ← 扩展消息
token = ef5bd5887e59cb85a6e870ee0d4a2ea5e73150a9b1a5108c5e58b970a6bc411f
服务端计算 SM3(secret || message') 时,完整数据为:
secret(16B) || message(20B) || padding₁(28B) || "&admin=true"(11B) → 75 字节
第一块 64B → CF 输出 H(等于攻击者截获的 token),第二块以 H 为 IV 压缩扩展块 → 输出刚好等于攻击者伪造的 token,验证通过。
四、详解:为什么攻击可行?
直观类比: 把 SM3 看作一台消息搅拌机——每次投入 64 字节原料,搅拌 64 轮后输出 32 字节浓缩液。最终浓缩液就是搅拌机的内部状态。攻击者拿到浓缩液后,直接倒入自己的原料继续搅拌,产出新浓缩液——无需知道之前投入了什么原料。
攻击者需要什么?
| 需要 | 不需要 |
| — | — |
| token (截获的 hash 值) | 密钥内容 |
| 密钥长度(如 16 字节) | 原始消息内容(仅长度) |
| SM3 算法实现 | 破解密钥 |
正常场景 vs 攻击场景
| | 正常场景 | 攻击场景 |
| — | — | — |
| 输入 | secret || "user=alice&role=user" | secret || "user=alice&role=user" || padding₁ || "&admin=true" |
| 权限 | 普通用户 | 管理员 |
| token | 合法截获 | 伪造 |
五、防御方案
| 方案 | 原理 | 推荐度 |
| — | — | — |
| HMAC-SM3 | HMAC(K, M) = SM3(K⊕opad || SM3(K⊕ipad || M)) ,外层 hash 阻断了从输出恢复内部状态的路径 | ★★★★★ |
| 密钥放末尾 | token = SM3(message || secret) ,攻击者无法构造末尾未知的扩展 | ★★★ |
| 截断输出 | 只取 SM3 前 128 bit,攻击者无法获得完整 256-bit 内部状态 | ★★★ |
| KMAC | NIST SP 800-185 标准,天然免疫长度扩展攻击 | ★★★★ |
结论:不要用裸 hash 做消息认证,使用 HMAC。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:利刃信安 利刃信安 利刃信安《你拿到一个 SM3 Hash,就等于拿到了认证权 —— 图解长度扩展攻击》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论