来源:PDF签名系列(1):PDF签名机制的漏洞分析 - 知乎
研究PDF文件的签名机制有一段时间了,刚开始学习的时候就看到有提到说,被签名的PDF内容的Range gap,会成为这个机制的漏洞,但是一直不能完全参透。直到昨天看到一篇文章的分析,才对这个问题有了一个更清晰的认识,所以做一个小小总结整理。
这里假设读者对PDF文件格式和数字签名的原理有一定了解。
首先,我们来看一下PDF文档里的Signature Dictionary:
我们知道,PDF文件是由一系列Objects组成的,通过objects的互相引用,组织成了一个文档的appearance. 对于一个签过名的文档来说,最重要的就是上图这样一个dictionary类型的object.
用一张图来简单说明一下PDF的签名机制。
上图是一个加入了数字签名的PDF文档内容。被签名的文档内容是整个文档,但是除去了Contents入口下面的具体值。简单来说,是对图中蓝色部分的所有内容做哈希,然后对哈希做签名,最后把签名值以及相关内容写进图中粉色部分。
好了,这样的方法,有什么漏洞呢?
先简单描绘一下PDF文档的签名流程:
1. 把文档序列化成一个字节串,中间预留出合适的空间存放签名值,此步骤称为preliminary serialization
2. 将字节串预留的空间去掉,生成新的字节串,称为signing serialization
3. 根据signing serilization生成签名数据块写入步骤一的preliminary serialization。签名完毕。
下面来详细描述下这个机制隐含的一个漏洞:
假设生成两个preliminary serilization字节串A1,A2;可以看到文档里包含两个catalog.
Note:一般来说,catalog是整个PDF的入口,所有用来显示PDF所用到的内容object都是从catalog开始逐级引用的。所以理论上来说,文档内容里允许有冗余的object,也就是说,此object虽然在文档中定义了,但是在显示的时候,并没有被引用到。
另外,我们可以知道,object的相对位置是可以改变的,只要修改了xreftable里对object的offset的描述,那么最终显示的效果是一样的。
我们可以看到,通过将A1和A2中的gap去掉,生成signing serialization B1和B2之后,B1和B2可以是完全一样的字节串。因此最终的签名值也会是一样的。
当签名值被嵌入回原来的A1和A2之后,生成了最终签名文件C1和C2.
可以看到,C1和C2是两个不同的文档了,虽然是相同的xreftable,相同的catalog的offset,但是C1索引到了catalog1,C2索引到了catalog2,所以C1和C2渲染出来的文档可以是totally不同的内容。但是签名值确实对两个文档都是合法的签名值,这有悖于数字签名的原则。
思考下这个问题的根源来自PDF文档允许冗余的object存在,所以这个漏洞能否存在取决于是否认为这是PDF符合语法规范的。
参考文档:
Collisions in PDF Signatures