2019-数字经济大赛决赛-Browser
捡一下一年前入门的v8,这道题目是19年比赛线下的题目,调完oob之后就没再研究v8了,拿题目回顾一下基础的browser-pwn的解题思路。
漏洞分析
看下patch文件,patch为Array类型增加了一个coin函数,该函数接受两个参数,分别为length和value,当array_length大于37时,设置arr[37]=value->Number()
,这里乍一看没什么问题,因为判断了数组的长度,但是注意这里的elements是之前取出的局部变量FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
,这里的问题在于执行ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, length, Object::ToNumber(isolate, args.at<Object>(1)));
获取length的数值时,ToNumber会调用对象的回调函数。这一点我们可以跟进这个方法查看。
1 | diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc |
可以看到,当输入为数字时直接返回该数字,否则,调用ConvertToNumberOrNumeric
,在该函数进一步判断后如果仍无法确定则调用ToPrimitive
函数,在该函数中使用GetMethod
获得回调函数,最后Execution::Call(isolate, exotic_to_prim, receiver, 1, &hint_string),Object);
调用该函数。结合patch,可以看到elements被取出之后我们调用了ToNumber,假如我们在回调函数中修改arr的大小,就会调用GC重新分配数组的空间,而之前局部变量保存的elements指向了释放的空间,因此存在UAF。假使我们最开始给一个arr_length合适(<37)的Array,则可以通过越界写Array后面的ArrayBuffer的size,从而得到一个oob数组,通过越界读可以得到double_arr的map和obj_arr的map,从而可以构造addressOf和fakeObj原语。
再往后伪造ArraryBuffer借助Dataview实现任意地址读写,写wasm的shellcode最后替换成我们谈计算器或者get shell的shellcode。
1 | // static |
漏洞利用
因为一年没调v8了,搭环境花了一天,在Ubuntu 18.04/20.04还是失败了,还是用回之前Ubuntu 16.04搭的环境.
debug没有加patch,没有job调试比较麻烦,为了了解内存布局我是同一个exp两边对比调试,确定好偏移后我们最终构造另一个Length为30的Array,element对应的37的位置为下一个Arrary的length位
因为16.04的gnome被我搞崩了所以没法弹计算器了:(,这里贴一下最后执行shellcode的图。
exp如下,有一个很关键的问题是addressOf不能执行两次,第二次会crash,没有深入了解过v8因此不清楚原因,写一个新函数调用即可leak,很神奇。
1 | // create a dataview |