哈希长度拓展攻击
哈希长度拓展攻击
1.什么是MAC?
MAC就是Message Authentication Codes,即消息验证码
🔐 MAC的核心作用
- 认证性(Authenticity):确保消息的发送者是声称的人,即消息确实是由合法的发送者生成。
- 完整性(Integrity):验证消息在传输过程中是否被篡改或损坏。
为了实现这些功能,发送者使用一个秘密密钥和MAC算法生成一个认证标签(MAC值),这个标签随消息一起发送。接收者也拥有同样的密钥,可以用相同算法重新生成MAC值。如果重新计算的值和收到的值一致,就可以确认消息没有被改动。
这个标签也叫做tag
MAC系统包含以下三个算法
- A key generation algorithm selects a key from the key space uniformly at random.
- A MAC generation algorithm efficiently returns a tag given the key and the message.
- A verifying algorithm efficiently verifies the authenticity of the message given the same key and the tag. That is, return accepted when the message and tag are not tampered with or forged, and otherwise return rejected.

发送者和接受者共享密钥k,然后发送者用k和message计算MAC值,然后将message和MAC一起发送,接受者用k再计算一次message的MAC,比较是否相等
MAC值可以用来验证文件(消息)是否改动,MAC值具有“雪崩效应“,哪怕1bit的变化,就会产生完全不同的MAC值
2.HMAC
In cryptography, an HMAC (sometimes expanded as either keyed-hash message authentication code or hash-based message authentication code) is a specific type of message authentication code (MAC) involving a cryptographic hash function and a secret cryptographic key.
总而言之HMAC就是基于hash的MAC
它的MAC generation algorithm是用HASH函数计算的
具体算法是
3.Merkle–Damgård结构
也称MD结构,耳熟能详的MD5算法
MD结构算法是
🛠️ 具体流程:
1. 消息填充(padding):
- 把原始消息
M
填充成多个 固定长度的块(通常是 512 bit) - 填充方式会在最后加上原始消息的长度信息
这里值得细说:
先分块,但是通常不会刚好完整分块,总会剩几个
举个例子,“HashInput",它的hex就是0x48617368496e707574
假设我们以64bits为一个block,那就是16个hex位
48 61 73 68 49 6e 70 75, 74 00 00 00 00 00 00 00
第二块只有一个74,假设我们后面全部填充成0
那这样会导致什么后果呢?
这导致0x48617368496e70757400,0x48617368496e7075740000,0x48617368496e707574000000
这一系列的'HashInput','HashInput'
得到的hash值都一样了,那不就碰撞了吗?我们还是希望hash函数尽量是一个单射
所以我们要填充一个bit位,也就是填充一个开头为1,后面都是0的,这样看1在哪里,就能区分出前缀一样的message了
也就是填充一个10000000(一个字节,也就是0x80)
48 61 73 68 49 6e 70 75, 74 80 00 00 00 00 00 00
MD结构还把长度信息填充到最后
48 61 73 68 49 6e 70 75, 74 80 00 00 00 00 00 09

2. 设置初始值(IV):
- 固定的哈希初始值(如 SHA-256 的 IV 是 8 个固定常数)
3. 迭代压缩函数 F:
- 每个块依次输入
F(prev_hash, block)
得到下一个中间状态 - 每一步的输出变成下一步的输入
4. 最后一个输出就是 hash 结果:
- 最后一轮的压缩输出就是最终的摘要值
4.Length-extension attack
In cryptography and computer security, a length extension attack is a type of attack where an attacker can use Hash(message1) and the length of message1 to calculate Hash(message1 ‖ message2) for an attacker-controlled message2, without needing to know the content of message1.
如果我们知道Hash(message1)和message1的长度,我们就可以计算Hash(message1||message2)而不用知道message1的内容
1.MD5拓展长度攻击

MD5分块填充后,每个块会分成小块和四个寄存器ABCD运算,每个寄存器里32bits,最后输出寄存器的值就是MD5值
然后前一个块计算完之后的ABCD会作为下一个块的初始ABCD
那么这就意味着,我们可以从Message1的md5值中恢复ABCD的值,然后修改MD5算法中ABCD的初始值,用新的参数计算message2的MD5
得到的就是MD5(message1||message2)了
但注意这里的message2的前缀是要有要求的,必须是message1原来的填充pad,不是随便添加message2
举个例子,message1是
message1 = b"this is a test"
填充完是
1 | b'this is atest\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x00\x00\x00\x00\x00\x00\x00' |
message2是b"extension"
如果是message1||message2
1 | b'this is a testextension\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb8\x00\x00\x00\x00\x00\x00\x00' |
如果直接添加message2的话,MD5只会在后面填充,这就不对了,所以我们要手动填充message2,
也就是令
1 | pad+message2 = \x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x00\x00\x00\x00\x00\x00\x00extension |
看看这样的填充,这样message1+pad +message2填充就是
1 | b'this is a test\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x00\x00\x00\x00\x00\x00\x00extension\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00H\x02\x00\x00\x00\x00\x00\x00' |
可以看到前面一部分一样了,这样当我们计算extension后面的md5的时候,ABCD就是计算完message1的ABCD
也就能计算出整个MD5了
我们可以使用魔改MD5
先提取ABCD
1 | def big_endian(hexstring): |
然后用这个ABCD去计算message2的hash值,但要注意,message2填充的长度是总长度,这样才能和message1+pad+message2的长度对上
exp:
1 | import struct |
2.SM3拓展长度攻击
SM3算法也是基于MD结构的,和MD5的填充基本一致,只有一处不同
那就是SM3算法填充长度是大端序,MD5是小端序
SM3 填充过程
对于消息 "abc" (长度3字节 = 24位):
- 原始消息:
0x61 0x62 0x63
- 添加 0x80:
0x61 0x62 0x63 0x80
- 填充0x00直到长度56字节:
[0x61 0x62 0x63 0x80] + [0x00]*52
- 添加64位长度(大端序):
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x18
MD5 填充过程
对于同样的消息 "abc":
- 原始消息:
0x61 0x62 0x63
- 添加 0x80:
0x61 0x62 0x63 0x80
- 填充0x00直到长度56字节:
[0x61 0x62 0x63 0x80] + [0x00]*52
- 添加64位长度(小端序):
0x18 0x00 0x00 0x00 0x00 0x00 0x00 0x00
SM3的压缩算法更加复杂,有8个寄存器,不过由于是大端序,倒是少了转换的步骤
我们大体了解一下就行,压缩函数是CF(IV,block)
IV是上一个block算出来的SM3值,然后初值IV是给定的,这都和MD5类似,那么照搬MD5的长度拓展攻击就行了
1 | def SM3_len_ex_ak(num_block,IV,plaintext): |
以熵密杯2024为例
1 | import binascii |
虽然说是HMAC吧,但是他这里实现的其实就是MAC
题目要求给定一个counter+hash
然后输出一个更大的counter+hash
我们只要在后面加上一个,这样一定是最大的
exp:
1 | def zero_fill(a,n): |