论文阅读——Shadow Attacks:Hiding and Replacing Content in Signed PDFS

论文阅读报告

—— Shadow Attacks:Hiding and Replacing Content in Signed PDFS

阅读背景

本次阅读的论文是由Christian Mainka、Vladislav Mladenov和Simon Rohlmann于2021年发表在NDSS的《Shadow Attacks:Hiding and Replacing Content in Signed PDFS》,论文的作者一直致力于PDF文件中安全问题的研究,这篇文章介绍了一种新型的针对PDF文件的攻击方法,称其为影子攻击(Shadow Attacks)。

论文概述

数字签名PDF被广泛利用在合同和发票之中,用于确保内容的真实性和完整性。本文介绍的攻击方法——影子攻击,可以规避所有现有的针对PDF签名保护的对策,破坏数字签名的完整性保护。它是基于PDF规范提供的巨大灵活性进行攻击,因此由这种方法制作的影子文档(Shadow
Document)符合PDF的标准,难以被检测。根据实验,测试的29个PDF查看器中有16个易受到影子攻击(包括最常用的Adobe Acrobat和Foxit Reader)。本文还介绍了可以自动生成影子攻击的工具PDF-Attacker,以及检测工具PDF-Detector。

PDF规范基础

PDF文件结构

如下页图一所示,PDF文件主要有三部分组成。Part1定义Body,其中包含了不同的对象,他们由对象编号标识,个人理解相当于对于这些内容的定义。其中Catalog的对象标识符为1 0,作为根对象,由其链接到其他对象来定义整个PDF结构。Part2定义Xref table,可以理解为对于所有对象的位置的应用,注意可以在这里将未使用的对象显示表示为free,例如Image 9 0对象是free的,它就不会显示在PDF当中。Part3是Trailer,它包含指向前两个对象的引用。

请添加图片描述

增量更新(Incremental Update)

PDF的内容可以修改,在修改后,增量更新将新对象添加到新的PDF主体中,该主体直接附加在前一个Trailer之后。为充分处理新对象,每个增量更新还附加了一个新的Xref表和Trailer。

PDF签名

当对一个PDF文件进行签名后,会使用增量更新创建签名对象并将其附加到文件之中。同样,也可以多次对PDF签名(例如签署多方合同的情形),从而会进行多次的增量更新。用户打开包含PDF签名的PDF后,查看器应用程序会自动验证签名,如果内容已被修改,则会发出警告。

攻击模型

影子攻击的思想模拟于现实中的文件篡改。想象这样一种情形,学校要求学生们往家里寄成绩单,挂了好几门课程的张三导出初始的成绩单后,拿到了教务处盖章,教务处在核对成绩单无误后,在最后一页盖章,之后需要由张三将成绩单寄回家,可是,当张三拿到成绩单后,发现挂科记录在前面几页,于张三修改了这部分内容,然后将其和最后盖章的一页重新装订起来,给家里寄回了一份满绩的成绩单。

拓展到PDF文件,作者发现这种攻击方法也是可以凑效的。

如图2,攻击者在签名前将不可见的部分注入PDF。签名后,攻击者再次操纵已经签名的PDF,使得在不使签名无效的情况下,对PDF的内容进行可见的更改。之后,攻击者把影子文档发送给接收方,接收方则看到的是攻击者修改的内容,而且签名显示是正确的。

请添加图片描述

这种情形的实现,主要是因为PDF应用程序会分析签名后所做的更改,并尝试估计这些更改是否合法。例如,不允许覆盖文档页面上的内容,从而导致无效的签名验证。在本文中,首先分析了PDF应用程序认为哪些更改无害,并滥用这些更改来替换PDF文档中的内容。

本文中主要利用的允许的更改有以下几个部分:

  • 追加新的Xref表和Trailer

  • 覆盖无害对象(比如字体)

  • 更改重叠对象的显示顺序

  • 更改交互式表单

攻击方法

隐藏

此类影子攻击旨在将与受害者相关的内容隐藏在可见层后面。如图3所示,攻击者可以在全页图片“签名以获得奖励!”后面隐藏文本“您已被解雇!”。攻击者一旦收到签名的文档,便会操纵该文档,以使PDF阅读器不再呈现图片。这种攻击方法有两种变体。

请添加图片描述

  • 变体1 通过引用的对象隐藏内容

在此攻击变体中,攻击者创建覆盖对象(例如图像或表单域),并在文档签名后隐藏它们,以显示这些对象下面的内容。有三种不同的漏洞利用方法,它们分别通过恶意图像隐藏内容,通过恶意表单字段隐藏表单字段以及通过恶意表单字段隐藏内容。

首先,攻击者注入一个或多个图像并将其放置在原始内容上。图像可以覆盖整个页面或仅覆盖部分内容,例如数字或文本段落。然后在增量更新中使用相同的对象ID,但将其定义为不同的对象类型。例如,将叠加层类型Image更改为XML/Metadata。此外,添加了一个Xref表,该表指向元数据对象,但保留了叠加层的对象ID。当打开此操作过的文档时,由于无法显示元数据,因此叠加层被隐藏。由于使用增量更新将元数据添加到签名的PDF被认为是无害的,因此签名保持有效。

  • 变体2 按对象顺序隐藏内容

对于两个大小相同且在文档中x-y位置相同的不同表单域,仅显示最后一个域。此外,只要内容没有更改,就可以在增量更新中重新声明相同的表单字段。

在这种攻击方式中,攻击者将shadow
form字段注入到原始未签名文档中,该影子表单字段与要隐藏的内容位于相同的页面位置,但是他们在原始对象之前声明了它们的表单对象。攻击者收到文档后,将附加一个增量更新,该更新将复制并粘贴原始字段和影子表单字段。但是,在这种情况下,他们首先显示原始图像,然后显示影子表单字段。结果,显示了影子表单字段及其值,而不是原始字段。由于对象本身并没有修改,只是它们的声明顺序而已,因此增量更新被认为是无害的。

替换

这种影子攻击主要思想是直接更改先前声明的对象的增量更新。由于不允许对所有类型的对象进行修改,因此攻击者只能更改认为无害的对象,但仍然可以更改文档的可见内容。例如,字体的(重新)定义不会直接更改内容。但是,它会影响所显示内容的视图并使数字或字符交换成为可能。它有两种变体。

请添加图片描述

  • 变体1 默认替换

PDF表单支持不同的输入,例如文本字段,文本区域和单选/选择按钮。表单可以具有默认值,例如预定义的文本。用户可以动态更改这些值并将其存储在PDF文档中。

例如,在PDF的文本字段,有一个文本的默认值和实际值,只要输入了实际值,则默认值就会消失(可以类比输入框中的默认输入,不过PDF中的默认输入显示样式和实际输入相同)。如图4所示,表单字段的实际值包含在名为/V的对象键中默认叠加元素的内容在/BBox对象中定义。首先,攻击者创建一个包含交互式表单的转账传票(PDF1),签名者在签署文档之前完成该表单。攻击者使用默认值初始化某些表单元素。在上图提供的示例中,攻击者将前三个表单字段的值/V设置为Attacker和攻击者的IBAN和BIC。其次,攻击者将默认值(/BBox)设置为unicef并设置相应的IBAN和BIC。只要签名者不关注默认的值,他们就认为正确的值已经预先填充。签名者可以在不更改预填写表单的情况下对PDF进行签名。一旦攻击者收到PDF2,他们就会通过使用不同的值替换存储在/BBox中的默认值来更新文本字段。/V中存储的值保持不变。由于原始文本字段值未更改,而仅默认值发生了更改,因此查看器认为此替换无害。

  • 变体2 覆盖替换

在许多应用程序中,字体被认为是无害的,因此可以在增量更新中定义它们。攻击者可以利用这一漏洞,在文件签名后,攻击者将添加新的字体描述并覆盖以前的字体描述。新的字体描述完全改变了原始文本的显示方式。例如,创建了一个漏洞利用程序,将原始文本US905628 3174 5628 3174的显示方式更改为US01 2345678923456789。由于新字体的定义被认为是无害的,因此验证签名的应用程序不会警告所做的更改。而关于恶意字体,可以通过软件FontForge创建。

隐藏加替换

这一攻击方法是攻击者创建一个影子PDF文档,该文档将发送给签名者。PDF文档中包含了内容不同的另一个文档的隐藏说明。由于签名者无法检测到隐藏的(恶意)内容,因此他们对文档进行签名。签名后,攻击者将收到文档,并仅附加一个启用了隐藏对象的Xref表和Trailer,这样,受害者收到的将是一封显示了隐藏内容的文档。这一攻击方法有两种变体。

请添加图片描述

  • 变体1 更改对象引用

此攻击变体的想法是使用外部参照表将对文档目录(或任何其他隐藏对象)的引用更改为指向影子文档。

如图5所示,攻击者创建一个PDF文件,其中包含两个具有相同objectID(例如4 0 obj)但内容不同的对象:“Sign the document to get a reward!”和“You are fired! Get out immediately”。如上图左侧所示,在Xref表中,引用了看似无害的内容。签名者只能看到此内容并签名PDF文件。攻击者收到签名的PDF后,将添加一个新的Xref表,并将该引用与具有恶意内容“You are fired! Get out immediately”的对象(例如4 0 obj)交换。还添加了新Trailer。由于在指向区域内包含指向已定义对象的Xref表被认为是无害的,因此不会对所做的更改发出警告。签名验证成功。

  • 变体2 更改对象用法

在PDF对象的属性中的Xref表种,可以指定了哪些对象在使用和哪些对象未被使用。通过这种方式,攻击者可以隐藏“使用中”的对象并显示free对象。这可以在不更改对象本身的情况下实现。攻击者只接触Xreft表,但是可以完全更改已签名文档的表示。

PDF-Attacker

本篇论文还介绍了作者团队设计的自动创建影子攻击漏洞的工具集PDF-Attacker,下面简单对此工具加以介绍:

PDF-Attacker是Jupyter Notebook编写的Python脚本程序。这种设计实现了类似于影子攻击所必需的高灵活性。针对每种影子攻击类别的每种攻击变体,创建了一个单独的Jupyter Notebook,以便可以独立研究和扩展所有攻击。

请添加图片描述

如图6所示,PDF-Attacker的工作方式和前文叙述的攻击方式是一致的,作者主要利用了一些PDF的python库,reportlab库提供了许多有用对于表单的攻击功能,pypdf4库可以对PDF对象进行低级别访问,用隐藏和替换攻击。

之后论文总结了PDF-Attacker的配置和漏洞利用的相关库,这部分内容实际上和之前的攻击方式差不多,就不再详述。

效果评估

本文总共找到了29个适用于Windows,macOS和Linux的PDF应用程序进行实验,结果如下表:

请添加图片描述

如上表所示,在29个PDF阅读器中,有16个易受至少一个存在的攻击(●)。对于12个PDF查看器,这三个攻击类别均获得了成功,其中包括了最普遍使用的Foxit Reader和Adobe Acrobat Reader。表中的◐表示有限的漏洞,这表示用户在收到影子文件修改前和修改后的文件都会收到相同的提示,因此用户无法区分是合法修改还是恶意修改。

从结果上看,这项攻击效果十分明显,大多数的PDF阅读器都遭到沦陷,可见这个攻击方式的普适性极高。

论文总结

PDF签名旨在保护PDF的完整性和真实性。与传统的数字签名本质上的用例仅在目标上应用一次签名不同,PDF允许在没有使签名无效的情况下更新已签名的文档,但仅在特定情况下才可以。此外,PDF可以连续签名几次。

本文展示了如何在不使签名无效的情况下滥用这种灵活性来替换PDF的整个内容,发现29个应用程序中有16个是易受攻击的。

对于这一问题,本文在当前的PDF规范中找到原因:(1)当前规范没有精确描述如何执行签名验证。(2)它没有记录案例,也没有提出解决方案或指南,因此开发人员必须自行解决这些问题。(3)PDF规范需要考虑功能的丰富性,这会削弱安全性,所以它需要在密码保护方面应用更严格的限制和更有限的处理。

个人拓展

在阅读了本篇论文后,我对于文章使用的具体方法感到十分好奇,查阅相关资料发现,本文作者Christian Mainka等人将他们的研究成果分享在了网站https://pdf-insecurity.org/上面,其中包含了PDF-Attacker的源码等资料,于是我简单阅读了其中的代码部分并加以注释,浅尝辄止地探索了文中使用的攻击模型。下面是通过引用的对象隐藏内容的攻击实现分析(对照图3):

定义文件引用如下:

malicious_text = "You are fired!" \# 签名前内容

overlay_content = "Sign me to get a reward." \# 覆盖内容

overlay_file = "1_original-document.pdf" \# 初始文件

filename_unsigned = "2_original-document-shadowed.pdf" \# 影子文件

filename_signed = "3_original-document-shadowed-signed.pdf" \# 签名后的影子文件

filename_manipulated1 = "4_original-document-shadowed-signed-manipulated_v2pdf" # 修改后的文件
  • 第一步:准备影子文件(filename_unsigned)

首先是设计原始PDF文件,利用了python的FPDF库,之后是对其进行修改生成影子文件,核心函数_enddoc()的部分内容如下:

pdf = PDF() \#生成了原始pdf

pdf.add_page()

\# Setup hidden Content

pdf.set_font('Arial', 'B', 24)

pdf.cell(20, 30, malicious_text) \# 例如输入 You are fired!

\# 将malicious_filename转换为PNG格式

overlay_image = overlay_file.replace(".pdf", ".png")

with Image(filename=overlay_file, resolution=300) as img:

with Image(width=img.width, height=img.height, background=Color("white")) as bg:

bg.composite(img,0,0)

bg.save(filename=overlay_image)

\# 对影子文件添加覆盖图像

pdf.image(overlay_image,0,0,pdf.w) \# 这里覆盖了整个页面

pdf.set_author("Creator")

pdf.set_title("Sign me")

\# 保存未签名的文件

pdf.output(filename_unsigned, 'F')

print(f"Successfully created {filename_unsigned}")
  • 第二步:签名影子文件

在这一步中,模拟了用户签名文档的步骤,不涉及具体攻击,这一步完成后,得到了签名过的影子文件。

  • 第三步:进行影子攻击

攻击者现在操纵签名的PDF进行攻击时,会创建一个新的PDF,其中包含所有最初签名的数据,然后使用增量更新来隐藏图片。

with open(filename_manipulated2, "wb") as fp:

fp.write(data_signed_pdf)

info_obj = "{:d}".format(objectid_image).encode() +
info_obj[len("{:d}".format(objectid_info)):] \# 这里替换了obj ID

fp.write(info_obj)

fp.write(xref) \# 增加了修改后的xref

fp.write(trailer) \#增加了新的trailer

print(f"Successfully created {filename_manipulated2}"

完成上述操作后,可以得到四个pdf文档,分别是①原始pdf,②影子文档,③签名的影子文档和④执行攻击的影子文档。对比③和④,如7图所示,隐藏了“Signme to get a reward”之后,“You are fired!”浮出水面。

请添加图片描述

补充说明,在这篇论文发表前,作者已经向Foxit Reader等PDF阅读器厂商通报了这一漏洞,现在这一漏洞已经一定程度上得到了解决,如图8所示,在影子文件进行攻击后,Foxit Reader打开时会进行提示发现该文档包含交互式表单域,而且还可以通过检查内容检查,还原到修改前的版本。

请添加图片描述

读后感悟

很久没有这么深入的去研究一篇论文,读完之后觉得非常过瘾,让我感觉到读论文其实没有那么难,论文是学者在向大家讲述他们的研究成果,很多优秀的论文,其内容都是清晰且生动的,除了英文阅读有点费劲外,其实读论文才是研究某个知识前沿最直接最有效的方式。比如这篇论文就一点都不枯燥,作者叙述很清晰具体,使用的例子也有趣形象。

具体到这篇论文,让我深切的感受到,对于一个能供用户使用的程序,它一定是可用性和安全性的折衷,在让其具备丰富的功能的同时,势必会对其安全信造成影响,开发人员要把握好其中的平衡,而安全人员更应该从功能中寻找漏洞,防止不法分子的破坏!
论文其实没有那么难,论文是学者在向大家讲述他们的研究成果,很多优秀的论文,其内容都是清晰且生动的,除了英文阅读有点费劲外,其实读论文才是研究某个知识前沿最直接最有效的方式。比如这篇论文就一点都不枯燥,作者叙述很清晰具体,使用的例子也有趣形象。

具体到这篇论文,让我深切的感受到,对于一个能供用户使用的程序,它一定是可用性和安全性的折衷,在让其具备丰富的功能的同时,势必会对其安全信造成影响,开发人员要把握好其中的平衡,而安全人员更应该从功能中寻找漏洞,防止不法分子的破坏!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值