Monday, 28 February 2022

CVE-2021-42287/CVE-2021-42278 Weaponisation

On the 9th November 2021, Cliff Fisher tweeted about a number of CVE's related to Active Directory that generated a lot of attention. These included CVE-2021-42278, CVE-2021-42291, CVE-2021-42287 and CVE-2021-42282. The one that stood out to me the most was CVE-2021-42287 as it related to PAC confusion and impersonation of domain controllers, also having just worked on PAC forging with Rubeus 2.0.

This post discusses my quest to understand how to exploit this issue and the findings I discovered along the way.

I want to highlight that there is no new research in this post, this issue was discovered by Andrew Bartlett of Catalyst IT. I however, found one way to weaponise it, there may well be other methods in the issues he found. 

 

A Little Digging

Immediately upon seeing Cliff's tweet, Ceri Coburn and I started trying to investigate how this could be exploited. We (perhaps incorrectly) latched onto the text on Microsoft’s description of CVE-2021-42287 which seemed to be based around the idea of TGT's being issued without PACs. This led me to modify Rubeus to allow for requesting TGT's without a PAC.

After Ceri debugged the Windows KDC and we had dug through the leaked XP source we were convinced that to trigger the code path we needed to insert a PAC into a ST when it was requested with a TGT lacking a PAC. This required a cross domain S4U2self but we were unable to get it to work. The only way we could get a DC to add a PAC when a service ticket (ST) was requested using a TGT without a PAC was by configuring altSecurityIdentities.

This process involves modifying the altSecurityIdentities attribute of an account in a foreign domain to Kerberos:[samaccountname]@[domain] to impersonate that user.

In the below screenshot you can see a low privileged user (internal.user) of the local domain (internal.zeroday.lab) has GenericAll over a high privileged user (external.admin) of a different domain (external.zeroday.lab): 

As this user we can add ourselves to the altSecurityIdentities attribute as shown in the following screenshot:

Now we can get a TGT from our local DC and request it without a PAC using the new /nopac switch:


This results in an obviously small TGT. We then use that TGT to request a referral to the target domain (external.zeroday.lab) from our local DC: 


That referral can then be used to request ST's for services on our target domain (external.zeroday.lab). Here I'm requesting a ST for LDAP/EDC1.external.zeroday.lab the DC's LDAP service: 


The size of the ST is very large compared to the previous 2 tickets; this is because (as we'll see) a PAC has been added. As shown in the klist output below, this ST is for the original user internal.user who has no special privileges on either domain: 


Using this ST, however, we can DCSync:


Here the DC has searched for the account in the local database, it hasn't found it, so it has then searched to see if any accounts have this account listed in their AltSecurityIdentities attribute, which external.admin does because we added it earlier, and if so, the DC adds a PAC belonging to that account. This can be verified using the Rubeus' describe command and the AES256 key we just DCsync'd:


 We now effectively have the privileges of the external.admin user on the external.zeroday.lab domain.

This did not help us exploiting the issue we wanted but I did find it interesting. 

 

Some Progress

Clément Notin then posted this tweet which actually mentioned the Samba information regarding these issues and led me to CVE-2020-25719 and this patch. What particularly caught my attention was this paragraph:

“Delegated administrators with the right to create other user or machine accounts can abuse the race between the time of ticket issue and the time of presentation (back to the AD DC) to impersonate a different account, including a highly privileged account.”

I realised to make the local lookup fail, we didn't need to attack a foreign domain but perhaps remove the account after retrieving the TGT.

I started playing with naming a machine account the same as the local DC (minus the $), requesting a TGT (still without a PAC) and removing the machine account and using that TGT. I noticed something unusual.

When using this PAC-less TGT with a U2U request but without supplying an additional ticket, it failed to decrypt the resulting ST: