V8堆沙箱绕过方法分析总结

admin 2024年10月9日13:40:03评论25 views字数 4082阅读13分36秒阅读模式

一、What is Sandbox

二、V8 Sandbox

三、V8 Sandbox Breaking

四、Conclusion

What is Sandbox

沙箱机制(Sandboxing)是一种安全技术,用来隔离运行中的应用程序或代码,使其在受限的环境中执行。这种机制的目的是限制应用程序或代码与系统资源(如文件系统、网络、硬件)的直接交互,从而防止恶意软件或不受信任的代码造成安全威胁或数据泄露。

当提到chrome沙箱时通常会想到的是用于限制应用程序或代码与系统资源的直接交互的沙箱,而在实际的漏洞利用层面内存安全仍然是一个重要问题,在众多chrome可利用的漏洞中,V8漏洞可以说占到大多数,而V8漏洞很少是“经典”的内存损坏错误(释放后使用、越界访问等),而是微妙的逻辑问题,反过来再利用这些问题来损坏内存。因此,现有的内存安全解决方案在很大程度上并不适用于V8,于是在此背景下衍生出了V8 Sandbox。本文主要对V8 sandbox的一些绕过方法进行汇总分析。

V8 Sandbox

V8沙箱是一个基于软件的沙箱,其背后的基本思想是隔离 V8堆内存,使任何内存损坏都不会“扩散”到进程内存的其他部分,从而增加V8漏洞的利用难度。具体实现方式有两种:第一种,如果buffer位于沙盒内,就将40位的地址偏移左移24位后得到的64位结果写入相应字段地址中:

  • disable sandbox:

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

  • enable sandbox:

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

通过对%DebugPrint具体的实现代码下断,可以找到具体的decode过程,首先从指定字段地址出得到64位值sandboxed_pointer),再将sandboxed_pointer右移24位(kSandboxedPointerShift)得到偏移(offset),最后将offset与基址(cage_base)相加得到真实的地址指针:

V8堆沙箱绕过方法分析总结

第二种,如果buffer位于沙盒外,则会将指定字段地址内的值作为索引,通过指针表间接的引用buffer:

V8堆沙箱绕过方法分析总结

例如blink对象,在V8中所有的blink对象都分配在V8堆外,并以api对象的形式在V8中表示:

V8堆沙箱绕过方法分析总结

V8 api对象实际上是blink对象的包装器,其中embedder fields字段存储内容用实际为一个表索引,此索引位置保存着对应的blink对象的实际地址及其类型:

V8堆沙箱绕过方法分析总结

同样也可以通过对%DebugPrint实现下断找到具体的decode过程:

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

V8 Sandbox Breaking
01

signature confusion breaking sandbox

在V8 webassembly中,wasm模块导出函数主要由函数签名(signature)和函数实现(call_target)组成,假设有以下代码,此代码可以导出read_0read_1两个函数:

V8堆沙箱绕过方法分析总结

在js代码中使用read_0(0x41)触发对read_0函数的调用,然后对Builtins_JSToWasmWrapper下断可以得到signaturecall_target的获取过程:

1. 先通过函数对象获取shared_info字段,其中r14寄存器存的一直都是基地址,而rdi则是函数对象地址:

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

2. 通过shared_info字段得到function_data

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

3. 通过function_data获取signaturesignature对象不在沙盒中,所以是通过外部表的形式间接引用的,所以此处得到的是一个表索引:

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

4. 通过function_data获取func_ref

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

5. 通过func_ref可以得到internalinternal是一个外部对象,而call_target就在internal中并且也是一个外部对象,所以都只能得到一个表索引:

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

最后Builtins_JSToWasmWrapperAsm函数会通过call rdx进入call_target指向的地址,在通过几个Jmp后会进入真实的jited代码,rax为传入的地址偏移:

V8堆沙箱绕过方法分析总结

总结整个获取过程大致就是:function -> shared_info -> function_data -> func_ref -> internal -> call_target通过调试会发现signaturecall_target并没有太多的联系,而wasm导出函数的参数类型,及其后面的返回值类型声明列表由signature来决定,而对类型的检查也是在builtins函数中,所以在调用call_target时会直接将参数传入:

V8堆沙箱绕过方法分析总结

所以如果将read_0read_1call_target进行混淆那在调用read_1函数时就可以实现64位地址空间的读取,在前面的分析中可知call_target是沙盒外对象,所以只能得到一个表索引无法直接读取到call_target对象地址,不过func_ref在沙盒内,可以直接将read_0func_ref写入read_1。还有一个问题,那就是read_0call_target代码在从内存中读取内容时还会与rcx也就是(memory 1)代码中申请的线性内存地址的基址相加,这个线性内存地址实际为一个arraybuffer对象的backing_store

V8堆沙箱绕过方法分析总结

通过前面第一章对JSArrayBuffer backing_store对象的说明,可以提前得到rcx中的值,当然得到的是其偏移地址,而基地址可以用Uint32Array对象来泄露,用泄露出的基地址加偏移地址就可以得到真实的backing_store地址,现在用我们要读写的64位地址减去backing_store地址,再将结果传入对应的读写函数就可以实现64位地址空间读写。

为方便解释,以下所有代码示例将通过sandbox对象来实现双数组混淆来实现读写原语的构造,此对象主要用对沙箱的测试,在稳定版中不可用,官方说明:V8 Sandbox - Readme (googlesource.com)。

以下wasm代码与开头的类似,为了方便构造写原语我又添加了oob_writedo_write函数:

V8堆沙箱绕过方法分析总结

之后将转换为二进制数的wasm代码放入数组中,以便于将函数导入到js代码中调用:

V8堆沙箱绕过方法分析总结

然后利用sandbox对象来完成双数组混淆,具体做法就是直接去修改数组d_arr的长度字段,将其长度修改为0x1000,此时d_arr就可以去读写o_arr数组中保存的数据内容:

V8堆沙箱绕过方法分析总结

在得到两个混淆的不同类型的数组后就可以像其他V8漏洞那样构造地址泄露函数:

V8堆沙箱绕过方法分析总结

此时我们就可以着手准备构造任意地址读写原语了,首先通过混淆数组得到double型数组的map,随后在一个新的double型数组f_arr中伪造一个假的double数组fake_obj,之后我们就可以通过f_arr去控制fake_obj的elements地址,但要注意的是因为堆沙箱的存在,被填入的elements地址将被限制在堆沙箱内存区域内:

V8堆沙箱绕过方法分析总结

在得到读写原语与地址泄露函数后就可以通过上文中提到的方法去混淆call_target了:

V8堆沙箱绕过方法分析总结

最后要注意的是,oob_write函数在向内存中写入数据时还会将传入的目标地址与wmemory堆地址相加,所以为了确保写入的地址正确,我们还需要得到wmemory堆地址,并用要写入的目标地址减去wmemory堆地址,这样在写入时就可以写入到正确的目标地址:

V8堆沙箱绕过方法分析总结

V8堆沙箱绕过方法分析总结

此方法在chrome 126版本之后修复,在修复程序中添加了新的IsAccessedMemoryCovered函数:

V8堆沙箱绕过方法分析总结

此函数先回检查目标地址是否为空,如果不是将会检查访问的目标地址是否位于沙盒内。gV8SandboxBasegV8SandboxSize也是新添加的内容,gV8SandboxBase为沙盒区域的基地址,gV8SandboxSize则是沙盒区域的大小。

02

blink object confusion breaking sandbox

一开始提到在V8中所有的blink对象都以外部指针表索引的形式被V8 api对象所引用,虽然外部指针表受到保护无法篡改里面的内容,但是api对象是在堆中,可以篡改api对象中的embedder fields字段,使两个blink对象产生混淆,比如将DOMRectDOMTypedArray 混淆。

DOMRect 对象只有四个属性:xywidthheight,访问这些属性本质上只是对相应对象的指定偏移进行读写,如果DOMRectDOMTypedArray 发生了混淆,那就可以通过DOMRect 中的属性字段自由控制DOMTypedArray对象的backing_store指针,该指针用于指向DOMTypedArray实际的数据存储区域,可以将此指针覆盖修改为其他任意64位地址指针从而实现64位地址空间读写。先创建要被混淆的DOMRectDOMTypedArray

V8堆沙箱绕过方法分析总结

此处与上一种方法相同,同样使用sandbox构造堆读写原语,不再进行说明:

V8堆沙箱绕过方法分析总结

最后将DOMRectDOMTypedArray 进行混淆:

V8堆沙箱绕过方法分析总结

此方法至少在chrome 126版本之前都可用。

Conclusion

尽管 V8 引擎在其设计中引入了沙箱机制以增强安全性,但攻击者仍然能够通过复杂的对象混淆和内存操控来打破沙箱的边界。因此,在未来的安全研究中,如何更好地隔离这些敏感数据对象以及如何进一步优化沙箱设计将成为研究重点。

参考链接

1. The V8 Sandbox

2. V8 Sandbox Bypass

3. V8 Sandbox - Readme

4. Sandbox

5. From object transition to RCE in the Chrome renderer

【版权说明】

本作品著作权归Anansi所有

未经作者同意,不得转载

V8堆沙箱绕过方法分析总结
Anansi

天工实验室安全研究员

研究领域:Chrome浏览器漏洞研究

往期回顾
01
正则表达式安全研究
02
VMware设备虚拟化漏洞挖掘(下篇)
03
VMware设备虚拟化漏洞挖掘(中篇)
04
VMware设备虚拟化漏洞挖掘(上篇)
V8堆沙箱绕过方法分析总结
每周三更新一篇技术文章  点击关注我们吧!

原文始发于微信公众号(奇安信天工实验室):V8堆沙箱绕过方法分析总结

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月9日13:40:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   V8堆沙箱绕过方法分析总结https://cn-sec.com/archives/3245374.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息