CVE-2019-0717
CVE-2019-0717 :Microsoft Hyper-V虚拟网络交换机VmsMpCommonPvtSetRequestCommon越界读取
Hyper-V依靠名为Virtual Network Switch的组件为虚拟机提供各种联网服务。虚拟网络交换机(vmswitch.sys)是驻留在根分区内核中的符合RNDIS的虚拟设备。它作为半虚拟化虚拟以太网控制器直接暴露给第2代VM,在第1代VM中,它用作模拟DEC以太网控制器的后端而被间接利用。在所有情况下,从提供Internet连接和虚拟LAN到虚拟机,再到将所有内容桥接到主机上的物理以太网适配器,vmswitch都是Hyper-V云中所有网络背后的支柱。
第2代VM通过在VMBUS上发送RNDIS协议数据和网络流直接与vmswitch对话。根分区中的vmswitch.sys模块侦听Windows上DPC线程中VMBUS上的数据可用性,而该可用性将由VM生成的综合中断唤醒。然后,对VM提供的RNDIS协议数据进行解析和调度,以在虚拟设备的各个子系统中进行处理。
当vmswitch收到Set类型的RNDIS消息时,经过一系列数据清理之后,将最终调用VmsMpCommonPvtSetRequestCommon()过程进行处理。在VmsMpCommonPvtSetRequestCommon内部,一个特定的路径负责处理带有OID RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER的RNDIS设置请求:
.text:00000001C001FB07 loc_1C001FB07: ; CODE XREF: VmsMpCommonPvtSetRequestCommon+D0↑j
.text:00000001C001FB07 movups xmm0, xmmword ptr [r9]
.text:00000001C001FB0B mov r12d, [r9+10h]
.text:00000001C001FB0F mov [rbp+var_10], r12d
.text:00000001C001FB13 movd edx, xmm0
.text:00000001C001FB17 movups [rbp+arg_offset_copy2], xmm0
.text:00000001C001FB1B cmp edi, edx ; size < parameter_name_offset ?
.text:00000001C001FB1D jb @@error
.text:00000001C001FB23 mov rcx, qword ptr [rbp+arg_offset_copy2]
.text:00000001C001FB27 mov eax, edi
.text:00000001C001FB29 shr rcx, 20h
.text:00000001C001FB2D sub eax, edx
.text:00000001C001FB2F cmp eax, ecx ; size - parameter_name_offset < parameter_name_length ?
.text:00000001C001FB31 jb @@error
.text:00000001C001FB37 mov rcx, qword ptr [rbp+arg_offset_copy2+8]
.text:00000001C001FB3B shr rcx, 20h
.text:00000001C001FB3F cmp edi, ecx ; size < parameter_value_offset ?
.text:00000001C001FB41 jb @@error2
.text:00000001C001FB47 mov eax, edi
.text:00000001C001FB49 sub eax, ecx
.text:00000001C001FB4B cmp eax, r12d ; size - parameter_value_offset < parameter_value_length ?
.text:00000001C001FB4E jb @@error2
.text:00000001C001FB54 mov r15d, edx
.text:00000001C001FB57 lea r13, [r9+rcx]
.text:00000001C001FB5B add r15, r9
.text:00000001C001FB5E lea rcx, aNetworkaddress ; "NetworkAddress"
.text:00000001C001FB65 mov rdx, r15 ; Buf2
.text:00000001C001FB68 mov r8d, 1Ch ; Size
.text:00000001C001FB6E call memcmp
在以上代码片段的开头,$ r9寄存器指向rndis_config_parameter_info类型的InformationBuffer:
/ * Linux集成服务* /
/ *在OID的SetRequest中传递的信息缓冲区的格式* /
/ * OID_GEN_RNDIS_CONFIG_PARAMETER。 * /
struct rndis_config_parameter_info {
u32 parameter_name_offset;
u32 parameter_name_length;
u32 parameter_type;
u32 parameter_value_offset;
u32 parameter_value_length;
};
此数据的内容和长度由VM完全控制。
上面的代码片段中的四项检查正确地验证了rndis_config_parameter_info成员提供的数据偏移量是否在请求的范围内。但是,有一个窄边情况无法验证。当在静态长度值为0x1c的00000001C001FB6E处最终调用memcmp()时,$ r15指向VM控制的parameter_name值,该值可以小于0x1c。如果parameter_name小于0x1c,并且也落在与未分配空间相邻的已分配内存页的末尾,则根分区OS将由于读取访问冲突而进行错误检查。
此错误的潜在影响是整个Hyper-V云的持久性DoS。利用起来并不容易,必须专门整理堆,才能在没有专门的调试工具的情况下使易受攻击的Hyper-V主机崩溃。
开发注意事项
从理论上讲,有可能在未启用驱动程序验证程序的情况下利用此问题,并在任意来宾操作系统和默认配置下的Hyper-V根分区操作系统中导致持久性DoS。
首先,请注意,来宾VM对vmswitch中的内存管理有很大程度的控制。例如,考虑故障存储器上!verifier的输出:
1: kd> !verifier 80 r9
...
======================================================================
Pool block ffffd90f1b0f2fa0, Size 0000000000000060, Thread ffffd90f189d5080
fffff8005d74ee0d nt!VfAllocPoolNotification+0x31
fffff8005d73c21f nt!VeAllocatePoolWithTagPriority+0x2cf
fffff8005d74a6b8 nt!XdvExAllocatePoolInternal+0x18
fffff8005d73c6b7 nt!VerifierExAllocatePoolWithTag+0x87
fffff8005fd2949a vmswitch!RndisDevHostDispatchControlMessage+0x72
fffff8005fd28c26 vmswitch!VmsVmNicPvtKmclProcessingComplete+0x156
fffff808e58727a5 VmsProxy!VmsProxyVmNicPvtKmclProcessingComplete+0x15
fffff808e5882772 vmbkmclr!InpFillAndProcessQueue+0x242
fffff808e588243c vmbkmclr!KmclpVmbusIsr+0x13c
fffff8005fc610b0 vmbusr!ParentRingInterruptDpc+0xa0
fffff8005cecfc67 nt!KiExecuteAllDpcs+0x2e7
fffff8005cecf2ae nt!KiRetireDpcList+0x1ae
fffff8005cfd1445 nt!KxRetireDpcList+0x5
RndisDevHostDispatchControlMessage:
.text:00000001C0039485 movsxd rdx, edi ; NumberOfBytes
.text:00000001C0039488 mov ecx, 200h ; PoolType
.text:00000001C003948D mov r8d, 44527356h ; Tag
.text:00000001C0039493 call cs:__imp_ExAllocatePoolWithTag
.text:00000001C003949A nop dword ptr [rax+rax+00h]
.text:00000001C003949F mov rbp, rax
.text:00000001C00394A2 test rax, rax
.text:00000001C00394A5 jz loc_1C0069E31
.text:00000001C00394AB mov rax, [r15]
.text:00000001C00394AE mov rdx, r12 ; Src
.text:00000001C00394B1 mov ecx, [rax+28h]
.text:00000001C00394B4 mov [rbp+10h], ecx
.text:00000001C00394B7 mov r8d, ecx ; Size
.text:00000001C00394BA lea rcx, [rbp+14h] ; Dst
.text:00000001C00394BE call memmove
分配回溯中的RndisDevHostDispatchControlMessage是分派来自来宾的RNDIS请求的*功能。对于每个传入的请求,它都会根据来宾VM提供的请求大小分配内存。这样,来宾VM既可以控制vmswitch中的时序,也可以控制内存分配的大小,这有助于进行堆整理。
尚不清楚Guest VM是否对释放内存具有相同程度的控制。 vmswitch完成处理请求后,将释放为该请求分配的内存。但是,通过利用某些RNDIS请求可能比其他请求花费更长的时间这一事实,可以(至少)综合构造free()原语。
利用漏洞的一般思路是创建两种类型的内存页的交替模式:一种经过修饰,以使我们的恶意请求成为内存页中的最后一个字节序列;另一个是免费的,或者可能由VM释放。
通过首先发送许多适当大小的小型RNDIS请求,刷新Windows内核中的后备列表和*列表,可以设计出类型1页面;这将触发分配新的内存页面;然后以可控的方式占用少量分配来占据新页面。
由于攻击者对内存释放的控制的限制和KASLR缓解的限制,第2类页面的构建似乎比较棘手。但是,由于KASLR,在某些情况下,我希望在类型0页面之后自然会发生未分配的内存页面。
主机BSOD重启后,Guest VM将自动启动,并再次运行漏洞利用程序。因此,可以实现Hyper-V云的持久DoS。