对VMware虚拟机逃逸补丁的分析

一、前言

虚拟机指的是安装在正常宿主机操作系统内的一个完全隔离的客户机操作系统。虚拟机逃逸指的是突破虚拟机的限制,实现与宿主机操作系统交互的一个过程,攻击者可以通过虚拟机逃逸感染宿主机或者在宿主机上运行恶意软件。在最近举办的PwnFest黑客大会上(由Power of Community组织,在韩国首尔举办),研究人员成功实现了VMware的虚拟机逃逸,这也是VMware首次在公开场合被攻陷,我们对此很感兴趣,因此,McAfee IPS漏洞研究团队决定深入研究这个过程,加深对该漏洞的理解。

二、背景知识

VMware反应非常迅速,他们很快发布了一个安全补丁修复这些漏洞,同时公布了一份安全公告。根据我们在闭源软件安全问题方面的一贯做法,我们研究了一下这份公告。公告里面提到这样一段话:

“VMware Workstation和Fusion的拖放(drag-and-drop,DnD)功能中存在一个越界内存访问漏洞。在运行Workstation或Fusion的操作系统上,攻击者可以利用这个漏洞实现客户机逃逸,在宿主机上执行代码。在Workstation Pro和Fusion上,如果拖放功能和复制粘贴(copy-and-paste,C&P)功能都被禁用,那么这个漏洞就无法利用”。

这个漏洞存在于拖放和复制粘贴功能中。这两个功能都用到了VMware远程过程调用(remote procedure call,RPC)机制。VMware的RPC机制一直以来都是一个非常容易被攻破的点,容易实现客户机到宿主机的逃逸。

在我们深入分析VMSA-2016-0019(CVE-2016-7461)的补丁之前,我们必须先对VMware Workstation如何处理客户机到宿主机或者宿主机到客户机之间的复制粘贴操作有所了解。

下图从类的层次化角度描述了VMware的拖放和复制粘贴(DnDCP)模式(来源:VM Tools开源代码):

为了无缝实现宿主机与客户机之间的拷贝粘贴操作,客户机操作系统需要安装VMware tools。VMware tools负责处理客户机到宿主机或者宿主机到客户机之间的通信。在我们研究过程中,我们使用的环境为Windows客户机以及Windows宿主机。在Windows客户机中,tools的主进程为vmtoolsd.exe。

宿主机与客户机之间相互通信的一种方法是借助RPC接口。VMware使用了一种名为后门(Backdoor)的RPC接口。

2.1 客户机的RPC机制

让我们好好研究一下客户机与宿主机系统彼此如何通过RPC接口进行通信。为了理解客户机的RPC机制,我们参考了VMware tools的开源组件,即open-vm-tools,这个组件使用如下函数来处理客户机的RPC调用:

从理论上讲,任何用到RpcChannel_Send()或者RpcOut_send()函数的报文都可以使用rpctools.exe工具来发送,这个工具是VMWare Workstation内置的一个命令行工具。

RpcOut_Send()调用了Message_Send(),后者会调用Backdoor()函数。

Backdoor函数负责通过VMware专用的I/O端口来发送消息。

当调用Backdoor函数从客户机往宿主机发送消息时,通常情况下我们可以看到如下指令集:

安装完VMware tools后,我们可以在vmtools.dll中找到这个函数。如下图所示,我们可以看到Backdoor()正在调用sub_10050190函数:

深入研究后,我们发现这个函数会执行特权指令“in.”。

让我们回到漏洞上来。我们之所以对DnDCP RPC消息感兴趣,原因是根据安全公告,该漏洞位于DnDCP RPC中。在VM Tools源码中,我们可以找到DnDCP RPC消息的具体结构:

从源码中,我们可知该结构体的第一个成员为RPC命令。如果我们研究客户机中vmtoolsd.exe的vmtools!RpcOut_send(x,x,x,)函数,我们也会看到相同的信息。

1

Bool RpcOut_send(RpcOut *out, char const *request, size_t reqLen,char const **reply, size_t *repLen);

RpcOut_Send()函数的第二个参数是request-RPC报文。如果我们从客户机操作系统的vm-tools进程中导出相关报文,在数据报文中我们首先可以看到一个RPC命令(也就是DnDCPMsgHrV4结构中的第一个成员),我们也可以看到一个复制粘贴请求报文,这个报文与我们放在客户机桌面上的debasish.txt测试文件有关。

2.2 客户机中RPC报文的处理过程

我们来看看宿主机操作系统如何处理RPC请求。在宿主机中,正在运行的每个虚拟机都有一个独立的进程,进程名为vmware-vmx.exe。

当客户机发起RPC请求时,vmware-vmx.exe内部的代码会搜索客户机的RPC处理表,找到与请求对应的处理程序。

如果我们使用IDA Pro反汇编vmware-vmx.exe,从中查找原始字符串信息,我们可以找到大部分处理程序。

三、漏洞分析

根据以上信息,我们可知vmware-vmx.exe是宿主机上的主要组件,负责处理存在漏洞的复制粘贴RPC组件。接下来,我们从二进制角度对比了已打补丁和未打补丁的程序。VMware Workstation从12.5.2版本起就打上了漏洞补丁,因此我们从二进制角度对比了12.5.2版和12.5.1版的vmware-vmx.exe之间的区别。

我们发现补丁修改了vmware-vmx.exe中的几个函数,如下图所示。

其中比较有趣的一个函数是vmware_vmx!sub_140621520。

这个函数之所以会引起我们的注意,原因在于该函数内部调用了memcpy()函数,这个地方是触发越界漏洞的完美场景。

经过一番调试及逆向分析后,我们确认vmware_vmx!sub_140621520函数负责处理RPC报文,并且我们在客户机系统中可以控制该函数的某个参数。该参数为指向某个结构体的一个指针,因此我们得以控制传入的结构体的具体内容。

具体情况如下图所示。左图表示的是客户虚拟机,右图表示我们已将windbg附加到vmware_vmx.exe进程上。

如上图所示,我们在vmtoolsd.exe进程运行期间修改了一个RPC报文,随后该报文被vmware-vmx.exe进程中的vmware_vmx!sub_140621520函数接收。

让我们反编译已打上补丁的函数,看看函数添加了哪些代码来解决这个问题。

为了能够发送一个有效的RPC报文,我们参考了VM Tools的源码,源码中定义了RPC报文的具体结构。RPC报文头部结构如下图所示,我们可知该报文头部大小为0x38。

binarySize以及payloadSize字段对应的是反汇编代码中的v6及v5变量。我们可以控制这两个字段的值,触发越界访问漏洞。为了能从客户机往宿主机发送任意RPC报文,我们开发了一个独立工具,该工具利用Backdoor函数,可以实现从客户机往宿主机发送RPC报文。经过完整的逆向分析后,我们发现利用存在漏洞的这个函数,可以实现vmware-vmx.exe进程中的越界读取及写入目标。

3.1 越界读取

根据前文分析,我们可以控制payloadSize字段。在发送报文时,如果我们使用了一个以非常大的payloadSize,同时又没有分配payload缓冲区,那么当程序执行到memcpy()时,就会越界读取其他一些内存数据。

如上图所示,程序会拷贝从0x36E4D96到0x378A0D0之间0x500个字节长度的数据。然而,我们自己的数据在0x36E4DB7处的0x4C那就已经结束了,0x36E4DB7之后的数据会导致越界读取漏洞。

3.2 越界写入

如果RPC消息中包含多个报文,那么sub_1406215F0就会被调用。

为了在这个函数中实现越界写入,我们需要在一个会话内发送多个RPC报文,然后vmware-vmx会创建一个新的缓冲区,将所有报文的载荷结合在一起。经过全面的逆向分析实验后,我们最终发现,我们可以从客户机往宿主机发送包含如下特征的RPC报文,实现越界写入目标。

首先,我们需要发送带有如下特征的一个拖放RPC报文:

1、packet->binarySize为0x10000。

2、packet->payloadOffset为0x0。

3、packet->payloadSize为0x500。

通过这些选项,我们就能通过前面所有的检查条件,这些条件包括:

1、packetSize大于DND_CP_MSG_HEADERSIZE_V4。

2、packet->payloadSize小于0xFF64。

3、packet->binarySize小于0x400000。

4、packet->payloadOffset + packet->payloadSize binarySize。

程序将会创建一个新的缓冲区,将我们所有的报文载荷复制到缓冲区中。

接下来,我们需要发送另一个报文,该报文使用相同的会话ID,具体特征为:

1、packet->binarySize为0x10100。

2、packet->payloadOffset为0x500。

3、packet->payloadSize为0xFC00。

这些选项依然满足过滤条件,这些条件包括:

1、packetSize大于DND_CP_MSG_HEADERSIZE_V4。

2、packet->payloadSize小于0xFF64。

3、packet->binarySize小于0x400000。

4、packet->payloadOffset + packet->payloadSize binarySize。

由于这个报文与第一个报文的会话ID相同,并且程序已经分配了一个新的缓冲区,因此程序会继续将载荷数据复制到当前缓冲区中(因为0x500 + 0xFC00的值等于0x10100,不等于0x10000)。这样就会导致程序越界写入0x100字节大小的数据。

上图显示了vmware-vmx.exe进程在越界写入之前的内存状态。

上图显示了vmware-vmx.exe进程在越界写入之后的内存状态,其中0x40E3070处的内存为新的缓冲区尾部(0x10000)之后的内存。我们发送完第二个报文后,成功实现将0x100字节大小的数据覆盖了位于0x40E3070处的内存数据。

四、总结

本文简要介绍了VMware Workstation中的RPC机制,也分析了如何利用RPC中的漏洞实现从客户机系统到宿主机系统的虚拟机逃逸。在之后的一系列文章中,我们将详细分析漏洞利用的每个步骤,并向大家演示如何组合这些漏洞,在VMware中实现完整的虚拟机逃逸。