基于虚拟化的安全(VBS,Virtualization-based security)
很多人都认为内核是可信赖的,因为其自身拥有的高权限以及其与用户模式下的应用隔离。然而,每个月都有无数的第三方驱动发布,微软通过遥测发现每个月有超过百万个不同的驱动哈希值。这些驱动每一个都可能有很多漏洞,更不要说恶意的内核模式代码。这种现实情况下,想要内核是一个小规模的受保护的组件并且用户模式应用也不会被攻击是不现实的。这种状况导致无法完全信任内核,并使得可以的内核模式程序(利用有问题的内核模式组件)可以危害到高度私密的用户数据的用户模式关键应用程序。
正如第二章,系统架构中讨论的,Windows10和Server 2016包含了一个VBS架构,它启用了一个额外的正交信任等级: 虚拟信任级别(VTL,virtual trust level )。本节,你讲了解到Credential Guard和Device Gurad如何利用VTLs来保护用户数据和为数字代码签名提供一个可信硬件层。
正常用户模式和内核代码运行在VTL 0并且根本不知道的VTL 1的存在。这意味着任何VTL 1中的事物对于VTL 0代码都是隐藏和无法访问的。即使恶意软件能够入侵普通内核,它仍然无法获取任何存储在VTL1里面的数据,甚至包括运行在VTL1中的用户模式代码(称为隔离用户模式)。图7-2描述了本章节我们将讲到的VBS的主要组件。
Device Guard主要由Hypervisor-Based Code Integrity (HVCI) and Kernel-Mode Code Integrity (KMCI)来实现。
Credential Guard主要是通过LSA (Lsass.exe) 和隔离LSA (LsaIso.exe)实现的。
当然,和所有可信组件一样,VTL1也假设它的所有依赖都是可信的。因此,VTL1需要Secure Boot工作正常,超级监督者(hypervisor)没有被盗用。
Credential Guard
要想了解Credential Guard提供的安全和保护范围,我们需要先了解用来提供获取用户数据和资源或者登陆网络环境的几种组件:
Password:这是用户用来与计算机认证所使用的最主要的凭据。
NT one-way function(NT OWF):在成功登陆之后,使用NTLM协议来为用户在遗留组件中为用户提供认证的Hash值。这个Hash是MD4的,因此在现有的计算能力下和容易被破解。
Ticket-granting ticket(TGT):与NT OWF相同,不过使用了更现代一些的远程认证机制:Kerberos。登录成功后,TGT和相应的秘钥将会被提供给本地计算机。一旦这两部分被攻击者截获,用户的凭证将立刻被攻陷,虽然用户的密码并无法被破解和重用。
如果没有启用Credential Guard,这些组件中用户认证的信息将会在LSASS的内存中保存。
如何启用Credential Guard:
gpedit.msc -> Computer Configuration -> Administrative Templates -> System -> Device Guard -> Turn on Virtualization Based Security -> Enabled
保护密码
密码会被本地对称秘钥加密,用来为多种协议(例如Digest、RDP)提供SSO(single sign-on)。由于这些协议都使用明文认证,因此密码必须保存在内存中,这样通过代码注入,调试器或者破解技术就可以解密这些密码。Credential Guard无法改变这些固有的不安全协议。因此,唯一的解决方案就是这些关闭协议的SSO功能。这将会降低兼容性并迫使用户重新认证。
显然,更可取的解决方式是完全不使用密码。Windows Hello 允许通过生物凭证,例如人脸或者指纹来替代输入密码。如果用户根本没有密码,也就不会被盗了。另外比较熟悉的是智能卡和PIN码。由于PIN码可能在输入时被窃取,智能卡是用来保证输入不被拦截的一种途径。这是双重认证的一种。
保护NTOWF/TGT 密钥
即使有了受保护的交互式凭证,域控制器的密钥分配中心(KDC,key distribution center)还会返回TGT和它的密钥。然后用户就可以使用NTOWF来获取资源,并用TGT向KDC获取Service Ticket。Service Ticket可以用来获取远程资源,例如共享文件。
因此,只要攻击者获取到储存在LSASS中的NTOWF或者是TGT和密钥,他们就可以绕过智能卡,PIN或者面部和指纹识别来获取到用户的资源。保护LSASS不被攻击者获取是一个选择。我们可以通过PPL(Protected Process Light)架构来实现。
我们可以通过设置HKLM\System\CurrentControlSet\Consol\Lsa的RunAsPPL键值为1来运行LSASS的PPL保护。不幸的是,尽管这样能够保护NTOWF和TGT 密钥不被用户模式攻击者攻击,但是他无法保护内核模式攻击者,或者用户模式攻击者利用数以百万级的驱动漏洞来进行攻击。Credential Guard通过运行在VTL1中的LSAISO.exe进程来解决这个问题。这个进程会将用户的秘钥保存在它的内存里。
安全通信(Secure communication)
正如第二章中描述的,VTL1的被攻击面很小,因为它并不是一个完整的“NT”内核,它也并没有任何驱动或者连接任何获取IO的硬件。这也意味着,LSAISO无法直接与KDC通信。因此,上图所描述的步骤还是需要有LSASS来实现。这就引入了两个问题:LSASS与LSAISO之间如何接受和发送这些secrets,以及我们如果防止攻击者做同样的事情?
第一个问题,我们可以通过ALPC(Advanced Local Procedure Call)来实现。ALPC可以使隔离用户模式环境支持ALPC协议的RPC运行时库(Rpcrt4.dll)。这将允许一个VTL0和VTL1应用通过本地RPC来通信。通过Process Explorer我们可以看到LsaIso.exe进程有一个名为LSA_ISO_RPC_SERVER 的ALPC端口。这就是用来与LSASS通信的。
要想回答第二个问题,我们需要先了解一下密码协议和质询/响应(challenge/response)模型。如果以已经对SSL/TLS技术以及他们如何在互联网通信中避免中间人攻击(MitM, Man in the Middle)有了基本的了解,你可以把KDC和LSAISO协议想象成类似的情况。尽管LSASS在中间充当了代理的角色,它只能看到KDC和LSAISO之间发送的加密过的信息,无法知道信息的内容。因为LSAISO建立了一个只存在于VTL1中的本地“session key”,然后通过安全协议发送使用只有KDC才有的密钥加密过的“session key”。KDC将使用这个“session key”加密TGT和密钥并将其返回。
这一模型也同样可以被用来保护基于质询/响应模型的NTLM认证。比如,当用户使用明文凭证登录时,LSA将凭证发送给LSAISO,LSAISO将使用“session key”加密并返回给LSASS。当一次NTLM质询/响应发起时,Lsass将NTLM质询和之前加密的凭证发送给LSAISO。这时只有LSAISO有可以解密的密钥,于是它将凭证解密并基于NTLM质询生成响应。
然而,需要知道的是,这个模型里有四个可能存在的攻击:
1.如果机器已经被劫持,明文密码可能在输入或者发送到LASISO阶段就被截获。使用WindowsHello可以缓解这个问题。
2.正如之前提到的,NTLM没有反重播(anti-replay)属性。因此如果NTML响应被截获,它将可以被用来回复同样的质询。另外,如果攻击者在登陆之后攻陷了LSASS,它将可以截获加密的凭据并强制LSAISO来为任意NTLM质询来生成新的NTLM响应。不过这种攻击只能生效与reboot之前,因为一旦重启,LSAISO将会生成一个新的“session key”。
3.如果机器已经被劫持,在使用Kerberos登录时,NTOWF可能像标准的Hash传递攻击(pass-the-hash attack)那样被截获并重用。
4.物理登陆的用户可以关闭Credential Guard。这种情况下,旧的登录模型将会被启用(也称之为降维攻击,downgrade attack),于是旧的攻击模式将可以被实施。
UEFI lock
由于攻击者很容易就能关闭Credential Guard(只不过是一个注册表选项),Secure Boot和UEFI可以用来防止攻击者来关闭Credential Guard。我们可以通过启用Credential Guard的UEFILock。这种模式下,一个EFI运行时变量将会被写入固件的内存并需要重启生效。重启时,WindowsBootLoader会写入这个变量来记录Credential Guard是启用的。同时,一个BCD(Boot Configuration Database)选项会被记录。
当内核启动后,他会自动重写BCD选项/UEFI 运行时变量里存在的Credential Guard注册表键值。如果BCD选项被攻击者删除,BitLocker(如果启用)和基于TPM的远程验证(如果启用)会检测到改动,然后会要求启动前输入管理员的恢复密匙,并通过UEFI运行时变量来恢复BCD选项。因此,攻击者将无法在UEFI Lock模式下禁用Credential Guard。
认证策略和加强的Kerberos(Authentication policies and armored Kerberos)
“只要登录就是安全的”模式相对于哪些没有基于Credential Guard模式确实是一个很大的进步。然而,有些公司或组织可能想要更强的安全承诺:哪些被挟持的机器也不能被用来伪装或者重发用户的凭证,或者即使凭证被截获,他们也不能被用在某些特定的系统里。利用Server 2016的一个称为认证策略和加强的Kerberos(Authentication Pilicies and armored Kerberos)的功能,Credential Guard可以实现这种提升的安全模式。
在这种模式下,VTL1安全内核将通过TPM收集一个特殊的Machine ID 密钥。这个密钥将在机器加入域的操作时被用来生成一个machine TGT 密钥,这个TGT密钥会被发送到KDC。一旦配置成功,当用户使用他们的凭证登录时,他们的凭证将会结合机器的凭证形成一个来源证明(proof-of-origin)密钥。KDC将会返回用来源证明密钥加密过的NTOWF和用户TGT 密钥。这种模式下,提供了两种安全保障:
1.用户从一个已知的机器认证:如果用户或者攻击者有原始凭证并尝试在其他机器上使用它们,它们基于TPM的机器凭证将会不同。
2.NTLM响应/用户Ticket将从LSAISO生成而并不是在Lsass中手动生成:这就保证了Credential Guard在这台机器上是启用的,尽管用户可以通过某些途径关闭它。
不幸的是,如果机器被劫持,攻击者获得了来源证明密钥加密过的KDC响应。攻击者可以使用它向LSAISO来获取“session-key”加密的service tickets。这样他们就可以获取远程文件了。避免这种问题唯一的方法就是重启,因为重启会生成一个新的session key。所以,在一个启用了Credential Guard的系统中,建议每次用户登出就重启。
未来的改进
正如在第二章和第三章中讨论的,VTL1中的安全内核是目前正在进行的改进。他可以用来添加对特殊类型的PCI和USB硬件支持,这些硬件可以独占的与超级监督者和VTL1代码使用SDF(Secure Device Framework)来通信。结合BioIso.exe和FsIso.exe,基于VTL0内核模式的组件无法截获Windows Hello的认证。一旦发布,Windows Hello 凭证将会在硬件层保障对VTL0不可用。在这种模式下,Lsass将不需要参与Windows Hello认证。LsaIso将会直接从隔离的生物识别和帧服务(Frame Service)获得凭证。
Device Guard
Credential Guard主要关心用户的凭据的安全,Device Guard则有一个完全不同的目标:保护用户的机器免于各种软件或者硬件的攻击。Device Guard利用了Windows Code Integrity服务,例如内核模式代码签名(KMCS,Kernel-Mode Code Signing),用户模式代码完整(UMCI,User-Mode Code Integrity)然后通过HVCI(HyperVisor Coide Integrity)来增强他们。(CI请参考第八章第二节)
另外,由于有Custom Code Integrity和签名策略,Device Guard是完全可配置的。这些策略可以通过文件的SHA-2 Hash值或者证书签名者来设置强制允许/排除清单。(AppLocker的策略则是使用文件名或者文件路径)
这里我们不会详细描述具体的各种配置CI策略的方法,我们会列出Device Guard的几个主要保证原则:
1. 如果内核模式代码签名被实施,只有签名的代码可以被加载,即使内核被攻击者劫持
2. 如果内核模式代码签名被实施,签名代码一旦被加载将不可修改
3. 如果内核模式代码签名被实施,动态分配代码(dynamically allocated code)将被禁止
4. 如果内核模式代码签名被实施,UEFI运行时代码将不能被修改,另外Secure Boot也需要验证UEFI运行时代码在加载时是签名的。
5. 如果内核模式代码签名被实施,只有内核模式(ring 0)签名代码可以执行
6. 如果用户模式代码签名被实施,只有签名的用户模式镜像可以被加载,这就意味着所有可执行进程必须被签名,无论是exe文件或者是它所调用的dll。
7. 如果用户模式代码签名被实施,内核将禁止用户模式应用程序使现有可执行代码页可写
8. 如果用户模式代码签名被实施,并且签名策略要求Hard Code保证,则动态分配代码将被禁止
9. 如果用户模式PowerShell约束语言模式被启用,那么所有使用动态类型,反射或者其他功能允许执行Windows/.NET API的PowerShell脚本也需要被签名
有一点很重要,为了优化性能,HVCI机制在系统从休眠状态(S4 sleep state)恢复的时候不会重新认证所有的页面文件。因此,超级监督者需要信任休眠文件没有被篡改过。这是通过使用TPM中存储的本地机器秘钥来加密这些休眠文件来实现的。不过,不幸的是,如果没有TPM,这个秘钥必须存储在UEFI运行时变量中。这就给攻击者机会获得这个密钥来解密,修改并加密休眠文件。