Google CTF 2023 - v8box
字数 1419 2025-08-29 22:41:10

Google CTF 2023 - v8box 漏洞分析与利用教学文档

环境搭建

  1. 根据提供的 build.dockerfile 在本地编译环境
  2. 修改 args.gn 文件内容,使用以下编译选项:
    • 关闭了 JIT、Maglev、TurboFan 和 WebAssembly
    • 开启了 v8_expose_memory_corruption_api 选项

关键API分析

内存操作API

位于 src/sandbox/testing.cc 的 API 提供了对沙箱内空间的直接操作能力,相当于提供了 OOB(越界)原语。

在新版本 V8 (12.x 以上)中:

  • 编译选项变为 V8_ENABLE_MEMORY_CORRUPTION_API
  • 新增了一些方法
  • 开启了 v8_code_pointer_sandboxing 保护机制

代码指针沙箱化保护

  • 目的:防止通过修改函数指针控制 RIP
  • 实现方式:
    • 可执行函数指针有固定入口(code pointer table)
    • 通过偏移索引对应代码

漏洞分析

Patch 分析

  1. v8.patch

    • 关闭了一些 d8 选项以使编译更轻量化
  2. 0001-Protect-chunk-headers-on-the-heap.patch

    • 新增了三个方法:
      • ProtectSpace:修改指定地址段的权限(使用 mprotect
      • SetWritable:将地址空间设为 RW 权限
        • 对 young generation 且未被 GC 处理的内存:设置 from space 和 to space 为 RW
        • 对 old generation:直接调用 ProtectSpace
      • SetReadOnly:结构与 SetWritable 相同,设为只读

条件竞争问题

  • 使用 nesting level 变量约束
  • 多线程情况下会出现问题(非预期解法)

Ignition 解释器漏洞

  • Ignition 是 V8 的字节码解释器
  • 即使关闭 Maglev/JIT,仍可工作
  • 漏洞点:
    • LdarStar 操作码缺乏边界检查
    • 允许修改字节码数组内容实现任意指令执行

Ldar 指令分析

  • 功能:从栈上读取值
  • 汇编实现:
    • r12:字节码数组基址
    • r9:索引
    • 返回值 raxrbx 赋值
    • rbx 通过 rdx+rbx 索引得到
    • rdxrbp 赋值

效果:可以泄漏栈上低 32 位值(由于指针压缩)

  • 注意:SMI 类型会右移 1 位(需要乘以 2 恢复)

Star 指令分析

  • 功能:将值写入栈上
  • 汇编实现:
    • rax(上一次执行的返回值)写入栈上
    • 通过 rbp+0x0 处的值被修改为 rax 寄存器值

利用思路

现有能力

  1. 泄漏栈上低 4 字节数据
  2. 栈上 8 字节数据写

控制流劫持

  1. 通过多次执行 leave 指令实现栈迁移

    • 修改字节数组控制执行流程
    • 劫持控制流
  2. 获取 gadget:

    • 从栈上可以找到位于 d8 的代码片段
    • 地址随机化特点:
      • 低 4 字节变化很小/基本不变
    • 泄漏 d8 基地址:
      • 从堆块开头读取高位字节(仍有读权限)
      • 拼接低 4 字节
      • 减去尾部偏移得到 d8 基地址
  3. ROP 链构造:

    • 使用获取的 gadget 构造 ROP 链
    • 实现提权

参考链接

Google CTF 2023 - v8box 漏洞分析与利用教学文档 环境搭建 根据提供的 build.dockerfile 在本地编译环境 修改 args.gn 文件内容,使用以下编译选项: 关闭了 JIT、Maglev、TurboFan 和 WebAssembly 开启了 v8_expose_memory_corruption_api 选项 关键API分析 内存操作API 位于 src/sandbox/testing.cc 的 API 提供了对沙箱内空间的直接操作能力,相当于提供了 OOB(越界)原语。 在新版本 V8 (12.x 以上)中: 编译选项变为 V8_ENABLE_MEMORY_CORRUPTION_API 新增了一些方法 开启了 v8_code_pointer_sandboxing 保护机制 代码指针沙箱化保护 目的:防止通过修改函数指针控制 RIP 实现方式: 可执行函数指针有固定入口(code pointer table) 通过偏移索引对应代码 漏洞分析 Patch 分析 v8.patch : 关闭了一些 d8 选项以使编译更轻量化 0001-Protect-chunk-headers-on-the-heap.patch : 新增了三个方法: ProtectSpace :修改指定地址段的权限(使用 mprotect ) SetWritable :将地址空间设为 RW 权限 对 young generation 且未被 GC 处理的内存:设置 from space 和 to space 为 RW 对 old generation:直接调用 ProtectSpace SetReadOnly :结构与 SetWritable 相同,设为只读 条件竞争问题 : 使用 nesting level 变量约束 多线程情况下会出现问题(非预期解法) Ignition 解释器漏洞 Ignition 是 V8 的字节码解释器 即使关闭 Maglev/JIT,仍可工作 漏洞点: Ldar 和 Star 操作码缺乏边界检查 允许修改字节码数组内容实现任意指令执行 Ldar 指令分析 功能:从栈上读取值 汇编实现: r12 :字节码数组基址 r9 :索引 返回值 rax 由 rbx 赋值 rbx 通过 rdx+rbx 索引得到 rdx 由 rbp 赋值 效果:可以泄漏栈上低 32 位值(由于指针压缩) 注意:SMI 类型会右移 1 位(需要乘以 2 恢复) Star 指令分析 功能:将值写入栈上 汇编实现: 将 rax (上一次执行的返回值)写入栈上 通过 rbp+0x0 处的值被修改为 rax 寄存器值 利用思路 现有能力 泄漏栈上低 4 字节数据 栈上 8 字节数据写 控制流劫持 通过多次执行 leave 指令实现栈迁移 修改字节数组控制执行流程 劫持控制流 获取 gadget: 从栈上可以找到位于 d8 的代码片段 地址随机化特点: 低 4 字节变化很小/基本不变 泄漏 d8 基地址: 从堆块开头读取高位字节(仍有读权限) 拼接低 4 字节 减去尾部偏移得到 d8 基地址 ROP 链构造: 使用获取的 gadget 构造 ROP 链 实现提权 参考链接 Google CTF 2023 v8box 官方解决方案