作者:Ange Albertini 与 Marc Stevens。
问:能否让一个文件获得任意的 MD2/MD4/MD5/MD6/SHA1/SHA2/SHA3 哈希,或者与另一个文件相同的哈希?
答:不能。
问:能否创建两个哈希相同的不同文件?
答:对于 MD5,在标准计算机上只需几秒钟。对于 SHA1,这可行,但对最终用户而言并不实用(复杂度:2^61.2 成本:1.1 万美元)。
问:能否通过附加数据让两个不同文件获得相同哈希?
答:对于 MD5,在标准计算机上只需几小时。对于 SHA1,这可行,但对最终用户而言并不实用(复杂度:2^63.4 成本:4.5 万美元)
问:这两个文件仍会保持有效吗?
答:一般来说是的,因为大多数文件格式都能容忍附加数据。但另一方面,文件签名很可能会失效。
问:能否制作两个内容任意但哈希相同的不同文件?
答:可以,借助特殊的文件结构可即时实现:
问:我可以为哪些格式获取即时 MD5 碰撞文件对?
答:JPG、PNG、GIF、GZIP、可移植可执行文件、MP4、JPEG2000、PDF、DOCX/PPTX/XSLX、EPUB、3MF、XPS。只需运行对应的脚本即可。
问:那 SHA1 呢?
答:对于 SHA1,PDF 中的 JPG 已计算并实现。
问:那已经支持 MD5 的格式(JPG、PNG……)换成 SHA1 呢?
答:它们很可能同样支持 SHA1,但相应的碰撞尚未被计算出来。
问:对于相似(但不同)的内容,计算会更快吗?
答:不会。任何微小的差异都需要一次完整的计算。
问:哪些格式没有这种捷径?
答:ELF、Mach-O、Java Class、TAR、ZIP(以及其他一些……)
问:这些格式仍能产生经典碰撞(几小时内)吗?
答:可以,只要容忍任意数量的附加数据(也就是说,ZIP 或 Class 很可能不行)。
问:你们提供碰撞示例吗?
答:提供。
目标是广泛探索现有的攻击方式——并在此过程中展示 MD5 有多么脆弱(任何 JPG、PNG、PDF、MP4、PE……都能即时碰撞)—— 同时深入探索常见文件格式,以确定 它们如何被现有或未来的攻击所利用。
事实上,相同的文件格式技巧可应用于多种哈希 (同样的 JPG 技巧曾用于 MD5、 恶意 SHA-1 和 SHA1), 只要碰撞遵循相同的字节模式即可。
本文档并非关于新攻击(最新的攻击于 2012 年被记载), 而是关于现有攻击的新利用形式。
已知攻击的当前状态:
让一个文件获得另一个文件的哈希或指定哈希:不可能
让两个不同文件获得相同 MD5:即时
让两个任意文件获得相同 MD5:几小时(72 小时.core)
让两个特定文件格式(PNG、JPG、PE……)的任意文件获得相同 MD5:即时
让两个不同文件获得相同 SHA1:6500 年.core
import crypt crypt.crypt("5dUD&66", "br") 'brokenOz4KxMc' crypt.crypt("O!>',%$", "br") 'brokenOz4KxMc'
# 攻击
MD5 和 SHA1 以 64 字节的块为单位工作。
如果两个内容 A 和 B 具有相同的哈希值,那么向两者追加相同的内容 C 将保持相同的哈希值。``` text
hash(A) = hash(B) -> hash(A + C) = hash(B + C)
碰撞攻击的原理是在块边界处插入若干个计算出的碰撞块, 其数量取决于文件中此前的数据。 这些碰撞块看起来非常随机,仅有细微差异 (每种攻击都遵循特定的模式), 它们会引入微小的差异,但最终在这些块之后使哈希值相同。
这些差异被滥用来构造具有特定属性的合法文件。
文件格式也是自上而下工作的,且大多数按字节级块(chunk)处理。
可以插入一些“注释”块,以使文件块对齐到块边界、 使特定结构与碰撞块差异对齐、 将碰撞块的其余随机性对文件解析器隐藏, 并隐藏原本有效的内容(从而使解析器看到的是另一份内容)。
这些“注释”块往往并非真正的官方注释: 它们只是被用作数据容器,解析器会忽略它们 (例如,ID 以小写字母开头的 PNG 块是辅助块(ancillary),而不是关键块(critical))。
大多数时候,碰撞块中的差异被用来修改某个注释块的长度,
该长度通常在此块数据之前声明:
在这个块的较短版本与较长版本之间的空隙中,
声明另一个注释块,以跳过一个文件的内容 A。
在这个文件内容 A 之后,再追加另一个文件内容 B。

由于文件格式通常会定义一个终止符,解析器会在遇到它后停止解析,
A 将终止解析,从而使追加的内容 B 被忽略。
因此通常至少需要两个注释——往往是三个:
文件格式的这些常见属性使上述攻击成为可能——它们通常不被视为弱点,但可以被检测出来或被规范化消除:
| 前缀 | = | 前缀 |
|---|---|---|
| 碰撞 A | ≠ | 碰撞 B |
| 后缀 | = | 后缀 |
两个文件几乎完全相同(它们的内容只有几个比特的差异)
利用方式:
将两个内容捆绑在一起,然后要么:
具有此结构的两个文件:
| 前缀 | = | 前缀 |
|---|---|---|
| 碰撞 A | ≠ | 碰撞 B |
| A | = | |
| = | B |
将显示 A 或 B 中的其中一个。
最终版本于 2009 年发布。
.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
.. .. .. X. .. .. .. .. .. .. .. .. .. .. .. ..
.. .. .. .. .. .. .. .. .. .. .. .. .. X. .X ..
.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
这些差异不在块的起始/结束位置附近,因此很难利用,因为你无法控制任何邻近的字节。 一个潜在的解决方案是暴力破解周围的字节——参见 PoCGTFO 14:10。
示例:
使用空前缀时:``` MD5: fe6c446ee3a831ee010f33ac9c1b602c SHA256: c5dd2ef7c74cd2e80a0fd16f1dd6955c626b59def888be734219d48da6b9dbdd