这是一个natashenka在16年报的漏洞,关注浏览器的同学应该都有看过她的《The ECMA and the Chakra
-Hunting bugs in the Microsoft Edge Script Engine》
poc首先定义了一个array a和object o,之后给object o设置一个getter。并将a的proto设为o,之后对a调用Array.prototype.reverse。
5a3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1N6h3N6K6i4K6u0W2j5$3S2J5L8$3#2A6N6h3#2Q4x3X3g2G2M7X3N6Q4x3V1k6H3i4K6u0r3M7s2u0G2K9X3g2U0N6q4)9J5k6s2A6W2M7X3!0Q4x3V1k6A6M7%4y4#2k6i4y4Q4x3V1k6V1k6i4c8S2K9h3I4Q4x3@1k6A6k6q4)9K6c8o6V1J5y4b7`.`.
异常出现在如下函数
其中length的值为0x000003ea,size值为1,导致Assert抛出异常。
观察函数可以知道nextLeft为JavascriptArray::MaxArrayLength也就是0xffffffff
而size = min(size, nextLeft - left)
,length值为我们设置的1002(0x000003ea)是正常值,nextLeft和length值为正常说明left值存在问题。
观察left值来源
跟踪函数流程,发现是由以下函数调用到的
由文档可知JavascriptArray::ReverseHelper
对应于Array.prototype.reverse
reverse方法用于将数组中元素的位置颠倒
首先来看下Array.prototype.reverse是怎么实现的
1.Let O be ToObject(this value).
2.Let len be ToLength(Get(O, "length")).
3.Let middle be floor(len/2).
4.Let lower be 0.
首先获取长度设为len,取中间值设为middle,起始值设为lower为0
之后就是循环交换upper和lower指向的值,如下图

对应到Chakra的代码中则是如下情况
其中ReverseSegment函数负责真正的置换操作,根据数组元素的类型执行对应的模版函数。
其中length是由上层函数传递的,seg->length是从对象中获取的,调试发现这两个值有所不同
length为1000,而seg->length为1002,这里相减产生负值导致crash发生。
根据POC猜测这里对象中的length改变是由于对a数组设置的getter
根据调试发现ForEachOwnMissingArrayIndexOfObject中调用ES5Array::GetItem触发了Getter导致array的segment发生变化
那么getter中的j.fill.call(a, 7.7)
存在的意义是什么呢,如果只是把a.length设为1002,虽然array object的length会变成1002但是array对应的segment的length和size并不会变成1002,因为这块内存并没有实际使用因此就不会更改segment的长度,从而无法触发漏洞。
至此我们明确了漏洞产生的原因在于segment的长度可以在函数执行过程中被改变(比如通过getter)。
这里的seg->left是直接使用上层传递的参数length进行运算的,因此length是固定的但是seg->length是可以通过user callback控制的,从而使得用户可以控制seg值。
此外还有一个版本的POC是通过Proxy劫持getPrototypeOf
实现的user callback篡改segment长度,基本原理一致不再详细分析。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课