对chrome cve-2018-6065的粗略分析
漏洞成因
作者在issue中说是因为在初始化一个新的js对象时,分配内存大小的计算出现了整数溢出,以下是漏洞相关函数:
JSFunction::CalculateInstanceSizeForDerivedClass
1 | // v8 version 6.4.301 src/objects.cc |
从函数名字中就可以看出这个函数是计算继承类所需内存大小的函数,[1]处变量expected_nof_properties表示该对象有多少个属性(这些属性的名字即使是重复的也算多个);接着在[2]处遍历该类的原型链,并在[3]处依此加上各个原型链构造函数的expected_nof_properties,最后在[4]处计算分配对象所需的内存大小(计算的值会存放在instance_size里)。
JSFunction::CalculateInstanceSizeHelper
1 | // v8 version 6.4.301 src/objects.cc |
该函数会计算出对象的instance_size,而instance_size的值来自 Min(header_size +((requested_embedder_fields + requested_in_object_properties) << kPointerSizeLog2),JSObject::kMaxInstanceSize);
其中的requested_in_object_properties就是来自前面的expected_nof_properties变量,
poc:
1 | <html> |
poc中用eval函数创建了一个具有i个prototype的类构造函数,为了能打印出来,把i改小一点:
这里使用的i为3
poc中的i为0x7ff,DerivedN构造函数的expected_nof_properties是0x40000,Derived构造函数的expected_nof_properties是0x3fffe,Object的header_size是0x18,header_size +((requested_embedder_fields + requested_in_object_properties) << kPointerSizeLog2)
计算完会整数溢出为0x8,导致instance_size为8,后续分配的时候只会给该对象分配8个字节的大小
漏洞利用
根据作者的exp代码注释:
1 | // So, we target the integer overflow to create a JSRegExp object with an |
作者利用该整数溢出申请了一个JSRegExp对象,其instance_size还是只有8字节,只够存放该对象的map,接着在申请一个JSArray对象,他们的内存会重叠,JSRegExp的data会被当作是JSArray的elements,JSRegExp对象的source会被当作是length,这会导致JSArray越界
测试代码:
1 | const derived_n = eval(`(function derived_n(i) { |
在60版本的chromium浏览器中:
可以看到array.length被写成了c01db33f,直接就是一个越界数组了
代码里repeat里面数量减去8的操作应该是和计算expected_nof_properties的函数相关:
1 | // v8 version 6.4.301 src/objects.cc |
该函数应该是给对象多预留8个属性的空间
代码中instance_size的计算为 :
1 | >>> hex(((0x3ff*0x80000+0x7fffb)<<3)+0x30) // RegExp对象的header_size为0x30 |
漏洞利用代码中的new ctor(pattern);
,经过调试,初始化的顺序为先分配出DerivedN对象所需要的内存空间,这里因为漏洞原因是8字节,接着会调用到pattern的toString函数来将其转换成String,这期间就会调用到pattern.toString函数中的array = new Array(8);
,使得array和ctor的内存重叠,之后ctor会更新其data和source属性,覆写array的element和length。
参考链接
https://bugs.chromium.org/p/chromium/issues/detail?id=808192