善变的PDF:利用浏览器渲染差异进行攻击
想象一下,某公司的CEO收到一封包含PDF发票文件的电子邮件。在Safari和MacOS Preview中,显示的总价为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 Preview)中显示。
有趣的是,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:显示总价399英镑。 Google Chrome和Drive预览渲染不同的总价:显示999英镑。 Firefox与Google Chrome一致:显示999英镑。
有趣的是,ChatGPT不支持注释。如果您要求它分析发票,它将返回以下内容:
PDF文件是Carlos Montoya的发票,详细信息如下: 发票编号:1 签发日期:2001年1月1日 到期日期:3001年1月1日 项目: 项目:L33T Leather Jacket 数量:1 单价:399英镑 总计:399英镑
最终说明
PDF文件的渲染过程复杂且模糊。在将文件发送到会计部门进行付款或授予聊天助手访问邮箱的权限时,请务必谨慎。您可以在Github上找到Fickle pdf文件。