深入理解Double Free:CVE-2015-2419 Exploit分析 – 互联网安全新媒体平台

*本文原创作者:elli0tn0phacker,本文属原创奖励计划,未经许可禁止转载

0×00 背景

CVE-2015-2419是IE11中jscript9.dll的一个Double Free破绽。虽然从发明在野进击样本到本日已有三年多,然则今朝依然被一些闻名的破绽使用对象包应用,可见其破绽使用的稳定。经由过程阐发其破绽道理和使用技巧,对付深入理解Double Free破绽和jscript9.dll的exploit编写会有必然的赞助。

0×01 破绽成因

触发破绽的PoC如下:

PoC经由过程create_triggering_json() 创建了一个触发破绽的Json工具:

PoC经由过程create_triggering_json() 创建了一个触发破绽的Json工具:

经由过程JSON.stringify将这个Json工具转换为JSON字符串,着末经由过程手动GC触发破绽,开启HPA UST察看破绽现场:

经由过程JSON.stringify将这个Json工具转换为JSON字符串,着末经由过程手动GC触发破绽,开启HPA UST察看破绽现场:

IE Crash,edx指向了未分配的内存,阐发edx的滥觞,查看 Js::TempArenaAllocatorWrapper::Dispose 函数:

IE Crash,edx指向了未分配的内存,阐发edx的滥觞,查看 Js::TempArenaAllocatorWrapper::Dispose 函数:

这里edx = [this+0x34]-0×8,同时察看绿框代码,可以发明这里存在一个双向链表的卸载操作:

(1)[[edx+4]]= [edx](Prev pointer)

(2)[[edx]+4]= [edx+4](Next pointer)

(3)Free[edx]

经由过程察看Call stack,可以发明这里是在脚本履行CollectGarbage()时调用:jscript9!Js::TempArenaAllocatorWrapper::Dispose 开释已经被开释的内存触发的Double Free破绽,察看edx保存的地址是在什么时刻被开释的:

可以看到edx保存的地址是在履行JSON.Stringify操作时,调用了:ThreadContext::ReleaseTemporaryGuestAllocator 被开释的,察看这个函数的开释逻辑:

这里当[this+0x3c4]>= 5时就会进入free memory分支,可以看到free memory分支的代码和上面阐发的:Js::TempArenaAllocatorWrapper::Dispose 逻辑时一样的,然而两次free memory本身逻辑时没有问题的,这个破绽的根滥觞基本因时free memory开释内存后,保存这块memory地址的指针(edx)没有被清零,而这个指针恰是 Js::TempArenaAllocatorWrapper 的成员变量,保存在Js::TempArenaAllocatorWrapper + 0×34处。

阐发patch过的jscript9.dll可以发明这个破绽是若何被修复的:

可以看到patch过的jscript9.dll,在free memory前会先反省edx是否为0,不为0则进入free memory分支,并且在free后将指针置0。

那么触发破绽前提是什么呢,着实便是[this+0x3c4] >= 5,经由过程调试可以知道[this+0x3c4]保存的便是Json工具的嵌套深度,以是今朝阐发获得信息有:

(1)当Json工具嵌套深度>=5时,GC后会触发Double Free破绽

(2)破绽的根滥觞基本因是开释了类的成员变量保存的内存然则没有将该成员变量清零,GC时开释这个工具再次开释这块内存导致Double Free

(3)使用这个破绽不能直接RCE,然则可以触发一个双向链表的开释操作

现在我们开始考试测验破绽使用。

0×02 破绽使用

经由过程前面的阐发可以知道使用这个破绽我们会得到一次双向链表卸载操作的时机,双向链表的卸载操作可以简化如下:

(1)[[edx+4]]= [edx]第一个DWORD被复制到第二个DWORD指向的内存

(2)[[edx]+4]= [edx+4]第二个DWORD被复制到第一个DWORD指向的地址+0×4的内存

假如edx可控的话,我们就可以得到一次向随意率性2个DWORD([edx]和[edx]+4)写入数据的时机。那写入数据的时机若何使用呢,这里就必要HeapSpray,Jscript9.dll里HeapSpray常用的数据类型如下:

Array:

JavaScript的Array在IE11中有两种形式:

(1) Js::JavascriptNativeIntArray

主要数据布局:

Js::JavascriptNativeIntArray

+0x0vftable

+0x10Array size

+0x14first buffer address

+0x18second buffer address

Buffer

+0x4item size

+0x8item buffer size (跨越这个size会分配一块新内存)

(2) Js::JavascriptArray

数据布局同 Js::JavascriptNativeIntArray,当Array中保存了工具后,数字元素会以2N+1的奇数形式寄放用来差别工具指针。

ArrayBuffer和TypedArray:

ArrayBuffer表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer不能直接操作,而是要经由过程类型数组工具TypedArray或 DataView 工具来操作,它们会将缓冲区中的数据表示为特定的款式,并经由过程这些款式来读写缓冲区的内容。

Js::JavascriptArrayBuffer

+0x0vftable

+0x1CArrayBufferAddress

+0x20ArrayBufferSize

Js::TypedArray

+0x0vftable

+0x10Js::JavascriptArrayBuffer

+0x1CArrayBufferSize

+0x20ArrayBufferAddress

必要留意的是ArrayBuffer是在CRT堆上分配的。

我们可以应用 Js::TypedArray 做破绽使用的数据类型,这里只必要改动+0x1C的ArrayBufferSize和+0×20的ArrayBufferAddress就可以实现用户态随意率性地址读写。

首先必要经由过程Heap Spray将Js::TypedArray放到可猜测内存地址。

Heap Spray

使用Heap Spray我们可以将Js::TypedArray排布在指定内存地址,应用如下代码进行Heap Spray:

每个spray[size]分配的内存大年夜小 = 0×20(JavascriptArray Header) + 0x3BF8*4(JavascriptArray Data) + 0×55*0×30(Js::TypedArrayHeader) = 0xFFF0(对齐)= 0×10000 (64kB):

每个spray[size]分配的内存大年夜小 = 0×20(JavascriptArray Header) + 0x3BF8*4(JavascriptArray Data) + 0×55*0×30(Js::TypedArrayHeader) = 0xFFF0(对齐)= 0×10000 (64kB):

Heap Spray完反省内存:

这样经由过程Heap Spray可以在0x128ef000处稳定找到一个Js::TypedArray。 接下来使用这个Double Free的破绽就可以经由过程2个DWORD改动的时机窜改这个Js::TypedArray的长度(0x128ef01c)和ArrayBuffer肇端地址(0x128ef020),实现随意率性地址读写。

这样经由过程Heap Spray可以在0x128ef000处稳定找到一个Js::TypedArray。 接下来使用这个Double Free的破绽就可以经由过程2个DWORD改动的时机窜改这个Js::TypedArray的长度(0x128ef01c)和ArrayBuffer肇端地址(0x128ef020),实现随意率性地址读写。

随意率性地址读写

在 jscript9!ThreadContext::ReleaseTemporaryGuestAllocator + 0x5bc4a 处下断点,查看Double free内存大年夜小:

被开释的内存为0x0CBytes,是以这里可以用0x0C Bytes ArrayBuffer占位,并将必要改动2 DWORD地址写到ArrayBuffer数据:

这里用0×10000 0xC Bytes的ArrayBuffer考试测验占位,占位成功后,在 jscript9!Js::TempArenaAllocatorWrapper::Dispose :触发破绽,使用双向链表的卸载操作改动我们之前经由过程HeapSpray排布在0x128ef000处的Js::TypedArray的长度(0x128ef01c)和Arraybuffer肇端地址(0x128ef020):

这样就获得了一个肇端地址为0x128ef018大年夜小为0x128ef020的Js::TypedArray,再使用这个Js::TypedArray得到一个随意率性地址读写的Js::TypedArray:

被开释的内存为0x0CBytes,是以这里可以用0x0C Bytes ArrayBuffer占位,并将必要改动2 DWORD地址写到ArrayBuffer数据:

这里用0×10000 0xC Bytes的ArrayBuffer考试测验占位,占位成功后,在 jscript9!Js::TempArenaAllocatorWrapper::Dispose :触发破绽,使用双向链表的卸载操作改动我们之前经由过程HeapSpray排布在0x128ef000处的Js::TypedArray的长度(0x128ef01c)和Arraybuffer肇端地址(0x128ef020):

这样就获得了一个肇端地址为0x128ef018大年夜小为0x128ef020的Js::TypedArray,再使用这个Js::TypedArray得到一个随意率性地址读写的Js::TypedArray:

泄露基址

履行Shellcode前,必要得到一些关键函数的地址。比如 kernel32!VirtualProtectStub,经由过程如下措施泄露函数地址:

(1)使用随意率性地址写权限,在0x128e0020(即spray[i][0])处写入标记数据0x1BD81BD (0xDEC0DE * 2 |1)

(2)经由过程遍历spray[i][0]找到被写入标记数据0x1BD81BD的spray[k]

(3)再将JavascriptArray工具保存再spray[k][2]处,经由过程随意率性地址写权限,读取0x128e0020处的DWORD,这个便是JavascriptArray的vftable,那么jscript9_base_addr = vftable & 0xffff0000

(4) 搜索jscript9.dll导入表,泄露kernel32!VirtualProtect地址

构造ROP

我们依然应用0x128e0000的spray[k]来寄放ROP gadget和 shellcode,此中spray[k][0]寄放ROP gadget, spray[k][1]寄放shellcode:

履行Shellcode

spray[k][2][0]= 0会调用 jscript9!Js::JavascriptArray::SetItem 给JavascriptArray赋值,SetItem函数在jscript9!Js::JavascriptArray::`vftable’+0×90处,经由过程窜改spray[k][2]的JavascriptArray的虚表从而履行终极shellcode:

参考文献:

(1)https://www.fireeye.com/blog/threat-research/2015/08/cve-2015-2419_inte.html

(2)https://blog.trendmicro.com/trendlabs-security-intelligence/a-look-into-recent-exploit-kit-activities/

(3)https://blog.checkpoint.com/2016/02/10/too-much-freedom-is-dangerous-understanding-ie-11-cve-2015-2419-exploitation/

*本文原创作者:elli0tn0phacker,本文属原创奖励计划,未经许可禁止转载

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包