【技术分享】如何绕过Windows上的VirtualBox进程保护机制
译者:興趣使然的小胃
一、前言
Windows上的进程属于一种安全的对象,可以阻止已登录Windows主机的某个用户危害其他用户的进程。至少从非管理员的用户角度来看,这是一种非常重要的安全特性。在这种安全特性下,非管理员用户无法破坏任何进程的完整性。然而,这种安全屏障在针对管理员、特别是具有调试(Debug)权限的管理员时会显得捉襟见肘,因为启用这种权限后,管理员就可以无视进程拥有的安全属性,打开任意进程。
在某些情况下,应用程序或者操作系统希望能够保护进程免受用户的影响,这些用户包括管理员用户,甚至在某些情况下,包括当前进程对应的具有完全访问权限的同一个用户。因此,许多解决方案使用了内核支持的(kernel support)特性来实现这种保护机制。在大多数情况下,这种实现方案仍然会存在缺陷,我们可以利用这些缺陷来突破“受保护”的进程。
在本文中,我们会介绍Oracle的VirtualBox在进程保护方面的具体实现方法,也会详细介绍如何通过三种方法绕过这种保护机制,将任意代码注入到进程中(这三种方法目前已被修复)。本文所展示的技术同样可以应用在类似的进程“保护”机制上。
二、Oracle VirtualBox进程防护机制
想在用户模式下完全实现进程的保护几乎是不可能的一件事情,现在有很多方法可以将数据注入到某个进程中。特别是当你想要保护的进程正运行在你想要阻止的用户的上下文环境中时,进程保护更加难以实现。比如,攻击者可以使用PROCESS_CREATE_THREAD访问权限打开某个进程的句柄,然后直接插入一个新的线程,或者攻击者可以使用THREAD_SETCON_TEXT访问权限打开进程中的线程,然后直接修改指令指针(Instruction Pointer)以跳转到任意地址,这些都是直接的攻击方式。攻击者也可以修改进程所处的注册表或者环境,强迫进程加载任意COM对象或者Windows挂钩(Hook)。攻击者能够做的修改操作可以说是不胜枚举的。
因此,VirtualBox(VBOX)借用了内核的帮助来保护它的进程。这种保护机制在源代码中对应的是进程加固(Process Hardening)技术。VBOX会尝试保护进程免受当前所属的同一用户的影响。我们可以在源代码注释中找到详细的解释以及技术概述。在这段注释的开头部分详细描述了VBOX内核驱动方面的保护机制,VBOX内核驱动中包含大量方法,这些方法可以用来突破内核或者至少可以用来提升权限。这也是为什么VBOX要去保护其进程免受当前用户的修改,因为如果用户可以访问VBOX内核驱动,那么就可以以此为据点,获取内核或系统权限。虽然某些进程保护机制也会阻止管理员控制当前进程,但这并不是进程加固代码的目标。
我的同事Jann从设备访问角度开展研究,在Linux系统的VBOX驱动及保护机制上发现过许多问题。在Linux上,VBOX限制了只有root才能访问VBOX驱动,然后利用SUID二进制程序赋予VBOX用户进程访问驱动的权限。VBOX驱动在Windows系统没有使用SUID程序,而是使用内核API来尝试阻止用户以及管理员打开受保护的进程,阻止他们注入代码。
内核组件的核心位于Support\win\SUPDrv-win.cpp文件中。这段代码注册了Windows内核支持的两种回调机制:
1、PsSetCreateProcessNotifyRoutineEx。当新进程创建时,驱动就会得到通知。
2、ObRegisterCallback。当进程以及线程句柄创建或者复制时,驱动就会得到通知。
从PsSetCreateProcessNotifyRoutineEx发出的通知可以用来配置新创建进程的保护结构。随后,当进程尝试打开VBOX驱动的句柄时,加固机制会调用supHardenedWinVerifyProcess函数,确保如下验证步骤通过后才允许相应的访问动作:
1、确保没有调试器附加到进程上。
2、确保进程中只有一个线程,并且该线程是打开驱动的唯一线程,以避免出现进程内竞争(in-process races)问题。
3、确保除了一小部分允许的DLL之外,没有其他可执行的内存页面。
4、验证所有已加载的DLL的签名。
5、验证主执行文件的签名,确保该文件为允许的执行文件类型(如VirtualBox.exe)。
VBOX将自定义运行时代码编译到驱动中,依靠这部分代码完成内核中的签名校验工作。这个过程中,只有很少一部分可信根(Trusted Roots)才能通过校验,主要包括微软的操作系统及代码签名(Authenticode)证书,以及用来签名所有VBOX程序的Oracle证书。你可以在官方的代码仓库中找到可信的证书列表。
ObRegisterCallback通知用来限制系统上其他用户进程对被保护进程的访问权限范围。ObRegisterCallback API主要是针对反病毒服务设计,以避免反病毒进程被恶意代码注入或者终止。VBOX使用了类似的方法,限制受保护进程的句柄只能具备如下访问权限:
PROCESS_TERMINATE
PROCESSVMREAD
PROCESSQUERYINFORMATION
PROCESSQUERYLIMITED_INFORMATION
PROCESSSUSPENDRESUME
DELETE
READ_CONTROL
SYNCHRONIZE
这些访问权限可以赋予用户通常需要的大多数权限,比如他们可以读取内存、同步进程以及结束进程,然而他们无法将新的代码注入到进程中。类似地,用户对线程的访问权限也被限制在如下范围,以阻止对线程上下文的修改或其他类似攻击:
THREAD_TERMINATE
THREADGETCONTEXT
THREADQUERYINFORMATION
THREADQUERYLIMITED_INFORMATION
DELETE
READ_CONTROL
SYNCHRONIZE
为了证实这一点,我们可以打开VirtualBox的进程或者其中某个线程,查看我们获得的访问权限。我们获得的有关进程以及线程的访问权限如下图高亮部分所示。