PDF的多面性:一个justCTF挑战
Trail of Bits赞助了最近的justCTF竞赛,我们的工程师帮助设计了几个挑战,包括D0cker、Go-fs、Pinata、Oracles和25519。在这篇文章中,我们将介绍另一个挑战,标题为“PDF is broken, and so is this file”。它通过一个不寻常的隐写谜题展示了PDF文件格式的一些特性。CTF挑战通常涉及在干草堆中寻找隐写针,这很少具有启发性,更不用说令人愉快。LiveOverflow最近有一个关于文件格式技巧的优秀视频,并以类似的观点结束。因此,我们设计了这个挑战,以教授justCTF参与者一些PDF技巧,以及Trail of Bits的开源工具如何轻松处理这些取证挑战。
一个PDF作为Web服务器,提供自身的副本
挑战中的PDF文件实际上是损坏的,但大多数PDF查看器通常会将其渲染为空白页面而不报怨。file
命令报告挑战文件仅为“数据”。在十六进制编辑器中打开文件,我们看到它看起来像一个Ruby脚本:
|
|
第5行的PDF头嵌入在第4行开始的Ruby多行注释中,但这不是损坏的部分!几乎所有PDF查看器都会忽略%PDF-1.5头之前的所有内容。第7和8行是PDF注释,确认了我们从file
命令看到的内容,以及一个我们稍后会提到的readelf
提示。
Ruby脚本的其余部分嵌入在PDF对象流中——“9999 0 obj”行——,它可以包含PDF忽略的任意数据。但PDF的其余部分呢?这如何不影响Ruby脚本?
|
|
Ruby有一个特性,词法分析器会在__END__
关键字处停止,并有效地忽略其后的一切。果然,这个奇特的PDF有这样一个符号,后面是封装PDF对象流的结束和PDF的其余部分。
这是一个Ruby/PDF多语言文件,您可以使用类似的方法将任何PDF转换为这样的多语言文件。如果您的脚本足够短,您甚至不需要将其嵌入PDF流对象中。您只需将其全部附加在%PDF-1.5头之前。尽管一些PDF解析器会在文件的前1024字节内未找到头时抱怨。
您不会认为那么容易,对吧?
所以让我们勇敢地尝试将PDF作为Ruby脚本运行。果然,它运行了一个Web服务器,提供一个带有“flag.zip”下载链接的网页。哇,那很容易,对吧?进一步检查Ruby脚本,您会发现下载是PDF文件本身重命名为.zip。是的,除了是Ruby脚本,这个PDF也是一个有效的ZIP文件。PoC||GTFO多年来一直使用这个技巧,这也可以通过运行binwalk -e
在挑战PDF上观察到。
解压PDF产生两个文件:一个MμPDF mutool二进制文件和false_flag.md,后者建议玩家通过mutool二进制文件运行损坏的PDF。
显然,这个版本的mutool被修改以正确渲染损坏的PDF,尽管它有什么“损坏”。CTF玩家是否应该逆向工程二进制文件以找出修改了什么?如果有人尝试了,或者他们尝试了上面嵌入为PDF注释的readelf
线索,他们可能会注意到这一点:
您应该做的第一件事是:在十六进制编辑器中打开PDF。您可能需要“修复”PDF,以便它可以被普通PDF解析器解析。您可以逆向这个二进制文件以找出如何做到这一点,但可能更容易使用它来渲染PDF,跟随线索,并将原始PDF对象与“常规”PDF的对象进行比较。您可能只需用bbe
修复它!
二进制块编辑器(bbe)是一个类似于sed的实用程序,用于编辑二进制序列。这意味着导致PDF渲染为空白页面的任何问题都可以轻松地用二进制正则表达式修复。
深入探索
当我们使用修改版本的mutool渲染PDF时,它导致了这个表面上无意义的模因蒙太奇:
使用修改版本的mutool渲染的“损坏”挑战PDF。
在Google上搜索LMGTFY字符串将带您到Didier Stevens的优秀文章,详细描述PDF流格式,包括PDF对象如何编号和版本化。一个重要因素是,两个PDF对象可以有相同的编号但不同的版本。
页面上的第一个提示标识了PDF对象1337,所以那可能很重要。Stevens文章中的图表单独与损坏PDF的流对象的十六进制转储并列,提供了更改内容的清晰描述。
Didier Stevens的PDF流对象注释图。
|
|
挑战PDF中的PDF流对象。
如提示所建议,PDF规范只允许六个空白字符:\0、\t、\n、\f、\r和空格。ZIP中的mutool版本被修改以也允许ACK(0x06)用作第七个空白字符!果然,在文件的第十二行我们看到:
|
|
那个“^F”是一个ACK字符,而PDF规范说那里应该是空白!所有PDF对象流都类似地损坏。这可以用以下方式修复:
|
|
解决谜题
修复文件严格必要解决挑战吗?不,标志可以在PDF对象0x1337中使用十六进制编辑器找到:
|
|
并手动解码流内容。Binwalk甚至会自动解码第一个流,因为它可以解码Flate压缩。那包含:
|
|
Binwalk不会自动扩展第二个流,因为它还编码了ASCIIHex和DCT PDF过滤器。一个随意的观察者,没有跟随所有线索且不熟悉PDF规范,可能甚至没有意识到PDF流对象0x1337的第二个版本存在!而那是带有标志的版本。当然,可能已经梳理了binwalk提取的数十个文件以手动解码标志,甚至直接从十六进制编辑器中的流内容,用PDF解码器的快速实现。但为什么要这样做,当Polyfile可以为您做呢?
|
|
PolyFile的HTML输出为挑战PDF。
哦,嘿,那是PDF对象的分层表示,带有交互式十六进制查看器!我们如何转到对象0x1337的流?
我们立即看到PDF对象0x1337。
PolyFile可以自动解码对象。
最后,让我们看看对象0x1337的第二个版本,包含多重编码的标志:
PolyFile自动解码分层PDF过滤器以产生标志。
标志!
结论
PDF是一个非常…灵活的文件格式。仅仅因为PDF看起来损坏,并不意味着它是。仅仅因为PDF损坏,并不意味着PDF查看器会告诉您它是。PDF核心是一个容器格式,让您编码甚至不贡献于文档渲染的任意二进制块。而这些块可以用任意数量的编码堆叠,其中一些是PDF的定制特性。如果这感兴趣,请查看我们关于文件 treacherousy的演讲,以及我们驯服它们的工具,如Polyfile和PolyTracker。