本议题由微软安全工程师Joe Bialek和Nicolas Joly提供,由冰刃实验室(IceSword Lab)成员闫广禄、秦光远和廖川剑为大家进行分析解读。议题主要内容包含了Hyper-V的整体架构,通过讲解Hyper-V的各个组件提取攻击面,最后又分析了五个相关漏洞,包括DoS、信息泄露、和任意内存读写,其中任意内存读写漏洞(cve-2018-0959)获得了微软Hyper-V的最高奖金150000$。
Hyper-v漏洞奖励计划的奖金非常丰厚,最高价值250000$,这也说明挖掘Hyper-V漏洞的难度。下面将从Hyper-V的总体架构、相关组件及其攻击面来阐述Hyper-V,最后通过5个漏洞案例来进一步分析Hyper-V的安全特性。
架构总览
Hyper-V的总体架构如上图所示,Hyper-V属于第一类虚拟化架构,即裸金属架构(Bare Metal)。它直接运行在硬件上,而非像第二类虚拟化架构那样作为操作系统的一部分。
Hyper-v以Partition的形式隔离虚拟机。hypervisor通过扩展页表(Extended Page Table)EPT来管理Partition的物理内存,允许非特权Partition只能访问自己的物理内存;它还可以通过配置拦截partition的特定事件(例如hypercall、I/O指令、EPT页异常等),同时还能把中断交付给客户虚拟机进行处理。这样Hyper-v就可以管理Partition对硬件的访问,同时对不同的Partition进行隔离,使得非特权Partion不能访问其他partition的物理内存,当然也无法访问Hyper-v的物理内存。
在这些Partion中,Root Partion是一个特殊的特权Partion。它负责管理其他虚拟机(如虚拟机的创建、销毁等),也可以访问其他Partion的物理内存,以及所有的硬件设备(例如显卡、声卡),很多提供给其它partition的服务都实现在Root Partion中,例如设备模拟、半虚拟化网络和存储等。而其它的partition不能直接访问硬件,只能访问自身的物理内存,它们也不能与除Root Partion之外的其它partion直接通信。即hyper-v没有提供guest-to-guest的通信方法,只提供了guest-to-host的通信方式。
专业术语
为了更容易让读者理解后面的知识,这里罗列了相关的专业术语。
系统物理地址(SPA):真实的硬件物理地址。
客户物理地址(GPA):客户虚拟机所看到的物理地址。
客户物理地址描述符列表(GPADL):一个存储GPA的MDL,客户虚拟机可以利用GPADL描述一段物理内存。
虚拟设备(VDEV):在Host OS用户态的模拟设备或半虚拟化设备。
虚拟服务提供者(VSP):半虚拟化设备的内核部分,与一个VDEV对应。
集成原件(IC):从攻击者的角度,它与VDEV一致,是客户虚拟机可以与之通信的用户态组件。
Root Partition
Root Partition作为一个特权Partition,是攻击者首要考虑的目标,所以首先对Root Partition的服务和架构进行总结。
Root Partition Service
Root Partion负责给Guest Partion提供相应的服务,这是非常有必要的,因为Guest Partion无法访问到硬件,所以像存储、网络这类需要与硬件打交道的功能,都需要借助于Root Partion。
Root Partion提供的服务包括三类:模拟设备、半虚拟化设备和其它。
模拟设备包括:网络、存储、软盘、声卡、PCI/ISA总线、母版、串口等。模拟设备会非常慢。
半虚拟化设备包括:网络、存储、声卡、PCI。
Root Partion还需要向Guest Partion提供其它设备或服务,包括:BIOS硬件、实时迁移、动态内存、事件同步、心跳、SMB服务等等。
Hyper-v有两类虚拟机:一代和二代。二代虚拟机相比于一代虚拟机,大大减少了模拟设备的使用,而是使用了更多的半虚拟化设备。这些Root Partion提供的服务中,一些是系统强制的,另一些则可以根据配置来实施。
Hyper-v根据最小权限原则进行设计,在hypervisor中以及Root Partion内核中的代码要尽可能少。也就是说这些服务代码一般很少部署在hypervisor中,而是主要部署在Root Partion的用户空间中。
Root Partition架构
了解Root Partition的架构有助于我们确定攻击目标,Root Partition的组件主要分为内核模式组件和用户模式组件。
内核组件包括:
VMSwitch.sys:提供半虚拟化网络
StorVSP.sys:提供半虚拟化存储
VID.sys:虚拟化设施驱动
WinHVr.sys:内核到hypervisor的接口(hypercall)
VMBusR.sys:VMBUS,负责guest与host的通信
vPCI.sys:半虚拟化PCI
用户组件包括:
VMMS.exe:负责管理虚拟机的状态(没有直接的攻击面)
VMCompute.exe:负责VM管理和容器管理
Vmmem.exe:负责内存映射
Vmwp.exe(最重要):每一个虚拟机对应一个vmwp进程,虚拟机崩溃只会影响到进程,很难影响到host。它又包含了虚拟设备、vSMB服务、Plan9FS、集成原件。
通信管道
了解通信管道可以帮助我们知道如何通过guest去触发hypervisor和Root Partition中的相关组件,有助于提取攻击面。通信管道包含以下几个方面:hypervisor信道、内核态信道和用户态信道。
Hypervisor信道:
Hypercalls:即hypervisor的系统调用,它们通过物理页面或者寄存器进行参数传递。客户虚拟机可调用的hypercall的描述可参考TLFS文档。
Faults:例如虚拟机引起triple fault、或EPT页异常,都会被hypervisor捕获到。
指令模拟:执行一些特殊指令如cpuid、rdtsc、rdpmc、INVLPG、IN、OUT等也会被hypervisor捕获。
寄存器访问:如读写被监控的控制寄存器、MSR等。
overlay pages:即 hypervisor强制映射一个物理页到Partition中,例如hypercall代码页。它主要用于将数据传递给Guest Partition中。
内核态信道:
VMBUS:可通过内核模式客户端库抽象层快速访问的信道。
扩展hypercalls(较少):直接通向VID的hypercall。
Aperture:host可以映射客户虚拟机的物理页面并作用于它,很少被使用。
拦截处理(Intercept Handling):hypervisor会将部分拦截到的事件转交给host进行处理,例如I/O端口读写、EPT异常等。
用户态信道:
IO端口:当指定的IO端口被读写时,用户态组件可以注册通知回调,这通常是用来模拟硬件设备。
MMIO:组件可以注册GPA作为MMIO区域,当有读写操作时会接收通知,通常也被用来模拟硬件设备。
VMBUS:通过命名管道和socket来访问的快速通信管道。
Aperture:将客户物理地址映射到VMWP进程的虚拟地址空间中,由于guest依然映射这块物理内存,需要小心处理来防止共享内存的问题,如double-fetch攻击。
读写通知:当指定的GPA被读写时被触发。通常用于实时迁移中来标记脏的内存页。
下面将主要讲解几个容易引发漏洞的通信管道。
VMBUS
VMBUS是基于共享内存实现的,host提出channel offer申请,guest回应并提供物理内存,host把这些物理内存映射到自己的地址空间中。之后guest可以对这些物理内存进行写操作,并通知host,host进行处理。Linux integration驱动实现了这个协议,所以在逆向过程中可以参考它。
我们需要抽象层来与VMBUS进行交互,这包括:内核模式客户端库(KMCL)、VMBUS管道和VMBUS socket。
KMCL
KMCL主要被VSP使用,例如VMSwitch、StorVSP、vPCI。它是建立在回调函数的基础上(例如消息接收回调),当从VMBUS接收到消息后,它会把消息从共享内存中拷贝到内核内存池中,这样可以防止double-fetch攻击。但是外部数据的机制可能带来安全隐患,外部数据是指传递消息所附加的GPADL,它描述了包含额外数据的GPA,需要以MDL的形式进行映射。由于也映射在guest中,所以需要小心处理,防止double-fetch攻击。
下面是KMCL接收数据包的入口函数:
ProcessPacketCallBack将被调用来处理从guest接收到的数据包;ProcessingCompleteCallback将会在一组数据包被投递后调用;EVT_VMB_CHANNEL_PROCESS_PACKET函数的参数buffer存储着guest控制的数据,并非在共享内存中。
IO端口、MMIO的入口点
IO端口和MMIO的入口点主要包含四个函数:
NotifyIoPortRead、NotifyIoPortWrite、NotifyMmioRead、NotifyMmioWrite,这些函数指定了IO地址、大小以及读写的数据缓存区。
挖掘漏洞
作者列举了5个Hyper-V的漏洞,这将有助于我们了解哪些组件更容易引发漏洞,以及这些漏洞会带来什么危害。其中cve-2017-0051、cve-2018-0964、cve-2017-8706来自于VMBUS,cve-2018-0888、cve-2018-0959来自于IO解析。
cve-2017-0051
在错误处理的路径中,VmsMpCommonPvtSetNetworkAddress传递了一个攻击者可控的WSTR到日志函数中,攻击者可以构造一个并非以null结尾的WSTR,这样日志函数就会一直寻找null而产生读越界,直到产生页异常。这个漏洞可以造成针对host的DoS攻击。该漏洞提交者获得了15000$。
cve-2018-0964
VirtualDeviceCreateSingleInterrupt并非总初始化栈上对象slatedMessage,从而导致在host内核中泄露敏感信息。该漏洞提交者获得了25000$。
cve-2017-8706
主要影响vmwp.exe,相关的代码在vmuidevices.dll。消息会由VideoSynthDevice::OnMessageReceived接收,由VideoSynthDevice::SendNextMessageInternal发送。在这个过程中,会导致0x86字节未初始化的堆内存泄露。该漏洞提交者获得了15000$。由于还存在一个栈对象泄露,所以赏金又增加了15000$。
cve-2018-0888
NotifyMmioRead从ReadBuffer中返回NumberOfBytes字节到VM,虚拟设备没有填充readbuffer,导致未初始化的栈数据返回到guest中。该漏洞提交者获得了15000$。
cve-2018-0959
会影响到vmwp.exe中的EmulatedIDE,相关代码在VmEmulatedStorage.dll中。由于未预测的内部状态以及缺少边界检测,它会导致越界读写,可在4GB内存空间中进行任意读写。该漏洞提交者获得了150000$,是目前Hyper-V项目中最高的赏金。
总结
Hyper-v是一个非常有趣并且有着良好设计的攻击目标,至今还没有人拿到250000$的最高奖金,所以同学们,加油吧!