先把几道比较简单的OOB类型的题目做了,希望能增加对V8有一点点点的了解
漏洞环境
1 | git checkout 8.6.358 |
漏洞分析
1 | diff --git a/src/codegen/code-stub-assembler.cc b/src/codegen/code-stub-assembler.cc |
patch
文件如上所示,主要修改了BuildAppendJSArray
函数,这里将原本的Increment(&var_length)
函数修改为了Increment(&var_length, 3)
。
查找该函数的上层调用,发现其在TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler)
函数里被调用,而TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler)
函数是js中的Array.prototype.push
函数的具体实现,因此该漏洞与push
操作有关。
看一下完整函数如下:
1 | // Resize the capacity of the fixed array if it doesn't fit. |
其中看到,在存储数据之前,先进行了扩容,但这个扩容的计算是根据元素的个数来算的,而patch
后,原本每次push
一个数据,末尾指针加1,现在加了3。
最后,数据都push
完成后,将var_length
的值作为Array
的length
,这就导致了数组的length
大于其本身elements
的大小,导致了oob。
漏洞利用
测试代码
1 | var arr = []; |
我们使用如上代码,来测试是否会触发漏洞:
1 | Starting program: /home/alex/v8/traning/xnuca/babyV8/d8 --allow-natives-syntax ./test.js |
可以看到arr
的长度变为了19,接着我们看一下此时的arr
的内存布局:
1 | gdb-peda$ x/30xg 0x23bb08088178 |
从上面可以看到arr
的内存布局是 ``0x23bb08088180的低4位地址存储的是
elements元素的低4位地址,因为这个版本的
v8开启了指针压缩,所以这里每个指针如果高4位地址相同,那么就只存储低4位地址。但是这里我们已经看到
array的长度已经变为了
19`,是可以越界的。
那么可以考虑直接在一个arr
对象下面,继续布置第二个array
数组对象arr2
,通过第一个arr
对象的越界来修改arr2
对象的属性。例如将arr2
的长度改大,从而使得arr2
能够越界更多。实现前面例如issue 1793
或者2021数字经济
两道题目的做法。
测试代码如下:
1 | var arr = []; |
我们查看内存布局如下,这里经过测试,发现无法直接溢出修改arr2
的length
,其位于0xc2108088284
,而我们越界溢出只能修改0xc2108088260
。
1 | 0x20c70808819d <JSArray[19]> |
从上面的内存布局可以看出,如果直接想用arr1
的oob
去覆盖arr2->length
是覆盖不到的,因为arr2->length
在0x20c708088264
地址处,而arr1
能够覆盖的地址为0x20c708088250
。这里的原因是由于array
对象的elements
是在array object
的上面,如果让elements
地址在对象的下面,那么就有可能覆盖length
.
这里通过var arr2 = new Array(1.1,2.2);
产生的对象的内存布局是什么。测试代码如下:
1 | var arr = []; |
内存布局如下:
1 | 0x2831080881a5 <JSArray[19]> |
可以看到new Array
产生的对象的element
布局与数组对象布局不同,其elements
是位于对象的下面,所以这时候我们的arr1
是能够覆盖到arr2->length
地址0x283108088254
的。那么这里就可以将arr2
的length
改得更大。
此时,就能够修改arr2
的对象结构。由于这个版本的V8
开启了compression pointer
,所以我们不能直接去修改arr2
的指针。
EXP
1 | var buf = new ArrayBuffer(0x8); |
参考
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2021/10/05/2020-XNUCA-babyv8/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!