原文链接:https://www.zerodayinitiative.com/blog/2018/12/11/when-one-corruption-is-not-enough-analyzing-an-adobe-pwn2own-exploit

坐在飞往温哥华的飞机上,这是我去东京Pwn2Own的第一站,我决定打开笔记本分析我们之前在另一个Pwn2Own上收到的漏洞,屏幕上的JavaScript代码似乎吸引到了旁边座位的人,他问我这是JavaScript语言吗,我说是的。他问我是在读代码吗,我回复说我正在学习JavaScript,不得不说一定程度上这是一个挑衅的回答。完成一个函数的注释后,我合上笔记本准备休息一下。我想到了我刚才“我正在学习”的回复,我确实是在学习,不过我是在学习其他人在写漏洞利用时是如何思考的。

我准备分析的漏洞利用是360 Codesafe团队在2017年写的Adobe Reader PDF的一个漏洞利用,选择分析这个exploit有几个原因:首先,它利用了JPEG2000解析过程中的一个堆溢出漏洞(ZDI-17-280),其次,它缺乏相关文档和注释。实际上,甚至用来触发或利用bug的JavaScript代码也没有被清楚地描述,我不得不从他们提供给我们的PDF漏洞中提取代码。最后一个理由,我是Adobe Reader的超级粉丝,还不错吧。

在这篇博文中,我将介绍360利用Adobe Reader中基于堆的缓冲区溢出漏洞的技术。在他们最初的提交中,他们使用了一个内核的漏洞来做沙箱逃逸,这篇文章对不会涉及内核漏洞部分,只关注Adobe Reader相关部分

相关细节

我用来分析这个漏洞的Reader版本是2015.023.20070,可以从Adobe的FTP服务器上获取。

很明显,进行漏洞分析的第一步是从PDF中提取用于利用漏洞的JavaScript代码。这步操作有很多种方法可以实现,去编写一个Python脚本来达到目的并不是很复杂,额,实际上这个脚本效果很好。如果你对脚本和Python不怎么感兴趣,你可以使用Foxit Phantom PDF工具来提取。

提取代码结束后,我们就获得了超量的JavaScript代码,我尝试把shellcode剥离出来以增加可读性,代码由以下指令开始:

这里没什么特别的,设置了几个变量然后调用memory1函数,之后又设置了几个变量然后调用memory2。

那么memory1和memory2函数是什么东西呢,我们先来看看memory1:

该函数分配一个大小为len2的数组,并将第一个元素(我称之为标记元素)设置为0x11223344,之后用大小为len1的arraybuffer填充数组,最后进入另一个循环,在数组中delete一些元素,像是在数组上戳洞,看起来很像下面的场景:

在调试器里验证如下:

但是为什么要这么做呢?首先这是一个堆溢出漏洞,这样做的目的是为了设置堆以确保我们溢出的受攻击对象能被扔进一个戳过的洞中。在这个例子中,我们溢出了一个大小为0x1200的对象。

接下来看memory2:

我们先分析最明显的东西。它一开始获取一个对按钮的引用,如果成功,则循环遍历数组并开始填充之前戳出的一些洞。然后留下了一些未填充的洞。最后设置btt1.display为display.visible,调用memory3。

填充洞但保留一些洞不填充的目的是,确保被利用对象在分配时能够分配到其中一个洞里。在我看来,这个函数里最有趣的部分是“btt1.display = display.visible”。为什么要这么做呢?

关于这个问题我不得不好好思考一会,得到的结果是,如果将icon设置为一个button,并且它最初的属性被设置为hidden,那么您可以强制通过JavaScript解析它——特别是在将button的display属性设置为display.visible时。所以实际上,他们所做的工作基本可以理解为,创建一个button对象,将一个icon附加到它上面(在本例中是JPEG2000图像)并将其设置为hidden。当display属性被设置为display.visible时,会发生以下情况:

  1. 创建一个大小为0x1200的对象
  2. 触发了溢出漏洞

这个漏洞最吸引人的地方在于,当它溢出到下一个块时,它会写一个可控的WORD类型的值。在我们的例子中,如果所有东西都对齐正确,它最终会覆盖下一个ArrayBuffer块的size。

为了能更清楚的说明这一点,这是ArrayBuffer对象的size被覆盖之前的情况:

在size被覆写之后:

由于这个bug允许我们用一个WORD值覆盖下一个临近的ArrayBuffer对象的长度,360团队的人认为这个值可以被修改到最大——0xFFFF。之后memory3函数被调用:

我们用0xFFFF覆写了byteLength,但是这并没有提供给我们太多的读或写的自由。这个函数根据被破坏的变量byteLength来循环遍历数组寻找ArrayBuffer对象,如果不为空就为它创建一个DataView对象。

之后发生了什么呢?他们利用这个被破坏了的ArrayBuffer的DataView对象,用一个更大的byteLength(0x77777777)去破坏下一个ArrayBuffer对象。

最后一个循环是找到新被破坏的ArrayBuffer并为它创建一个DataView对象。代码的其余部分主要是泄漏内存和使用新的DataView对象获得RCE。

综上所述:

  1. 漏洞只被触发了一次。
  2. 漏洞是在解析JPEG2000图像时触发。
  3. 漏洞是过JavaScript触发的,具体来说是将图像作为按钮的图标附加上去,并且最初被设置为隐藏状态。
  4. 按钮的显示模式被更改为visible,之后开始图像解析,并触发错误。
  5. 他们分布了一堆大小为0x1200的数组缓冲区并且对其戳洞布局
  6. 他们填补了一些洞,然后触发了这个bug。注意当bug被触发时,易受攻击的分配操作是在解析发生之前执行的。
  7. 溢出漏洞允许用一个受控制的WORD类型值0xFFFF覆盖下一个块的size。
  8. 这对于进行读或写操作来说还不够,所以它们使用损坏的块(大小0xFFFF)来损坏下一个块的大小使其为0x77777777。
  9. 循环遍历数组中的元素,直到找到最新损坏的块并为其创建一个DataView,以使其具有读/写自由。

本文只覆盖了整个利用链的Adobe Reader部分。早在2017年,该团队将堆溢出与Windows内核信息泄漏以及通过Windows内核中未初始化缓冲区得到的RCE结合在一起,赢得了5万美元。当然,Adobe在一段时间前修复了这个问题。尽管如此,我们至今仍能看到一些利用PDF的恶意软件在使用类似的技术,并且Acrobat和Reader仍然存在比较广泛的受攻击面。毫无疑问,对于我自己和提交漏洞给ZDI的安全研究人员来说,这仍然是一个值得投入的研究领域。

你可以关注我的Twitter@AbdHariri,或者关注我们的团队以了解最新的漏洞利用技术和安全补丁。

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖