导语:哈希转储并不新鲜,基于PowerShell的哈希转储也不新鲜。因此,本文我们的方法虽然与现有工作基本相同,但却有很多优势。
引言
我不打算在这篇文章中介绍安全描述符的完整内容,如果你有兴趣,请点此查看它的详细介绍。如果你对这方面的了解还是一片空白,我建议你先浏览一下这里的链接。不过,我会对本文用到的术语先进行一个粗略地解释:
安全对象:由微软定义为可以具有安全描述符的对象,包括诸如文件,线程,远程注册表,Active Directory对象等许多东西。
安全描述符:包含许多字段的二进制结构,包括对象的所有者,以及指向对象的SACL和DACL的指针,这其中还包括头控制位和其他字段。
ACL:访问控制列表,SACL和DACL的超集的通用术语。
SACL:系统访问控制列表,一组控制审计访问或修改对象的ACE。
DACL:自主访问控制列表,这是一组定义哪些主体(或受托人)对给定对象具有特定权限的ACE集合。
ACE:访问控制条目,控制(如果包含在DACL中)或监控器(如果包含在SACL中)指定受托人访问对象的单独规则。
委托人或受托人:对特定对象具有特定权利的组或用户可以是主机上的本地组/用户,Active Directory中的组或用户或者众所周知的内置标识符,如“所有人”或“认证用户”。
使用基于主机的安全描述符(这是本文的重点)要记住的主要事情之一是本地计算机“管理访问”比渗透性测试更加复杂。绝大多数安全专家使用这个术语作为本地管理员组成员的同义词,但是我要说的是其实这是不对的。实际上,根据主机服务的安全描述符,本地或域组可以访问特定的远程资源(RPC,远程注册表,WMI,SQL等)。
几乎每个服务都由一个单独的安全描述符支持,该安全描述符定义特定的安全主体(“受托人”)如何与远程服务进行交互。本地管理员组只是一个安全主体,默认情况下会添加到系统上大多数可远程访问的服务的安全描述符中。你是可以从这些服务中删除此主体,并可以添加其他(非“本地管理员”)主体。因此,在大多数情况下,本地管理员的成员资格可以与“管理访问”交替使用,但在某些情况下,这种想法就会出现问题。
比如在系统遭受攻击后,攻击者可以修改单个远程访问服务的安全描述符信息,以允许他们指定的用户访问该“管理”功能。攻击者指定的用户不必是本地管理员组的成员,但可以执行DACL更改中指定的“管理”操作。另外,我还发现现有的基于主机的服务ACL配置允许非“管理”用户在主机上执行一些更有攻击性的操作。
注意:为了修改我们正在讨论的服务的访问控制信息,你需要有修改我们正在讨论的服务的访问控制信息的权限。由于这不是特权升级,因此你需要一个特定的ACE来授予你这种能力,在大多数情况下,这些能力将成为本地管理组的成员。
后门远程注册表
在研究世界黑客大会DerbyCon有关此项研究的过程中,我在探索远程访问服务时,@tifkin_和@ enigma0x3查看了远程注册表。“RemoteRegistry”服务(默认情况下在Windows Server上启动,但不在Windows工作站上启动)允许与系统的注册表配置单元进行远程交互。这包括密钥读取,密钥写入,权限修改等。值得注意的是,即使用户可以访问远程注册表,个人配置单元或密钥也具有其自己的关联权限。
那么如何访问远程注册表?如下图所示。
因此,在此密钥上设置的权限(HKLM:\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg)授予远程受托人或委托人与系统上的远程注册表进行交互的能力。
首先,我们需要确保远程注册表服务正在运行。我们可以通过WMI来做到这一点,不过假设我们有此权限。
$RemoteServiceObject = Get-WMIObject -Class Win32_Service -Filter "name='RemoteRegistry'" -ComputerName <computer> if ($RemoteServiceObject.State -ne 'Running') { $Null = $RemoteServiceObject.StartService() }
接下来,我们需要更改特定winreg密钥的权限。值得注意的是,由于我们只是添加了一个新的明确的允许ACE,我们可以将委托人或受托人设置为任何我们想要的。这意味着我们可以允许特定的域用户,域组或SID(如S-1-1-0/‘Everyone’)访问远程注册表。
我们在这里使用WMI的StdRegProv而不是[Microsoft.Win32.RegistryKey]远程注册表接口是因为我们遇到了几个使用.NET接口修改远程注册表项上的DACL的问题。虽然其中可能会有一些问题,但作为一种解决方法,我还决定决定采用WMI方法。
首先,我们通过WMI获取StdRegProv提供的程序。
$Reg = Get-WmiObject -Namespace root/default -Class Meta_Class -Filter "__CLASS = 'StdRegProv'" -ComputerName <computer>
然后,我们获取winreg密钥的当前安全描述符,其中2147483650值是指HKEY_LOCAL_MACHINE配置单元:
$SD = $Reg.GetSecurityDescriptor(2147483650, "SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg").Descriptor
接下来,我要创建一个新的win32_Ace WMI对象,将访问掩码设置为983103(ALL_ACCESS),将AceFlags设置为0x2(CONTAINER_INHERIT_ACE)以确保继续运行,并将AceType设置为0x0(允许):
$RegAce = (New-Object System.Management.ManagementClass("win32_Ace")).CreateInstance() $RegAce.AccessMask = 983103 $RegAce.AceFlags = 2 $RegAce.AceType = 0x0
然后,我会创建一个win32_Trustee WMI对象,将.Name和.Domain属性设置为我们的委托人或受托者的值,然后将此对象设置为win32_Ace对象的.Trustee属性:
$RegTrustee = (New-Object System.Management.ManagementClass("win32_Trustee")).CreateInstance() $RegTrustee.Name = ‘matt’ $RegTrustee.Domain = ‘external.local’ $RegAce.Trustee = $RegTrustee
最后,我会设置检索到的安全描述符对象的.DACL属性,并使用远程注册表提供程序上的SetSecurityDescriptor()方法为winreg项设置此新构建的安全描述符:
$SD.DACL += $RegAce.PSObject.ImmediateBaseObject $Null = $Reg.SetSecurityDescriptor(2147483650, "SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg", $SD.PSObject.ImmediateBaseObject)
后门注册表项
好的,现在我们已经完成了远程注册表访问,但是我们还可以用这个做更有意思的事情。比如,计算机帐户的哈希以及本地帐户和域缓存凭证的哈希值将存储在各种注册表项中。如果从我们指定的主体的帐户上下文中启用这些哈希的远程检索,该怎么办?
我们将在接下来的文章中详细介绍这些哈希的检索,但我会先在列出几个操作要点,并列出需要修改其权限的特定注册表项。
为了从远程系统中提取哈希,我们首先需要以某种方式检索系统的SysKey(通常称为bootkey),它是“一种Windows功能,它为存储在SAM中的密码哈希添加了额外的加密层”。要恢复此密钥,我们需要访问以下注册表项:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\JD HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Skew1 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Data HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\GBG
要恢复解密设备帐户哈希所需的LSA密钥和保护域缓存凭证的 NL$KM 密钥,我们需要访问以下密钥:
HKEY_LOCAL_MACHINE\SECURITY\Policy\PolEKList
加密的设备账号哈希存储在:
HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets\$MACHINE.ACC\CurrVal
加密的本地帐户哈希存储在:
HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\<RID>
最后,对于域缓存凭证,我们需要NL$KM密钥以及包含缓存凭证的子密钥:
HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets\NL$KM\CurrVal HKEY_LOCAL_MACHINE\SECURITY\Cache\NL$<1-10>
从我们有限的测试中看来,在某些系统中,LSA子项不会从其父容器继承权限,为此,我们就要添加我们特定的ACE:
SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg SYSTEM\CurrentControlSet\Control\Lsa\JD SYSTEM\CurrentControlSet\Control\Lsa\Skew1 SYSTEM\CurrentControlSet\Control\Lsa\Data SYSTEM\CurrentControlSet\Control\Lsa\GBG SECURITY SAM\SAM\Domains\Account
我们可以使用与winreg密钥相同的WMI StdRegProv提供程序方法向这些密钥添加显式允许的ACE。
这种方法在GitHub上新发布的DAMP(Discretionary ACL Modification Project)中的Add-RemoteRegBackdoor函数中被武器化。 -Trustee参数采用域用户或组('DOMAIN \ user'格式),众所周知的用户名(如‘Everyone’)或安全描述符字符串表示(‘S-1-1-0’)。一个或多个系统可以提供给-ComputerName参数。
接着,我就要开始研究如何修改这些帐户密钥上的安全描述符。首先想到的是滥用组策略,由于我们的同事Andy Robbins最近发布了“A Red Teamer’s Guide to GPOs and OUs”的研究成果,这让我想到了这种后门是否可以通过GPO修改被整体部署到设备上。事实证明,这并不难!
通过组策略管理编辑器中的“计算机配置 ->策略 -> Windows设置 ->安全设置 ->注册表”,可以部分处理应用组策略的任何计算机上的一个或多个注册表项的权限。微软似乎打算将这些注册表安全设置用于限制对特定系统上本地用户的各种注册表项的读取或修改访问。但对我们来说,这仅仅是这些注册表项的访问控制模型的接口!由于我们需要做的确保后台访问权限是授予委托人对winreg和System/Security/Sam密钥的适当权限,因此我们会创建如下这些项目。
如果我们打开定义这些设置的<GPOPATH> \ Machine \ Microsoft \ Windows NT \ SecEdit \ GptTmpl.inf,我们可以看到这些设置的文本表示。现在我们就知道了设置的值和格式,这样可以在可修改的GptTmpl.inf文件中替换,而不是通过GUI:
此GPO所应用的任何计算机都将允许我们的受托人远程检索计算机帐户和本地帐户哈希值!
远程哈希检索
注意:这里描述的功能包含在DAMP\RemoteHashRetrieval.ps1中。另外,哈希转储工具已经存在了十多年,开创性的PowerDump.ps1脚本(由Kathy Peters,Josh Kelley和Dave Kennedy编写)在PowerShell中实现了SysKey或本地哈希提取。而我们在本文所讲述的是“后门”方法和武器化的脚本,它是PowerDump工作的扩展。
我们需要解密设备帐号哈希,本地哈希或域缓存凭据的第一条信息是SysKey,通常也称为bootkey。关于bootkey,你可以点此详细阅读此博文,@moyix对它的用途以及如何被提取都做了详细解释,另外,@moyix还对LSA的秘密提取以及域缓存的凭据提取做了详细说明。
SysKey是从四个特定的注册表项中计算出来的:HKLM\SYSTEM\CurrentControlSet\Control\Lsa\{JD,Skew1,GBG,Data},但是@moyix提到“所需的实际数据存储在密钥的隐藏字段中,不能用像regedit这样的工具来查看。”因此,目前能为这些项提取类名称的唯一方法是通过RegQueryInfoKey API调用,它调用一个打开的注册表项。虽然许多现有的工具已使用RegOpenKeyEx在本地完成了此提取,但我们可以使用RegConnectRegistry API调用打开与远程系统上的某个项的连接。打开HKEY_LOCAL_MACHINE配置单元后,我们可以使用RegOpenKeyEx和RegQueryInfoKey为JD,Skew1,Data和GBG LSA密钥提取类。这些值然后被组合并用于计算bootkey。另外幸运的是,PowerDump脚本显示了如何实现PowerShell中所需的加密。
解密设备帐户的哈希
计算机帐户密钥存储在HKLM:\SECURITY\Policy\Secrets\$MACHINE.ACC 注册表项中。该数据是LSA秘密存储的一部分,并使用存储在HKLM:\SECURITY\Policy\PolEKList中的LSA密钥进行加密。该开机密钥用于加密存储在此注册表项中的填入LSA加密结构的AES128密钥。因此,我们可以使用bootkey解密临时AES密钥,然后解密用于保护设备帐户的LSA密钥。我们使用绑定的远程注册表连接RegOpenKeyEx打开SECURITY\Policy\PolEKList和RegQueryValueEx以提取此数据。
然后,我们使用RegOpenKeyEx打开SECURITY\Policy\Secrets\$MACHINE.ACC\CurrVal项以提取设备帐户哈希的加密数据。我们使用LSA密钥来计算提取的计算机帐户数据的前32个字节中的SHA-256哈希值,并使用此临时密钥对原始设备帐户哈希字节进行AES解密,最后MD4对数据进行哈希以得到生成的NTLM设备帐户哈希。
要在你已备份的远程设备上执行此过程,可以使用Get-RemoteLocalAccountHash <ComputerName>:
然后,可以使用远程检索的计算机帐户哈希来生成Silver Ticket以重新破坏系统,这样,我们就可以将该哈希滥用到任何ACL编辑的设备帐户,这些账户可能针对Active Directory的权限。
解密本地帐户哈希
为了提取本地帐户哈希值,Bootkey与HKLM:\SAM\SAM\Domains\Account中“F”值的一部分数据以及一些静态字符串值相结合,这样就会得到MD5哈希值,然后将得到的值用作RC4项从“F”项值解密附加数据以产生哈希引导项。此密钥用于解密来自HKLM的本地帐户哈希值:HKLM:\SAM\SAM\Domains\Account\Users\<val>,其中<val>是本地帐户的RID的十六进制表示。该解密使用了RC4,DES和二进制路径的组合,执行此操作的代码位于samdump2源代码中。
要在你已备份的远程设备上执行此过程,可以使用Get-RemoteLocalAccountHash <ComputerName>:
域缓存的凭据也以不同的方式提取,不过我们首先要提取LSA密钥,就像我们提取设备帐户哈希所做的那样,并使用它来解密存储在SECURITY\Policy\Secrets\NL$KM\CurrVal中的NL$KM密钥。然后,我们从Security\Cache\NL$<1-10>中提取加密的域缓存的凭据结构。该结构的一部分使用解密的NL$KM密钥进行解密,并提取MsCacheV2格式化的哈希和关联的用户名或域。
要在你已经备份的远程设备上执行此过程,可以使用Get-RemoteCachedCredential <ComputerName>:
总结
哈希转储并不新鲜,基于PowerShell的哈希转储也不新鲜,所以远程哈希转储也不是新事物(Impacket的secretsdump.py已经存在了很长一段时间)。因此,本文我们的方法虽然与现有工作基本相同,但却有很多优势:
1.基于ACL的后门方法,允许特定的安全主体提取这些哈希,即使它们不在目标计算机上的本地管理员组中。
2.不涉及卷影副本或注册表配置单元重复的远程哈希检索方法。
3.使用一个纯粹的PowerShell版本2兼容的武器化包,它不产生任何磁盘工件。
4.LSA秘密提取和域缓存凭证提取的PowerShell实现。
不过要注意的是,这不是一个漏洞,相反,这是一种有用的后期开发方法,可确保你在完全被攻击的设备能够继续访问,我只是利用了现有的Windows系统的访问控制模式。