Bootkitting Windows Sandbox | secret club
mrexodia, sdoogm
2022年8月29日
引言与动机
Windows Sandbox是微软于2019年5月为Windows添加的功能。正如微软所述:
Windows Sandbox提供了一个轻量级桌面环境,用于在隔离环境中安全运行应用程序。安装在Windows Sandbox环境中的软件保持“沙盒化”并与主机分开运行。
启动通常非常快速,用户体验出色。您可以通过.wsb文件进行配置,然后双击该文件启动一个干净的虚拟机。
沙盒可用于恶意软件分析,正如本文所示,它还可用于内核研究和驱动程序开发。我们将更进一步,分享如何通过bootkit在启动过程中拦截引导过程并修补内核。
TLDR:访问SandboxBootkit存储库亲自尝试bootkit。
用于驱动程序开发的Windows Sandbox
几年前,Jonas L在推特上提到了未记录的CmDiag命令。事实证明,在沙盒中启用测试签名和内核调试几乎微不足道(此部分直接复制自我的StackOverflow答案)。
首先,您需要启用开发模式(所有操作都需要从管理员命令提示符运行):
|
|
然后启用网络调试(您可以使用CmDiag Debug查看其他选项):
|
|
这应该会提供连接字符串:
|
|
现在启动WinDbg并连接到127.0.0.1:
|
|
然后启动Windows Sandbox,它应该连接:
|
|
现在,为了加载驱动程序,您必须将其复制到沙盒中,并使用sc create和sc start运行它。显然,大多数设备驱动程序将无法工作/会使VM冻结,但这肯定有助于研究。
当然,缺点是您需要做相当多的手动工作,这并不是一个顺畅的开发体验。您可能可以通过.wsb文件中的
PatchGuard和DSE
附加调试器运行Windows Sandbox将禁用PatchGuard,并且启用测试签名后,您可以运行自己的内核代码。但每次附加调试器并不理想。启动时间大大增加,软件可能会检测到内核调试并拒绝运行。此外,网络连接在主机重新启动后不一定稳定,您需要每次重新启动WinDbg以将调试器附加到沙盒。
类似于EfiGuard的工具对我们的目的来说是理想的,在本文的其余部分,我们将研究实现具有等效功能的自己的bootkit。
Windows Sandbox内部回顾
2021年3月,一篇名为《Playing in the (Windows) Sandbox》的优秀文章发布。这篇文章包含大量关于内部结构的信息,下面的许多信息来自那里。另一个好的资源是微软官方的Windows Sandbox架构页面。
Windows Sandbox使用VHDx分层和NTFS魔法使VM极其轻量级。大多数系统文件实际上是指向主机文件系统的NTFS重解析点。对于我们的目的,相关文件是BaseLayer.vhdx(更多细节请参考上述参考文献)。
文章未提及的是,有一个名为BaseLayer的文件夹直接指向主机上以下路径中挂载的BaseLayer.vhdx内部:
|
|
这很方便,因为它允许我们读写Windows Sandbox文件系统,而无需每次尝试某些操作时停止/重新启动CmService。唯一的限制是您需要以TrustedInstaller身份运行,并且需要启用开发模式才能修改那里的文件。
启用开发模式后,在同一位置还会有一个名为DebugLayer的额外文件夹。此文件夹存在于主机文件系统上,允许我们覆盖某些文件(BCD、注册表配置单元)而无需修改BaseLayer。DebugLayer的配置似乎位于BaseLayer\Bindings\Debug中,但没有进一步调查。启用开发模式的缺点是快照被禁用,因此启动时间显著增加。在BaseLayer中修改某些内容并禁用开发模式后,您还需要删除Snapshots文件夹并重新启动CmService以应用更改。
在启动时获取代码执行
要了解如何在启动时获取代码执行,您需要一些关于UEFI的背景知识。我们几年前发布了《Introduction to UEFI》,还有一个非常有用的系列《Geeking out with the UEFI boot manager》对我们的目的很有用。
在我们的情况下,知道固件将首先尝试从默认引导设备加载EFI\Boot\bootx64.efi就足够了。您可以通过设置BootOrder UEFI变量来覆盖此行为。要了解Windows Sandbox如何引导,您可以运行以下PowerShell命令:
|
|
由此我们可以推导出Windows Sandbox首先加载:
|
|
如前一节所述,我们可以通过以下路径在主机上(作为TrustedInstaller)访问此文件:
|
|
为了验证我们的假设,我们可以重命名该文件并尝试启动Windows Sandbox。如果您在Process Monitor中检查,您将看到vmwp.exe无法打开bootmgfw.efi,之后什么也没有发生。
也许可以修改UEFI变量并更改Boot0000(Hyper-V管理器可以为常规VM执行此操作,所以可能有一种方法),但目前直接修改bootmgfw.efi会更容易。
Bootkit概述
为了获得代码执行,我们将payload的副本嵌入到bootmgfw中,然后修改入口点到我们的payload。
我们的EfiEntry执行以下操作:
- 获取当前运行模块的映像基址/大小
- 必要时重定位映像
- 钩住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的一部分执行。
在钩子中,我们从返回地址遍历到ImageBase,并检查映像是否导出BlImgLoadPEImageEx。然后恢复OpenProtocol钩子,并 detour BlImgLoadPEImageEx函数。这个函数很好,因为它允许我们在ntoskrnl.exe加载后(并在调用入口点之前)立即修改它。
如果我们检测到加载的映像是ntoskrnl.exe,我们调用HookNtoskrnl,在那里我们禁用PatchGuard和DSE。EfiGuard修补非常相似的位置,因此我们不会在这里详细介绍,但这里是一个快速概述:
- 通过修补函数SepInitializeCodeIntegrity中CiInitialize的参数来禁用驱动程序签名强制(DSE)
- 通过修改KeInitAmd64SpecificState初始化例程来禁用PatchGuard
额外内容:从Windows Sandbox记录日志
要在常规Hyper-V VM上调试bootkit,tansadat有一个很好的指南。不幸的是,没有已知的方法为Windows Sandbox启用串行端口输出(如果您知道,请联系我们),我们必须找到另一种获取日志的方法。
幸运的是,Process Monitor允许我们查看沙盒文件系统访问(过滤vmwp.exe),这允许一个巧妙的技巧:访问名为\EFI\my log string的文件。只要我们保持路径长度在256个字符以下并排除某些字符,这非常有效!
一种更原始的调试方法是在某些点杀死VM以测试代码是否按预期执行:
|
|
额外内容:UEFI入门
SandboxBootkit项目仅使用EDK2项目的头文件。这在开始时可能不方便(例如,我们必须实现自己的EfiQueryDevicePath),并且可能更容易从VisualUefi项目开始。
最后的话
目前就这些。您现在应该能够加载像TitanHide这样的驱动程序,而无需担心启用测试签名或禁用PatchGuard!通过一些注册表修改,您还应该能够加载DTrace(或更可破解的实现STrace)来监视沙盒内发生的系统调用。
标记:windows, UEFI, bootkit
上一篇:使用等式饱和改进MBA反混淆
下一篇:滥用未记录功能伪造PE节头