V8漏洞利用之对象伪造漏洞利用模板
V8漏洞利用之对象伪造漏洞利用模板
XiaozaYa 看雪学苑 2024-04-13 17:59
这个利用方式是我在Tokameine
✌的一篇文章中看到的,这里给出链接(https://bbs.kanxue.com/thread-273709.htm)。看了之后感觉比较通用,于是赶紧去适配了下之前我复现过但是没有写出exp
的漏洞(之所以没有写出exp
,就是因为伪造对象的地址非常不稳定),然后发现基本上都可以套用一个模板(毕竟差不多都是一样利用原语的漏洞),所以在此作下记录并将这种利用方式总结一下。然后先叠个buff
,由于写这篇文章的时候脑袋晕晕的,所以会出现一些错误(自己也感觉到了,但是不想改了),所以如果发现错误或者不严谨的地方,希望读者可以雅正。
指针压缩下的通用堆喷技术,效果为:获取一个低 4 字节固定的对象。
感觉利用堆分配特性也行
先来看下V8
中堆块管理结构:
一般而言,普通对象(Array/JSObject
)都分配在rw-
页面上,我们来看下最后一个堆块的信息:
在堆块页面的起始部分,有一段空间是用来存储堆块的元信息的,在V8
的堆结构中有0x2118
字节用来存储堆结构相关信息
堆块相关结构如下:
而该rw-
段的大小为0x80000
,所以紧接着后面还有其它堆块:
结构同上,所以0x000020e0081c0000 0x000020e008240000 0x0000000000000000 rw-
内存区域由两个大小为0x40000
的v8的
堆组成。如果这个时候,我申请一个0xf700
大小的数组,在新版v8
中,一个地址4字节,那么就是需要0xf700 * 4 + 0x2118 = 0x3fd18
,再对齐一下,那么就是0x40000
大小的堆,我们来测试一下:
a
的信息输出如下:
注意这里elements
指针 为0x0e1008242119
,此时查看堆布局:
elements
字段地址为0x00000e10081c0000+ 0x80000 + 0x2118 + 0x1 = 0x0e1008242119
。在启动指针压缩时,在堆中储存的地址为4字节,而根据上述堆的特性,我们能确定低2字节为0x2119
,而一般情况下其高2字节也是不变的,所以这里其实4字节都已经确认的。还有一个比较重要的点是,该FixedArray
是一个大对象,其是不受gc
影响的,所以这里的效果就是获取一个已经地址的内容可控的内存区域。
该方法主要配合上述的通用堆喷技术进行利用。
前提:
◆开启指针压缩
针对漏洞类型:
◆任意地址对象伪造
◆特定地址对象伪造
◆只能修改Array
对象的element
字段(这里包含同时修改len
和element
的情况)
效果:
◆实现4GB
堆内的任意地址读写
(后续的利用就看情况了,如何没有沙箱则打ArrayBuffer
就行了,如果有沙箱就想办法绕过沙箱,比较通用的绕过沙箱的方式就是利用JIT
构造立即数shellocde
了)
缺点:
◆exp
不具有通用性,只能针对特定版本利用
这里的漏洞类型只是一个泛称,比如:如何进行任意地址对象伪造需要根据具体的漏洞来看,这里讲的是我们已经构造了一个任意地址对象伪造的原语,接下来该如何进行利用。针对上面的漏洞类型,笔者也就一些CVE
完成了利用,我发在CSDN
上了,感兴趣可以点击链接查看。任意地址对象伪造比如:CVE-2021-38001(https://blog.csdn.net/qq_61670993/article/details/137217893)、CVE-2023-4427(https://blog.csdn.net/qq_61670993/article/details/137133853)
对于该漏洞,提出以下问题:
◆在哪里伪造对象?要求稳定,比如你在addr
这个位置伪造了一个对象,那么这块内存就不能被释放或者内存的内容不能被破坏。
◆如何进行伪造?伪造一个对象,比如JSArray
,其基本字段有map/property/len/element
,这里的property/len
好说,关键是map/element
该指向何处。
通过之前讲的通用堆喷技术,我们可以获得一块地址稳定的element
区域,这个大对象的地址是已知的,并且不受gc
的影响,而且其内容还是可控的,所以在这里伪造再合适不过。
然后就是考虑如何去伪造map
,这里需要知道的时在取对象时,会检查map
,但是只是检查其类型,所以这里我们只需要伪造其前 16 字节即可,这里调试即可知道,前16字节基本是不变的。
这里可能得把map
的前8字节改为一个可以访问的地址
然后是伪造对象,map
字段已经伪造好了,那么element
该如何伪造呢?这里我们在利用上述通用堆喷技术得到一个地址固定的element
区域(理由后面你就懂了)。
此时我们就在fake_object_array_addr
地址处伪造好了一个JSArray
,其是double
类型的,并且其element
指向leak_element_start_addr
,我们将其称作fake_object。
然后通过任意地址对象伪造漏洞获取这个对象fake_object。
此时就可以构造addressOf
原语了:
leak_object_array
和fake_object
指向的是同一个element
,而leak_object_array
将其解析为element
类型数组,而fake_object
将其解析为double
数组
然后就是先想办法实现堆内的任意地址读写了,嗯,其实很简单,因为我们的fake_object
是完全伪造在spray_array
数组的element
中的,所以我们可以直接通过spray_array
修改fake_object
的element
从而就可以实现堆内的任意地址读写了(当然了,不完全任意,因为element
要减 8,所以最开始的 8 字节无法读写)。
所以最后模板如下:
只能修改JSArray对象的element字段比如Issue 2046(https://blog.csdn.net/qq_61670993/article/details/136671278)。
这里只能修改JSArray
对象的element
字段其实不准确,这里是因为有些漏洞,我们必须写入 8 字节,而在指针压缩下len|element
一起构成了 8 字节,所以修改len
的同时会修改element
,并且还无法错位写element
,上面的示例漏洞就是这样的。
这里其实可以将element
修改为1
,然后选几个区域进行爆破,但是成功率你懂的,反正我没成功过
这里就直接说结论了,这里被修改element
字段的对象笔者称作victim_array
先利用通用堆喷技术创建一个spray_array
,这里这里spray_array
的类型为element。
然后利用漏洞修改victim_array
的element
字段,使其指向element_start_addr
:
此时就可以构造addressOf
原语了。
spray_array
和victim_array
指向的是同一个element
,而spray_array
将其解析为element
类型数组,而victim_array
将其解析为double
数组
然后就考虑如何进行堆上的任意地址读写了,很简单其实,还是利用spray_array
和victim_array
的element
指向的是同一块内存,所以利用victim_array
在element
上伪造一个对象,然后spray_array
直接读取就获取这个伪造的对象了。
这里还可以更简单的,笔者搞复杂了,但是不管了
然后利用spray_array
取出该伪造的对象:
后面的堆内任意地址读写就比较简单了,直接利用victim_array
修改fake_object
的element
即可。
模板如下:
特定地址对象伪造比如CVE-2022-1310(https://blog.csdn.net/qq_61670993/article/details/136708292)
这个其实跟二个差不多,单独拿出来,是因为写这个exp
时,我遇到了很多玄学问题,如果劫持backing_stroe
后无法往rwx
页面写shellcode。
所以这里就不写了,读者可以尝试复现上面的CVE
,毕竟自己动手才有深刻的印象。
总的来说,笔者感觉这个方法还是比较好用的,笔者也用该方法完成了之前没有完成的利用(各种玄学原因),但是其需要版本适配,所以并不是特别完美。
参考:
https://bbs.kanxue.com/thread-273709.htm
看雪ID:XiaozaYa
https://bbs.kanxue.com/user-home-965217.htm
*本文为看雪论坛精华文章,由 XiaozaYa 原创,转载请注明来自看雪社区
#往期推荐
2、.NET 恶意软件 101:分析 .NET 可执行文件结构
3、CVE-2024-0015复现 (DubheCTF DayDream)
球分享
球点赞
球在看
点击阅读原文查看更多