首页
社区
课程
招聘
[原创]对CVE-2018-4990漏洞的补充分析
发表于: 2019-3-26 13:06 9662

[原创]对CVE-2018-4990漏洞的补充分析

2019-3-26 13:06
9662

去年这个漏洞出来时我曾在论坛发过一篇分析,当时行文仓促,许多细节没来得及深入探究。最近重新看了一下这个漏洞,发现之前的分析文章中存在一些错误,这篇文章中做了相应更正。此外,本文对 CVE-2018-4990 漏洞的利用细节进行了补充描述。本文中一并对 Adobe Reader DC 下的堆喷射做了一些研究。水平有限,不足之处请见谅。

要深入理解这个漏洞的利用,关键是要理解利用中涉及到的相关对象在内存中的分布,这些对象包括:

我们需要构造一些在 Adobe Reader 中操作 js 对象的小 demo。方法是基于原始 CVE-2018-4990 样本和 PDFStreamDumper 工具来修改文档,基本方法在这篇文章已做介绍。

我们先来写一些小例子看一下上述 4 个 js 对象在内存中的情况。

首先我们将原样本的 js 代码修改为如下形式:

打开新生成的 pdf 文档,在弹出对话框后将 windbg 附加到 Acrord32.exe 进程,我们在内存中观察上述对象:

通过上述调试,我们得到了一些关于 ArrayObject 在内存中的认知:

一个 ArrayObject 在内存中占用大小为 0x28 字节,两个重要的成员如下(不作特殊说明大小均为 4 字节,下同):

ArrayObject.buffer 指向一片 buffer 区域,buffer 前有 0x10 字节的头部:

用一样的方法我们可以得到 Uint32ArrayObject 在内存中的情况,现在我们将原样本的 js 代码修改如下:

我们来看一下调试器给出的信息:

通过上述信息,我们可以得到关于 Uint32ArrayObject 对象的一些认知:

一个 Uint32ArrayObject 在内存中占用大小为 0x58 字节,几个重要的成员如下:

Uint32ArrayObject.buffer 指向一片 buffer 区域,buffer 前有 0x10 字节的头部:

接下来我们再将 js 代码替换为如下:

继续看调试器:

通过上述日志我们得到关于 ArrayBufferObject 的一些认知:

ArrayBufferObject 在内存中的大小为 0x98,其中比较重要的成员如下:

ArrayBufferObject.buffer 指向一片 buffer 区域,buffer 前有 0x10 字节的头部:

最后我们将 js 代码换成如下:

在调试器中看一下 DataView 的内存:

通过上述日志,我们得到关于 DataViewObject 对象的如下信息:

DataViewObject 在内存中的大小为 0x58 字节,几个比较重要的成员如下:

DataViewObject.buffer 指向一片 buffer 区域,buffer 前有 0x10 字节的头部:

对这个漏洞来说,其实只需要知道 Windows 的每一个堆块前会有一个 8 字节的 HEAP_ENTRY 结果用于管理。当两个相邻的堆块合并的时候,比如两个大小为 0xfff8 字节的相邻堆块合并时,合并后的实际大小为 0xfff8 + 0xfff8 + 0x08(第二个堆块的 HEAP_ENTRY 被回收) = 0x1fff8。

这里我假定读者已经阅读过我之前的一篇分析文章。那篇文章中关于 js 对象的内存说明存在理解上的偏差,本文的前半部分我已经通过调试器对相关对象的内存进行清晰的描述。

下面我们来进一步回答几个问题。

为什么下面的代码中要写成 252 而不是 255 或其他的值?

执行下面这句时,Unit32ArrayObject 会申请一块大小为 0n252 * 0n04 = 0x3F0 大小的 buffer 区域,再加上 buffer 区域的头部 0x10,一共申请了 0x400 大小的内存。利用代码一共申请了 0x3000 组这样的 buffer。

上面的代码显然是想保证越界访问释放时释放的是精确布控的 spraybase 和 spraybase+0x10000 两处地址的内存,如何保证这一点呢?利用代码首先释放 a1 的奇数部分成员,从而造成 0x3000 组 buffer 中的一半被释放,造成大量的 0x400 内存空洞。

紧接着解析 jpeg 图片,图片实际大小恰好为 0x3f4 字节,解析时会占用上述一个空闲的 0x400 堆块,随后触发漏洞,解析逻辑会将图片末尾的每个 4 字节内存当作地址去释放,一共释放到 图片内存基地址 + 0x3fc(max_count = 0xff) 为止。因为图片大小只有 0x3f4,所以利用代码需要控制 0x3f4 开始的4个字节和 0x3f8 开始的 4 个字节,也就是 0x400 区域内倒数第 3 个 DWORD 和 倒数第2个 DWORD,我们来看一下示意图:

这是原先 Uint32ArrayObject.buffer

这是漏洞触发时 jpeg 图片的内存,图片数据只占用 0x3f4 字节,漏洞导致 0x3f4 开始的 4 字节和 0x3f8 开始的4字节对应的两个地址被释放,恰好是 原来对应的 [249] 和 [250] 两个成员,所以利用代码选择 Uint32Array(252) 并设置 [249] 和 [250] 两个值为被释放的内存。

为什么堆喷射时需要用 ArrayBuffer(0x10000-24) ?

回答:堆喷射时用 ArrayBuffer(0x10000-24) 去进行喷射,通过前面的对象调试我们已经知道,这句代码每执行一次会申请 0xffe8 字节的 buffer,加上 0x10 的头部,其实际分配的内存大小为 0xfff8,再加上 0x08 的 HEAP_ENTRY 数据,其实际占用的空间大小为 0x10000 字节。

为什么重新占位的时候需要用 ArrayBuffer(0x20000-24) ?

回答:触发漏洞后,0x0d0e0048 和 0x0d0f0048 处两个相邻的 0x10000 大小内存块被释放并合并,此过程中回收的内存示意如下:

所以回收后的空闲内存为 0x1fff8,而 ArrayBuffer(0x20000-24) 会申请一段大小为 0x1ffe8 的 buffer 区域和一个 0x10 的头部,加起来恰好 0x1fff8。

为什么改写 ArrayBufferObject 的长度时改写的区域是 0x10000-12 ?

回答:显然,利用代码想改写的字段为某个 ArrayBuffer.buffer 对应 head 中的 byteLength 成员。那么 0x10000-12 肯定对应着这个成员,我们来看一下:

第一个 buffer 大小为 0xffe8,再加上 8 字节的 HEAP_ENTRY,来到原先第二个 buffer 的 head,再加上 4,跳过 head 的第1个成员,而 head 的第 2 个成员恰好就是 byteLength,从而将其改写为 0x66666666。

想必大家看到 CVE-2018-4990 的利用代码后都会有这个问题:为什么 ArrayBuffer 的堆喷射会精准喷射到 0x0d0e0048 的位置且且此位置恰好为 ArrayBuffer.buffer 的 head 起始处。

实际调试发现,Adobe Reader 会在内存中 commit 一些较大的堆块,大小为 0xfc1000,在这些堆块的起始处有一个大小为 0x48 的头部,这 0x48 字节其实是一个 HEAP_SEGMENT(0x40字节) 加一个紧邻的 HEAP_ENTRY(0x08字节) 。(此处感谢代码疯子前辈指导)

可以看到在32位进程地址空间的特定内存区域内的上述堆段块存在稳定的重复模式:

所以,当在不开启额外堆选项(堆申请栈回溯、页堆)的情况下, js 代码中连续申请大小为 0x10000 - 0n24 的 ArrayBuffer 对象时,每个 pattern 实际占用空间的总大小为 0x10(当前 ArrayBufferObject 的 buffer head) + (0x10000 - 0n24)(当前 ArrayBufferObject 的 buffer 大小) + 0x08(下一个 0x10000 堆块的 HEAP_ENTRY) = 0x10000 字节,在上述 0xfc1000 堆块区间内分配时,buffer head 地址是从每个 acro_header_obj + 0x48 开始,并以 0x10000 往后递增,所以会存在如下稳定的 pattern:

所以当以如下例子进行堆喷射时,利用编写者几乎可以确定 0x0d0e0048 处一定为某个 ArrayBuffer.buffer head 的起始处,后面依照这个假定来进行利用。而且 pdf 中 js 对象堆喷射的速度非常快,这些特性都有利于 Adobe Reader 中 UAF/类型混淆等类型的漏洞利用。

本文对 CVE-2018-4990 的利用编写者在 Acrobat Reader DC 下借助一个越界释放漏洞实现任意地址读写原语的过程进一步了研究。从这些分析中可以看到利用编写者具体是如何精确地操作堆数据以实现稳定利用。

关于该漏洞在实现任意地址读写原语后进行 ROP 构造的相关细节可以参考这篇文章

关于该漏洞的其他细节可以参考这篇这篇这篇文章。

A tale of two zero-days

CVE-2018-4990 Adobe Reader 代码执行漏洞利用分析

Adobe, Me and an Arbitrary Free :: Analyzing the CVE-2018-4990 Zero-Day Exploit

CVE-2018-4990 ACROBAT READER DC DOUBLE-FREE VULNERABILITY

CVE-2018-4990 Acrobat Reader堆内存越界访问释放漏洞分析

 
 
 
 
 
 
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2019-3-28 13:06 被银雁冰编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 26205
活跃值: (63302)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
感谢分享~
2019-3-26 14:05
0
雪    币: 270
活跃值: (97)
能力值: ( LV8,RANK:140 )
在线值:
发帖
回帖
粉丝
3
0x48 那个不是 acro_header ,李海飞那个 PPT 年代比较久远了,现在像这种大堆块都是直接 malloc / calloc 申请的,Adobe 没有自己做管理。

前面 0x40 是 ntdll!_HEAP_SEGMENT 结构所占用的空间。后面 8 字节是第一个堆块的 HEAP_ENTRY,和其他堆块一样,这个肯定都是有的,并不存在第一个堆块没有的情况。

0:012> dt _HEAP_SEGMENT 0a1b0000
ntdll!_HEAP_SEGMENT
   +0x000 Entry            : _HEAP_ENTRY
   +0x008 SegmentSignature : 0xffeeffee
   +0x00c SegmentFlags     : 2
   +0x010 SegmentListEntry : _LIST_ENTRY [ 0xb180010 - 0x9010010 ]
   +0x018 Heap             : 0x014e0000 _HEAP
   +0x01c BaseAddress      : 0x0a1b0000 Void
   +0x020 NumberOfPages    : 0xfcf
   +0x024 FirstEntry       : 0x0a1b0040 _HEAP_ENTRY
   +0x028 LastValidEntry   : 0x0b17f000 _HEAP_ENTRY
   +0x02c NumberOfUnCommittedPages : 0xe
   +0x030 NumberOfUnCommittedRanges : 1
   +0x034 SegmentAllocatorBackTraceIndex : 0
   +0x036 Reserved         : 0
   +0x038 UCRSegmentList   : _LIST_ENTRY [ 0xb170ff0 - 0xb170ff0 ]

0:012> dx -r1 (*((ntdll!_LIST_ENTRY *)0xa1b0038))
(*((ntdll!_LIST_ENTRY *)0xa1b0038))                 [Type: _LIST_ENTRY]
    [+0x000] Flink            : 0xb170ff0 [Type: _LIST_ENTRY *]
    [+0x004] Blink            : 0xb170ff0 [Type: _LIST_ENTRY *]

2019-3-26 16:00
0
雪    币: 9662
活跃值: (4588)
能力值: ( LV15,RANK:800 )
在线值:
发帖
回帖
粉丝
4
原来如此,非常感谢!
2019-3-26 16:04
0
游客
登录 | 注册 方可回帖
返回
//