PDF双重释放漏洞CVE-2018-4990分析

2019-05-13 约 3637 字 预计阅读 8 分钟

声明:本文 【PDF双重释放漏洞CVE-2018-4990分析】 由作者 kgsdy 于 2019-05-13 08:21:00 首发 先知社区 曾经 浏览数 143 次

感谢 kgsdy 的辛苦付出!

漏洞概述

CVE-2018-4990是Adobe在2018年5月修复的一个Adobe DC系列PDF阅读器的0day漏洞。该漏洞为双重释放(Double Free)漏洞,攻击者通过一个特殊的JPEG2000图像而触发Acrobat Reader双重释放,再通过JavaScript对于ArrayBuffers灵活的控制来实现任意地址读写。
攻击者可以通过这个漏洞实现对任意两个4字节地址的释放,漏洞触发前用精准的堆喷射巧妙地布局内存,然后触发漏洞,释放可控的的两块大小为0xfff8的相邻堆块。随后,Windows堆分配算法自动将两块空闲的堆块合并成一个大堆块,接着立即重新使用这个大堆块,并利用这个该堆块的读写能力改写一个ArrayBuffer对象的长度为0x66666666,从而实现任意地址读写。

漏洞细节

代码分析
分析漏洞样本,通过PDF流解析工具PdfStreamDumper可以看到pdf文件里面的objects流。其中第1个object流使用了JavaScript来触发并利用漏洞。通过对该段分析可以知道,JavaScript中的dlldata为PDF阅读器漏洞触发后加载运行的载荷,主要用于提权并执行恶意代码,而之后的JavaScript代码用来进行内存布局和漏洞触发。上面JavaScript代码中通过两个Array实例sprayarr及a1来进行内存控制,这两个Array在这里构造了大量对象,申请了大量的堆空间来实现Spray布局。再对a1的Array中奇数下标的堆空间进行了释放,借助堆分配算法,Windows堆管理器(Windows Heap Manager)会对这些块进行合并,产生一个0x2000大小的空间,JP2Klib在申请漏洞对象时,会从释放的堆块里面直接复用一个。
下面的代码会先从释放的内存空间中重新使用内存。并且,因为空间较大(由于之前的合并),所以需要分配比原来大一倍的空间,每个数组成员分配一个长度为0x20000-0x24的ArrayBuffer。接着遍历sprayarr可以发现其对应的某一个sprayarr的成员长度被修改为了0x20000-0x24(默认的长度为0x10000-0x24),此时通过超长的sprayarr[i1]即可修改相邻的sprayarr[i1+1]对象的len长度属性,从脚本代码中可以看到长度被修改为了0x66666666,最终通过该超长的sprayarr[i1+1]即可实现全内存的读写。

数据结构分析
由于Adobe DC没有符号表,很多结构也没公开只有自己测试和总结。可以利用PdfStreamDumper对pdf分析dump出需要修改的stream流,在修改dump出的stream流,最后替换实现对内嵌的javascript代码的修改。通过添加一些调试代码,以方便下断和调试。
对Array结构进行分析,可以创建一个Array的实例myContent,将该Array中第0个element赋值为0x1a2c3d4f,以便于内存搜索,之后分别将感兴趣的变量赋值到该Array中即可很方便的定位内存进行分析。通过”s -d 0x0 L?0x7fffffff 0x1a2c3d4f”命令可以定位到0x1a2c3d4f,查到附近的内存可以看到myContent结构的实例。可以看到Array结构每个element占8字节,0x1a2c3d4f对应的是值,后面的0xffffff81对应的为element的类型,所以0xffffff81对应的类型为数值,0xffffff87对应的类型为数组。
现在有了sprayarr的地址0x391b3358,查看该地址的值可以看到该地址也是一个结构用来描述sprayarr数组。通过测试发现0x49d50000地址保存了数组值,0x1000为数组的大小。第0个element没有赋值所以为0,后面的element都赋值为数组。在代码里sprayarr被两次赋值,第一次为Uint32Array,第二次为ArrayBuffer,那么sprayarr被赋的值应该是ArrayBuffer。查看第一个element 0x49f9e420的值,可以看到连续的内存区域用来保存ArrayBuffer的结构信息,每个结构0x98大小,该结构偏移0xc的值0x49d5a018表示ArrayBuffer保存数据的内存区域。进入到具体的Arraybuffer内存空间查看(0x49d5a018),可以看到在0x49d5a018前面的0xc字节保存了Arraybuffer的实际内存长度ffe8(0x10000-0x24)。再看看a1的结构,a1的地址为0x391b3380,a1的结构和sprayarr相同都为Array。0x47c01000为a1保存数据的地址,可以看到基数的element已经被释放。再查看a1[3]所指向的Uint32Array结构,该结构大小为0x58字节,其中0x3f0为结构的大小(252*4),0x39137388描述下一个结构。
在0x39137388的地址又保存的为0x98大小的结构用来描述实际数据的存放地址,在偏移0xc的地址指向实际数据的存放地址。在0x42638c10前面偏移0xc字节保存了Uint32Array的大小0x3f0,这个头大小应该为16字节。对应的JavaScript脚本,其中a1[i1][249],a1[i1][250]的值在此时分别为0x0d0e0048和0x0d0f0048。
漏洞调试
设置windbg为默认调试器,对AcroRd32.exe进程使用命令开启页堆”gflags /i AcroRd32.exe +ust +hpa”,附加AcroRd32.exe进程后运行poc文件,windbg将暂停到发生crach的地方。通过栈回溯可以看到释放的调用者是JP2KLib!JP2KCopyRect+0xbad6,证明漏洞很可能在该模块里面。在该模块里面又调用了HeapFree函数,很可能是释放空间引发的异常。对应的代码为如下代码片段。该片段F5的代码如下。通过对关键部分进行整理后如下代码,可以看到这个代码从基地址循环并使用变量count作为空闲内存的计数器,变量mem_base是在此循环中开始的内存地址。可以设置断点来监控mem_base,max_count和count的值。可以通过如下断点来监控mem_base,max_count和count值的变化。可以看到mem_base的地址为0x47560c08,max_count的值为0xff。可以看到在count为0xfd的时候释放了0xd0d0d0d0的地址。

bp JP2KLib!JP2KCopyRect+0xbaea "dd eax+4 l1; g;"// max_count
bp JP2KLib!JP2KCopyRect+0xbac9 "r eax; r ecx; g;"// eax = mem_base,ecx = count
bp JP2KLib!JP2KCopyRect+0xbad0 "r eax; g;"//free addr

再通过!heap -p -a 47560c08查看基地址0x47560c08的信息,可以看到使用的大小为0x3f4,而while循环可以访问到mem_base ~ mem_base+3fc(4*0xff)区间的内存。两者的差值为8个字节3fc - 3f4 = 8,于是可以借助上述while循环越界访问两个4字节地址并释放,来实现任意释放两个地址。攻击者可以通过内存布局(例如堆喷射)提供的任意两个4字节地址,并实现任意释放。从前面的代码知道攻击者在漏洞触发前利用精心控制大小(0x400)的堆喷射构造大量对象,然后释放其中的一半,借助堆分配算法,JP2Klib在申请漏洞对象时,会从a1释放的堆块里面直接复用一个。JP2Klib释放对象时会触发漏洞多释放2个4字节的地址,在特殊的构造下释放的地址正好是0x0d0e0048和0x0d0f0048,而这两个地址在堆喷下会被sprayarr申请使用,在触发漏洞的情况下两个地址被释放后合并大小正好是0x20000,在随后攻击者通过以下代码立即将上述合并的堆块重新使用。这段代码会从双重释放的内存空间中回收已经释放的内存。并且因为内存较大(由于之前的合并),所以需要分配比原来大一倍的空间。在sprayarr2被分配为0x20000-24大小的空间后,由于堆的特性sprayarr中之前被释放的elment的长度就会被修改为0x20000。在此之后,攻击者已经回收了释放的内存,他们接下来需要找出sprayarr中的哪个ArrayBuffer的大小增加了一倍。接着攻击者查找所需的ArrayBuffer之后利用长度为0x20000-24的ArrayBuffer的读写能力去改写对应ArrayBuffer对象的长度,将其改写为0x66666666。然后利用之前构造的sprayarr数组找到长度为0x66666666的“ArrayBuffer”对象,紧接着将其赋值给一个DataView对象借助DataView来实现任意地址读写。

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now