域内提权票据篇之剖析CVE-2021-42278&42287漏洞

admin 2025年3月25日08:59:10评论8 views字数 3631阅读12分6秒阅读模式

免责声明:由于传播、利用本公众号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!

文章作者:奇安信攻防社区(中铁13层打工人)

文章来源:https://forum.butian.net/share/2408

0x00 漏洞背景

21年披露了AD域的一个组合漏洞(CVE-2021-42278、CVE-2021-44287),利用AD域对机器账户的认证缺陷和kerberos协议缺陷,只需一个域用户即可拿到域内最高权限,影响巨大。

0x01 漏洞

【CVE-2021-42278】默认情况下加入域内的主机都会创建一个机器账户,该账户名称为机器名加上$结尾,加入域的机器默认在CN=Computers这个容器里,账户名为sAMAccountName属性:

域内提权票据篇之剖析CVE-2021-42278&42287漏洞

1、域中是默认允许域用户创建机器账户的,该属性为MS-DS-Machine-Account-Quota(允许用户在域中创建的机器帐户的数量,普通域用户最多可以创建10个该账户);

2、域控没有针对机器账户名的验证机制,没有校验sAMAccountName结尾的$,即使删除结尾的$照样可以以机器用户身份申请TGT票据。

域内提权票据篇之剖析CVE-2021-42278&42287漏洞

0x02 漏洞

【CVE-2021-42287】配合上述漏洞使用,简述一下漏洞原理,创建一个普通机器账户,将其sAMAccountName改为和域控机器账户名相同但不以$结尾,用该账户进行TGT请求后将sAMAccountName改回原名,然后使用之前得到的TGT去申请该主机的ST,在申请服务过程中,由于二次改名后,此时域内已经没有该账户,导致DC找不到它,协议的处理逻辑会自动在主机名后加上$继续搜索,结果就会搜索到域控机器账户,然后在PAC中添加信息(这些信息就是域控机器账户的用户名和所在的组)并以域控机器身份签名下发ST,达到了提权操作。

0x03 漏洞利用

利用第一个漏洞需要注意,机器账户改名之前必须清除SPN值(servicePrincipalName,服务主体名称),该标志是作为网络控制器服务实例的唯一标识符;域内添加一个机器账户会自动添加四个默认的SPN值(使用脚本:Powermad.ps1)

域内提权票据篇之剖析CVE-2021-42278&42287漏洞
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
我们假设不清除SPN,直接修改sAMAccountName值结果如下图,后面的Kerberos身份验证使用它来将服务实例与服务登录帐户相关联,会导致请求异常,所以在修改samAccountName前要删除其SPN属性
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
我们创建的可利用账户应该是这样:第一步清除SPN值,第二步改名为域控机器名(使用脚本:PowerView.ps1)
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
域内提权票据篇之剖析CVE-2021-42278&42287漏洞

AS-REQ

这里我们以创建的机器账户利用 Rubeus工具 发出请求:.Rubeus.exe asktgt /user:"sAMAccountName" /password:"Password" /domain:"domain.local" /dc:"sAMAccountName.domain.local" /nowrap

域内提权票据篇之剖析CVE-2021-42278&42287漏洞
进行身份验证的账户名,这里是我们修改后的DC
域内提权票据篇之剖析CVE-2021-42278&42287漏洞

AS-REP

域内提权票据篇之剖析CVE-2021-42278&42287漏洞
到这一步,PAC中的Group RID还是515(Domain Computers这个组),即ticket中所代表的身份还是我们创建的这个机器账户:
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
这里参考 在Wireshark中解密Kerberos,即可解密数据包中的PAC,步骤如下:
1、在域控导出ntds.dit和system.hivevssadmin create shadow /for=C:copy \?GLOBALROOTDeviceHarddiskVolumeShadowCopy1WindowsNTDSNTDS.dit C:ntds.ditcopy \?GLOBALROOTDeviceHarddiskVolumeShadowCopy1WindowsSystem32configSYSTEM C:system.hivevssadmin delete shadows /all2、使用esedbexport工具导出表文件,这里没有该工具需要编译一下(建议在linux环境编译),下载并解压:https://github.com/libyal/libesedbsudo apt-get install autoconf automake autopoint libtool pkg-config./configuremakemake installsudo ldconfig3、将导出的ntds.dit以及system.hive复制到Linux中与esedbexport同目录下即:/usr/local/bin,并执行命令,可以将ntds.dit导出成多个文件,文件会存放在当前目录的ntds.dit.export目录内esedbexport ntds.dit4、使用NTDSXtract导出需要的keytab,这里使用python2git clone https://github.com/csababarta/ntdsxtract.gitcd ntdsxtract/ && python -m pip install pycryptodomeesedbexport ntds.ditpython2 dskeytab.py ntds.dit.export/datatable.4 ntds.dit.export/link_table.6 system.hive /usr/local/bin/ntdsxtract/ 1.keytab# datatable.*以及link_table.*都是esedbexport处理ntds.dit之后存在ntds.dit.export中的文件# system.hive 是之前导出的文件# /usr/local/bin/ntdsxtract/是当前ntdsxtract目录# 1.keytab是最后我们需要的keytab文件5、获取1.keytab后,把该文件导入到Wireshark中,编辑->首选项->Protocols->KRB5,勾选"Try to decrypt Kerberos blobs",然后开始抓包即可,wireshark会自动对当前的数据包进行解密尝试,如果解密成功,就会是显示蓝色,不成功就是黄色。
将机器账户改除域控机器名之外的名,这里改回原名:
域内提权票据篇之剖析CVE-2021-42278&42287漏洞

接下来准备请求 ”这台域内已经不存在的机器“ 上的服务票据。

TGS-REQ

Administrator的身份请求 “这台域内已经不存在的机器” 上的cifs服务票据:Rubeus.exe s4u /self /impersonateuser:"Administrator" /altservice:"cifs/DomainController.domain.local" /dc:"DomainController.domain.local" /ptt /ticket:[Base64 TGT]

注意这里使用的是S4U2self协议,为什么要用这个协议来请求我们后面阐述,先看这样请求发生了什么?貌似已经成功申请了一张高权限票据:

域内提权票据篇之剖析CVE-2021-42278&42287漏洞
TGS-REP
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
果然到这一步,PAC中的User RID已经变成了500即Administrator,Group RID为513即Domain Users组:
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
使用 klist 命令查看主机上的票据:
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
成功访问域控服务:
域内提权票据篇之剖析CVE-2021-42278&42287漏洞

0x04 漏洞成因

这里我们来分析一下为什么要使用S4U2self协议?

S4U2self(Server-for-User-to-Self)是 Kerberos 协议中的一种扩展,用于允许Service代替用户向KDC发起服务票据的请求,而无需用户的明文凭证,目的是为了简化服务器的授权,以便调用者自身受益。

域内提权票据篇之剖析CVE-2021-42278&42287漏洞
1、用户向服务1发出请求,服务1已通过KDC进行身份验证并获得其TGT,但服务1没有用户的授权数据;2、服务1通过S4U2self扩展代表指定用户请求服务票证,用户由S4U2self数据中的用户名和用户域名来标识;3、KDC返回一个服务票据到服务1,就像用户使用自己的TGT请求的一样,服务票证包含用户的授权数据;4、服务1可以使用服务票据中的授权数据来满足用户的请求,然后该服务响应用户。

这里从协议代码处理逻辑层面再分析下具体过程,参照 从XP源码泄露看nopac漏洞 这篇分析文章,可总结漏洞成因如下:

KDC Server是如何处理S4U和非S4U请求中的PAC,从KdcInsertAuthorizationData函数中可以找到:

1.如果不是S4U的请求,则直接从TGT的AuthData中提取PAC(沿用最初的PAC,最初的PAC在AS-REP阶段凭请求用户身份生成);

2.如果是S4U请求,首先调用KdcGetS4UTicketInfo请求,这个请求的处理逻辑中又调用了KdcGetTicketInfo,然后再调用kdcGetPacAuthData函数来构造PAC data。

这里的两个函数应该就是漏洞点所在:

  • KdcGetTicketInfo函数,用于从Ticket中获取TicketInfo,该函数会对请求的服务账户名进行顺序判断:

    首先判断是否是krbtgt账户,如果是,则直接调用函数获取TicketInfo --> 如果不是,会查找传入的用户名判断是否是本域的用户 --> 如果在域内找不到用户名则会给传入的用户名加上$继续查找 --> 仍未找到则查找其 altSecurityIdentities (Alternate Security Identities,备用安全标识)属性的value。这就是导致漏洞产生的一个重要原因,由于我们的改名操作,此时域内已经没有一个Service的sAMAccountNameDC,根据处理逻辑会继续加$进行重试,必然会匹配上域控的机器账户DC$,所以此时就是域控机器向KDC发起申请ST请求。

    通过这个函数还是还无法解释为什么获取DC的TGT之后,通过S4USelf请求DC$的TGS可以成功?按理说我们是拿着DC这个机器账户的TGT去请求ST最终获取的ST也应该是这个账户权限的,最终是如何提权到域管的?

  • kdcGetPacAuthData函数,这个函数在处理PAC存在缺陷,网上其它文章说的“若原票据不存在PAC,则会构造一个新的PAC;若无法构造,则直接复制PAC”,其实并不是TGS_REQ中没有携带PAC然后才去生成PAC,而是正常的S4U请求就会重新生成对应模拟用户的PAC到Ticket中,这里我们是用Administrator用户去请求,所以自然会生成高权限票据。

//这里对比一下S4U的if逻辑与这个else if逻辑,所调用的生成PAC函数               KerbErr = KdcGetPacAuthData(                     S4UUserInfo,                     &S4UGroupMembership,                     TargetServerKey,                     NULL,                 // no credential key                     AddResourceGroups,                     FinalTicket,                     S4UClientName,                     &NewPacAuthData,                     pExtendedError                     );        KerbErr = KdcGetPacAuthData(                        UserInfo,                        &GroupMembership,                        TargetServerKey,                        NULL,               // no credential key                        AddResourceGroups,                        FinalTicket,                        NULL,               // no S4U client                        &NewPacAuthData,                        pExtendedError                        );

利用S4U2Self协议请求的数据包有什么不同?

S4U2Self协议扩展中,Service会使用自己的TGT并添加一个新的padata,就是PA-FOR-USER结构:

域内提权票据篇之剖析CVE-2021-42278&42287漏洞
这个函数原型如下:
PA-FOR-USER ::= SEQUENCE {       -- PA TYPE 129       userName              [0] PrincipalName,       userRealm             [1] Realm,                   cksum                 [2] Checksum,                    auth-package          [3] KerberosString    }
参数可以通过数据包看到其含义:
userName    为administrator,所以我们猜测KdcGetPacAuthData(),取的就是PA-FOR-USER结构中的name;userRealm   为域名USER.COMChecksum    之前文章提到的PAC尾部签名auth-package用于验证用户身份的验证机制的字符串名称,必须将其设置为字符串“Kerberos”。

0x05 防御措施

  1. 安装微软的补丁KB5008602 和 KB5008380
  2. 限制域内可创建机器账户的用户权限,修改MachineAccountQuota的属性值为0;
  3. Tips:在域内创建一个机器账号,将其samAccountName改为DC的主机名,因为域内sAMAccountName是唯一的,不能重复,所以攻击者就无法创建与域控同名的主机名。

0x06 日志检测

1、Name impersonation 可通过分析以下安全日志

【4741事件】  创建机器账号 【4742事件】  删除SPN     【4781事件】  修改sAMAccountName
2、KDC bamboozling 可通过分析以下安全日志
4768事件:请求TGT】 TargetUserName(发起Kerberos身份验证请求的用户名)为域控主机名而不是主机名+$4769事件:请求ST】  TargetUserName不包含$(区分正常的机器账户请求),并且ServiceName(请求服务的名称)为域控主机名并不是主机名+$

0x07 参考文章

https://mp.weixin.qq.com/s/Ar8u_gXh2i3GEcqdhOD8wA

https://exploit.ph/cve-2021-42287-cve-2021-42278-weaponisation.html

https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/1fb9caca-449f-4183-8f7a-1a5fc7e7290a

 

原文始发于微信公众号(七芒星实验室):域内提权票据篇之剖析CVE-2021-42278&42287漏洞

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月25日08:59:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   域内提权票据篇之剖析CVE-2021-42278&42287漏洞https://cn-sec.com/archives/3881383.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息