所以我一直在试图了解什么是 SHA-1 碰撞以及它的含义,但有一件事我不太了解。
他们是如何设法不仅找到具有相同哈希值的两个文档,而且还找到一个在视觉上与原始文档几乎完全相同的文档?所有的文字都是一样的,唯一的区别是背景颜色。
我的理解是,其中真正困难的部分是找到哈希匹配的任何两个文档,并且需要 10 万美元的计算能力才能做到这一点。
如果您只受到看起来相似的文档冲突的限制,那不是更难吗?他们是否提前计划了这件事,还是只是巧合?
所以我一直在试图了解什么是 SHA-1 碰撞以及它的含义,但有一件事我不太了解。
他们是如何设法不仅找到具有相同哈希值的两个文档,而且还找到一个在视觉上与原始文档几乎完全相同的文档?所有的文字都是一样的,唯一的区别是背景颜色。
我的理解是,其中真正困难的部分是找到哈希匹配的任何两个文档,并且需要 10 万美元的计算能力才能做到这一点。
如果您只受到看起来相似的文档冲突的限制,那不是更难吗?他们是否提前计划了这件事,还是只是巧合?
这种攻击背后的主要思想依赖于差分密码分析。您有两个输入 t 0和 t' 0,相差 Δ 0。一旦这两个输入通过函数f(在 SHA-1 的情况下为压缩函数),你就会得到一个差异 Δ 1等等(见下图)......
这里的目标是找到输入 Δ 0的差异,这样经过一些迭代后,您会得到 Δ 2 = 0,换句话说,没有差异。
由于 SHA-1 的 Merkle-Damgård 构造,可以选择更改迭代之间的差异,以改进差异以满足他的需要。在 SHAttered Attack 的情况下,他们选择了一个初始前缀 (P),然后在接下来的块中,他们引入了一个差异 (M 1 (1) ,M 1 (2) ) 并将其移除 (M 2 (1)和M 2 (2) )。此时他们已经发生了碰撞。他们只需要继续使用相同的后续块 (S),从而导致整个输入发生冲突。
SHA-1(P|| M 1 (1) || M 2 (1) || S)= SHA-1(P||M 1 (2) || M 2 (2) || S)
M 1 (1)、M 1 (1)、M 2 (1)和M 2 (2)的取值如下:
因此,困难在于找到正确的 M i,它满足所选的前缀标准 P 并在之后很好地取消它们自己。这种情况发生的可能性非常小,因此需要大量调用 SHA-1 压缩函数 (2 63.1 )。
对于提供的 PDF,他们还提供了一个很好的信息图,解释了 PDF 中的差异所在:
使用精心构造的 PDF 的前 192 个字节进行的半蛮力(2 63 SHA-1 操作)选择前缀攻击。计算和加密方面令人印象深刻,但利用这种攻击所需的诡计同样有趣。
技术解释可以在 Biv 的回答中的cryptography.SE上找到(他在此部分重复了)。对此的英文简化是 SHA-1 已有十多年的已知问题(此处为 PDF 论文),这可用于选择前缀冲突攻击。PDF 中的标头和 X.509 证书的前导结构是对此有用的目标(这是证书上的 SHA-1 RSA 签名被认为不安全的部分原因)。
对于给定的输入,2005 年的攻击允许对一对额外的输入块进行比暴力计算更好的计算,这些输入块相互“反转”,如果将它们附加到输入,则对 SHA-1 输出没有影响。仅此一项并不那么令人兴奋,但它也允许计算一对这样的块,这就是这次攻击中使用的。一旦您可以以这种方式操作 SHA-1,您就可以继续附加更多数据以生成具有 SHA-1 冲突的真实文档,尽管差异仅限于两个相邻的 64 字节块。
其中P是前缀(PDF 标题),M (1)和 M (2)是操作 SHA-1 状态的块对,S是后缀(文档有效负载)。需要明确的是:前缀和后缀必须相同,只有魔术块可能不同。
这部分解释了提供的检测器如何工作以及为什么它只需要一个文档。它对每个 SHA-1 块输入大小(64 字节)的文档长度执行增量 SHA-1,并检查重复/可疑的 SHA-1 内部状态(有关详细信息,请参阅他们的论文)。您可以部分看到 (from bash) 发生了什么:
for ss in {0..512..64};do head -c $ss shattered-1.pdf | sha1sum -; done
for ss in {0..512..64};do head -c $ss shattered-2.pdf | sha1sum -; done
现在发生的事情是,它已被证明在计算上是实用的(并且可以在 GPU 上运行),并且通过操纵具有可见结果的文档也会产生现实世界的后果(老实说,有些攻击不会)。
有一个棘手的小问题被掩盖了,这些魔法块不受攻击者控制,他必须为定义的输入前缀计算它们(最多 2 63次操作),并将结果作为结果(几乎可以肯定会赢) '不是红色或蓝色 JPEG)。
可以做的是采用一种允许内容镇流器的格式(PDF 很好,就像任何在末尾忽略额外内容或典型的文字处理器文档一样),并且只需有两个版本,其中隐藏了备用的魔法块对,但无法区分的内容呈现。不是那么令人兴奋。
那么他们是怎么做到的呢?在 JPEG 中。
首先,PDF 格式图有点误导,JPEG 并没有明确定义的标题或数据部分,它们是一个段流,每个段都标记有一个类型和(大部分)有一个 2 字节长度的字段。
诀窍是 JPEG 在开头有两个仔细对齐的注释段:
第一条注释(带有强制性的俏皮话)对齐第二条注释,以便长度 LSB 是魔术块的第一个字节(64 字节 SHA-1 块对齐)。在 PDF 1 文档中,该字节为 0x73(上图),第二个为 0x7f,这使得该注释段更长。在(相对)偏移量 0x0173 处是另一个长度为 0x00fc 的注释,它在偏移量 0x017f 处消耗 JPEG 数据的开头。PDF 2 中的 JPEG 图像数据从 0x017f 开始。在文档 1 的图像中,有一组 (12) 仔细放置和调整大小的注释,用于在文档 2 的图像中处理的数据段上反弹。这些主要是解释图像差异的量化表和霍夫曼表(I想想,那里有点乱)。
所以攻击中的前缀是PDF头+内容前导+JPEG流的开始。然后是魔法块。后缀是剩余的 JPEG 流 + PDF 的结束部分。
X.509 DER 表单证书使用 ASN.1 编码,它支持相当任意的元数据字段,并且还使用与 JPEG 类似的长度编码字段类型,因此相同类型的攻击可以在那里工作(只要 ASN.1 嵌套不t 绊倒你),但我想不出一种有用的方法来利用(比如MD5),因为这是一种已知的前缀攻击,在证书签署之前你不会知道前缀。
另一个有趣的观察是,这些 PDF 的前 320 字节可以用作任何其他任意 PDF 的前缀,仅受前 320 字节施加的约束,即必须是 PDFv1.3 并以 JPEG 开头(或更准确地说,是与 PDF 过滤器一起使用的 Xobject 类型图像DCTDecode,甚至图像尺寸和大小都是可变的,因为它们出现在 PDF 序列化中的图像数据之后)。那个精灵现在已经从瓶子里拿出来了:https ://github.com/nneonneo/sha1collider
他们提前计划好了,有类似的文件与它无关。
他们同时控制 PDF,使其工作的主要部分隐藏在 pdf 文件格式中。看看:https ://shattered.it/static/pdf_format.png
两个 SHAttered PDF 显示的不同图像完全是由于位于偏移 192 处的单个字节,即 shattered-1.pdf 中的“\x73”和 shattered-2.pdf 中的“\x7f”。该字节构成双字节大端整数的第二部分,它定义了 JPEG 图像流中 COM(“注释”)段的长度。COM 段中包含的字节不用于任何用途,因此被 JPEG 解析器跳过。
在 shattered-1.pdf 中,此 COM 段长度被读取为“\x01\x73”(371 字节),而在 shattered-2.pdf 中,它被读取为“\x01\x7f”(383 字节)。该 COM 段长度的差异允许构建包含两组图像数据的相同后缀,这些图像数据的排列方式使得在一个文件中解析为图像数据的内容将在另一个文件中解析为 COM 段。结果是文件显示不同的图像。
下图比较了 shattered-1.pdf 和 shattered-2.pdf 的前 832 个字节:
魔术块(两个不同的二进制 blob,当附加到相同的前缀时,会产生相同的 SHA-1 哈希)用蓝色标出。红色字体的十六进制数字是两个 PDF 之间的唯一区别。COM 段标记及其长度字节以黄色突出显示,COM 段中包含的其余字节以灰色阴影显示(请注意,除了魔术块的第一个字节之外的所有字节都包含在 COM 段中,并且不用于任何用途)。图像数据以绿色突出显示。文件开头未突出显示的字节是样板 PDF 标题材料。
事实上,这两个 PDF 文件都完全包含这两个图像。这很容易演示(下面的代码引用了 shattered-1.pdf,但如果替换 shattered-2.pdf,结果将是相同的)。
首先在 PDF 中定位 JPEG APP0 和 EOI 段标记(分别为“\xff\xe0”和“\xff\xd9”)的字节偏移量:
$ grep -oba "$(printf '\xff\xe0')" shattered-1.pdf
574:��
187049:��
$ grep -oba "$(printf '\xff\xd9')" shattered-1.pdf
187043:��
421532:��
由于恰好有两个 APP0 段标记(从偏移量 574 和 187049 开始)和两个 EOI 标记(从偏移量 187043 和 421532 开始),我们可以推断出一组图像数据位于字节范围 574 到 187045(186,471 字节)和另一个在字节范围 187049 到 421534(234,485 字节)中。
创建两个仅包含 SOI 标头 (\xff\xd8) 的空 JPEG 文件,然后在每个文件中附加 PDF 中的两个字节范围之一:
$ printf '\xff\xd8' | tee a.jpeg >> b.jpeg
$ dd if=shattered-1.pdf of=a.jpeg oflag=append conv=notrunc bs=1 skip=574 count=186471
$ dd if=shattered-1.pdf of=b.jpeg oflag=append conv=notrunc bs=1 skip=187049 count=234485
这样就产生了两个JPEG,a.jpeg和b.jpeg,而这两个JPEG确实是shattered-1.pdf和shattered-2.pdf显示的两个不同的图像。
