域信任关系攻击指南

文章目录

  • 什么是域信任关系
  • 为什么我们要关注信任关系
  • 信任关系攻击策略
  • 如何枚举信任关系
    • .NET方法
    • Win32API
    • LDAP
    • 使用PowerView通过信任关系枚举信息
    • 映射Domain信任关系
    • 域信任关系可视化
    • 外部关系枚举
      • 场景一: 本地用户组关系
      • 场景2:外部组成员
  • Another Sidenote: Kerberoasting Across Domain Trusts
      • 注解:

原文链接

距离我上一次发表关于Action Directory 域信任相关的博客已经差不多过去两年了。我发现我之前的文章中有些关于信任关系和组成员的误解。结合去年PowerView做出的改动,最终使我更新了枚举和攻击域信任关系的文章。这将是我最后一篇讲解关于域信任关系攻击的文章,大概有8000字,读起来并不轻松。通常情况下,我不会仅仅写出我的操作笔记----我尽量详细描述,以期我的文章可以作为大家的一份全面的指南。

我尽可能让这篇文章完整,这样我就可以覆盖到关于信任关系的方方面面。就像我之前的文章,我想在这个时间点将我的想法封装在这个主题中,强调“这个时间点”。我们的知识和技术总是在发展,信任关系也是一样。当我开始写关于域信任关系的文章时,我发现自己有好多关于他的模糊的理解。我从未当过系统管理员或者AD架构师----我零碎地学习了一些知识,以期填补我之前的文章中的不同的地方,当然以后还会出现不同的地方。(因为技术一直在演进)

因此我决定从心开始,以防你对之前的我的关于域信任关系的文章不太熟悉。所以,这篇文章的一些地方可能会和之前的有所重复。

PowerView的最新版本总是存在于这个分支中:https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1

什么是域信任关系

从高层次来讲,域信任关系使得一个域中的用户可以访问另一个域中的资源。正如微软描述的:大部分的拥有一个以上的域的组织都有让一个域中的用户访问另一个域中资源的合法需求,信任关系允许组织可以让多个域为分散在不同域中的用户赋予适当的资源访问权限的能力。域林是域的集合,同一个域林中的域都是互相信任的。当然,不同的域林之间也可以建立信任关系。关于信任关系的详细基础知识,请参考这篇文章https://blog.csdn.net/include_heqile/article/details/102537646

基本上,所有的信任关系都会联系两个域之间的认证系统并允许认证相关的流量在两个域之间流通。当当前域中的用户访问域外资源的时候,当前域的域控制器会向用户返回一个目标域的KDC的引用票据。

这个用户的TGT被包含在TGS-REP(ticket-granting service reply)引用,这个票据是使用信任关系key(应该是跟信任关系密码相关的东西)签名过的。这个票据通常被称作跨域TGT。外部域使用之前协商好的trusts key解包并对该数据包进行确认,然后再进行剩下的正常的kerberos通信过程。

Sean Metcalf在他的文章“It’s All About Trust”中对该过程有一个详细的描述,他将这个过程描述为“一旦两个域之间有了信任关系,每一个域的ticket-granting服务都会在另一个域的KDC中被注册为一个安全实体。这使得每一个域中的ticket-granting服务可以将另一个域中的ticket-granting服务当做一个提供跨域资源访问的服务。”

因此,基本上来说,当一个外部域解密引用票据时,他看到用户的TGT票据并回应到“好了,我已经认证过该用户了,并且相信该用户就是该用户(不是别人冒名顶替的),因此我将相信这个信息是准确无误的,因为我信任颁发这个引用的域。“

图解:
域信任关系攻击指南_第1张图片

建立信任关系的目的是为了让用户可以从一个域去访问另一个域中的资源,(就像是一个服务器上的本地管理员组合),寄宿在组里,或者在另一个域中成为安全实体(比如AD对象ACLs)。一个例外就是内部林信任关系,创建于同一个林中的任意域都和林中的其他域拥有着双向可传递的信任关系。这对我们有着巨大的启示,下面将会细讲。

但是在此之前,我们必须再讲一些关于信任关系的特性。

下面介绍的十几种信任类型,这个在这篇文章中有详细介绍,不再赘述:
原文
中文

为什么我们要关注信任关系

信任关系总是会在攻击环境中制造出意料之外的访问路径。在很多组织内部,信任关系是在10+年以前建立的,而且当时并未考虑到安全性。一些处于收购过程中的公司经常将新公司的Active Directory直接插到当前Active Directory中,要么作为子域,要么建立外部信任关系,丝毫不考虑安全问题。

因为历史原因,没有太多的工具供你进行map、enumerate、可视化这些由于误配置造成的安全问题,许多域架构师根本没有意识到这些存在于他们的信任架构种得unintentional风险。这就使我想到了我今年的这篇文章,这是这样,这些年来,各式各样的红队(进行网络攻击的一些组织)滥用Active Directory信任关系在攻击中取得巨大的成功。

一个常见的场景就是获取一个正处于开发中的域或者子公司的域,然后利用这个作为跳板访问到目标组织的安全网络。这也给持久化留下了机会,当攻击者可以在一个安全性较低的域中运行植入代码的时候,当然就不会再在安全性较高的环境中留下持久化代码,因为他们可以直接在该低安全性的环境中随时执行代码,进而再中转进入到安全性较高的域中。

一个域林内的信任关系引出了一种非常炫酷的攻击方法,待会儿会详细讲述。外部林信任关系不能保证任何种类的访问权限,但至少一个信任关系意味着你可以从信任当前域的域中查询任意正常的Active Directory信息。毕竟,Active Directory本来就是一个可供查询的数据库,即使是通过信任关系,我们依然可以进行查询。

信任关系攻击策略

在我们详细讨论关于列举和滥用信任关系的技术细节之前,我想要先认真讨论以下我在审计信任关系时所使用的高级策略。当我谈到信任关系攻击策略的时候,我想表达的是一种从你当前能够访问的域到你想要访问的域的横向移动。

1、第一步就是枚举你当前所在的域的所有的信任关系。基本上,你想要生成一个关于你当前所处的环境所能到达的域的mapping。这将会使得你知道你需要跳过哪几个域来到达目标域,以及你完成这个目标所要使用到的技术。在你这张虚构出的地图上,任何位于同一个域林中的域都应该是你所感兴趣的对象,因为针对这些域有一种特定的攻击手段叫做SIDhistory-trust-hopping,它的作者是Sean Metcalf和Benjamin Delpy,这个技术将会在下面进行介绍。

2、下一步就是枚举域1中所有的用户/用户组/计算机(安全实体)。关键点就在于从信任关系边界中找到跨域的方法,可以给我们提供一个跨域的access bridge。与此同时,跨域的关系并不能保证遍历畅通的访问,信任关系的建立通常都是有原因的,这意味着通常存在着某种类型的跨域用户/组/资源的嵌套,在很多组织内部,这个关系没有被正确配置。另一个策略,就像我们之前提到的,跨信任关系Kerberoasting2:可能成为另一种攻击向量来帮助我们完成域的跳转。下面有该子主题Another Sidenote: Kerberoasting Across Domain Trusts的介绍

3、现在你有了一份关于信任路径、信任类型、和跨域的nested relationships的地图,有了这份地图,你就知道应该获取哪几个账户来跳到你的目标域中了。通过获取目标账户和利用SID-history-hopping,我们可以最多在一个域林中跳跃7+次来到达我们的目标域。

在最次的情况下,请记住,如果一个域信任你,比如一个双向的信任关系或者单向的、进入方向(意思就是别的域信任你)的信任关系,那么你就可以在该信任域中查询信息。还有,要记住所有的父子域之间都是双向可传递的信任关系。并且,Enterprise Admins组会自动添加到林中所有域的Administrators domain local group中。这意味着信任关系流从forest root往下,使得我们的攻击目标是从子一级向父一级的方向移动(往上),直到forest root。

如何枚举信任关系

我要如何才能知道我当前所处的环境中都存在那些心热关系呢?

就我所知,有三种主要的的方法来枚举域信任关系

  • win32 API调用
  • 各种各样的.NET方法
  • LDAP
    每一种方法都会返回不同形式的信息,而且每一种都有不同的执行方式。我会讲解所有的老式方法和新式方法,从内置的可执行文件到.net、win32 api 调用,再到powershell/powerview和BloodHound

在这篇文章中我使用的域环境的架构如下:
域信任关系攻击指南_第2张图片
这张图是由 TrustVisualizer生成的,这个工具将会在域信任关系可视化一节中进行介绍。在这张图中,绿色箭头表示同一个域林中的信任关系,红色表示外部信任关系,蓝色表示外部林信任关系。和sixdub的工具DomainTrustExplorer一样,图中的单向箭头代表访问的方向,比如A指向B就意味着A可以访问B中的资源。

.NET方法

.NET给我们提供了一些不错的方法来枚举域和林中的信任关系信息。这也是PowerView所实现的第一个方法。

在命名空间System.DirectoryServices.ActiveDirectory.Domain中有一个叫做GetCurrentDomain的静态方法,他可以返回一个System.DirectoryServices.ActiveDirectory.Domain类的实例,这个类很好地实现了GetAllTrustRelationships()方法,它的作用就像它的名字一样,获取当前域相关的所有信任关系信息。该方法的一个优点就是,它非常简洁,它所返回的数据清晰易读。它的优点就是“其他的枚举方式所能获取的一些数据它获取不了”,意思就是相比其他的枚举方式,它获取的信息量会相对少一点。

([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).GetAllTrustRelationships()

这个方法也是过去的PowerView的默认Get-DomaniTrust枚举方法。我最近将这个默认方法改成了LDAP方式,因为之前的方式默认情况下不会枚举林信任关系数据。所以在最新的PowerView版本中,要想再使用.NET方式,你需要调用Get-Domain -NET

在我的这个实验环境中,运行结果如下:
域信任关系攻击指南_第3张图片
林信任和域信任的工作方式不同。因此如果你想枚举关于当前林之间的信任关系,你需要调用System.DirectoryServices.ActiveDirectory.Forest。这个类也有一个GetAllTrustRelationships()方法,该方法会返回当前林相关的信任关系信息。
([System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()).GetAllTrustRelationships()

现在该方法已经在PowerView中进行了实现,调用的方式是Get-ForestTrust,运行结果如下:

域信任关系攻击指南_第4张图片

Win32API

你也可以通过DsEnumerateDomainTrusts()Win32APU枚举域信任关系,它会返回一个DS_DOMAIN_TRUSTS 结构体类型的变量。这个信息看起来可能会比.NET方式返回的结果复杂一点,它会返回目标域的SID和GUID,还有一些有用的标志位和属性。这些标志位的文档参考这里,这个文档将会告诉你信任方向,以及信任关系是否是同一个林中的信任。关于属性的文档在这里。

你可以在PowerView中调用Get-DomainTrust -API来调用Win32API,运行结果如下:
域信任关系攻击指南_第5张图片
和nltest.exe /trusted_domains返回的结果是一样的。
在这里插入图片描述
可以在刚才的PowerView方法后面再加上-Domain 来查询林信任信息。

LDAP

域信任关系被当做TDO(trust-domain-object)存储在Active Directory中,这些TDO的objectClass为trustedDomain。这意味着你可以使用LDAP查询所有的域信任关系信息,通过使用LDAP 筛选器(objectClass=trustedDomain)

例如:
dsquery * -filter "(objectClass=trustedDomain)" -attr *

域信任关系攻击指南_第6张图片
在PowerView中,可以使用Get-DomainTrust命令来执行LDAP查询获取域中的信任关系信息。

现在LDAP是PowerView的Get-DomainTrust的默认实现方式,我将讲解一些容易让人感到疑惑的属性

TrustType:

TrustAttributes

上面的介绍的所有方法也可以针对信任你的域执行。这意味着,如果你当前的域和外来的域有一个双向的信任关系,或者信任关系是单向的但外来域是信任你的,那么你就可以找出那个域的信任关系信息。你可以通过给PowerView的Get-DomainTrust后面加上-Domain 参数来指定你想要针对进行查询的域。

使用PowerView通过信任关系枚举信息

去年我描述了我的重写PowerView的过程。其中提到的一项改动就是:现在,任意的Get-Domain*方法都是用LDAP,这意味着我们可以从信任我们的域中获取信息。主要参数 -Domain

运行结果如下:
域信任关系攻击指南_第7张图片
所以在这个方法调用的背后究竟发生了什么?

很长时间以来,我都以为这个过程是通过我们当前域的域控制器将该LDAP查询传递到目标域(信任我们的域)的域控制器上,然后执行查询。这是一个很酷的方法来穿过目标网络的边界,但是悲哀的是,是我理解错了。实际上发生的是,当前域的域控制器返回的是一个引用,这个引用会指示你的搜索方法绑定到外部域上,一个领域内(inter-realm)的TGT将会被返回给客户端。

这意味着如果你的客户机和你要访问的域的PDC的网络是被隔离的,那么你就无法获取到任何信息。

从Kerberos的角度来讲,背后发生的过程就是一系列的inter-realm引用票据被自动颁发以允许我们最终可以向目标域申请一个LDAP服务票据。下面是我们尝试在sub.dev.testlab.local查询prod.contoso.local时发生的kerberos ticket传递的过程:

域信任关系攻击指南_第8张图片
从下往上看,就是按照上最上面的域环境的结构走的。

映射Domain信任关系

据我所知有好几种方法可以将你所在的环境中的信任关系映射出来。第一种方法是通过Global Catalog。我曾经在这篇文章中提到过,不过我会在这里重申一下。

global catalog 是域林中所有对象的一个不完整拷贝,这意味着一些对象属性是包含在global catalog中的,但并不是所有的。这些数据会在域林中所有被标记为global catalog的域控制器之间进行复制。TDO也在Global Catalog之间进行复制,因此我们可以以很快的速度枚举当前林中域的每一个内部和外部的信任关系,而且在这个过程中,指挥跟我们的当前域的PDC进行通信,在PowerView中运行这条命令:
Get-DomainTrust -SearchBase "GC://$($ENV:USERDBSDOMAIN)"
下面是我们在sub.dev.testlab.local中的运行结果:
域信任关系攻击指南_第9张图片
可以看到这个产生的结果比Get-DomainTrust要多很多,

第二种方式运行的速度要相对慢一些,但是会返回比第一种方法还要多的结果。我们可以枚举出当前域中的任意信任关系,通过LDAP的票据引用,我们可以爬取所有我们能到达的域的信任关系。

在PowerView中可以使用Get-DomainTrustMapping命令,该命令返回的结果可以通过管道符传送给另一个命令,来将结果格式化为CSV格式的:

Get-DomainTrustMapping | Export-CSV -NoTypeInformaion trusts.csv

最后一种方式是使用BloodHound/SharpHound。使用SharpHound.ps1猎食者通过Invoke-BloodHound -CollectionMethod trusts语法来执行域信任关系的搜集,还可以在后面加上-Domain 来枚举外部域。

关键是要记住你所能得到的准确的信任关系映射取决于你所处的当前域。 由于external.localsub.dev.testlab.local域之间的信任关系是单向且非传递的,所以如果你从external.local进行查询的话,是看不到contoso.local域的信任关系的,这是因为sub.dev.testlab.local不会将你的TGT打包成可以在域中进行传递的inter-realm TGT。而且如果你尝试枚外部域上的信任关系,你需要有能力绑定到该外部域的域控制器上(通常是PDC/primary domain controller)。因此,即便是有一个可传递的信任关系允许你去查询信任关系信息,如果目标域的网络被隔离,那很抱歉,你真够倒霉的。

域信任关系可视化

数据是一方面,可视化是另个一方面,数年以前,我的一个前同事,Justin Warner,看到这些原始数据,感觉不够清晰易读,所以就创建了一个工具叫做DomainTrustExplorer,该工具可以针对PowerView返回的结果进行一些节点分析和可视化工作。

由于默认的信任关系输出结果被改变了,并且也有了BloodHound为我们生成可视化分析的结果,于是我重写了Justin的工具,叫做TrustVisualizer:
域信任关系攻击指南_第10张图片
输出结果可以使用yEd进行查看,最上面的域拓扑图就是这样生成的。

再一次说明,在新的输出格式中,绿色代表域林内信任红色代表外部信任蓝色代表域林间信任关系箭头指向的域为信任域,也就是被访问的域

当时用SharpHound搜集信任关系数据时,BloodHound也会生成可视化的图标,对于我们的环境,使用BloodHound生成的图表如下:
域信任关系攻击指南_第11张图片

外部关系枚举

现在我们已经映射了域信任关系。下一步要做什么需要根据信任关系类型来确定。下面这几步可以用于实现域间跳转。

如果下一跳的域和当前域在同一个林中,并且我们可以获取到当前域的krbtgt的hansh,那么我们就可以使用下面这种方法来获取林根域的权限。这种方法会在Trustpocalypse章节中讲解

如果我们没办法获取到当前域的kerbtgt hash,或者下一跳的域不在同一个林中,那么我们就需要枚举出当前域中的哪些用户在目标域的用户组中。

不幸的是,在这一步中有很多注意事项,当前域和目标域之间的信任关系的特性会影响到你能获取到的信息,以及你所能使用的获取信息的方式。总的来说,枚举方式完全取决于你要查询的信息是外部域的信息还是同一个林中的域的信息。我会尽量把这些都说明白。

一共有三种主要的方式可以使得一个域中的安全主体访问到另一个域中的资源:

  • 将用户加入到计算机的local administrators组中
  • 将用户加入到外部域的用户组中。有一些注意事项,取决于信任类型和group scope。
  • 他们可以被作为安全实体加入到access control list中,我们最感兴趣的就是ACE和DACL。想了解更多关于ACL/DACL/ACE,请参考An ACE Up The Sleeve白皮书。

场景一: 本地用户组关系

通过远程SAM(SAMR)或者通过GPO correlation枚举一个或者多个系统上的本地成员。关于这一种情况,我不会讲解得太详细,但是需要注意的是我们曾经成功地通过目标SAMR枚举。PowerView也有一个对应的方法实现这个目的,调用方法如下:Get-NetLocalGroupMember ,如果你是用的是BloodHound,那么他会自动完成这些操作。

场景2:外部组成员

组成员场景有些棘手,ActiveDirectory组中的·member属性和用户或者组的memberOf属性之间有一个特别的关系叫做linked attributes.。我在这篇文章中介绍过它,对于Linked Attributes,ActiveDirectory计算给定属性的值,被称作来自另一个属性的值的back link(例如:用户/用户组的memberOf) ,被称作forward link(比如:用户组中的成员
其实这一段我也翻译不明白,读了半天也没弄明白到底是个什么意思
给一个讲解前向后向链接的文章。主旨是组成员最终会被保存在目标用户组的member属性中,这在信任关系中会变得有些复杂。memberOf属性是否保存了一个外部组成员的用户或者用户组对象取决于信任关系的特性以及外部组的范围。

下面详细讲解了三种group scoping:

  • Domain Local Groups 可以拥有林内跨域的用户以及非同林的域中的用户(外部安全实体)
  • Global Groups 不能拥有任何跨域的用户(不管是否为同一个林内的),所以该组对我们没什么作用
  • Universal Groups 可以拥有任何一个同林内的用户作为自己的成员,但是外部安全实体(来自外部信任关系和林信任关系的用户)不能成为该组的成员。

如果一个用户或者用户组被nested到同林内的另一个域的组中(domain loca组合universe groups),然后根据目标组的group scope,用户或者用户组的memberOf属性的组成员可能会被更新。如果用户所在的group scope是domain local,那么这个用户的memberOf将不会被更新(in the global catalog),因为拥有domain local group scope的用户组不会在域林中复制它的成员信息。因此想知道一个用户的外部域组的组成员的唯一的方式就是单独查看该用户对象,查看他是否 被添加到了同一个林内的universal group中。尽管如此,这也意味着如果我们可以绑定到一个域林的Global Catalog上,我们就能很简单地枚举所有这些跨域的关系。

如果用户被nested到了外部林中的用户组中,那么事情就没那么简单了。存在于外部林中的信任关系仍然可以被添加到domain local组中。

Another Sidenote: Kerberoasting Across Domain Trusts

我们喜欢 Kerberoasting

注解:


  1. 注意这个域指的是满足下面这种条件的域:
    拥有对另一个域中的资源的访问权限或者存在于另一个域的group中又或者有一个来自于其他域的用户。 ↩︎

  2. Another Sidenote: Kerberoasting Across Domain Trusts ↩︎

你可能感兴趣的:(active,directory,信任关系,Windows)