2020年11月10日,微软发布补丁修复了Kerberos KDC 安全功能绕过漏洞(CVE-2020-17049,Kerberos Bronze Bit Attack),漏洞等级为严重级别。2020年12月,国外安全厂商公开发布了该漏洞的验证脚本和详细的技术分析并将其命名为Kerberos Bronze Bit Attack(青铜比特攻击),同时该漏洞的利用工具也在github上公开,导致该漏洞被广泛利用的风险大大提升。
1、 漏洞背景
Kerberos协议用古希腊神话中看守地狱的三头猛犬命名,所以Kerberos验证也就有三个参与者, 分别是访问服务的Client、提供服务的Service和KDC(Key Distribution Center)密钥分发中心。在KDC中存在两个服务,分别是身份验证服务Authentication Service(简称AS)和票据授予服务Ticket-Granting Service(简称TGS),在windows的域环境下KDC一般是在Domain Controller(简称DC)中的。Kerberos是基于对称加密认证的,Kerberos中的每个主体都具有一个密匙,该密匙只有主体本身和KDC知道。
1.1 Kerberos 协议介绍
当 Client 访问 Server 上的某个服务时,首先要向AS 证明自己的身份,然后通过 AS 发放的 TGT 向 Server 发起认证请求,这个过程分为三块:
The Authentication Service Exchange:Client 与 AS 的交互;
The Ticket-Granting Service (TGS) Exchange:Client 与 TGS 的交互;
The Client/Server Authentication Exchange:Client 与 Server 的交互。
1.1.1 The Authentication Service Exchange
如上图所示,Client使用用户密匙对当前时间戳进行加密,并将其与用户名一起放置在AS_REQ消息中,然后将AS_REQ消息发送到KDC。KDC通过用户名检索到用户密匙,使用用户密匙尝试解密时间戳,若解密成功则证明该用户的用户密匙正确。验证成功后生成会话密匙,然后将会话密匙放入KRB_AS_REP消息中返回给Client。AS_REP的结构示意图如下所示:
AS_REP消息中包括用户名、enc_part和TGT。enc_part是使用用户密匙加密的,它包括服务名称和标志等字段以及KDC生成的会话密匙,Client可以使用用户密匙解密后得到该会话密匙。TGT(Ticket-Granting Ticket,票据授予票据,也可以叫做黄金票据)是使用KDC密匙加密的,它包括标志、请求服务的用户名以及KDC生成的会话密匙等字段,返回TGT表示该用户已经被KDC成功认证。
1.1.2 The Ticket-Granting Service (TGS) Exchange
如上图所示,Client使用会话密匙对当前时间戳进行加密,并将其与请求的服务名、TGT一起放置在TGS_REQ消息中,然后将TGS_REQ消息发送到KDC。KDC在收到KRB_TGS_REQ后需要对TGT进行验证,因此KDC通过自身密匙加密对TGT进行解密,从解密的TGT得到会话密匙解密时间戳进行验证,如果验证成功将返回TGS_REP消息。TGS_REP的结构示意图如下所示:
TGS_REP消息中包括用户名、enc_part和ST。enc_part是使用会话密匙加密的,它包括请求的服务名和标志等字段以及KDC生成的服务会话密匙,Client可以使用会话密匙解密后得到该服务会话密匙。ST(Service Ticket,服务票据,也可以叫做白银票据)是使用服务密匙加密的,它包括标志、请求服务的用户名以及KDC生成的服务会话密匙等字段。可以看到ST的结构和TGT很像,这是因为TGT可以看做是一种特殊的服务票据,它是TGS(Ticket-Granting Service,票据授予服务)的服务票据。下面来详细介绍一下ST的结构:
- 服务会话密匙:由KDC生成的服务会话密匙,Client和Service均可解密获得。
- 标志:Kerberos票据的标志位包括可转发、可更新、规范化、可更新OK等,其中可转发(ForWardable)我们将在后续详细讨论。
- PAC: Privilege Attribute Certificate(特权属性证书),PAC 就是为了区别用户的不同权限。这里的PAC分别使用了KDC的密匙和服务密匙进行加密,防止被篡改。
1.1.3 The Client/Server Authentication Exchange
Client收到KRB_TGS_REP后使用会话密匙解密服务会话密匙,Client就可以直接和服务进行交互,而无需使用KDC作为中间人了。
可以看到在验证完时间戳后,Service还会使用服务密匙解密PAC进行验证,验证请求服务的用户是否由权限访问该服务。同时,Service也可以选择将该PAC发送至KDC进行验证,KDC使用其密匙解密后验证该PAC是否正确,如果是正确的则说明该PAC没有被篡改过。如果上述验证全部成功,则Service让Client访问其请求的资源,否则拒绝访问。
1.2 Kerberos 委派
委派简单来说就是A使用Kerberos身份验证访问域中的服务B,而B再利用A的身份去请求域中的服务C。例如,当client去访问Server1上的HTTP服务,而HTTP服务需要请求server2的数据库,但是Server1并不知道client是否有server2的数据库访问权限,这时HTTP服务会使用client的身份去访问server2的数据库,如果client有server2的数据库访问访问权限就能访问成功。
Kerberos中的委派分为非约束委派(Unconstraineddelegation)和约束委派(Constrained delegation)两种方式,下面就这两种方式分别进行介绍。
1.2.1 非约束委派
用户发送ST和TGT去访问Service1,然后该服务可以使用用户的TGT去向TGS请求其他服务的ST,然后模拟该用户进行操作。
可以看到由于非约束委派需要将用户的TGT发送给Service1,那么Service就可以用该TGT模仿用户是否获得任何其他的ST,具有极大的不安全性。
1.2.2 约束委派
由于非约束委派的不安全性,微软在windows2003中发布了约束委派的功能。在约束委派中client不会直接发送TGT给服务,而是对发送给service1的认证信息做了限制,不允许service1代表User使用这个TGT去访问其他服务。这个过程使用了一组名为S4U2Self(Service for User to Self)和S4U2Proxy(Service forUser to Proxy)的Kerberos协议扩展。
S4U2Proxy:用户通过域控制器请求访问service1,域控验证并返回service1服务票据ST1,用户发送此ST1给service1与service1认证并建立连接。若该service1允许委派给service2,则service1能使用S4U2Proxy协议将用户发送给自己的ST1 (此ST1必须是可转发的)再转发给域控制器认证服务器,为用户请求访问service2的ST1,此后,service1便能使用新获得的ST2模拟用户访问service2。如下图所示:
S4U2Self (协议转换):上图中Client是通过Kerberos协议与Service1进行认证的,而当用户以其他方式(如NTLM认证,基于表单的认证等方式)与Service1进行认证后,用户是无法向Service1提供请求该服务的服务票据ST的,因而服务器也无法进一步使用S4U2Proxy协议请求访问Service2。S4U2Self协议便是解决该问题的方案,配置了TrustedToAuthForDelegation的服务向认证服务器为用户请求访问自身的可转发的服务票据,S4U2Self的示意图如下图所示:
通过S4U2Self Service1便能够获得请求其自身服务的服务票据ST1,该ST1的Cname字段标识为Client,此后,便可通过S4U2Proxy使用这张服务票据向域控制器请求访问Service2的票据。
1.3 基于资源约束的委派
基于资源的约束委派(RBCD)是在Windows Server 2012中新加入的功能,与传统的约束委派相比,它不再需要域管理员权限去设置相关属性。RBCD把设置委派的权限赋予了机器自身,既机器通过修改msDS-AllowedToActOnBehalfOfOtherIdentity属性来决定谁可以被委派来控制我。
2、漏洞分析及poc复现
2.1漏洞分析
在前面的内容中,我们已经介绍了Kerberos协议的认证过程和Kerberos委派的相关知识,在介绍CVE-2020-17049 漏洞之前我们先来简单介绍一下攻击者如何利用约束委派进行攻击。
假设攻击者已经获得了Service1的密码hash值,且Service1和Service2之间存在委派信任关系(Service1配置为对Service2的约束委派或Service2接受来自Service1的基于资源约束的委派)。如果Service1允许进行协议转换(配置了TrustedToAuthForDelegation属性),就可以利用impacket套件中的GetST.py脚本来获得指定身份的Service2的服务票据ST2。具体攻击流程如下图所示:
利用服务票据ST2,攻击者就能伪造成目标用户与Service2进行交互。
由于委派攻击的危害性,因此微软官方提供了多种配置来降低委派攻击的危害。首先可以通过禁止协议转换(即关闭TrustedToAuthForDelegation属性),如果下图所示。
其次是可以在AD中配置域内账户为“敏感账户,不能被委派”。
最后还可以将域内账户添加到 “Protected Users”安全组内,该组成员都不能通过委派进行身份验证。
如果某域内账户设置了上述配置至少一个,那么为该域内账户申请服务票据时,该服务票据的“ForWardable”将始终设置为0。即Service1仍然能通过S4U2self 协议获取该域内账户的服务票据ST1,但由于该服务票据ST1的ForWardable标志位0,那么就不能在S4U2 proxy中使用该服务票据ST1获取其他服务票据,如下图所示。
“ForWardable”设置为0时的TGS_REP的结构示意图如下图所示。
仔细观察上图,可以发现在ST1是使用Service1密匙进行加密的。这意味着Service1可以解密ST1后修改forwardable值,然后重新使用Service1密匙进加密后发送给KDC ,forwardable标志不在PAC中,所以KDC无法检测到该值是否已经被篡改。具体攻击示意如下如所示:
绕过限制后,攻击者就可以模拟目标用户与Service2进行交互。
2.2POC复现
本节中复现所使用的环境为一台Windows Server 2012的域控环境和两台Windows 10的普通域内PC机器。
2.2.1 约束委派攻击示例
首先我们需要在域控上对测试环境进行配置,我们将Service1(域内Windows102004机器)配置为对Service2(域内Windows10机器)的约束委派(通过“仅使用Kerberos”选项来禁止协议转换)。
同时将域内的域管理员账户设置为“敏感账户,不能被委派”并将其添加至“Protected Users”安全组内。
通过mimikatz或impacket 中的secretsdump.py等方式获取域内Service1(域内Windows102004机器)的密码hash。
尝试直接使用getST.py 获取Administrator访问Service2(域内Windows10机器)的服务票据ST2。
可以看到由于Administrator设置了保护,使用S4U2self获得的服务票据ST1是不可转发的,该票据无法在S4U2 proxy中使用,因此会导致出错。
接下来我们使用最新修改的getST.py 并添加-force-forwardable参数来对服务票据ST1进行修改。
可以看到通过-force-forwardable参数我们将原本不可转发的服务票据ST1修改成了可转发的,然后用该服务票据ST1来申请访问Service2的服务票据ST2。接下来就可以通过将服务票据ST2导入内存就能以Administrator的身份来访问Service2。
2.2.2 基于资源约束的委派攻击示例
在约束委派的攻击示例中,我们需要控制一个域内的服务账户Service1且该服务账户配置了对目标服务账户Service2的约束委派。在实际的渗透过程中是很难获得以上条件,更多的情况是在某个站点上上传了一个webshell,通过连接该webshell所获得的权限一般都是比较小的。下面我来介绍一下如何在这种情况下利用资源约束委派和CVE-2020-17049漏洞在目标机器上提权。
首先假设我们已经连接到一台域内目标机器的webshell,通过连接该webshell所获得的权限为iis apppool\defaultapppool,该权限不在本地管理员中且只在当前webshell所在目录下有读写权限。
通过Webshell上传我们的恶意程序exp.exe,并执行该恶意程序。该恶意程序的作用在域内是生成一个不存在机器账户evil1,并修改目标机器Win10 的msDS-AllowedToActOnBehalfOfOtherIdentity属性为evil1的SID,即配置Win10接受来自evil1的基于资源约束的委派。能够通过iis apppool\defaultapppool修改Win10 的msDS-AllowedToActOnBehalfOfOtherIdentity是因为在Windows系统中,iis apppool\defaultapppool的出网身份为机器账户,而机器账户是拥有修改自身msDS-AllowedToActOnBehalfOfOtherIdentity属性权限的。
尝试直接使用机器账户密码来运行getST.py提示服务票据ST1是不可转发的,运行S4U2 proxy失败。
通过mimikatz获取到evil1的密码hash值。
重新利用getST.py 并添加-force-forwardable参数来对服务票据ST1进行修改。
然后用该修改后的服务票据ST1来申请访问Service2的服务票据ST2。接下来就可以通过将服务票据ST2导入内存就能以Administrator的身份来访问Service2。
3、参考文档
- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/bacff5f1-9127-457b-877c-db97b1e1802f?redirectedfrom=MSDN([MS-ADTS]:Active Directory Technical Specification)
- https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html(Wagging the Dog: Abusing Resource-Based Constrained Delegation to Attack Active Directory)
- https://blog.netspi.com/cve-2020-17049-kerberos-bronze-bit-attack(CVE-2020-17049: Kerberos Bronze Bit Attack )
- https://www.zdnet.com/article/windows-10-update-problem-were-fixing-kerberos-authentication-bug-says-microsoft/