【二进制漏洞-走进虚拟机逃逸技术之VMware Escape漏洞CVE-2023-20872复现】此文章归类为:二进制漏洞。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 起初,为了学习虚拟机逃逸相关技术,也为了搞懂硬件虚拟化。于是请教了某巨佬后告诉我一本书,看完之后为了验证我理解到的硬件虚拟化及虚拟化逃逸原理是否正确,于是便有了此次实验,继而有了本文。
在看完书后,网上看到了HITB 2023 大会上的一个虚拟机逃逸的议题,名字是:“Escaping From VMware Workstation Through The Disk Controller - Wenxu Yin”,有兴趣的可以自行搜索。看完视频后就觉得这个漏洞值得一试,理由是:
1 从视频内容上看,这个漏洞相当得简单直接,用来学习虚拟机逃逸是个绝佳得案例
2 视频内容可以看出,这个漏洞相当稳定好用,毕竟现场演示用的是真机实际环境直接演示,连个视频都不录,所以肯定是个稳定好用的漏洞。
基于这两条信息,就想要复现一下这个漏洞,于是有了下面的事情。
我看书学到的就是,虚拟机逃逸的本质其实就是,虚拟机软件(vmware)对non - root模式下CPU发出的IO指令的接管和处理,也就是none - root模式下发出的IO中断请求,通过VMCB结构中的信息退出到root模式来管理,而接管了guest发出的IO请求后,hypervisor(虚拟机软件)通过VMCB拿到并处理这个请求,而虚拟机软件逃逸漏洞,其实就是发生在这个处理请求时,虚拟机软件中的BUG导致。此为漏洞原理本质。
按照视频中的配置安装vmware和guest操作系统,宿主机就直接用我日常用的电脑,卸载了最新版的vmware,然后搜索到 17.0 . 0 - 20800274 版的vmware安装,guest操作系统使用ubuntu 22.04 。
首先,我们来到视频中所说的检查函数:
|
![](http://resources.yxfzedu.com/images/article_images/370883_XQ6GBAUJT4DUWBR.webp)
1 2 3 4 5 | 代码一目了然,CDB_Info就是第三个参数a3,这个结构偏移 0x30 的地方就是传进来的_MSG_SCSI_IO_REQUEST的CDB数据,只有 0x10 大小,偏移 0x28 处就是CDBlength,是一个byte,而在随后的 33 行,根据CDB数据的第一个字节右移 5 位作为下标,数组就是 1409D9238 处,数组各元素为可用的CDB长度,这个就不截图了,视频中有。其中第 4 个元素(下标 3 )为 0x40 ,我们只要将CDB数据第一个字节(OPcode)设置为 3 << 5 = 0x60 即可。
接下来代码进行了一个判断,判断传递进来的_MSG_SCSI_IO_REQUEST结构中的CDBlength是否和通过数组中下标元素得到的长度相等。首先CDB数据整个有 16 个byte,根据相关CDB文档,只要是 16 个字节以内,则不会产生越界溢出,故我们需要将CDBlength设置为 0x40 or 0x41 ,如何做到这一点?此为问题 1 ,且先按下不表。
回头看视频中,我们看到调用溢出函数的上层返回地址为 14072CC67 ,对应调用:
|
![](http://resources.yxfzedu.com/images/article_images/370883_9VSTVBAZVPZ3X9D.webp)
1 | 其中第 21 行的间接调用导致进入了目标函数,而在call前的第 16 行中,判断了a1 + 8 的指针偏移 0x18 的地方是否为空,不为空后续则调用这个地方的函数地址。而实际调试时:
|
![](http://resources.yxfzedu.com/images/article_images/370883_HXTCANWK3U8HGYB.webp)
1 | 可看到偏移 0x18 的地方根本就是 0 ,根本就不可能进入目标函数。根据上图第 16 行的间接可知,必然有一条路径是把目标函数放到偏移 0x18 的地方的,并且a1 + 8 的地方很可能是个函数表之类的结构,于是查看目标函数的交叉引用:
|
![](http://resources.yxfzedu.com/images/article_images/370883_FW6P82NS6H6TAB8.webp)
1 | 其中 0x140BC4D38 处的引用有一次对该结构的引用:
|
![](http://resources.yxfzedu.com/images/article_images/370883_HJH2DB7CJTQUV59.webp)
![](http://resources.yxfzedu.com/images/article_images/370883_P3AV8SZP8ZFPXPK.webp)
1 | 看来是在这里会根据前面的各种情况对a1 + 8 赋予各种不同的函数表指针,所以我们的工作就是让流程走到这个函数,于是交叉引用之,来到函数sub_14072D170:
|
![](http://resources.yxfzedu.com/images/article_images/370883_ZN5R2E94FUUP8Y4.webp)
1 | 在这里就很明白了,sub_14072D170会根据第二个参数来决定a1使用哪张函数表。调用sub_14072D170的有两处,不废话只说有用的一处(另一处一看就没用),来到上层:
|
![](http://resources.yxfzedu.com/images/article_images/370883_KKUD4APU6UHD8NW.webp)
1 | 这就很有意思了,这第二个参数取决于v16(结构),而v16的唯一赋值 / 使用是在 26 行,看来应该是 16 行的sub_1400F07C0根据a1 + a8的数据对v16进行了设置,我无心去看这个sub_1400F07C0内部到底怎么设置v16的,因为看到了第 32 行的那串英文,很明显那是个输出调试信息的,其中就有第二个参数,对应的输出是“ type = x”,意味着v12是类型,所以这里是根据CDROM的类型来选择不同的处理方式?于是:
|
![](http://resources.yxfzedu.com/images/article_images/370883_R8CU93QMD5YFC5Y.webp)
1 | 虽然我有十足信心这里是CD的类型,但总要实践来证明。可大事不妙,我没有物理光驱:
|
![](http://resources.yxfzedu.com/images/article_images/370883_8H8BNH7N543NW5F.webp)
1 | 随后测试在这里下断后,手工更改sub_14072D170函数调用时第二个参数的值:
|
![](http://resources.yxfzedu.com/images/article_images/370883_7Q5RYSB23PV4HDU.webp)
1 | 失败!看来此 type 值还会影响其他后续操作。但是,这个失败至少证明了一点:我们更改这第二个参数 type ,真的应更改了CDROM的类型,即CDROM的类型就可以决定这第二个参数 type 。于是:
|
![](http://resources.yxfzedu.com/images/article_images/370883_2T8RC78UMYZQ9MN.webp)
1 2 | 金钱的力量就是好使(虽然才 48 块)!接下来就是等待到货了,在这期间我也没闲着,一直在测试问题 1 怎么解决。
很显然,问题 1 其实就等同于:如何在LINUX操作系统中,给光驱发送控制命令CDB,具体是发送自己任意构造的控制信息_MSG_SCSI_IO_REQUEST。此乃代码之事自然是要交给AI:
|
![](http://resources.yxfzedu.com/images/article_images/370883_Z8ZV72PV9URQ8DF.webp)
1 | 在跟chatGPT一顿聊天后,写出了第一版的发送自定义CDB信息给光驱控制器。但随后只要将CDB长度改为大于 16 字节,就发现调试vmware的windbg中不会打印信息,而长度 6 、 10 、 12 、 16 则可正常显示。这有两种可能: 1 是Guest中的Linux内核根本就没有把IO请求发送给控制器 - - vmware,即LINUX内核中有检查, 2 是vmware对_MSG_SCSI_IO_REQUEST结构有检查。由于视频中并未提及情况 2 ,故本人倾向于情况 1 导致。于是继续翻了一两天Linux内核代码,首先发现的是chatGPT给我的方法是比较上层的内核函数,这个方法只是把CDB请求加入到了底层驱动请求队列中,并非直接与IO驱动打交道:
|
![](http://resources.yxfzedu.com/images/article_images/370883_7UM5B67DQ3TZZJ5.webp)
1 | 大致来说就是,文件驱动发送一个请求插入到队列里,而这个队列中的各种块设备操作,可能涉及到各种设备磁头位置的读写,于是采用统一的块设备驱动层来管理这些队列中的请求,一般情况下写操作因为比较慢都是直接写的内存缓存,在一定条件下才会真正落实写文件操作。而块设备会真正落实写文件时,才会真的跟块设备控制器交互。于是我们的ko真正应该调用的是落实块设备交互的那些函数 - - mptspi,这里虽然视频中也提到了。于是看Linux SCSI驱动相关模块,找到mptspi相关代码部分,调用其中函数直接去控制块设备 - - CDROM。终于,实现了可以发送任意篡改的_MSG_SCSI_IO_REQUEST结构给“块设备控制器” - - vmware:
|
![](http://resources.yxfzedu.com/images/article_images/370883_JNMGHNHDY37F7ZN.webp)
1 | 上图中,红框的 40 就是该改掉的CDBlength,之后 60 开头的那一串 22 就是CDB。至此问题 1 完美解决。同时(其实是稍早一点)USB光驱也到货了:
|
![](http://resources.yxfzedu.com/images/article_images/370883_4T3UMP4C3JS4G5Y.webp)
![](http://resources.yxfzedu.com/images/article_images/370883_DG3PUD3UP7KCJ74.webp)
1 | 可以看到直接传进来的参数二就是 0 ,直接就是另一个类型,于是后续自然调用vmware_vmx + 80ddd0 处的函数设置a1结构,之后a1 + 8 处的函数表的第 4 个元素即 + 0x18 处已经有了具体的某函数。之后便是一路畅通的走到判断 + 18 处函数并调用:
|
![](http://resources.yxfzedu.com/images/article_images/370883_5FYEG7CZJG62ND7.webp)
1 | 最后,成功触发漏洞!越界写造成crash vmware:
|
![](http://resources.yxfzedu.com/images/article_images/370883_FDXYVQ3JZK6F6NQ.webp)
1 | 实验结束!成功获得虚拟机逃逸技术,技能树点亮 + 1 。
|
POC代码在这里:https://github.com/ze0r/vmware-escape-CVE-2023-20872-poc
最后于 9分钟前
被bksaro编辑
,原因: 添加代码地址
更多【二进制漏洞-走进虚拟机逃逸技术之VMware Escape漏洞CVE-2023-20872复现】相关视频教程:www.yxfzedu.com