【翻译】Bootkitting Windows Sandbox
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
背景与动机
Windows Sandbox 是微软于 2019 年 5 月推出的轻量级虚拟化功能。其官方定义为:
Windows Sandbox 提供轻量级桌面环境以安全隔离运行应用程序。在该环境中安装的软件将保持"沙盒化"(sandboxed)状态,与宿主机完全隔离运行。
该沙盒环境启动迅速且用户体验优异,可通过 配置文件 (.wsb 格式)实现双击启动纯净虚拟机。
除用于恶意软件分析外,本文还将展示其在内核研究与驱动程序开发中的应用。我们将进一步探讨如何通过引导套件(bootkit)技术劫持启动流程,在系统初始化阶段实施内核补丁。
技术概要:访问 SandboxBootkit 仓库获取可实操的引导套件实现。
Windows Sandbox 驱动程序开发实践
Jonas L 曾于推文 披露未公开命令 CmDiag
的使用方法。该命令可便捷启用沙盒环境的测试模式签名(test signing)与内核调试(kernel debugging)功能(本部分实现直接引用自笔者在 StackOverflow 的解答)。
启用开发模式需执行以下命令(所有操作需在 管理员权限(Administrator)命令行中完成):
CmDiag DevelopmentMode -On
接着启用网络调试(可通过 CmDiag Debug
命令查看其他可用选项):
CmDiag Debug -On -Net
执行此操作后,您将获得调试连接字符串 (connection string):
Debugging successfully enabled.
Connection string: -k net:port=50100,key=cl.ea.rt.ext,target=<ContainerHostIp> -v
现在启动 WinDbg 并连接到 127.0.0.1
:
windbg.exe -k net:port=50100,key=cl.ea.rt.ext,target=127.0.0.1 -v
此时启动 Windows Sandbox 即可建立调试连接:
Microsoft (R) Windows Debugger Version 10.0.22621.1 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
Using NET for debugging
Opened WinSock 2.0
Using IPv4 only.
Waiting to reconnect...
Connected to target 127.0.0.1 on port 50100 on local IP <xxx.xxx.xxx.xxx>.
You can get the target MAC address by running .kdtargetmac command.
Connected to Windows 10 19041 x64 target at (Sun Aug 7 10:32:11.311 2022 (UTC + 2:00)), ptr64 TRUE
Kernel Debugger connection established.
要加载自定义驱动程序,需将其复制到沙盒环境后使用 sc create
和 sc start
命令进行注册和启动。需注意,多数设备驱动程序可能无法正常运行或导致虚拟机冻结,但该方法仍可为安全研究提供有效途径。
这种方式的局限性在于需要大量手动操作,开发体验不够流畅。可通过在 .wsb
配置文件中配置 <MappedFolder>
(映射文件夹)和 <LogonCommand>
(登录命令)选项来优化流程。
PatchGuard & DSE(驱动签名强制)
在附加调试器的情况下运行 Windows Sandbox 会禁用 PatchGuard 内核保护机制,配合测试模式签名(test signing)即可执行自定义内核代码。但每次启动都附加调试器并非理想方案:系统启动时间显著延长,部分软件可能检测到内核调试环境而拒绝运行。此外,网络调试连接在宿主机重启后可能失效,需重新启动 WinDbg 建立与沙盒的调试会话。
为实现更高效的绕过方案,我们将参考 EfiGuard 工具的设计思路,在后文探讨如何实现具有等效功能的引导套件(bootkit)。
Windows Sandbox 内部机制回顾
2021 年 3 月,Checkpoint 研究团队发布了题为 Playing in the (Windows) Sandbox 的深度分析报告,其中详细揭示了沙盒的内部工作机制(下文部分内容基于该研究成果)。微软官方文档 Windows Sandbox 架构 亦是重要参考资料。
Windows Sandbox 采用 VHDx 分层架构和 NTFS 重解析点(reparse points)技术实现轻量化虚拟机。多数系统文件实际通过 NTFS 重解析点指向宿主机文件系统,其中关键文件为 BaseLayer.vhdx
(详见前文参考文献)。
现有研究未提及的是,宿主机上存在名为 BaseLayer
的目录直接映射至已挂载的 BaseLayer.vhdx
虚拟磁盘,具体路径如下:
C:ProgramDataMicrosoftWindowsContainersBaseImages<GUID>BaseLayer
这种方式非常实用,因为它允许我们直接读写 Windows Sandbox 的文件系统,而无需在每次尝试修改时反复停止/重启 CmService
服务。唯一需要注意的是,操作时必须以 TrustedInstaller
身份运行并启用开发模式(development mode)。
启用开发模式后,宿主机会在同级目录下生成名为 DebugLayer
的附加文件夹。该目录存在于宿主文件系统中,使我们能够直接覆盖特定文件(如 BCD
启动配置数据库和注册表配置单元 registry hives),而无需修改 BaseLayer
基础层。调试层的配置文件似乎位于 BaseLayerBindingsDebug
路径下,但未进行深入探究。启用开发模式的主要缺点是快照功能将被禁用,导致虚拟机启动时间显著增加。在 BaseLayer
中进行修改并关闭开发模式后,还需删除 Snapshots
快照目录并重启 CmService
服务才能使更改生效。
实现启动时代码执行
要理解如何实现启动时代码执行,需要掌握 UEFI(统一可扩展固件接口)的基础知识。我们团队曾发布过 UEFI 技术导论,此外推荐阅读技术博客系列 深入探索 UEFI 启动管理器 作为补充学习资料。
对于当前研究目标,只需了解固件会首先尝试从默认启动设备加载 EFIBootbootx64.efi
文件。可通过设置 BootOrder
UEFI 变量来覆盖此默认行为。要探查 Windows Sandbox 的启动机制,可执行以下 PowerShell 命令:
> Set-ExecutionPolicy -ExecutionPolicy Unrestricted
> Install-Module UEFI
> Get-UEFIVariable -VariableName BootOrder -AsByteArray
0
0
> Get-UEFIVariable -VariableName Boot0000
�VMBus File System�VMBus�EFIMicrosoftBootbootmgfw.efi�
据此可推断 Windows Sandbox 首先加载:
EFIMicrosoftBootbootmgfw.efi
如前一节所述,我们可以通过以下路径在宿主机上访问该文件(需以 TrustedInstaller
身份):
C:ProgramDataMicrosoftWindowsContainersBaseImages<GUID>BaseLayerFilesEFIMicrosoftBootbootmgfw.efi
为验证我们的假设,可以重命名该文件并尝试启动 Windows Sandbox。若使用 Process Monitor 观察,会发现 vmwp.exe
无法打开 bootmgfw.efi
且后续无任何操作执行。
理论上可通过修改 UEFI 变量调整 Boot0000
(常规虚拟机可通过 Hyper-V Manager 实现),但目前直接修改 bootmgfw.efi
更为便捷。
Bootkit 实现原理
为实现代码执行,我们将 payload 嵌入 bootmgfw
并修改其入口点指向该 payload。
我们的 EfiEntry
函数执行以下操作:
-
获取当前运行模块的映像基址(Image Base)及大小 -
必要时进行映像重定位(Image Relocation) -
挂钩 BootServices->OpenProtocol
函数 -
从 .bootkit
节区提取原始AddressOfEntryPoint
-
执行原始入口点
为简化 SandboxBootkit.efi
注入 .bootkit
节区的过程,我们使用链接器参数 /FILEALIGN:0x1000 /ALIGN:0x1000
。该设置将 FileAlignment
和 SectionAlignment
调整为 PAGE_SIZE
,实现磁盘文件与内存映像的一一映射。
Bootkit 挂钩技术
注:本节技术思路源自 Dmytro Oleksiuk 的 DmaBackdoorHv 项目,推荐参考!
修改磁盘中的 bootmgfw.efi
会触发自完整性校验失败,相关功能由 BmFwVerifySelfIntegrity
函数实现(该函数直接读取设备文件,未使用 UEFI BootServices
API)。绕过方案有二:
-
挂钩 BmFwVerifySelfIntegrity
函数强制返回STATUS_SUCCESS
-
使用 bcdedit /set {bootmgr} nointegritychecks on
关闭完整性检查(理论上可通过修改LoadOptions
动态注入该选项,但未深入验证)
初始方案采用 bcdedit
,但因沙盒内可检测到此修改,故改用修补 BmFwVerifySelfIntegrity
的方案。
通过替换启动服务 OpenProtocol
函数指针,我们成功挂钩 winload.efi
。该函数通过 EfiOpenProtocol
调用,而后者在 winload!BlInitializeLibrary
执行过程中被触发。
在挂钩函数中,我们从返回地址(return address)回溯至 ImageBase
并检查映像是否导出 BlImgLoadPEImageEx
。随后恢复 OpenProtocol
挂钩并对 BlImgLoadPEImageEx
进行劫持。此函数优势在于允许我们在 ntoskrnl.exe
加载后(入口点执行前)对其进行修改。
当检测到加载的映像是 ntoskrnl.exe
时,调用 HookNtoskrnl
函数禁用 PatchGuard 和驱动签名强制(DSE)。因 EfiGuard 采用类似修补方式,此处仅简要说明:
-
在 SepInitializeCodeIntegrity
函数中修补CiInitialize
参数以禁用驱动签名强制 -
修改 KeInitAmd64SpecificState
初始化例程以禁用 PatchGuard
附:Windows Sandbox 日志记录
常规 Hyper-V 虚拟机调试可参考 tansadat 的 UEFI 调试指南。但 Windows Sandbox 暂未发现启用串口输出的方法,需另寻日志获取途径。
通过 Process Monitor 观察沙盒文件系统访问(过滤 vmwp.exe
进程),我们发现可通过访问特定路径文件实现日志输出:创建名为 EFImy log string
的文件路径(路径长度需小于 256 字符且排除特殊字符)。
另一种基础调试方法是在关键点终止虚拟机以验证代码执行流:
voidDie() {
// At least one of these should kill the VM
__fastfail(1);
__int2c();
__ud2();
*(UINT8*)0xFFFFFFFFFFFFFFFFull = 1;
}
附:UEFI 开发入门指南
SandboxBootkit
项目仅使用了 EDK2 项目的头文件。这对初学者可能不够便利(例如我们需要自行实现 EfiQueryDevicePath
设备路径查询函数),建议从 VisualUefi 项目入手会更简单。
结语
至此,您已实现在无需启用测试签名(Test Signing)或禁用 PatchGuard 的情况下加载如 TitanHide 等驱动程序!通过简单的注册表修改,还可加载 DTrace(或其更易定制的实现版本 STrace)来监控沙盒内部的系统调用(Syscalls)。
原文始发于微信公众号(securitainment):Windows Sandbox 引导套件攻击(Bootkitting)技术解析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论