PDF 是一种用于表示文档的文件格式。一个 PDF 由多个数据对象组成
简单基本对象 整数、数值、布尔值、Null
复杂对象
| 格式 | 名称 |
|---|---|
| [.*] | 数组 |
| (.*) | 字符串 |
| <<.*>> | 字典 |
| <.*> | 十六进制字符串 |
| /.* | 名称 |
| stream.*endstream | 流 |
这些对象定义了 PDF 的外观及其内容。PDF 中的结构以两种对象类型存在 - 直接对象(Direct)和间接对象(Indirect)。间接对象以对象编号(Object number)和生成编号(Generation number)开头,后跟实际对象。间接对象可以在其他对象中直接以 n m R 的形式引用,其中 n 和 m 分别是对象编号和生成编号。
字典对象是文档的基本构建块。有一些通用字典对象是构成页面或文档本身所必需的。最重要的是 Root 字典,它定义了指向所有其他 Pages、Metadata、Names 等的链接,其中每一项都可以是另一个对象。
流对象包含最多的二进制数据,例如字体、图片或压缩/加密数据。
PDF 文档可以被加密,以保护其内容免遭未经授权的访问。加密
适用于 PDF 文件中所有的字符串和流,但 Encrypt 字典本身等少数例外除外。加密主要适用于流对象。加密不适用于其他对象类型,例如整数和布尔值,它们主要用于传达文档的结构信息,而非内容。
与加密相关的信息应存储在文档的加密字典中,该字典应为文档 trailer 字典中 "Encrypt" 条目的值。
CPDF_Parser::StartParse 为 PDF 中已加密的间接对象设置 m_pCryptoHandler。当 CPDF_Parser::ReleaseEncryptHandler 完成时,m_pCryptoHandler 应该被置空。但实际上 CPDF_Parser::ReleaseEncryptHandler 并未移除 CPDF_Parser 中对 CryptoHandler 的引用,导致其成为悬空指针。
之后,当解析器开始解析 Root 字典中引用的对象时,会调用 m_pCryptoHandler+8 来解密数据。
类似漏洞已在 pdfium 的提交 741c362fb75fd8acd2ed2059c6e3e716a63a7ac8 中修复。参见 https://bugs.chromium.org/p/chromium/issues/detail?id=726503
PDF 允许在文档中嵌入 JS,如果将其写入 Catalog 类型字典的 OpenAction 中,则可以自动执行。一旦我们能够执行 JS,就可以在进程空间中堆喷对象,从而到达一个可预测的地址,我们将在该地址写入 ROP 链。
当 Foxit Reader 中签署 PDF 文档时,它会使用安装目录中的 plugins\jrsys\x86\jrsysMSCryptoDll.dll 来读取签名信息,该 DLL 会将 jrsysCryptoDll.dll 加载到静态地址 0x10000000 上。此 DLL 导入了 VirtualAlloc,这使得执行 payload 更加容易。所附的漏洞利用程序使用堆喷来获得可预测的内存布局,并使用 ROP 链来分配 RWX 页、复制并执行 payload。
此漏洞利用程序已在 MS Windows 7 Enterprise Build 7601 SP1 x86 上运行的 Foxit Reader 9.0.1.1049 x86 上测试通过。该漏洞利用程序要求堆处于特定状态,如果利用失败,请重试。请参考视频演示。此漏洞同样存在于 Android 版 Foxit PDF Reader 和 Converter 中。
bitcoins.pdf 是一个精心构造的 PDF,它会重新分配已释放的内存并触发核心漏洞。如果要在调试器中重现崩溃,请为 FoxitReader.exe 启用页面堆(Page Heaps),然后打开 bitcoins.pdf。
此崩溃由 Cloudfuzz 发现——这是由 Payatu 开发的一个模糊测试平台。进一步的分析和漏洞利用由 Sudhakar 完成。