cve-2023-3079漏洞与patch分析

POC

function set(arr, key, val) {
    arr[key] = val;
}
function leak_hole() {
    for(let i = 0; i < 10; i++) {
        set(arguments, "foo", 1);
    }

    set(new Array(), 0, 1);
    set(arguments, 0, 1);
    return arguments[1];
}
%DebugPrint(leak_hole());

分析

通过对此漏洞的patch分析可知,此漏洞主要是由于没有检查传入的receiver是否是arguments对象并对其做特殊处理导致的:
输入图片说明
当在循环执行到第9次时将会触发内联缓调用(前八次都将是no_feedback)AccessorAssembler::KeyedStoreIC(const StoreICParameters* p)函数,由于第一次触发IC所有会进入miss分支,此分支会进入Runtime_KeyedStoreIC_Miss函数:
输入图片说明
Runtime_KeyedStoreIC_Miss函数中会调用KeyedStoreIC::Store以此来获取Store操作对应的StoreHandler对象,此对象中保存着具体用于从receiver获取属性内容的code:
输入图片说明
输入图片说明
KeyedStoreIC::Store函数开始就将会进入漏洞的主要触发流程,在第九次执行set(arguments, "foo", 1);时由于key是属性name类型会直接在调用完StoreIC::Store函数后结束:
输入图片说明
在执行set(new Array(), 0, 1);时会与前一个处理过的arguments对象相同,由于receiver与key都不同所以将会触发miss分支,并调用KeyedStoreIC::Store函数,与上一行代码不同的是set(new Array(), 0, 1);key不是一个属性name类型,而是一个元素索引类型,所以KeyedStoreIC::Store函数会先执行到以下位置:
输入图片说明
此处主要用于获取is_argumentsold_receiver_mapkey_is_valid_indestore_mode几个变量,其中store_mode比较重要,因为在生成StoreHandle时会根据它来选择具体的存储代码,由于此处处理的是array所以is_argumentsis_proxy都将会是false,而key是0也是一个有效的index,所以此处会通过GetStoreMode来获取具体的存储模式:
输入图片说明
存储模式主要有四种,根据poc来分析漏洞的话可知此处我们需要STORE_AND_GROW_HANDLE_COW模式,此模式是一个可扩展的存储模式,由于可扩展模式没有比较严格的边界检查所以会导致之后的问题,想要得到这种模式,要满足四个条件:

  • receiver是一个JSArray对象
  • 必须得是越界访问
  • 当前索引必须小于JSArray最大索引数
  • 数组元素必须连续,也就是说必须是PACKED_ELEMENTS类型的数组

之后将会去调用UpdateElements函数,UpdateElements函数会先初始化一个target_maps_and_handlers列表,通过调试可知这个列表的内容与feedback反馈槽中保存的内容是一至的,通过名称可知这个列表里主要保存map与handler:
输入图片说明
输入图片说明
map通过执行lambda表达式通过map的过渡树向上遍历map转换关系得到更新后的map,而handler则是通过反馈网络对象获取:
输入图片说明
获取完target_maps_and_handlers后会对单态会用直接用StoreElementHandler函数去获取StoreHandler,如果不满足单态那就用多态的处理函数StoreElementPolymorphicHandlers去获取StoreHandler,此处会进入多态处理函数:
输入图片说明
StoreElementPolymorphicHandlers函数会遍历处理target_maps_and_handlers列表中的每一个map与handler,并且会根据当前map是否具有过渡map来进入不同分支,此处列表中的两个map都不存在过渡,所以会直接去执行StoreElementHandler函数,StoreElementHandler函数中会根据store_mode来获取具体的code,而store_mode在前面已经提到过是通过当前正在处理的对象,也就是Array对象获取到的STORE_AND_GROW_HANDLE_COW,所以在遍历中无论是处理Arguments map还是处理Array map都是用STORE_AND_GROW_HANDLE_COW模式来获取code:
输入图片说明
最后得到一个结构如下的feedback,0是arguments对象的map,1为STORE_AND_GROW_HANDLE_COW模式的code,2为Array对象的map,3同1一样也是STORE_AND_GROW_HANDLE_COW模式的code:
输入图片说明
最后在执行set(arguments, 0, 1)时由于COW(写入时拷贝)的原因当在具体向arguments对象elelments写入内容时会将elements中的内容完整的拷贝到一块新的elements中并将要写入的内容写入。最后通过arguments[1]越界读取内容时就会将hole错误的读出来导致hole泄露。
而在官方的patch中的修复方案也比较简单,当在StoreElementHandler遇到arguments map并且elements是PACKED_ELEMENTS时,直接不使用外部传入的store_mode,而是直接用标准存储模式STANDARD_STORE
输入图片说明

利用

可以在存在此漏洞的老版本上参考2021-38003的利用方法,也可以参考之后版本的一种新的利用方法2023-2033
至于代码执行的方式在后期的版本中由于ArrayBuffer中也加入了指针压缩的技术导致已经无法在任意内存空间中读写数据,所以wasm代码执行的方法没有很好的利用方案了,但是可以创建一个以下形式的函数,并使其被优化编译:

const foo = () => {
     return [
       1.0,
       1.2
       // shellcode...
     ];
 };
 
 foo();
 for (let i = 0; i < 0x10000; i++) {
   foo();foo();foo();foo();
 }
 foo();

通过查看foo函数的code属性会发现其中的字面量数组的浮点数成员会以实际字面量数值的形式以固定相对偏移格式以及大小被直接存放在一块可执行的jit代码内存中,可以根据这特性在这个字面量数组中构造一个可以rop的shellcode,再通过修改foo函数的code指针将其落在rop shellcode代码的入口处,在执行foo函数时就可以执行shellcode。不过这种方法在之后的版本更新中可能会使用CFI机制将其修复,所以还需要找其他新的执行方法,但目前这种方法似乎还能用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值