就最近的pdf相关漏洞谈谈如何修改PDF文档

简述

最近几个月,爆出来了几个 pdf 相关漏洞:

  • CVE-2024-4367: PDF.js 解析 pdf 时存在缺陷,可以执行任意的 javascript 脚本。
  • Foxit PDF 机制缺陷,windows平台下可以执行远程命令(有弹窗提醒)。

而这两个漏洞给出的 poc 都是简单的字符串模版替换,比如:

但是呢,有时会有一些特殊需求,我需要将正常的 pdf 文件转为这种“恶意”文档,这就比较麻烦了,我们需要了解 PDF 文件格式,然后才能在正常的 PDF 文件中加入 exp。

PDF 文件格式

先来看看 PDF 文件格式,我们以这个 poc_generalized_CVE-2024-4367 中的 PDF 文档为例。

其结构总体来说还是比较清晰的,pdf 文件解析的入口便是这个 trailer directorystartxref

可以参考 pypdf2 pdf-format 中解释的,trailer中的 /Root 便是文档的目录结构根节点,对应到图中的序号为 1,也就是 Body Section 中的: 1 0 obj这个条目。

Body Section 中的这些条目是怎么定位的呢?关键在于这个 startxref

startxref 指定了 xref 表的偏移:

这个 xref 表中定义了 pdf 文档节点的数量以及每个节点的偏移(具体的每一项是什么意思可以参考文档,这里不赘述)。

这些树状节点便是 pdf 文档的 "body" 了:

其格式为: (注:pdf 结构中空格与回车换行基本等价,都是分隔符)

counter generationnumber << the_object >> endobj

object body 的格式其实就是“键值对”,但是不同于我们熟知的 {"key": "value"} 这种形式,pdf 中的形式为 /Key /ValueKey部分一般是 Name ObjectValue 可以是:

  • Boolean Object
  • Numberic Object
  • String Object
  • Name Object
  • Array Object
  • Dictornary Object
  • Stream Object
  • Null Object
  • Indirect Object

具体可参考 adobe 对 PDF 文件格式的定义: PDF_ISO_32000-2.pdf 中的 7.3节。

手动修改一个正常的 PDF 文档

CVE-2024-4367 手动改起来比较麻烦,这里以 Foxit 的 pdf_exploit 为例,

这个“漏洞”所利用的其实就是 PDF 中的正常文档的格式:

参考 PDF_ISO_32000-2 中的 7.7.2 节 "Document catalog dictionary":

也就是说,我们只要在 /Catalog 节点中,添加 /OpenAction 即可。

随便找个 PDF 文件,尝试修改它。这里简单创建一个 PDF:

trailer dictionary 中可以看到 Root 节点编号为 9,这个 Root 节点的 Type 便是 Catalog 。我们只要在这个节点里加上 /OpenAction 即可。

为了方便看,简单格式化了一下这部分。

改完这里后,其实原始文档中有很多偏移都需要进行相应调整,如下图所示,xref自身的偏移也错误了、xref 表中的第9条以后的条目的偏移也错误了。

不过,大部分常用的pdf文件解析器并没有完全的依靠偏移地址,因为即便没有进行偏移修复,这个 pdf 文档还是可以被解析。这也就可以解释了,为啥很多 poc 都是直接用的模版变量,没有去修正偏移

不过呢,这种 pdf 毕竟不是完全“合格”的,在遇到一些只使用偏移来定位节点的库去解析时,就会发现问题,比如说: https://github.com/ledongthuc/pdf.git

方便的将exp集成到指定的PDF

其实如上面写的,忽略偏移直接修改相关节点,虽然会导致文件有点“不合格”,但总的来说还是能用的。foxit 这个例子还比较简单,不需要多加什么节点,只要在 /Catalog 中添加 /OpenAction属性就行了。

但是对于 CVE-2024-4367 来说,就比较麻烦了。如那个 poc 中所展示的,我们需要添加相应的字体节点,并在 /Page 下的 /Resources 中声明该字体,然后在 /Content 中添加文字内容,使用该字体。大致关联关系下图所示:

逻辑比较复杂,手动直接编辑文件可太麻烦了。那么有没有比较合适的库可以直接用呢?找了找 pdf 相关的一些解析库,还真有一个非常好用的库: pypdf

A pure-python PDF library capable of splitting, merging, cropping, and transforming the pages of PDF files

这个库相对来说很精简,而且提供了原子性的方法来操作 PDF 文档节点: _add_object。而且这样修改节点后,在生成新的PDF文件时,相关的偏移会自动重新计算,不会生成非法的PDF文件。

这个就非常棒,比如我们想在 PDF 文档中添加:

5 0 obj
<< /BaseFont /SNCSTG+CMBX12 /FontDescriptor 6 0 R /FontMatrix [ 1 2 3 4 5 (alert\(3\)) ] /Subtype /Type1 /Type /Font >>
endobj

就可以直接像下面这样写,非常的方便:

d = DictionaryObject()
d.update({
    NameObject("/BaseFont"): NameObject("/SNCSTG+CMBX12"),
    NameObject("/FontDescriptor"): ref2,
    NameObject("/FontMatrix"): ArrayObject([NumberObject(1), NumberObject(2), NumberObject(3), NumberObject(4), NumberObject(5), TextStringObject("alert(3)")]),
    NameObject("/Subtype"): NameObject("/Type1"),
    NameObject("/Type"): NameObject("/Font"),
})

ref = p._add_object(d)

我把这两个漏洞都集成到了一个工具里,可以直接根据原始 PDF 文件,根据需要生成PDF文件: pdf-exploit

THE END.

参考

点击收藏 | 3 关注 | 2 打赏
登录 后跟帖