Kerberos 委派攻击原理之 S4U2 利用详解
为了更好地防止约束委派的滥用,请查看"从 Kekeo 到 Rubeus"文章中的"s4u"部分。
几周前,我和同事李 · 克里斯滕森(在他的帮助下我完成了这篇文章和相关材料)花了一些时间深入研究了活动目录的 S4U2Self 和 S4U2Proxy 协议扩展。 就在最近,本杰明 · 德尔皮 和 Ben Campbell在推特上就同一话题进行了一次有趣的公开对话。 对话的结果是本杰明发布了一个修改过的 Kekeo,使得滥用 S4U 错误配置更为容易。 在我写这篇文章的时候,Ben 还发表了一篇关于这个话题的优秀文章,每个人在继续阅读本文之前都应该读一读他的文章。 说真的,读者们真的应该先去看看 Ben 的文章。
李和我想写出我们对这项技术的理解,以及在交战中如何滥用任何错误配置。 其中一部分内容将与 Ben 的文章重叠,但是我们已经合并了一些不同的方面,我们认为至少可以增加一些价值。 Ben 还介绍了 Linux 方面的漏洞利用,我们在本文中不会涉及到这一点。
这个问题的核心是特权委派——允许一个用户在活动目录中假冒成另一个用户。 这种委派(当前)有两种形式: 无约束和有约束的委派。 如果你不关心技术细节,请直接跳到滥用 S4U 部分。
无约束委派
假设你有一个服务器(或服务帐户) ,由于某种原因需要假冒为另一个用户。 一个常见的场景是,当用户使用 Kerberos 或其他协议向 Web 服务器进行身份验证时,服务器希望与 SQL 后端进行良好的集成。 活动目录提供了两种实现此目的的通用方法: 约束委派和无约束委派。
在 Windows 2000中,无约束委派曾经是唯一可用的选项,并且后来保留了这个功能(可能是出于向后兼容的原因)。 我们只简单介绍一下这种委派类型,因为 Sean Metcalf 有一篇很棒的文章深入介绍了这种委派类型。 在那篇文章中 Sean 说,"当 Kerberos 无约束委派在服务器上启用,服务器托管了在 TGS-REQ (步骤3)中引用的服务主体名称中指定的服务时,DC 域控制器将用户 TGT 的一个副本放到服务票证中。 当向服务器提供用户的服务票证(TGS)以进行服务访问时,服务器打开 TGS 并将用户的 TGT 放入 LSASS 中供后续使用。 此时,应用程序服务器就可以无限制地假冒该用户!” .
下面是来自微软官方对于该协议的图形概述:
https://msdn.microsoft.com/en-us/library/cc246080.aspx
Tl;dr: TGT 将被塞入内存,攻击者可以在以下情况下提取和重用 TGT :
你可以攻击具有无约束委派设置的服务器。
你可以欺骗未启用"帐户是敏感的,不能委派"设置的域用户(见下面的“保护措施”章节) ,以连接到计算机上的任何服务。 包括单击 \\SERVER\Share。
这允许攻击者假冒该用户到域上的任何服务或计算机! 很明显这种情况比较糟糕。 相比之下,如果不启用无约束委派,则只会提交一个没有填充 TGT 的普通服务票证,这样攻击者就不会获得额外的横向扩展的优势。
如何判断哪些机器具有无约束委派设置? 这实际上非常简单: 搜索任何具有 userAccountControl 属性的机器,该属性包含 ADS_UF_TRUSTED_FOR_DELEGATION。 你可以使用 LDAP 过滤器‘(userAccountControl:1.2.840.113556.1.4.803:=524288)’,这是 PowerView 的 Get-DomainComputer 函数在传递 –Unconstrained 标志时所做的:
约束委派
显然,无约束委派在粗心的管理员手中是相当危险的。 微软很早就意识到了这一点,并在 Windows 2003上发布了"约束"委派。 其中包括一组 Kerberos 协议扩展,称为 S4U2Self 和 S4U2Proxy。 这些扩展还支持所谓的协议转换,我们会在稍后详细介绍。
从本质上讲,约束委派是一种可以在假冒其他用户时准确地限制特定机器或帐户可以访问到某些服务的方法。 以下是配置了约束委派的服务帐户在活动目录 GUI 中的样子:
指定的"服务"是允许帐户在假冒其他用户时访问的服务主体名称。 在上面的例子中是 HOST/PRIMARY.testlab.local。 在我们详细讨论它是如何工作的之前,下面是目标对象在 PowerView 中的样子:
我们感兴趣的字段是 msds-allowedtodelegateto,但是也对帐户的 userAccountControl 属性进行了修改。 实际上,如果计算机或用户对象的 userAccountControl 值包含 TRUSTED_TO_AUTH_FOR_DELEGATION ,那么任何拿到该帐户权限的用户都可以模拟 msds-allowedtodelegateto中设置的 SPN。 Ben 提到要修改这些参数 SeEnableDelegationPrivilege 是必需的,后续我将更深入地讨论这个问题。
但是首先,我们来了解一下活动目录是如何实现整个过程的。 如果你不感兴趣,可以直接跳到本文的滥用 S4U 部分。
S4U2Self,S4U2Proxy 和协议转换
假设你拥有一个 web 服务帐户,该帐户需要将用户假冒为一个特定的后端服务账户,但是你不想开启无约束委派。 微软的解决方案是通过 Kerberos 扩展设置的用户服务(S4U)实现。这里有关于这个主题已有大量的文档; 李和我偏爱微软的"Kerberos 协议扩展: 面向用户和约束委派协议的服务"([ MS-SFU ])。 以下是我们目前的理解。 如果我们有什么谬误,请让我们知道!
实现约束委派的第一个扩展是 S4U2self 扩展,它允许服务代表特定用户向自己请求一个特殊的可转发服务票证。 这是为了在用户以不使用 Kerberos 的方式对服务进行身份验证的情况下使用,例如在我们之前提到的 web 服务案例。 在 KRB_TGS_REQ 第一次发送到 KDC 的过程中,它设置了可转发的标志,该标志要求 TGS 返回的标记为可转发的,从而能够与 S4U2proxy 扩展一起使用。 在无约束委派中,使用 TGT 标识用户,但在这种情况下,S4U 扩展使用 PA-FOR-USER 结构作为"padata"预身份验证数据字段中的新类型。
注意,S4U2self 进程可以被任何用户执行,并且不需要目标用户的密码。 此外,只有请求用户在其 userAccountControl 中设置了 TRUSTED_TO_AUTH_FOR_DELEGATION 字段时,才允许 S4U2self 进程。
现在,李和我首先想到的是,这可能是一种对任何我们想要的用户执行 Kerberoast 攻击的方式,但不幸的是,对于我们攻击者来说,情况并非如此。 PAC 是为源用户(而不是目标用户)签名的,在这种情况下是请求服务帐户,因此通用的 Kerberoast 攻击方法不在考虑范围之内。 但是我们现在有一个特殊的服务票证,在这种情况下可以转发到配置为约束委派的目标服务上。
第二个扩展是 S4U2proxy,它允许调用者(在我们的例子中是服务帐户)使用这个转发票证向 msds-allowedtodelegateto 中指定的任何 SPN 请求服务票证,假冒 S4U2self 步骤中指定的用户。 KDC 检查请求的服务是否在被请求服务的 msds-allowedtodelegateto 字段中,并在该检查通过时发出票证。 通过这种方式,委派"约束"到特定的目标服务。
下面是微软官方关于 S4U2self 和 S4U2proxy 工作过程的示意图:
https://msdn.microsoft.com/en-us/library/cc246080.aspx
这个扩展设置就是微软所谓的协议转换,即从 S4u2Self 组件期间的第一个 Kerberos 交换开始。 这意味着服务可以通过非 Kerberos 协议对用户进行身份验证,并将身份验证"转换"到 Kerberos,从而可以轻松地与现有环境进行互操作。
滥用 S4U
从头读到此处,如果你问自己“那又怎样”,或者跳过前面的内容直接来到本章节,那么我们可以想出一些方法,让 S4U 扩展在渗透测试中发挥作用。
第一种方法是使用非空的 msds-allowedtodelegateto字段设置枚举所有计算机和用户。 使用 PowerView 的 Get-DomainUser/Get-DomainComputer函数并带有 -TrustedToAuth 标志可以很容易地实现这一点:
现在,请记住,将 SPN 设置为 msds-allowedtodelegateto 的机器或用户帐户可以假冒成任何他们希望成为的目标服务 SPN 。 因此,如果你能够拿到其中一个帐户的权限,就可以欺骗对目标 SPN 的特权访问。 对于 HOST SPN,则可以实现完全的远程接管。 对于 MSSQLSvc SPN,则可以拿到 DBA 权限。 对于 CIFS SPN 则可以实现完全的远程文件访问。对于 HTTP SPN 则可能实现接管远程网络服务,而对于 LDAP 则可以执行 DCSync;) ,对于 HTTP 或 SQL 服务帐户,即使它们没有提升目标服务器上的管理员权限,也可能使用 Rotten Potato 进一步滥用,提权至 SYSTEM 的权限(尽管我还没有亲自测试过)。
幸运的是,本杰明最近发布了一个修改过的 Kekeo ,支持在我们知道特定账户的明文密码的前提下,轻易的执行这类横向传播攻击。 李和我设想了四种不同的情况,包括你可能想要滥用的 S4U。 我们已经在实验室中可靠地测试了其中的两个场景,但是没能成功测试另外两个场景(注释如下)。@gentilkiwi 联系并告诉李和我 asktgt.exe 接受一个 /key: NTLM 参数和一个密码。 这使得我们可以使用帐户哈希而不是纯文本密码来执行下面的场景3和4!
场景1: 用户帐户的配置为约束委派并且已知明文密码
这是本杰明在他的推特中展示的场景。如果你能够破解启用了约束委派的用户帐户的明文密码,那么你可以使用 Kekeo 请求 TGT,执行 S4U TGS 请求,然后就可以访问目标服务。
用 msds-allowedtodelegateto 枚举用户
为启用了约束委派的用户帐户请求一个 TGT
使用 s4u.exe 执行 S4U2Proxy
注入 S4U 票证获得访问权限
同样,如果你希望从 Linux 系统执行这种攻击,请阅读 Ben 的这篇文章。
场景2: 计算机上的代理配置为约束委派
如果你能够拿到一个配置为约束委派(而不是用户帐户)的计算机帐户的权限,那么攻击方法就有点不同了。 由于任何作为 SYSTEM 运行的进程都具有本地计算机帐户所具有的权限,因此我们可以跳过 Kekeo asktgt.exe 步骤。 你可以使用另一种方法来执行 S4U2Proxy 进程,这是微软提供的帮助。 李和我将这个过程从 C# 翻译成了 PowerShell 代码,如下:
# translated from the C# example at https://msdn.microsoft.com/en-us/library/ff649317.aspx # load the necessary assembly $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel') # execute S4U2Self w/ WindowsIdentity to request a forwardable TGS for the specified user $Ident = New-Object System.Security.Principal.WindowsIdentity @('[email protected]') # actually impersonate the next context $Context = $Ident.Impersonate() # implicitly invoke S4U2Proxy with the specified action ls \\PRIMARY.TESTLAB.LOCAL\C$ # undo the impersonation context $Context.Undo()
正如微软官方所详细说明的,在使用 WindowsIdentity 时,在大多数情况中,默认情况下返回的是"标识级"的标记。 这允许你查看哪些组与用户标记是相关联的,但不允许重用访问。 为了假冒上下文以访问其他网络资源,需要一个模拟级别的令牌,只有在请求帐户拥有"Act as part of the operating system"用户权限(SeTcbPrivilege)时才返回该令牌。 这种权限只在默认情况下授予 SYSTEM 账户,但是因为我们已经在网络上使用计算机帐户的权限拿到了 SYSTEM 权限,所以我们不需要担心这一点。
另外,我之前提到过,由于 PowerShell.exe 的一些特性,如果你正在使用 PowerShell Version 2,那么你需要在单线程的 apartment 模式(带有"-sta"标志)中启动 PowerShell.exe,以便令牌假冒正常工作:
设置了 msds-allowedtodelegateto 的计算机的 SYSTEM 权限
计算机帐户的 S4U2Proxy
场景3: 用户帐户配置为约束委派并且已知 NTLM 哈希
我们的下一个目标是仅给定目标用户的 NTLM 哈希就从 windows 系统执行这种转换攻击,遗憾的是,我们无法使用与场景2相同的方法正常工作。 我们的直觉是,我们忽略了一些愚蠢的细节,但我们想详细说明我们尝试了什么,哪里出了问题,以防有人给出一个能正常工作的建议。Ben 指出 /key:NTLM 也适用于 asktgt.exe,下面将介绍这一点。
我们尝试使用 Mimikatz 的 PTH 命令将用户的哈希注入内存(假设你是跳板机上的本地管理员) ,而不是使用 Kekro 的 asktgt.exe。 这里的一个问题(如场景2中所示)就是 SeTcbPrivilege,但是尽管明确地授予了我们的重要用户这种权限,我们依旧遇到了问题。 看起来 S4U2Self 操作步骤可以工作正常:
尽管我们已经拿到了必要的特权或权限,但似乎 S4U2Proxy 进程退回到了 NTLM,而不是 Kerberos,用一些值为 NULL 的身份验证信息代替了正确的进程:
你可以使用 asktgt.exe /s4u.exe 执行此方案,其执行方式几乎与场景1完全相同。 只需将 / assword:PLAINTEXT 替换为 /key: NTLM,如下图所示:
场景4: 计算机帐户配置为约束委派配置并且已知 NTLM 哈希
如果你通过某种方式拿到了计算机帐户的哈希,并希望从另一个域的计算机执行攻击,我们设想你将执行一个几乎与场景3相同的攻击流程。 不幸的是,我们也遇到了同样的问题。 再次强调,如果有人能告诉我们哪里做错了,请告诉我们,我们会非常感激🙂 这可以用 /user:MACHINE$ 和 /key:NTLM执行,与场景3相同:
保护措施
微软已经在活动目录中内置了很好的保护措施,可以帮助减少委派滥用。 如果一个帐户启用了"(敏感账户不能委派)Account is sensitive and can not be delegated",那么"即使将服务帐户设置为 Kerberos 委派的受信任帐户,也不会将用户的安全上下文委派给服务"。 通过再次检查 userAccountControl 属性,检查 NOT_DELEGATED 值,就可以轻松地检查帐户是否设置了这个属性。 Powerview 允许你轻松搜索已设置或未设置该值的帐户(Get-DomainUser -AllowDelegation/-DisallowDelegation) ,并且可以使用 ConvertFrom-UACValue 函数检查特定帐户中该属性的值,如前面的示例所示。
后续我将发表一篇与本文主题稍有重叠的文章,并提出更多关于修改用户对象的委派组件所需权限的防御性想法。