漏洞描述
4月2号,ZDI 博客上发布了一篇关于CVE-2020-3947的分析文章[1]。自从Pwn2Own在2016 年开始引入虚拟化类别的挑战后,guest-to-host(虚拟机逃逸)便成为比赛的亮点之一。CVE-2020-3947 并不是今年比赛中被利用的漏洞,而是由一位匿名研究员于去年12月份提交给 ZDI的,并且Vmware 官方已在比赛前提供了修复版本。
根据官方通告描述[2],CVE-2020-3947 是一个存在于Vmware Workstation 和Vmware Fusion DHCP组件(vmnetdhcp.exe)中的 UAF 漏洞,VMware官方评估这是一个CVSSv3 评分为 9.3 的严重漏洞。如果漏洞被成功利用,可能导致攻击者通过客户机在宿主机上执行任意代码,或者造成宿主机上 vmnetdhcp 服务的 DoS 情况。对于 Vmware Workstation 15.x 系列,15.5.2 之前的版本都受该漏洞的影响。
借助栈回溯 + DHCP 源码辅助分析
本次漏洞分析的环境如下表所示:
宿主机(host)操作系统 | Winsows 10 1909 |
客户机(guest)操作系统 | Winsows 10 1909 |
漏洞软件 | Vmware Workstation 15.5.1 |
调试器 | Windbg |
反汇编器 | IDA |
关于 DHCP 的相关基础,可参考文章最后的 DHCP 工作流程、Wireshark 抓包、DHCP 报文分析。
Vmware 并不开源,不过既然漏洞存在于 DHCP 组件中,那么可以找来 ISC DHCP的源码作为一个参考(下载地址https://www.isc.org/download/#DHCP)。
分析时,可以借助ret-sync 插件帮助ida 和 windbg 实现联动调试,动静结合提高效率(ret-sync https://github.com/bootleg/ret-sync)。
触发漏洞的程序使用 Python 编写。
用Windbg 附加到 vmnetdhcp.exe,执行 PoC 后效果如下:
此时如果通过 ipconfig /renew重新为虚拟机分配IP地址,会看到 DHCP服务器已无法正常提供服务。
接下来,通过触发异常后的栈回溯来分析漏洞。
通过 !heap 的结果可以看出,无法访问的内存是因为在之前已经被释放了。根据栈回溯,目前可以收集到 UAF 漏洞的以下信息:
- UAF 中的Use 发生在 vmnetdhcp+0x1896 ,也就是程序崩溃处,用 ub 命令看该地址前面的指令如下图所示:
被调用的内存地址来自 eax ,那么之后需要搞清楚 eax 的值来源于何处。
- UAF 中的Free 发生在 vmnetdhcp+0x337d 附近,用 ub 命令看该地址之前的指令:
对照 IDA 中的汇编码,vmnetdhcp+0x3377 处调用的正是 free 函数:
被释放的内存地址来自 ecx,所以需要搞清楚ecx中值的含义。
- 触发漏洞时的函数调用如下,这些函数也将成为接下来分析的关键线索:
下面先从调用 Free的函数 vmnetdhcp+0x3160入手。
不过这里,先不必着急去直接看反汇编,结合 ZDI 博客中给出的漏洞函数名supersede_lease(vmnetdhcp+0x3160),先去 DHCP 源码中查找看看。有同名函数,虽然它们的代码不可能完全相同,但是大体流程肯定还是有相似之处,所以还是很有参考价值。
supersede_lease源码中调用到 dfree 时,参数是租约(lease)结构体变量 comp 的 uid。
此时再回到反汇编代码中,唯一一处free 的参数有没有可能就是租约结构的 uid 字段呢?
这是很有可能的!在反汇编中,a1 是函数 supersede_lease 的第一个参数,而且根据其他使用了 a1 的地方多用到它的偏移,基本可以确定 a1 就是一个 lease 结构体的变量,至于 lease 结构体的具体内容,详见下图:
这里就根据上图,在 IDA 中创建结构体 lease :
将 al 转变成结构体变量,同时,参考 dhcp 源码,修改变量的名字:
此时再看 free 处,free 的果然是 comp->uid。
有了 lease 结构体和 DHCP 源码的加持,反汇编看起容易多了。还是以栈回溯中列出的关键函数为主,去看看崩溃点处的情况。
vmnetdhcp+0x1896 位于 vmnetdhcp+0x16E0 函数中,大致浏览该函数的反汇编之后,发现其中有不少字符串。一些单个的单词暂时没有太大用处,而下面这句打印语句成功的引起了我的注意。或许可以通过在 DHCP 源码中查找它来定位函数名称。
这句话在源码中原封不动找到了,也就是说崩溃极有可能发生在 write_lease 函数中,而且该函数的参数也是一个 lease 结构体变量。
在 IDA 中修改vmnetdhcp+0x16E0的函数名和变量类型。
整理以上信息:
vmnetdhcp.exe在处理vmware 客户机发来的 DHCPRELEASE 消息时,会调用到 supersede_lease 函数,supersede_lease 函数中又会调用到 write_lease 函数。当服务器反复收到构造好的 DHCPDISCOVER 消息 + DHCPRELEASE消息 时,会出现先在supersede_lease 中释放租约结构体变量 comp->uid ,然后又在 write_lease 中使用租约结构体变量 comp->uid 的情况,如此便形成了 UAF 漏洞。
附录 A DHCP 工作流程
DHCP(Dynamic Host Configuration Protocol)是动态主机配置协议,前身是BOOTP协议,是一个局域网的网络协议,使用UDP协议工作。常用有2个端口:67(DHCP server),68(DHCP client)[3]。
采用C/S(客户端/服务器)模式,主要作用是集中管理、分配IP地址,使网络环境中的主机动态的获得IP地址、Gateway地址、DNS服务器地址等信息。
DHCP客户端向DHCP服务器发送的报文称之为DHCP请求报文,而DHCP服务器向DHCP客户端发送的报文称之为DHCP应答报文。
DHCP 协议中出现的报文具体如下[4]:
备注:
- 发送方向中,S 代表 DHCP 服务器、C 代表 DHCP 客户端;
- 租约期限: 当DHCP客户端获取到一个IP地址后,并不代表可以永久使用这个地址,而是有一个使用期限,在DHCP中称之为租约期限。
报文类型 | 发送方向 | 描述 | |
1 | DHCP Discover | C -> S | DHCP客户端广播寻找DHCP服务器DHCP客户端在请求IP地址时并不知道DHCP服务器的位置,因此DHCP客户端会在本地网络内以广播方式发送Discover请求报文,以发现网络中的DHCP服务器。所有收到Discover报文的DHCP服务器都会发送应答报文,DHCP客户端据此可以知道网络中存在的DHCP服务器的位置。 |
2 | DHCP Offer | S-> C | DHCP服务器响应DHCP客户端广播请求DHCP服务器收到Discover报文后,就会在所配置的地址池中查找一个合适的IP地址,加上相应的租约期限和其他配置信息(如网关、DNS服务器等),构造一个Offer报文,发送给DHCP客户端,告知用户本服务器可以为其提供IP地址。但这个报文只是告诉DHCP客户端可以提供IP地址,最终还需要客户端通过ARP来检测该IP地址是否重复。 |
3 | DHCP Request | C -> S | DHCP客户端选择一个 DHCP服务器并请求IPDHCP客户端可能会收到很多Offer请求报文,所以必须在这些应答中选择一个。通常是选择第一个Offer应答报文的服务器作为自己的目标服务器,并向该服务器发送一个广播的Request请求报文,通告选择的服务器,希望获得所分配的IP地址。另外,DHCP客户端在成功获取IP地址后,在地址使用租期达到50%时,会向DHCP服务器发送单播Request请求报文请求续延租约,如果没有收到ACK报文,在租期达到87.5%时,会再次发送广播的Request请求报文以请求续延租约。 |
4 | DHCP Decline | C -> S | DHCP客户端告知所分配 IP 不可用DHCP客户端收到DHCP服务器ACK应答报文后,通过地址冲突检测发现服务器分配的地址冲突或者由于其他原因导致不能使用,则会向DHCP服务器发送Decline请求报文,通知服务器所分配的IP地址不可用,以期获得新的IP地址。 |
5 | DHCP ACK | S -> C | DHCP服务器确认租约DHCP服务器收到Request请求报文后,根据Request报文中携带的用户MAC来查找有没有相应的租约记录,如果有则发送ACK应答报文,通知用户可以使用分配的IP地址。客户端在接收到这个报文之后才会确认分配给它的IP和其他信息可以被允许使用。 |
6 | DHCP NAK | S -> C | DHCP服务器告知无法分配可用 IP如果DHCP服务器收到Request请求报文后,没有发现有相应的租约记录或者由于某些原因无法正常分配IP地址,则向DHCP客户端发送NAK应答报文,通知用户无法分配合适的IP地址。 |
7 | DHCP Release | C -> S | DHCP服务器请求释放IP 地址当DHCP客户端不再需要使用分配IP地址时,就会主动向DHCP服务器发送RELEASE请求报文,告知服务器用户不再需要分配IP地址,请求DHCP服务器释放对应的IP地址。 |
8 | DHCP Inform | C -> S | DHCP客户端请求详细配置信息DHCP客户端如果需要从DHCP服务器端获取更为详细的配置信息,则向DHCP服务器发送Inform请求报文;DHCP服务器在收到该报文后,将根据租约进行查找到相应的配置信息后,向DHCP客户端发送ACK应答报文。目前基本上不用了。 |
以上就是DHCP 客户端和服务器之间的交互过程了。
附录 B Wireshark 抓包
和本次漏洞相关的关注重点是DHCP Discover和 DHCP Release,在细看这两种类型的报文前,先来用Wireshark 把它们抓下来。
实验环境:
- 宿主机 Win10;
- Vmware Workstation 15.5.1;
- Win 10 的虚拟机(网络适配器选择 NAT 模式);
在Wireshark中点击start开始抓包
在过滤栏输入bootp,使其只显示DHCP数据包。
在cmd中输入ipconfig /release 断开网络连接,抓到DHCP Release 包。
在cmd中输入ipconfig /renew,抓到DHCP Discover 等。
附录 C DHCP报文分析
上述8 种DHCP类型报文的基本格式相同,只是报文中的某些字段值不同。 它们基本的格式如下图所示:
其中位于报文结构最后的 Option 字段结构如下:
optionCode字段定义了option的类型。0x35(53)代表DHCP消息类型,而 0x3d(61)则代表客户端标识符。
DHCP消息种必须包含一个DHCP消息类型Option。对于DHCP消息类型Option,optionLength字段的值为1,而optionData字段指示消息类型。值1表示DHCPDISCOVER消息,值7表示DHCPRELEASE消息。
对于客户端标识符 Option,抓包看到的内容如下:
DHCP客户端使用此option来指定其唯一标识符。DHCP服务器使用这个值对它数据库的地址绑定进行索引。这个option的值对于管理域中的所有客户端都是唯一的。即服务器凭option 61来做唯一区分客户端的。当没有给option 61 填充数据时,会默认使用硬件地址来填充[5]。
参考链接:
1
2
https://www.vmware.com/security/advisories/VMSA-2020-0004.html
3
https://www.cnblogs.com/happygirl-zjj/p/5976526.html
4
https://blog.csdn.net/one_in_one/article/details/51684551
5