几周前微软又发布了 MS16-063 的每月计划更新。本次更新对象为 IE ,修复了 jscript9.dll 中的 TypedArray 和 DataView 两个函数。
补丁前后改动分析:
同样的,本次也是用 BinDiff 来分析 5 月和 6 月的 jscript9.dll 的区别:
与上次不同,这次的改动就非常多了。但是如果仔细看下,大部分的改动都是和DirectGetItem、 DirectSetItem 方法和 TypedArray 及其关联类有关。同时也能看到 DataView 类里的 GetValue 和 SetValue 的函数的改动。
TypedArray 和 DataView:
有关 TypedArray 更多资料,可以阅读这里,但是它提供了的那个用于访问的函数是受一个 ArrayBuffer 备份原始二进制数据的机制。 ArrayBuffer 是不能被直接访问与操作的,只能通过 view 这类高级接口来操作,它会提供一段包含自身类型,偏移,多个元素的内容。 用 DataView,我们可以得到灵活的读取和写入任意的字节顺序(字节序)任意数据项。
用 TypedArray,我们可以指定数组元素的数据类型为以下之一:
Int8Array: 有符号 8-bit 整数
Uint8Array: 无符号 8-bit 整数
Uint8ClampedArray: 无符号 8-bit 插值整数 (插值基于 0 到 255) Int16Array: 有符号 16-bit 整数
Uint16Array: 无符号 16-bit 整数
Int32Array: 有符号 32-bit 整数
Uint32Array: 无符号 32-bit 整数
Float32Array: 32-bit IEEE 浮点数 (float)
Float64Array: 64-bit IEEE 浮点数 (double)
TypedArray 和 DataView 两个函数在访问与操作原始数据的功能上相似,接下来看看更新的内容。 分析下图中很清楚的看到更新后的版本添加了一些代码:(左 6 月右 5 月)
更新前, DirectGetItem、 DirectSetItem 每个类型数组只是检查索引是不是超出了范围,然后读入缓冲区。
伪代码大致如下:
请注意,这里的缓冲区根本就没有检查自身数据,因此缓冲区可以在被访问或操作前分离,从而产生 UAF 漏洞。
这时候可以强制一下 ArrayBuffer 通过使用 postMessage 传值来分离缓冲区,下面的代码段足以脱离由 AB 引用的 ArrayBuffer:
修改后的代码会自己检查缓冲区逃逸问题,从而防止 UAF发生。
有趣的是,这个漏洞明明已经在 ChakraCore16 年 1 月的首次提交里已经修复了....不知道这次漏洞再次产生是什么原因......
本次最终更新之后, jscript9 也会去验证一遍 TypedArray 和 DataView 两个函数的缓冲区逃逸,确保 UAF 不发生。 触发 PoC:
触发来说就简单很多了:
1.创建一个 TypedArray——我们可以用任何类型的数据,但是这里使用 Int8Array
2.通过第一步的 Int8Array 制造一个 ArrayBuffer 的缓冲区逃逸,用来释放缓冲区
3.使用 Int8Array 访问已经被释放的缓冲区
成功 crash:
总的来说,这个 PoC 在写入已经被释放的内存(即 ia[100]现在指向空闲内存)从而导致程序崩溃。对攻击来说,则需要创建并且先分配这个对象的元数据,这样才会有任意内存读写的效果。
Exploit:
测试环境为 Windows7,IE11,以及 edge,因为目前这些版本还没有修复该漏洞。
根据上文 PoC 说到的,首先要分配一个 ArrayBuffer 对象,使用 Int8Array,然后继续分配一个较大的 ArrayBuffer 缓冲区(大致 2MB),这样内存就会在使用后被系统释放。使用这种方法的好处是不需要对缓冲区大小做太严格的限制,大致 2MB 就可以了。
一旦触发了内存回收机制,就可以填充 N 多较小的数据到那个被释放的内存空间里,达到内容可控的效果。
以上的操作会触发 LFH 控制大小的类函数 sizeof(Uint8Array),同时多个内存块也会被 LFH 分配。
可以用 VMMap 来查看一下:
现在就要定位一个我们刚刚创建过的 Uint8Array 的对象, Uint8Array 类有 4byte 长,这时候需要搜索下刚刚定义的 ab2(0x1337),找到之后就需要手动增加对应数组 arr 的长度
现在这个刚刚分配的特殊的 Uint8Array 对象将用来作为读取存储 view 和写入任意内存的变量:
和上次一样,我们编写简单的辅助功能:
接下来,根据目标环境的不同,我们也有很多不同的方法来实现上面的攻击,对于开始提到的Win7IE11 环境,攻击方案如下:
1.计算泄露 vftable 地址的 jscript9 基址
2.在堆缓冲区建立一个 fake virtual function table 使用 stack-pivot gadget 替换子数组的指针 mov esp, ebx; pop ebx; ret 注意:EBX 是我们提供给子数组的第一个参数
3.读取 VirtualProtect 入口,导入表
4.构造 ROP Payload,调用函数 VirtualProtect 到 shellcode 的缓冲区
5.覆盖原来 Uint8Array 对象 的 vftable 地址为我们刚刚构造的那个
6.调用 mv.subarray,完成shellcode 成功的启动了 notepad:
不过这个记事本是运行在沙箱+低权限环境的,提权不在本文的讨论范围之内。 Exp 已经上传到众多程序猿都喜欢用的 Gayhub。
本文为网络安全技术研究记录,文中技术研究环境为本地搭建或经过目标主体授权测试研究,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,在挖掘、提交相关漏洞的过程中,应严格遵守相关法律法规。