另辟蹊径:利用活动目录的复制元数据检测域内恶意活动
导语:在本文中我想展示复制元数据如何帮助检测一些类型的恶意活动。
随着 BloodHound 最近发布的 ACL 攻击路径更新,以及@wald0和我自己对活动目录中的 DACL 后门的研究(请点击这里查阅白皮书) ,我开始从防御的角度研究基于 ACL 的攻击路径。 Sean Metcalf 在活动目录威胁检测方面做了一些很棒的工作(参见他在2017年 BSides Charm上的"Detecting the elive: Active Directory Threat Hunting"的演讲) ,在本文中我想展示复制元数据如何帮助检测这种类型的恶意活动。
此外,在这篇文章起草之后,Grégory LUCAND 向我指出了他在同一主题领域写的一篇广泛的文章(法语) ,题为"元数据复制在活动目录取证分析中的应用(法语版)"。 他详细介绍了 OU 的更改,以及一些复制组件(如链接值复制)的工作方式,并对此进行了深入研究(比本文更深入)。 我强烈建议你查看他的文章,即使你不得不像我一样使用谷歌翻译阅读他的文章。
我将深入研究一些与域复制元数据有关的背景知识,然后将分析每个 ACL 攻击原语以及如何检测到这些修改行为。 不幸的是,复制元数据可能有一些限制,但它至少可以帮助我们缩小发生的修改事件以及事件发生的域控制器的范围。
注意: 本文中的所有的例子都使用了我的测试域环境,它运行在 Windows 2012 r2的域功能级别。 其他域功能版本将有所不同。 此外,所有的例子都是在实验室环境中完成的,因此在真实网络中的确切行为也会有所不同。
活动目录 复制元数据
当对活动目录中的域控制器中的域对象进行更改时,这些更改将复制到同一域中的其他域控制器(请参阅这里的"目录复制"部分)。 作为复制过程的一部分,关于复制的元数据保留在两个构造的属性中,即从其他属性计算最终值的属性。 这两个属性分别是 msDS-ReplAttributeMetaDataand和 msDS-ReplValueMetaData。
旁注: 我在复制元数据方面的前期工作包括这篇关于跟踪 UPN 修改的文章,以及这个系列中关于这些数据的不同用例的文章。 这些文章阐述了如何使用 REPADMIN/showobjmeta 以及Active Directory cmdlet 枚举和解析返回的 XML 格式化数据。 几个月前,我push了一个 PowerView 提交,它简化了这个枚举的过程,我将在本文中演示这些新的函数。
msDS-ReplAttributeMetaData
1.1.1
首先,我们如何知道哪些属性被复制了? 对象属性本身在林模式中表示,并包含了一个包含各种元设置的 systemFlags 属性。 包括 FLAG_ATTR_NOT_REPLICATED 标志,它指示不能复制给定的属性。 我们可以使用 PowerView 快速枚举所有这些不可复制的属性,使用按位查询的 LDAP过滤器检查这个标志:
Get-DomainObject -SearchBase 'ldap://CN=schema,CN=configuration,DC=testlab,DC=local' -LDAPFilter '(&(objectClass=attributeSchema)(systemFlags:1.2.840.113556.1.4.803:=1))' | Select-Object -Expand ldapdisplayname
如果我们想要获得可以被复制的属性,我们可以添加!字符否定过滤器的逻辑:
Get-DomainObject -SearchBase 'ldap://CN=schema,CN=configuration,DC=testlab,DC=local' -LDAPFilter '(&(objectClass=attributeSchema)(!systemFlags:1.2.840.113556.1.4.803:=1))' | Select-Object -Expand ldapdisplayname
因此,针对上述对象集合中的任何属性的更改都会复制到其他域控制器,因此,在 msDS-ReplAttributeMetaData 中具有复制元数据的信息(链接属性除外,稍后将详细介绍)。 因为这是一个构造的属性,所以我们必须指定在 LDAP 搜索期间计算这个属性。 幸运的是,你可以使用PowerView为 Get-Domain* 函数指定 -Properties msDS-ReplAttributeMetaData 来实现这一点:
可以看到,我们得到了一个 XML 文本数组,它描述了修改事件。 Powerview 全新的 Get-DomainObjectAttributeHistory 函数将自动查询一个或多个对象的 msDS-ReplAttributeMetaData,并将 XML 文本块解析为自定义 PSObjects:
分解每个结果,我们得到了对象本身的区别名称、复制属性的名称、最后一次更改属性的时间(LastOriginatingChange)、属性更改的次数(Version) ,以及目录服务代理的区别名称(修改自LastOriginatingDsaDN)。
旁注: “解析 LastOriginatingDsaDN”部分描述了如何将这个大家所熟知的名称解析为合适的域控制器对象本身。 不幸的是,我们不知道是谁进行了更改,也不知道前面的属性值是什么; 但是,我们仍然可以用这些数据做一些有趣的事情,稍后我将进行介绍。
msDS-ReplValueMetaData
为了理解 msDS-ReplValueMetaData 以及为什么它与 msDS-ReplAttributeMetaData 分离,你需要理解 活动目录 中的链接属性。 Windows Server 2003域功能级别中的链接值复制"允许单独复制多值属性的值。" 。在英文中,构造或依赖于其他属性的属性被打破,这样整体的一部分可以被一个一个地复制,而不是一次性复制整个分组。 这是为了减少现代域环境中的复制流量而引入的。
使用链接属性,活动目录 从另一个属性(称为前向链接)的值计算给定属性(称为反向链接)的值。 最好的例子是组成员关系的 member和 memberof : 组的member属性是前向链接,而用户的memberof 属性是反向链接。 枚举用户的memberof 属性时,反向链接会延伸到以生成最终的成员关系集。
关于前向链接和反向链接,还有两个需要注意的地方。 首先,前向链接是可写的,而反向链接是不可写的,因此当前向链接属性更改时,相关的反向链接属性的值将自动更新。 其次,由于这个原因,域之间只复制前向链接的属性,然后自动计算反向链接。 要了解更多信息,请查看这篇关于这个主题的文章。
对我们来说,一个巨大的优势是,由于前向链接属性以这种方式复制,这些属性的原来的值存储在复制元数据中。 这正是 msDS-ReplValueMetaData 构造的属性存储的内容,也是 XML 格式的。PowerView中 新的 Get-DomainObjectLinkedAttributeHistory 函数为你包装了以下内容:
现在我们知道 member 和 memberof 是一个链接集合,因此上面的修改结果是针对 member 的。
为了枚举所有前向链接的属性,我们可以再次检查林模式。 链接属性在模式中有一个 Link 和 LinkID ——前向链接有一个偶数的非零值,而反向链接有一个奇数的非零值。 我们可以使用[DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetCurrentSchema() 获取当前模式,然后可以使用 FindAllClasses ()方法枚举当前的所有模式类。 如果我们按照偶数的类属性进行筛选,我们可以找到所有链接的属性,因此这些属性的原有的值会被复制到 活动目录元数据中。
这里有很多结果,但不幸的是,我们可能关心的主要结果是 member / memberof 和 manager / directreports。 因此,member和manager是对象中唯一有趣的属性,我们可以跟踪以前的修改值。 然而,与 msDS-ReplAttributeMetaData 一样,我们依旧看不到实际上是谁发起了更改。
利用复制元数据检测恶意活动
好吧,我们有一堆看似随机的复制元数据,我们到底怎么用它来"发现不好的地方呢?" 元数据不会神奇地告诉你一个完整的故事,但是我相信它可以为你指明正确的方向,而且还有一个额外的好处,那就是已经存在于你的域中的功能。 我将逐一分解检测@wald0和我自己提到的每个 ACL 攻击原语的过程,但对于大多数情况,检测过程如下:
1. 使用 活动目录复制元数据检测针对对象属性的更改可能 会发现一些恶意行为。
2. 从链接到更改的域控制器中收集详细的事件日志(如元数据的指标) ,以便跟踪谁执行了更改,以及更改的值是什么
这个检测过程有一个小小的例外:
组成员资格修改
这个例子的修改行为检测是最简单的。 控制关系是将成员添加到组中的权限(WriteProperty 到 Self-Membership) ,而通过 PowerView 的攻击原语是 Add-DomainGroupMember。 让我们看看 Get-DomainObjectLinkedAttributeHistory 中的信息可以告诉我们什么:
在第一个条目中,我们看到'EvilUser'最初是在21:13添加的(TimeCreated) ,现在仍然存在(TimeDeleted 为 epoch)。 版本为3意味着 EvilUser 最初是在TimeCreated时添加的,在某个时刻被删除,然后在17:53重新读取(LastOriginatingChange)。 注意: 这些时间戳格式是 UTC!
在第二个示例中,TestOUUser 在21:12(TimeCreated)时被添加到组中,并在21:19(TimeDeleted)时被删除。Version 为偶数以及TimeDeleted的值为非epoch,那么这意味着该用户不再出现在组中,并且在指定的时间被删除。 Powerview 的最新函数 Get-DomainGroupMemberDeleted 只返回元数据组件,指示已删除的用户:
如果我们需要更多的细节,我们可以查看目录系统代理(DSA) ,这个目录系统代理是更改的源头,也就是在这个环境中处理更改的域控制器代理(在这里是主域控)。 由于我们有被修改的组(TestGroup)和发生更改的大致时间(UTC 时间21:44) ,我们可以去启动更改的域控制器服务器(主域控)获取更多的事件日志细节(参见"旁注: 解析 LastOriginatingDsaDN"部分获得更多关于这个过程的更多细节)。
我们真正想要的审计在默认情况下是不开启的,但是可以通过"本地计算机策略-计算机配置-Windows 设置-安全设置-高级审计策略配置-帐户管理-审计安全组管理"来启用:
这将生成事件日志 id 为4735/ 4737/4755的日志,用于修改域本地、全局和全局范围的安全组:
我们可以在事件细节中看到,TESTLAB\dfm.a 是发起更改的主体,它与我们在复制元数据中观察到的删除事件相关。
用户服务主体名称修改
这是另一个很有趣的例子。 绝大多数用户将永远不会有服务主体名称(SPN)的设置,除非帐户注册为... 运行服务。 SPN 修改是我以前提到过的一种攻击原语,它为我们提供了一个很好的机会来利用元数据的"Version"字段,即属性被修改的次数。
如果我们为一个用户设置了一个 SPN然后再解除这个设置,那么与属性元数据相关联的 Version字段的值将是偶数,这表明曾经有一个集合形式的值:
如果我们启用「审核用户帐户管理」及「审核计算机帐户管理」设置,我们可以得到更多有关更改的详细信息:
事件 ID 为4738,但不幸的是,事件日志详细信息在更改时不会显示 servicePrincipalName 的值。 然而,我们再次得到了发起这一更改的主体:
注意,事件记录的时间戳与复制元数据的 LastOriginatingChange 是匹配的。 如果我们想对每个有 SPN 集合的用户帐户进行大规模枚举,然后删除它们,我们可以使用-LDAPFilter'(samAccountType 805306368)'-Properties servicePrincipalName,并过滤掉任何带有奇怪 Version 的内容:
对象所有者 /DACL更改
我最初认为这个场景也很困难,因为我已经猜到,每当在 OU 上更改委派时,那些新的权限都会反映在继承链上的任何用户对象的 ntSecurityDescriptor 中。 但是,我错了——任何委派更改都在 OU/容器的 ntSecurityDescriptor 中,我相信这些继承的权限是由服务器在 LDAP 枚举上计算的。 换句话说,用户/组/计算机对象的 ntSecurityDescriptor 只有在显式更改所有者,或者手动向该对象添加新的 ACE 时才应该更改。
由于对象的 DACL 和所有者都存储在 ntSecurityDescriptor 中,而且事件日志数据不提供关于前一个或更改的值的详细信息,因此我们无法知道它是 DACL 还是基于所有者的更改。 然而,我们仍然可以通过使用事件4738来找出是谁发起了这次更改:
就像使用 SPN 一样,我们也可以清除任何用户(或其他对象)的 DACL 或所有者更改后的值(即Version 的值大于1) :
如果我们定期枚举所有用户或其他对象的所有数据,我们就可以开始记录时间线并计算变更增量,不过这就是另一篇文章要说明的内容了:)
重置用户密码
首先,不幸的是,这个例子可能是最困难的部分。 由于密码更改 或重置相当常见,因此很难仅仅根据上次设置的时间从数据中提取出一个模式。 幸运的是,启用"Audit User Account Management"策略还会生成事件4723(用户更改了自己的密码)和事件4724(启动了密码重置) :
我们得到了重置的时间,被强制重置密码的用户以及初始化它的主体!
组策略对象编辑
如果你能够跟踪恶意的 GPO 编辑行为,并想知道受影响的系统 和用户,那么你可以阅读我的这篇文章,这篇文章中会谈到过相关的检测过程。 但是,本节将集中于尝试识别哪些文件被编辑以及被谁编辑。
每次修改 GPO 时,versionNumber 属性都会增加。 因此,如果我们提取与上次修改 versionNumber 有关的属性元数据,并将这次(作为一个范围)与 SYSVOL 路径中的所有文件和文件夹的编辑关联起来,我们就可以识别那些可能由于上次对 GPO 的编辑而被修改的文件。 以下是我们可以做到这一点的方法:
你可以看到上面的 Groups.xml 组策略首选项文件很可能是经过编辑的文件。 为了确定哪个用户做了更改,我们需要调整"本地计算机策略-计算机配置-Windows 设置-安全设置-高级审计策略配置-DS 访问-审计活动目录服务更改":
然后,我们可以梳理5136这个事件 ID,并使用警报数据来缩小导致 versionNumber 修改的事件范围:
我们可以看到 GPO 对象的区别名称(DN)正在被修改,以及是谁发起了更改。 如果你感兴趣的话,这里还有一些更多的信息供你参考。
旁注: 解析 LastOriginatingDsaDN
正如我前面提到的,LastOriginatingDsaDN 属性表示发生给定的更改来自的最后一个目录服务代理。 为了充分利用这些信息,我们希望将这个特殊的 DSA 记录映射回它所运行的域控制器。 不幸的是,这是一个多步骤的过程,但我将在下面使用 PowerView 向你详细介绍。
假设我们要追踪的更改是下图中已删除的域管理员组成员:
我们看到 DSA 区别名称存在于关联域的 CN=Configuration容器中。 我们可以使用 PowerView 的 Get-DomainObject 检索这个引用的完整对象,并将 -SearchBase 设置为" ldap://CN=Configuration,DC=testlab,DC=local ":
我们在上面看到它有一个 NTDS-DSA 对象类别,并且我们看到一个 serverreferencebl (反向链接)属性指向正确的方向。 如果我们解析这个新对象 DN,我们会得到如下结果:
现在我们在这个新结果的 msdfsr-computerreference 属性中看到了实际的域控制器区别名称,并且serverreference 与我们初始结果中的LastOriginatingDsaDN 相匹配。 这意味着我们可以跳过中间步骤,通过自定义的 LDAP 过滤器,查询通过 serverreference 属性链接的 ms-DFSR-Member 对象目录。 最后,我们可以提取 msdfsr-computerreference 属性并将其解析为实际的域控制器对象:
成功了!
总结
希望这至少会引起一些人从 活动目录方面考虑恶意活动检测和取证的可能性。 这里有很多机会来检测我们提出的基于 ACL 的攻击组件,以及无数其他活动目录的攻击技术。 而且,善于观察的读者可能已经注意到了,我忽略了整个防御组件,即系统访问控制列表(SACLs) ,它提供了实现附加审计的机会。 我将在以后的文章中介绍 SACL,展示如何利用 BloodHound 识别"关键领域"来设置非常具体的 SACL 审计规则。
发表评论