前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >没有 SPN 的 Kerberoasting

没有 SPN 的 Kerberoasting

原创
作者头像
Khan安全团队
发布2022-01-04 11:11:02
1.2K0
发布2022-01-04 11:11:02
举报
文章被收录于专栏:Khan安全团队

服务主体名称 (SPN) 是 Active Directory (AD) 数据库中的记录,显示哪些服务注册到哪些帐户:

具有 SPN 的帐户示例

如果一个帐户有一个 SPN 或多个 SPN,您可以通过 Kerberos 向其中一个 SPN 请求服务票证,并且由于服务票证的一部分将使用从帐户密码派生的密钥进行加密,您将能够破解强制此密码离线。这就是 Kerberoasting 的工作原理。

有一种方法可以在不知道目标服务的 SPN 的情况下执行 Kerberoasting 攻击。我将展示它是如何完成的,它是如何工作的,以及它何时有用。

Kerberos 基础知识

Kerberos 是一种基于 ASN.1 格式的开源二进制协议。Kerberos 的核心是密钥分发中心 (KDC) 服务,它使用 88/tcp 和 88/udp 端口​​。在 Active Directory 环境中,它们安装在每个域控制器上。

让我们运行 Impacket 中的 GetUserSPNs.py 工具来演示 Kerberoasting 的工作原理:

在实验室环境中执行 Kerberoasting 攻击

首先,该工具连接到 LDAP,并查找具有 SPN 且不是计算机帐户的用户。AD 中的每个机器帐户都有一堆 SPN,但它们的服务票证是不可暴力破解的,因为机器帐户有 240 字节长的密码。

然后,该工具连接到 KDC,并为每个发现的帐户使用其 SPN 之一获取服务票证。在我们的示例中,只发现了一个帐户,并且该工具选择了“MSSQLSvc/sp-sql:1433”SPN 来请求票证。

所选服务是否正常运行并不重要;AD 数据库中存在 SPN 就足以进行攻击。

这是此 GetUserSPNs.py 启动的流量转储,因此现在我们可以详细检查所有描述的阶段:

Kerberoasting 攻击的流量转储

客户如何获得 TGT

每个客户端都必须向 KDC 进行身份验证并获得一个票证授予票证 (TGT),这将允许他们继续请求任意数量的服务票证。

这种机制是用来减少需要认证的次数,没有TGT是没有办法绕过它来请求服务票的。

未经身份验证的 AS-REQ / Preauth 请求

AS-REQ 数据包用于请求 TGT。

在 AS-REQ 中,客户端在 sname 字段中指定特殊的“krbtgt/DomainFQDN”SPN,以及在 cname 字段中请求 TGT 的帐户的主体名称:

未经身份验证的 AS-REQ 数据包的内容 (#7)

第一个 AS-REQ 数据包在没有身份验证数据的情况下发送,以保持向后兼容性。只有在目标帐户的 Active Directory 中设置了 DONT_REQ_PREAUTH 标志时,它才会成功。

AS-REQ 的响应应该包含一个结构,该结构使用从客户帐户的密码派生的密钥进行加密和签名,因此如果 AS-REQ 在没有任何身份验证的情况下工作,任何人都可以离线暴力破解其他人的密码。

这称为 ASREPRoasting 攻击,在 Impacket 中它可以由 GetNPUsers.py 脚本执行:

使用来自 Impacket 的 GetNPUsers.py 执行 ASREPRoasting 攻击

ASREPRoasting 的一种应用是 Targeted Kerberoasting。它依赖于有意为您在 AD 中控制的帐户设置 DONT_REQ_PREAUTH 标志,并获取其$krb5asrep$ 哈希值。

由于我们使用的“管理员”帐户没有设置 DONT_REQ_PREAUTH 标志,因此 KDC 向客户端发送了一个 KRB-ERR 数据包,其中包含 KRB_PREAUTH_REQURED 错误。此数据包称为 Preauth 请求。

KRB-ERR 数据包的内容 (#8)

如果“管理员”帐户不存在,我们将收到 KDC_ERR_C_PRINCIPAL_UNKNOWN 错误。这是在 Kerberos 用户枚举攻击中使用的功能。

经认证的 AS-REQ

让我们检查下一个 AS-REQ 数据包:

已认证的 AS-REQ 数据包的内容 (#9)

下一个 AS-REQ 与第一个请求基本相同,但它包含可以授权客户端的数据。这个数据是一个包含当前时间戳的特殊结构,这个结构是用从账户密码派生的密钥加密和签名的。

从帐户密码派生的密钥称为 Kerberos 密钥,它们的计算方式取决于所使用的加密算法:

  • AES-128 和 AES-256:密钥是根据密码的 PBKDF2 哈希计算的
  • RC4:密钥是从密码的 NT 哈希计算出来的(总是与 Pass-The-Hash 攻击一起使用)
  • DES:密钥直接从密码中计算出来

在请求中使用客户端主体名称,KDC 尝试在 AD 数据库中查找客户端的帐户,提取其预先计算的 Kerberos 密钥,并验证客户端的身份。

AS-REP

在 KDC 验证客户端的身份后,它会发送一个 AS-REP 数据包,其中包含客户端可以从中构建 TGT 内存对象的数据:

AS-REP 数据包的内容 (#10)

TGT 本身是用 krbtgt 帐户的 kerberos 密钥加密和签名的,因此它旨在仅在 KDC 端解压缩。它包含会话密钥、元数据和客户端的特权属性证书 (PAC)。PAC 包括客户端的名称、安全标识符 (SID) 和组。

为了让客户端使用 TGT,它需要构造一个 TGT 内存对象,该对象将包含 TGT 本身、其会话密钥和所有元数据。客户端从由其密钥加密的 AS-REP 部分提取会话密钥。

客户如何获得服务票

客户端构造 TGT 内存对象后,它可以使用 TGS-REQ 数据包请求任意数量的服务票证。当这些请求被接受时,KDC 将使用 TGS-REP 数据包进行响应。

TGS请求

TGS-REQ 包含票证请求的服务主体名称、TGT 和使用 TGT 会话密钥加密并包含当前时间戳的结构:

TGS-REQ 数据包的内容 (#11)

当 KDC 收到 TGS-REQ 时,它会解密 TGT,提取会话密钥,并检查客户端的身份。

TGS-REP

TGS-REP 数据包用于将服务票据传输到 KDC 客户端。

在 KDC 验证客户端的身份后,将执行以下步骤:

  1. KDC根据解密后的时间戳检查TGT是否仍然有效;
  2. 如果 TGT 发出后超过 15 分钟,KDC 重新计算解密后的 PAC,并检查客户端是否在 Active Directory 中没有被禁用;
  3. KDC 查找发送的服务主体名称解析到的帐户;
  4. KDC 提取发现账户的 kerberos 密钥;
  5. KDC构建服务票据,由PAC和服务票据会话密钥组成;服务票证使用服务帐户的 kerberos 密钥进行加密和签名;
  6. KDC 使用服务票证会话密钥创建一个结构,并使用 TGT 会话密钥对其进行加密和签名。

服务票据和带有服务票据会话密钥的结构都包含在 TGS-REP 数据包中:

TGS-REP 数据包的内容 (#12)

服务票据的加密部分是在 Kerberoasting 攻击中被暴力破解的部分。

探索主体名称的格式

让我们检查之前收集的 AS-REQ 数据包中的主体名称:

Kerberos 流量中主体名称的示例

客户端主体名称在 cname 字段中传递,服务主体名称在 sname 字段中发送。所有主体名称都带有一个称为主体名称类型的整数。

主体名称通常由“/”字符分割成一系列字符串。例如,主体名称的krbtgt / CONTOSO .COM在Kerberos通信由两个字符串:KRBTGTCONTOSO.COM

根据RFC 4120, cname 和 sname 字段有不同的用途,但这些字段的结构是相同的:

代码语言:javascript
复制
KDC-REQ-BODY    ::= SEQUENCE {
 kdc-options  [0] KDCOptions,
 cname        [1] PrincipalName OPTIONAL
 realm        [2] Realm
 sname        [3] PrincipalName OPTIONAL,
 ...
}

PrincipalName   ::= SEQUENCE {
 name-type    [0] Int32,
 name-string  [1] SEQUENCE OF KerberosString
}

KerberosString  ::= GeneralString (IA5String)

cname 和 sname 字段的相同结构引起了我的注意,我决定在 Kerberos 协议中测试它们的不同用法。

Kerberos 的秘密

发现 Windows KDC 服务通过相同的函数集处理 cname 和 sname 字段,并且在任何给定时间选择哪种格式的主体名称都无关紧要。

解析为同一个帐户的所有主体名称都是相同的

如果您在 Kerberos 数据包中有一个 SPN 值,您可以将其替换为该 SPN 所属帐户的 SAM 帐户名称 (SAN) 值,并且不会有任何中断:

带有 SAN 的 TGT-REQ 数据包示例

这样您就可以在不知道目标帐户的任何 SPN 的情况下执行 Kerberoasting 攻击。但是,将继续需要目标帐户至少存在一个 SPN。

奖励:重温 S4U 和 AnySPN 攻击

我检查了 Impacket 源代码,我发现了两个有趣的地方,它们与所发现的技术密切相关,但与 Kerberoasting 无关。

使用 SAM 帐户名称的 S4U2Self 和 S4U2Proxy 请求

让我们尝试使用 getST.py 形式的 Impacket 来滥用基于资源的约束委托:

使用 Impacket 滥用基于资源的约束委托的示例

这里我们有“user01”帐户,该帐户具有“http/test”SPN 和授权访问“SRV02$”帐户的任何 SPN 的权限。

根据规范(S4USelf KRB_TGT_REQS4U2Proxy KRB_TGS_REQ),user01 的服务应该在 S4U2Self 和 S4U2Proxy 请求中使用其 SPN。但是,您可以看到 Impacket 在此类请求中使用了 SAN:

Impacket 的 S4U2Self 请求的流量转储

这些请求不符合规范,但会成功,因为 Windows KDC 对给定的主体名称格式不敏感。

AnySPN 攻击

Impacket 实现了一种叫做 AnySPN 攻击的东西。此攻击尝试修改给定服务票据文件中的 SPN,当它与目标服务 SPN 不同时:

使用 Impacket 执行 AnySPN 攻击

Alberto Solino 写了一篇优秀的文章Kerberos 委托、SPN 和更多 解释它是如何工作的。

这是本文的主要部分:

Alberto Solino 的文章片段

简而言之,Benjamin Delpy、Ben Campbell 和 Alberto Solino 注意到主机 A 上服务 A 的服务票可能适用于主机 A 上的服务 B。

实际上,如果我们解密任何服务票证的加密部分,我们将看到它不包含任何 SPN:

使用服务帐户的密码解密服务票的加密部分

打印服务票据加密部分包含的信息

服务票据的加密部分仅包含票据的会话密钥、元数据和验证用户的 PAC。服务票证的 SPN 包含在协议的未加密和未签名部分中,客户端可能根本不考虑它。

服务票证对其服务帐户运行的所有服务均有效

因此,如果您想知道在没有 SPN 的情况下请求服务票证时将服务票证颁发给哪个 SPN,现在您知道服务票证不包含任何内容。

奖励:使用主要名称类型

cname 和 sname 字段的结构包含一个称为Principal Name Type的整数。RFC 4120 规范为其定义了 9 个可能的值:

RFC 4120 的摘录:6.2。校长姓名

我做了一些研究,并创建了一个表,其中包含实际的 Principal Name Types 值及其在 Windows 中的含义:

姓名类型

价值

意义

NT-未知

0

代表 SPN 和 SAN 格式

NT-校长

1

等于 NT-UNKNOWN

NT-SRV-INST

2

等于 NT-UNKNOWN

NT-SRV-HST

3

等于 NT-UNKNOWN

NT-SRV-XHST

4

表示 SPN 格式

NT-UID

5

不支持

NT-X500-校长

6

代表DN格式

NT-SMTP-名称

7

等于 NT-UNKNOWN

NT-企业

10

代表UPN、SAN和多种DomainName+SAN格式

NT-MS-校长

-128

代表SAN和多种DomainName+SAN格式

NT-MS-PRINCIPAL-AND-ID

-129

等于 NT-MS-PRINCIPAL

NT-ENT-PRINCIPAL-AND-ID

-130

等同于 NT-X500-PRINCIPAL

*

等于 NT-UNKNOWN

我发现 NT-ENTERPRISE 类型比常用的 NT-UNKNOWN 类型更有价值。它支持以下一堆名称格式:

  • 用户主体名称
  • sAMA账户名
  • sAMAccountName@DomainNetBIOSName
  • sAMAccountName@DomainFQDN
  • 域NetBIOSName\sAMAccountName
  • 域FQDN\sAMAccountName

请注意,如果您使用SRV01 字符串作为 sAMAccountName,并且SRV01 帐户不存在,而SRV01$ 帐户存在,则此名称将被视为SRV01$ 帐户的主体名称。

其他有趣的主体名称类型是 NT-X500-PRINCIPAL。它支持RFC 1779结构中的 DN 。以下是如何在此结构中编写相同 Active Directory 对象的三个示例:

代码语言:javascript
复制
CN=SQL ADMIN,OU=LAB Users,DC=CONTOSO,DC=COM
CN="SQL ADMIN";OU="LAB Users";DC="CONTOSO";DC="COM"
OID.2.5.4.3=SQL ADMIN,OU=LAB Users,DC=CONTOSO,DC=COM

不幸的是,跨林信任不支持 NT-X500-PRINCIPAL 类型。

该技术在Kerberoasting中的应用

我已将 NT-ENTERPRISE 和 NT-MS-PRINCIPAL 类型的用法添加到 Impacket 的 GetUserSPNs.py。让我们看看当这些更改是 Kerberoasting 成功所必需的三种常见情况。

无法访问 LDAP 的 Kerberoasting

您可能会发现自己处于这样一种情况:您可以访问 KDC 服务,您获得了一个帐户列表(例如,通过 RID 循环攻击),但您没有 SPN。

由于您不再需要 SPN,您可以使用新的-userfile选项仅通过用户列表请求服务票证:

使用新的 GetUserSPNs.py 按用户列表执行 Kerberoasting

-userfile选项利用NT企业类型从指定的文件中查找accountd。

使用不正确 SPN 的 Kerberoasting 帐户

KDC 禁止退票的 SPN 有两种类型:

  • 错误的语法 SPN
  • 重复的 SPN,即当相同的 SPN 值分配给多个帐户时

如果 KDC 发现其中之一是这种情况,它会返回 KDC_ERR_S_PRINCIPAL_UNKNOWN 错误,就好像传递的 SPN 不存在一样:

使用不正确的 SPN 对帐户进行 Kerberoasting

新的 GetUserSPNs.py 将帐户列表从 LDAP 包装到 NT-MS-PRINCIPAL 类型,并且不使用 SPN,因此您甚至可以从误解的 SPN 中获取哈希值:

使用新的 GetUserSPNs.py 对 SPN 不正确的帐户进行 Kerberoasting

内部使用“DomainFQDN\sAMAccountName”格式,输出中的“\”字符改为“/”,以符合Impacket格式的用户名并防止其在其他工具中转义。

通过 Forest Trusts 使用 NetBIOS 名称 SPN 对帐户进行 Kerberoasting

当您从另一个域请求 SPN 的服务票证,并且此 SPN 具有 NetBIOS 名称格式的主机名时,您的 KDC 将无法找到目标服务:

通过林信任对具有 NetBIOS 名称 SPN 的帐户进行 Kerberoast 处理

使用新的 GetUserSPNs.py 文件,您将永远不会获得此类服务的 KDC_ERR_S_PRINCIPAL_UNKNOWN:

使用新的 GetUserSPNs.py 通过森林信任对具有 NetBIOS 名称 SPN 的帐户进行 Kerberoasting

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Kerberos 基础知识
  • 客户如何获得 TGT
  • 客户如何获得服务票
  • 探索主体名称的格式
  • Kerberos 的秘密
    • 解析为同一个帐户的所有主体名称都是相同的
    • 奖励:重温 S4U 和 AnySPN 攻击
      • 服务票证对其服务帐户运行的所有服务均有效
      • 奖励:使用主要名称类型
      • 该技术在Kerberoasting中的应用
      相关产品与服务
      多因子身份认证
      多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档