善变PDF:利用浏览器渲染差异实现内容欺骗
想象一下,某公司CEO收到一封包含PDF发票文件的电子邮件。在Safari和MacOS预览中显示的总价格为399英镑。经过批准后,发票被发送到会计部门,该部门使用Windows操作系统。然而,当同一个PDF文件在Google Chrome或Google Drive中打开时,价格变成了999英镑。
在本文中,我们将展示如何创建滥用小部件注解的混合PDF来制造渲染差异,并分享代码以便您生成自己的PDF。这项研究受到Konstantin Weddige的博客文章"Kobold Letters"的启发。
浏览器PDF渲染差异
每个主流浏览器都有自己渲染PDF文件的方法。Google Chrome使用名为PDFium的集成PDF查看器,Safari使用自己的PDF渲染引擎,而Firefox使用PDF.js。由于PDF渲染差异,同一个PDF文件在不同浏览器中可能呈现不同的外观。例如,交互式表单字段的外观在浏览器之间各不相同。
Google Chrome对交互式表单字段和小部件注解都有全面支持,在用户交互时会动态将显示的文本从注解值更新为表单字段的默认值。然而,Firefox和Google Drive预览都优先处理小部件注解,完全忽略默认值。相比之下,Safari的PDF渲染引擎完全绕过小部件注解,仅显示默认值。
构建概念验证
为了构建概念验证,我们将使用org.apache.pdfbox Java库。请注意,即使通过手动文件修改也可以达到相同的结果。我们的交互式表单应至少有一个输入文本字段及其注解。此字段的纯文本值可以是任何字符串,例如399英镑。此值将在不支持表单的PDF阅读器(如Safari和MacOS预览)中显示。
有趣的是,org.apache.pdfbox.pdmodel.interactive.form.PDTextField#setValue方法也尝试更新视觉外观,除非PDAcroForm.getNeedAppearances()为true。但是,我们不会使用默认外观,而是使用小部件注解渲染自己的外观。
这些小部件注解是添加到PDF文档中的对象,用于提供额外信息或交互元素,而不改变原始内容。小部件注解表示交互式PDF表单中表单字段的外观。它将显示文本999英镑而不是399英镑。伪代码可能如下所示:
|
|
请注意,注解可以包含任何文本,理论上没有什么能阻止您覆盖整个页面。完整文本可在https://github.com/PortSwigger/research-labs/tree/main/pdf-rendering-discrepancies找到。
浏览器渲染对比
- Safari渲染PDF:显示原始价格
- Google Chrome和Drive预览:渲染不同的总价格
- Firefox:与Google Chrome一致
有趣的是,ChatGPT不支持注解。如果您要求它分析发票,它将返回以下内容:
PDF文件是Carlos Montoya的发票,包含以下详细信息: 发票编号:1 签发日期:2001年1月1日 到期日期:3001年1月1日 项目: 项目:L33T皮夹克 数量:1 单价:399英镑 总计:399英镑
最终说明
PDF文件的渲染过程复杂且模糊不清。在将文件发送给会计部门付款或授予聊天助手访问邮箱权限时请务必小心。您可以在Github上找到Fickle PDF文件。