PDF文件格式的巧妙利用:justCTF挑战赛解析

本文详细解析了Trail of Bits在justCTF竞赛中设计的"PDF is broken"挑战,展示了PDF文件格式的特殊性及其在CTF中的巧妙应用,涉及Ruby/PDF多语言文件、ZIP文件隐写、二进制修复等技术细节。

PDF is Broken: justCTF挑战赛解析

Trail of Bits赞助了最近的justCTF竞赛,我们的工程师设计了几道挑战题,其中包括"D0cker"、“Go-fs”、“Pinata”、“Oracles"和"25519”。本文将重点介绍名为"PDF is broken, and so is this file"的挑战题,它通过一种不寻常的隐写谜题展示了PDF文件格式的一些特性。

PDF即Web服务器

挑战中的PDF文件实际上是损坏的,但大多数PDF查看器通常会将其渲染为空白页面而不报错。file命令报告该文件只是"data"。用十六进制编辑器打开文件,会发现它看起来像是一个Ruby脚本:

1
2
3
4
5
6
7
8
require "json"
require "cgi"
require "socket"
=begin
%PDF-1.5
%ÐÔÅØ
% <code>file</code>  sometimes lies
% and  <code>readelf -p .note</code> might be useful later

第5行的PDF头嵌入在从第4行开始的Ruby多行注释中,但这还不是文件损坏的部分!几乎所有PDF查看器都会忽略%PDF-1.5头之前的所有内容。第7行和第8行是PDF注释,确认了我们从file命令看到的结果,并提供了一个我们稍后会讨论的readelf提示。

没那么简单

如果尝试将PDF作为Ruby脚本运行,它会启动一个Web服务器,提供包含"flag.zip"下载链接的网页。进一步检查Ruby脚本会发现,下载的文件实际上是PDF本身重命名为.zip。这个PDF同时也是有效的ZIP文件。

解压PDF会产生两个文件:一个MμPDF mutool二进制文件和false_flag.md,后者建议玩家通过mutool二进制文件运行损坏的PDF。

深入探索

使用修改版的mutool渲染PDF会显示一个看似无意义的模因蒙太奇。页面上的第一个提示标识了PDF对象1337,这可能是关键。PDF规范只允许六种空白字符:\0、\t、\n、\f、\r和空格。而ZIP中的mutool版本被修改为也允许ACK(0x06)作为第七种空白字符!

可以通过以下命令修复文件:

1
bbe -e "s/\x06stream\n/\nstream\n/" -o challenge_fixed.pdf challenge.pdf

解决谜题

修复文件严格来说并不是解决挑战的必要条件。使用十六进制编辑器可以在PDF对象0x1337中找到flag。Binwalk甚至会自动解码第一个流,因为它可以解码Flate压缩。其中包含:

1
2
3
pip3 install polyfile
Also check out the `--html` option!
But you'll need to "fix" this PDF first!

使用Polyfile可以自动解码对象:

1
polyfile challenge_fixed.pdf -html challenge_fixed.html

结论

PDF是一个非常…灵活的文件格式。仅仅因为PDF看起来损坏,并不意味着它真的损坏了。PDF本质上是一种容器格式,允许你编码任意二进制数据块,这些数据块甚至不需要对文档的渲染做出贡献。如果这让你感兴趣,可以查看我们关于"文件的背叛"的演讲,以及我们用于驯服它们的工具,如Polyfile和PolyTracker。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计