查看原文
其他

蚂蚁安全实验室入选世界黑帽大会BlackHat USA 2022 两大榜单

在8月份举办的世界黑帽大会BlackHat USA 2022上,公布了网络安全领域最受关注的两大榜单:素有“安全界奥斯卡”之称的Pwnie Awards ,及微软安全响应中心(MSRC)公布的年度最具价值研究员榜单。

两大榜单中,Pwnie Awards 作为一项网络安全领域殿堂级的荣耀,由全球权威安全研究专家担任奖项评委,经过严格筛选才能入围提名,任何安全研究员被成功提名已是实力的象征。蚂蚁安全光年实验室研究员林性伟(@xwlin_roy)针对虚拟化设备漏洞挖掘的工作 V-SHUTTLE 获得 Pwnie Awards 2022 “Most Innovative Research”提名,连续两年获得“Most Innovative Research”提名。

第二份重量级榜单,来自于微软安全响应中心(MSRC)发布的2022 年度全球“最具价值研究员”榜单。每年度,微软根据全球安全研究人员对微软产品安全的贡献,严格甄选TOP100,以展示他们全球领先的漏洞挖掘能力。

今年,蚂蚁安全光年实验室研究员洪祯皓(@rthhh17)名列其中。在 BlackHat USA 2022 会议正式议题部分,洪祯皓和光年实验室研究员张子明(@ezrak1e)以《DirectX: The New Hyper-V Attack Surface》为题进行了深度分享,介绍了微软 Azure 云的虚拟化解决方案 Hyper-V 的新型攻击面——DirectX。

本文将对《DirectX: The New Hyper-V Attack Surface》这一议题进行深入解读。


全文首先介绍 Hyper-V DirectX 组件的基本架构,并讲解为了在虚拟机中使用这个虚拟设备,如何配置虚拟机参数。然后参考 WSL2 linux kernel 代码和逆向工程,本文将详细介绍 Hyper-V DirectX 组件的攻击面。最后会披露4个 Hyper-V DirectX 组件的漏洞,以便于读者更好的理解这个攻击面。

  • 1 Hyper-V DirectX 组件架构

  • 2 如何配置虚拟 GPU

  • 3 DirectX 攻击面

  • 4 漏洞细节

    • 4.1 CVE-2022-21918

    • 4.2 CVE-2021-43219

    • 4.3 CVE-2022-21912

    • 4.4 CVE-2022-21898


01

Hyper-V DirectX 组件架构


2020 年,Hyper-V 引入了 GPU-Paravirtualization 的新特性,这个特性基于 GPU 虚拟化技术。这个 GPU 虚拟化技术已经集成在 WDDM(Windows Display Driver Model)中,并且所有版本大于 2.5 的 WDDM 驱动都可以原生支持该虚拟化技术。然而,新的特性意味着新的攻击面。
WDDM 架构如图1 所示:
图1. WDDM 架构 [dx10arch.png]
Hyper-V DirectX 组件架构图如图 2 所示:
图2. Hyper-V DirectX 组件架构
WDDM 架构中的数据流如图 3 所示:
图3. WDDM 数据流
Hyper-V DirectX 组件架构中的数据流如图 4 所示:

图4. Hyper-V DirectX 组件数据流


02

如何配置虚拟 GPU


通过 powershell 命令,Add-VMGpuPartitionAdapter 命令来添加一个虚拟 GPU 到一个指定的虚拟机中。并且可以通过 Get-VMGpuPartitionAdapter 命令来查看指定虚拟机的虚拟 GPU 配置信息。如图 5 所示。
图5. 虚拟 GPU 配置命令
当完成了虚拟机的配置后,可以通过查看 Linux 虚拟机 Kernel Log 来确定虚拟 GPU 是否被启用。如图 6 所示。
图6. Linux 虚拟机 Kernel Log
比如说,图 6 中的虚拟机内核版本是 4.15,从图 6 中得知,这里有两个未知的 GUID,分别是虚拟 GPU DXGK channel GUID 和 global DXGK channel GUID。

如果虚拟机是 Linux 虚拟机,只有 WSL2 Linux Kernel 源代码树(https://github.com/microsoft/WSL2-Linux-Kernel/tree/linux-msft-wsl-5.10.y/drivers/hv/dxgkrnl)能原生支持 DirectX 虚拟设备。但是,Linux驱动 dxgkrnl.ko 很容易被编译并根据需求定制化。DirectX的 Linux 驱动在用户态生成了一个 /dev/dxg 设备文件,这个设备文件有很多 IOCTL 可以使用,这些 IOCTL 模拟了 WDDM 的 Windows 内核服务层。


03

DirectX 攻击面


下面着重展示 Hyper-V DirectX 的攻击面。首先要介绍的是在 Linux 虚拟机中如何初始化 Hyper-V DirectX 组件。

如图 7 所示,dxgvmbuschannel_init 函数调用 vmbus_open 函数去初始化一个 DXGK channel。
这里有两个函数分别调用了 dxgvmbuschannel_init 函数去初始化 DXGK channel,它们分别是dxgglobal_init_global_channel 函数和 dxgadapter_set_vmbus 函数。这两个函数分别初始化一个 global DXGK channel 和一个虚拟 GPU DXGK channel。
图7. DXGK Channel 初始化函数
Linux 内核驱动 dxgkrnl.ko 使用 dxgvmb_send_sync_msg/dxgvmb_send_async_msg 函数发送 DXGKRNL command 到宿主机。并且,dxgvmbuschannel_receive 函数被用来接收从宿主机发来的消息或者 command。
通过参考 WSL2 Linux kernel 源代码,可以看到这里有许多的 command 可以被使用。如图 8,图 9。
图8. DXGK Global Commands
图9. DXGK vGPU Commands
Command 消息的内存布局如图 10 所示。
图10. DXGK Command 消息内存布局
所有的 command 消息都由一个大小为 0x18 的头和消息体构成。消息的头包含四个成员,channel_type 成员用来决定是发送到虚拟 GPU DXGK channel 还是一个 global DXGK channel 中。Command_type 被用来决定发送哪个类型的 command 到宿主机。
举个例子,如图 11。
图11. 发送 dxgkrnl Command 到 Host
图 11 中的代码是 dxgvmb_send_lock2 函数发送 DXGK_VMBCOMMAND_LOCK2 command 到宿主机,在 command_vgpu_to_host_init2 函数中,可以看到 DXGK_VMBCOMMAND_LOCK2 command 是一个虚拟 GPU DXGK channel command,并且 dxgkvmb_command_lock2 结构体定义了 DXGK_VMBCOMMAND_LOCK2 command 消息体的格式。
在 Host 端的数据接收流程如 12 图所示。
图12. Host 端数据接收与处理
当 Guest 给 Host 发送 DXGK_VMBCOMMAND 数据后,VMBUS 组件会根据特定的 channel 调用 DXG_HOST_VIRTUALGPU_VMBUS::VmBusChannelProcessPacket 或者 DXG_HOST_GLOBAL_VMBUS::VmBusChannelProcessPacket 函数。
然后 VmBusProcessPacket 函数根据 channel_type 来决定在表 DXG_HOST_GLOBAL_VMBUS::VmBusCommandTableVmToHost 还是在表 DXG_HOST_VIRTUALGPU_VMBUS::VmBusCommandTableVgpuToHost 中查询 command_type 对应的处理函数。最后,调用特定的处理函数处理 Guest 发来的数据。
宿主机中 dxgkrnl.sys 驱动中的 CastToVmBusCommand 函数用来获取 Guest 发来的数据。VmBusCompletePacket 函数是 Host 用来将数据发送的 Guest 的函数,这个函数的第二参数是 databuffer,第三参数是 databuflength。
Hyper-V DirectX 组件的主要的攻击面存在于三个驱动文件中,它们分别是:dxgkrnl.sys dxgmms1.sys dxgmms2.sys。
如图 13 所示,这里一共有 87 个 command,并且每个 command 都有一个对应的结构体。可以看到,Hyper-V DirectX 组件有很大的攻击面。
图13. 攻击面

04

漏洞细节


4.1 CVE-2022-21918

下面通过四个漏洞来进一步了解 Hyper-V DirectX 组件的攻击面。第一个要介绍的是 CVE-2022-21918,这是一个空指针引用漏洞。这个漏洞发生在 DXGK_VMBCOMMAND_SIGNALSYNCOBJECT command 的处理流程中。
这个漏洞发生的核心原因是 VidSchiSignalSyncObjectsFromCpu 函数的第五参数引用了空指针,图 14 是漏洞触发时的栈回溯。
图14. CVE-2022-21918 漏洞栈回溯
VidSchiSignalSyncObjectsFromCpu 函数的第五参数由 VidSchSignalSyncObjectsFromCpu 函数第四参数赋值。VidSchSignalSyncObjectsFromCpu 函数的第四参数是 SignalSynchronizationObjectFromCpu 函数中的 v5.pfence_values,如图 15。
图15. SignalSynchronizationObjectFromCpu 函数
SignalSynchronizationObjectFromCpu 函数被 DXG_HOST_VIRTUALGPU_VMBUS::VmBusSignalSyncObject 函数调用,并且 SignalSynchronizationObjectFromCpu 函数的第一参数的 pfence_values 成员被 v24 赋值,如图 16。
图16. DXG_HOST_VIRTUALGPU_VMBUS::VmBusSignalSyncObject 函数节选
如图 17 所示,如果 v22 比 v17 小,v24 为 0。
图17. DXG_HOST_VIRTUALGPU_VMBUS::VmBusSignalSyncObject 函数节选
DXGK_VMBCOMMAND_SIGNALSYNCOBJECT command 消息的内存布局如图 18。
图18. DXGK_VMBCOMMAND_SIGNALSYNCOBJECT command 消息内存布局
内存布局中的 ObjectHandleArray,ContextArray 和 MonitoredFenceValueArray 都是可变大小的数组,它们的大小由 object_count 和 context_count 控制。
如图 19,如果 v10_ObjectCount 为 1,Context_Count 为 0,并且 v16_buffer_length 为 0x3C,那么 v17_Length_Of_MonitoredFenceValueArray 为 8,v18_Offset_Of_MonitoredFenceValueArray 为 0x3C。在图中代码的 151 行处,v22 = v16 - v18,所以 v22 等于 0,v17 等于 8,满足了 v22 < v17 的条件,所以v24 等于 0,在随后的处理流程中,会引用一个空指针。
图19
PoC 代码包含两个部分,第一部分是创建 sync_handle,第二部分是发送 DXGK_VMBCOMMAND_SIGNALSYNCOBJECT command。如 20 图设置 signalsyncobject 结构体会触发这个漏洞。
图20. CVE-2022-21918 PoC

4.2 CVE-2021-43219

下一个漏洞是 CVE-2021-43219,这也是一个空指针引用漏洞。这个漏洞发生在 DXGK_VMBCOMMAND_SUBMITCOMMAND command 的数据处理流程中。如 21 图是这个漏洞触发时候的栈回溯。
图21. CVE-2021-43219 漏洞栈回溯
在 DxgkSubmitCommandInternal 函数中,DXGCONTEXT::HandleVistaBltStub 函数的第二参数是 v37_present_history_token,并且 v37_present_history_token 可以从虚拟机中被控制。
在 DXGCONTEXT::HandleVistaBltStub 函数中,如图 22。
图22. DXGCONTEXT::HandleVistaBltStub 函数节选
如果 a2_present_history_token 小于 0,代码流程会进入到如图 22 的这个分支中,并会调用函数 CWin32kLocks::Lock 函数,CWin32kLocks::Lock 函数的第一个参数是 CWin32kLocks 结构体指针。当这个漏洞被触发,CWin32kLocks 结构体中的成员还尚未初始化,造成 BSOD。
图 23 是 PoC 代码。
图23. CVE-2021-43219 PoC
图 24 是这个漏洞的调试过程,首先设置断点在 DXGKSubmitCommandInternal+0xA689D。当这个断点被触发,检查 present_history_token 的值,可以看到现在是小于 0 的。单步到 CWin32kLocks::Lock 函数,并查看第一参数内存,可以看到在 CWin32kLocks 结构体中的许多成员都未初始化,这些未初始化的值会在 CWin32kLocks::Lock 函数被引用并造成崩溃。
图24. CVE-2021-43219 调试过程

4.3 CVE-2022-21912

下一个漏洞是 CVE-2022-21912,这是一个任意地址读漏洞。这个漏洞发生在 DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMGPU command 数据处理流程中,图 25 是这个漏洞触发时的栈回溯。
图25. CVE-2022-21912 漏洞栈回溯
图 26 是 DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMGPU command 消息的内存布局。
图26. DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMGPU command 消息内存布局
在 DXG_HOST_VIRTUALGPU_VMBUS::VmBusWaitForSyncObjectFromGpu 函数中,如果 legacy_fence_object 等于 1,syncgpu.fence_value 会赋值给 Dst.pFenceValue,并且 v23 会赋值为 0。如图 27。
图27
现在,DxgkWaitForSynchronizationObjectFromGpuInternal 函数的第四参数是 0。在图 28 中的红色标记处,因为 v5 等于 0,Src.pFenceValue 会被赋值给 v5,并且 Src.pFenceValue 可以从虚拟机中控制。在之后的代码流程中,v5 将作为 WaitForSynchronizationObjectFromGpu 函数的第三参数。
图28
在 WaitForSynchronizationObjectFromGpu 函数中,如图 29。V10 等于 a3,v10 直接解引用指针,并造成任意地址读取。
图29
图 30 是 PoC 代码。
图30. CVE-2022-21912 PoC
debug 流程如图 31 所示。
图31. CVE-2022-21912 调试流程

4.4 CVE-2022-21898

最后一个漏洞是 CVE-2022-21898,这个漏洞是任意地址写漏洞。这个漏洞发生在 DXGK_VMBCOMMAND_SUBMITVAILPRESENTHISTORYTOKEN command 数据处理流程中,图 32 是这个漏洞的栈回溯。
图32. CVE-2022-21898 漏洞栈回溯
如图 33,DXGK_VMBCOMMAND_SUBMITVAILPRESENTHISTORYTOKEN command 消息内存布局如下。在 DXG_HOST_VIRTUALGPU_VMBUS::VmBusSubmitVailPresentHistoryToken 函数中,present.unkown4_off18 被用于 DXGADAPTER::SubmitPresentHistoryTokenFromVm 函数的第七个参数。
图33
在 DXGADAPTER::SubmitPresentHistoryTokenFromVm 函数中,a7_unknown4_off18 被写入到 v29+0x300 地址的内存中。VidSchSubmitCommandContextless 函数的第一参数是 v29。如图 34。
图34. DXGADAPTER::SubmitPresentHistoryTokenFromVm 函数
VidSchSubmitCommandContextless 函数的第一参数是 VidSchiRedirectedFlipWaitOnSyncObject 函数的第二参数。
图 35 中,VidSchiRedirectedFlipWaitOnSyncObject 函数的第一参数加上 0x238 是 VidSchiAcquirePrivateDataReference 的第二参数。
图35. VidSchiRedirectedFlipWaitOnSyncObject 函数
如图 36,在 VidSchiAcquirePrivateDataReference 函数中,当 v2 等于 0,v4=*(_QWORD *)((char *)a2 + 0xC8)。a2 的值是 VidSchiRedirectedFlipWaitOnSyncObject 函数中的 v6+0x238,0x238+0xC8=0x300,所以 *(_QWORD *)((char *)a2 + 0xC8) 的值就是 DXGADAPTER::SubmitPresentHistoryTokenFromVm 函数中的 a7_unknown4_off18 的值。在之后的代码流程中,在 v4+0x0C 的地址处加一会造成任意地址写。
图36. VidSchiAcquirePrivateDataReference 函数
PoC 代码如图 37。
图37. CVE-2022-21898 PoC
Debug 流程如图 38。
图38. CVE-2022-21898 调试流程
继续滑动看下一个
蚂蚁技术AntTech
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存