CVE-2019-0717 原理

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。

上一篇:Lanproxy任意文件读取漏洞复现(CVE-2021-3019)


下一篇:CVE-2021-21972