关于在Active Directory中滥用Kerberos的研究,最近十分火爆。所以,这篇文章也讨论了一个相对未知(从攻击者的角度来看)但很危险的特性:无约束的Kerberos委派。另外,在撰写本文的过程中,我还发现了一些有趣的RPC调用,这些调用可以让域控制器对你进行身份验证,甚至允许跨越“森林边界”发起攻击。然后我发现了PrivExchange,它可以使交换验证以类似的方式进行。由于用于无约束委派滥用的工具非常少,所以我编写了一个新的工具包krbrelayx,它可以滥用无约束委派,并从连接到你主机的用户那里获取TicketGrantingTicket(TGT)。在本文中,我将深入研究无约束的委派滥用,以及krbrelayx工具包可能提供的一些更高级的攻击。
结合NTLM中继
在开始之前,让我们先澄清一个事情:你实际上不能以中继NTLM身份验证的方式中继Kerberos身份验证。我编写的这个工具之所以称为krbrelayx,是因为它的工作方式类似于impackets ntlmrelayx(中继工具),并且共享了相当一部分代码。Kerberos票据使用基于用户正在验证的服务的密码的密钥进行部分加密,因此将其发送到不同的服务是没有意义的,因为他们将无法解密票据,因此我们无法进行身份验证。那么这个工具到底是做什么的呢? 当Windows对启用了无约束委派的服务或计算机帐户进行身份验证时,会发生一些有趣的事情(稍后我会解释),这些帐户最终会有一个可用的TGT。如果我们(作为攻击者)是控制这个帐户的人,那么可以使用这个TGT对其他服务进行身份验证。Krbrelayx执行此操作的方式与使用ntlmrelayx进行中继时类似,即使用自动转储密码、获取DA权限或执行基于ACL的攻击,因此它们的命名也很类似。如果你想了解无约束委派是什么,我推荐你看一下Sean Metcalf 的博客。
攻击要求
要执行这种无约束的委派攻击,我们需要满足以下几个要求:
1.使用无约束的委派权限控制帐户;
2.修改该帐户的servicePrincipalName属性的权限(可选);
3添加/修改DNS记录的权限(可选);
4.一种连接受害者用户/计算机的方法。
无约束的委派帐户
首先,我们需要一个具有无约束委派权限的帐户。这意味着设置了TRUSTED_FOR_DELEGATION UserAccountControl标志的帐户,可以是用户帐户,也可以是计算机帐户。AD中的任何用户都可以使用PowerView来查询这些帐户:
$Computers = Get-DomainComputer -Unconstrained $Users = Get-DomainUser -ldapfilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
或使用ActiveDirectory Powershell模块来查询这些帐户也行:
$computers = get-adcomputer -ldapfilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)" $user = get-aduser -ldapfilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
或者可以使用我自己编写的工具ldapdomaindump提取它们,它将使用TRUSTED_FOR_DELEGATION标志报告具有此权限的用户/计算机:
grep TRUSTED_FOR_DELEGATION domain_computers.grep grep TRUSTED_FOR_DELEGATION domain_users.grep
此时,我们已经获取了帐户密码或Kerberos密钥,就可以解密Kerberos服务票据了,这些票据是用户在对与受攻击帐户关联的服务进行身份验证时使用的。以前滥用无约束委派的方法包括使用Mimikatz或Rubeus等从LSASS转储缓存的票据,但这需要在已受攻击的主机上执行代码。在本文中,我不会这么做,而是通过完全控制的主机通过网络完成整个操作,这样就不必担心端点检测或通过转储进程破坏执行服务器。不过这不适用于Rubeus,因为它使用native API。
对于用户帐户,可以通过Kerberoast攻击(Kerberoast是一种可以作为普通用户从Active Directory中提取服务帐户凭证而不需要向目标系统发送任何数据包的有效方法),破解NTLMv1 / NTLMv2身份验证,简单地猜测弱密码或从受损主机上的内存中转储密码来获取密码。计算机帐户很难获取,因为它们默认情况下具有非常强大的随机生成密码,并且其密码/密钥仅驻留在帐户所属的主机或DC上。当我们在关联主机上拥有管理员权限时,它变得相对容易,因为计算机帐户密码存储在注册表中,因此可以通过网络使用secretsdump.py获取,或者通过使用mimikatz lsadump :: secrets转储密码,这两种方法都支持从离线注册表配置单元转储密码。
要从明文密码计算Kerberos密钥,还需要指定salt值。如果你熟悉Kerberos,就会知道使用了不同的加密算法。现代AD安装支持的最弱密码使用RC4,密钥基于用户的NTLM哈希(不包括任何salt值)。然而,Windows默认选择的AES-128和AES-256密码确实包含一个salt值,我们需要将其包含在密钥计算中。计算这些salt值的步骤如下:
1. 对于用户帐户,它是大写的Kerberos域名+区分大小写的用户名;
2. 对于计算机帐户,它是大写域名+主机名(the word host )+完整的小写主机名;
Kerberos域名是域的完全限定域名(FQDN)(因此不是NETBIOS名称!),完整主机名也是主机的FQDN,而不仅仅是计算机名称,并且不包含$。用作用户帐户salt值的用户名是区分大小写的SAMAccountName,因此,如果用户名为awEsOmEusER1,那么awEsOmEusER1将不会生成正确的密钥。
对于计算机帐户,我已经为secretsdump.py添加了一些功能,如果在主机上运行它,它将自动转储设备Kerberos密钥(至少需要impacket 0.9.18或运行git的最新开发版本)。如果由于某种原因无法计算出正确的salt值,你可以使用——krbpass或——krbhexpass(用于十六进制编码的二进制计算机帐户密码)和——krbsalt值参数将其指定为krbrelayx.py。附带说明一下,由于计算机帐户密码是UTF-16-LE中的随机二进制,但是Kerberos使用UTF-8输入进行密钥推导,所以实现起来比预期的时间要长得多。然而,UTF-16字节不是有效的Unicode,当你试图将其转换为UTF-8时,Python会不太给力。我花了一些时间才发现,在将Kerberos密钥执行转换为UTF-8时,Microsoft实现实际上已经悄悄地替换了所有无效的Unicode字符。
对无约束委派帐户的ServicePrincipalName属性的控制
在获取受攻击帐户的Kerberos密钥之后,就可以解密票据了,但是我们还没有讨论如何让主机使用Kerberos进行身份验证。当用户或计算机希望通过SMB在主机somehost.corp.com上使用Kerberos进行身份验证时,Windows将向域控制器发送一个服务票据请求。这个请求将包括服务主体名称(SPN),它由协议和服务所在的主机组成。在本文所举的例子中,这将是cifs/somehost.corp.com。域控制器在分配了这个ServicePrincipalName的帐户(如果有的话)的目录中执行查找,然后使用与该帐户关联的Kerberos密钥加密服务票据。
为了确保受害者使用无约束委派对帐户进行身份验证,并且出于解密票据的需要,我们需要确保将其通信流量发送到SPN与我们模拟的帐户相关联的主机名。如果我们拥有主机名attacker.corp.com,而SPN没有注册到正确的帐户,则攻击将不起作用。最简单的方法是,如果我们控制了一个帐户,该帐户具有编辑计算机属性的权限,或者我们破坏的用户帐户,在这种情况下,我们可以使用krbrelayx中包含的addspn.py实用程序将SPN添加到该帐户。
[email protected]:~/adtools$ python addspn.py -u testsegment\\backupadmin -s host/testme.testsegment.local -t w10-outlook.testsegment.local ldap://s2016dc.testsegment.local Password: [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target [+] SPN Modified successfully
如果我们没有这些权限,情况会更复杂一些,而且对于用户帐户,我还没有找到一种方法在不分配这些权限的情况下修改SPN。默认情况下,计算机帐户可以通过“已验证的写入servicePrincipalName”添加它们自己的SPN,但是它们只能编写与它们的完整主机名或SAMAccountName匹配的SPN。这似乎进入了一个死胡同,但有一种解决方法!还有一个经过验证的额外写入权限,允许计算机更新自己的msDS-AdditionalDnsHostName属性,该属性在Server 2012中引入,并包含计算机对象的其他主机名。根据这份说明,只要我们有Validated-MS-DS-Additional-DNS-Host-Name验证写入权限,这个经过验证的写入就允许添加任何具有我们所在域的FQDN的主机名作为后缀。此权限不是默认分配的:
但是,在使用此属性时,我发现更新msDS-AdditionalDnsHostName属性实际上不需要Validated-MS-DS-Additional-DNS-Host-Name验证的写入权限。默认情况下为计算机对象启用的“验证写入DNS主机名”也允许我们写入msDS-AdditionalDnsHostName属性,并允许我们将当前域中的任何主机名分配给计算机对象,然后自动添加SPN。通过这个方法,可以在我们的帐户中添加一个SPN,并指向受攻击者控制的主机名:
[email protected]:~/adtools$ python addspn.py -u testsegment\\w10-outlook\$ -p aad3b435b51404eeaad3b435b51404ee:7a99efdea0e03b94db2e54c85911af47 -s testme.testsegment.local s2016dc.testsegment.local [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target [+] SPN Modified successfully [email protected]:~/adtools$ python addspn.py -u testsegment\\w10-outlook\$ -p aad3b435b51404eeaad3b435b51404ee:7a99efdea0e03b94db2e54c85911af47 s2016dc.testsegment.local -q [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target DN: CN=W10-OUTLOOK,CN=Computers,DC=testsegment,DC=local - STATUS: Read - READ TIME: 2018-11-18T20:44:33.730958 dNSHostName: W10-OUTLOOK.testsegment.local msDS-AdditionalDnsHostName: TESTME$ testme.testsegment.local sAMAccountName: W10-OUTLOOK$ servicePrincipalName: TERMSRV/TESTME TERMSRV/testme.testsegment.local WSMAN/TESTME WSMAN/testme.testsegment.local
如果出于某种原因,我们不能将SPN修改为攻击者控制下的主机名,则可以通过修改DNS记录或使用你最喜欢的欺骗攻击或中间人攻击劫持当前SPN,尽管这将会破坏与主机的连接,但我不建议在执行环境中这样做。
添加或修改DNS记录的权限
在添加指向网络上未使用的主机名的新SPN之后,我们当然需要确保我们添加的主机名解析为我们自己的IP。如果你所在的网络使用Active Directory集成DNS,这应该很简单。正如Kevin Robertson在他关于ADIDNS的文中描述的那样,默认情况下,只要没有主机名的记录,任何经过身份验证的用户都可以创建新的DNS记录。因此,在我们将attacker.corp.com的SPN添加到我们的无约束委派帐户之后,我们可以为这个主机名添加一条记录,该记录指向我们的IP,例如PowerMad:
我还在krbrelayx repo中添加了一个工具,可以通过LDAP在AD中执行DNS修改(dnstool.py):
[email protected]:~/adtools$ python dnsparse.py -u icorp\\testuser icorp-dc.internal.corp -r attacker -a add -d 10.1.1.2 Password: [-] Connecting to host... [-] Binding to host [+] Bind OK [-] Adding new record [+] LDAP operation completed successfully
之后,我们可以看到DNS中存在记录:
[email protected]:~/adtools$ python dnsparse.py -u icorp\\testuser icorp-dc.internal.corp -r attacker -a query Password: [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found record attacker DC=attacker,DC=internal.corp,CN=MicrosoftDNS,DC=DomainDnsZones,DC=internal,DC=corp [+] Record entry: - Type: 1 (A) (Serial: 36) - Address: 10.1.1.2
DNS服务器通过LDAP(默认情况下每180秒刷新一次)刷新记录后,解析记录:
[email protected]:~/adtools$ nslookup attacker.internal.corp 192.168.111.2 Server: 192.168.111.2 Address: 192.168.111.2#53 Name: attacker.internal.corp Address: 10.1.1.2
dnstool.py实用程序还有其他几个选项,包括一个在开发后再次删除记录的选项,在本文中我不会详细讨论这个选项,但是你可以在GitHub上获取该工具。如果修改DNS不起作用,或者你所在的网络不使用AD进行DNS,则始终可以执行网络攻击来接管DNS服务器,但这通常要求你与系统位于同一VLAN中。一种始终有效的方法是修改受感染计算机自己的DNS记录,但这可能需要一段时间才能通过DNS缓存进行传播。
获取通信流量
现在有很多方法可以获取从用户到攻击者主机的通信流量。在互联网上讨论NTLM身份验证收集技术的任何技术都可以让用户对恶意SMB或HTTP服务器进行身份验证。比如:
1. 带有UNC路径或重定向的钓鱼网站;
2. 使用responder, Inveigh或metasploit来回复LLMNR/NBNS请求;
3. 使用mitm6进行DNS劫持;
4. 将带有链接到UNC路径的图标的文件放在网络中流行的文件共享上;
……
在撰写本文时,通过无约束委派获取域管理权限非常有效,就是滥用仅需要常规用户凭据的漏洞,以便找到高价值目标。比如以下的连个例子:
1.SpoolService 漏洞: MS-RPRN协议中有一个远程进程调用部分,可以导致远程计算机通过SMB向任意主机进行身份验证,这是由Lee Christensen又名@tifkin发现的并称之为“打印机漏洞”。Harmj0y最近在他的博客中写了一篇关于滥用这个漏洞以及对森林信任进行无约束委派攻击的文章。MS-RPRN协议也由@agsolino在impacket中实现的,当然,我为它编写了一个小实用程序,作为krbrelayx工具包的一部分,名为printerbug.py,它会触发反向连接。
2.PrivExchange:Exchange Web Services (EWS) SOAP API公开了订阅推送通知的方法,任何具有邮箱的用户都可以调用此方法,并将Exchange连接到我们通过HTTP指定的任何主机。当请求时,Exchange将(除非它使用最新的CU修补) 使用正在运行的系统的计算机帐户进行身份验证。默认情况下,此计算机帐户在域中具有高权限。我在之前的一篇文章和privexchange.py工具中都提到了这一点。除了NTLM将此身份验证中继到LDAP之外,我们还可以使用无约束委派来获取Exchange的TGT,并使用它来执行基于ACL的权限升级。
这篇文章,我只讲了中继攻击的基本理论,下一篇文章,我会举两个示例来及具体说明。