就最近的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 directory
与 startxref
。
可以参考 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 /Value
。Key
部分一般是 Name Object
,Value
可以是:
- 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.