Skip to Main Content
January 06, 2022

An 'Attack Path' Mapping Approach to CVEs 2021-42287 and 2021-42278

Written by Andrew Schwartz
Active Directory Security Review Incident Response Incident Response & Forensics Penetration Testing Program Assessment & Compliance Purple Team Adversarial Detection & Countermeasures Security Testing & Analysis Threat Hunting

1.0 Introduction

On Friday, December 10, 2021, Charlie Clark (@exploitph) published a blog post detailing the weaponization of CVEs 2021-42287 and 2021-42278. In the blog post, Charlie extensively covered the background of the vulnerabilities, how the vulnerabilities were weaponized into Rubeus, with help from Ceri Coburn (@_EthicalChaos_), the full 'attack chain,' mitigations, and some detections. While Charlie afforded me the initial privilege and opportunity to help with looking into detections around this attack, I wanted to add more context and formalize the Windows Events noted in Charlie's blog post into Splunk SPL queries for both proactive and reactive defensive operations.

The objective of this post is to provide the detection guidance solely for this specific attack path. Attack paths further outlined in Charlie Clark's second post are not covered. This blog provides Splunk SPL queries for detecting the attacks described in Charlie’s blog, using only Windows Security Log events from a domain controller. Furthermore, this blog post only examines a subset of the Windows Event logging data source. Lastly, not all the possible telemetry data points within this data set have been analyzed.

2.0 Using 'Attack Path Mapping' to Build Detections

One of the things I love to do when building out detections, especially when multiple behaviors are encompassed in the attack lifecycle, is to make a process flow of all the different detection opportunities. This helps me map out and drive my workflow. I created the below diagram based on Charlie Clark's blog post, CVE-2021-42287/CVE-2021-42278 Weaponisation.

Figure 1 - CVE 2021-42287 and 2021-42278 Attack Path 1 Diagram

While each detection strives for high fidelity and may be able stand on its own accord, individually implemented detections may lack overarching context and attribution back to a full attack lifecycle. As such, this may hinder what response action is taken when an alert is generated. I use the process of 'attack path mapping' to drive my 'detection layering' approach. Drawing a map helps me identify different opportunities where detection may be possible at various phases of the attack execution process.

The goal is not to provide a single rule, but instead to provide as much context, coverage, and possible attribution that the likelihood of attack execution is underway or has occurred. It is through this detection's 'defense-in-depth' approach via 'attack path mapping' and ultimately 'detection layering' that an analyst can drive more effective, optimized, and streamlined proactive and reactive defensive operations. It is my belief that detection engineers should have a 'defense-in-depth' mindset and approach through the concept of 'detection layering' to provide more effective context. Context drives response to not only to understand what the alert is saying, but also to 'kick-start' the initial driver for response. In this post, I will attempt to articulate and capture the prerequisites and core elements of this 'attack path map' concept through Splunk SPL queries.

3.0 Detection Queries  

3.0.1  Create the New Computer Account

MITRE ATT&CK: https://attack.mitre.org/techn...

By default, the MS-DS-MachineAccountQuota (MAQ) domain attribute is set to a value of 10. This attribute allows for any user or computer, with active and valid credentials, to create and attach up to 10 computers in an Active Directory domain.

When using Impacket or Kevin Robertson's PowerMad, four Service Principal Names (SPNs) are created. Kevin specifically noted this in the blog post MachineAccountQuota is USEFUL Sometimes: Exploiting One of Active Directory's Oddest Settings. Interestingly, when I created a computer via the GUI tool Active Directory User and Computers (ADUC), no SPNs were created. Most notably, the User Account Control (UAC) value was different and SPNs were not added upon the creation of a new computer account. Using this information, we can build a detection any time a new computer account is created. However, this detection is specific to the use of Impacket or PowerMad in its current form as of writing this blog post. If an attacker uses a different tool, it might not produce the same artifacts.

3.0.1.1 Attack Replay:

Figure 2 - Creation of New Computer Account with Kevin Robertson's PowerMad
Figure 3 - Successful New Computer Created in Computer's OU
Figure 4 - SPNs Associated with New Computer Account in Active Directory Users and Computers (ADUC)

3.0.1.2              Detection:

index=windows (EventCode=4741 MSADChangedAttributes=*(*HOST/*) AND *(*RestrictedKrbHost/*) New_UAC_Value=0x80) OR (EventCode=4673 Privileges=SeMachineAccountPrivilege) 
| eventstats values(Process_Name),values(Privileges),values(EventCode) as EventCode by Logon_ID 
| search EventCode=4741
| rex field=_raw "(Message=(?<Message>[a-zA-z ].*))" 
| eval datetime=strftime(_time, "%m-%d-%Y %H:%M:%S.%Q") 
| stats count values(datetime),values(Process_Name),values(Privileges),values(EventCode),values(MSADChangedAttributes),values(Message),values(Account_Domain),values(Security_ID),values(SAM_Account_Name),values(DNS_Host_Name) by Logon_ID 
| search count >=2 
| rename values(*) as * 
| eval Effecting_Account=mvindex(Security_ID,1) 
| eval New_Computer_Account_Name=mvindex(Security_ID,0) 
| table datetime,Account_Domain,Effecting_Account,Logon_ID,New_Computer_Account_Name,DNS_Host_Name,Message,MSADChangedAttributes,Process_Name,Privileges,EventCode
Figure 5 - Detection 1: Creation of New Computer Account
Figure 6 - MSADChangedAttributes for Computer Added via ADUC

3.0.2  Clear the SPN

MITRE ATT&CK: https://attack.mitre.org/techn...

Machine account SPNs are directly linked to the sAMAccountName of the account. When changing the sAMAccountName, the SPNs are automatically updated to point to the new name. As the names in SPNs lack the '$', to avoid a conflict with the SPNs belonging to the domain controller, and ultimately the name change failing, the SPNs must be cleared.

It should be noted that some tooling may be necessary to create machine accounts without SPNs. Therefore, this step may not be required. Charlie Clark (@exploitph) also explained to me that this maybe further a case in point alternatively with an user account. This may be due to a user account, if it has SPNs they are not linked to the sAMAccountName of the account, rather they point to a machine so the SPNs are not updated. As Charlie Clark told me, "With a user account, if it has any SPNs, they aren't linked to the [sAMAccountName] of the account, they point to a machine, so the SPNs aren't updated anyway."

3.0.2.1                 Attack Replay:

Figure 7 - Clearing SPN's Associated to Computer Account via PowerView
Figure 8 - SPNs Shown to be Removed in ADUC

3.0.2.2                 Detection:

index=windows EventCode=4742 Service_Principal_Names="<value not set>" 
| rex field=_raw "(Message=(?<Message>[a-zA-z ].*))" 
| eval Account_Domain=mvindex(Account_Domain,0)  
| eval Effecting_Account=mvindex(Security_ID,0) 
| eval Computer_Account_That_Was_Changed=mvindex(Security_ID,1) 
| rename values(*) as * 
| table _time, Account_Domain,Effecting_Account,Logon_ID,Computer_Account_That_Was_Changed,Message,MSADChangedAttributes,EventCode
Figure 9 - Detection 2: Clear the SPN

3.0.3  Change sAMAccountName to Domain Controller

MITRE ATT&CK: https://attack.mitre.org/techn...

After clearing the SPNs on our created computer account, we can proceed with exploiting the vulnerability to rename the sAMAccountName to that of a domain controller

3.0.3.1                 Attack Replay:

Figure 10 - Change sAMAccountName to Domain Controller via PowerMad
Figure 11 - Renamed sAMAccountName to Domain Controller as Shown in ADUC

3.0.3.2                 Detection:

index=windows (EventCode=4742) OR (EventCode=4781)
| eventstats values(Security_ID),values(EventCode) as EventCode by Logon_ID 
| search EventCode=4742
| rex field=_raw "(Message=(?<Message>[a-zA-z ].*))" 
| eval datetime=strftime(_time, "%m-%d-%Y %H:%M:%S.%Q") 
| stats count values(datetime),values(Old_Account_Name),values(New_Account_Name),values(EventCode),values(MSADChangedAttributes),values(Message),values(Account_Domain),values(Security_ID),values(SAM_Account_Name) by Logon_ID 
| search count >=2 
| rename values(*) as * 
| eval Effecting_Account=mvindex(Security_ID,1) 
| eval Computer_Account_Impacted=mvindex(Security_ID,0) 
| table datetime,Account_Domain,Effecting_Account,Logon_ID,Computer_Account_Impacted,Message,MSADChangedAttributes,New_Account_Name,Old_Account_Name,EventCode
Figure 12 - Detection 3: Changing the sAMAccountName to Domain Controller

3.0.4  Request TGT

MITRE ATT&CK: None

To help identify anomalous Kerberos activity that occurs in this attack path from regular Kerberos usage, we can detect on a potential name mismatch. Benign traffic may have the same Account Name and User ID. As noted in Charlie Clark's blog post, what is abnormal in this case is the mismatch between these two values.

3.0.4.1                 Attack Replay

Figure 13 - Using Rubeus 'ASKTGT' to Request a TGT

3.0.4.2                Detection:

index=windows EventCode=4768
| eval alt_user_name=lower(mvindex(split(User_ID,"\\"),1)) 
| eval alt_account_name=lower(mvindex(split(Account_Name,"@"),0)) 
| eval match = if(alt_account_name==alt_user_name,"true","false") 
| where match == "false" 
| eval Accounts = mvappend(Account_Name,User_ID) 
| table _time,host,ComputerName,Client_Address,Account_Name,Service_ID,User_ID,Accounts,match,EventCode
Figure 14 - Detection 4: Request TGT

3.0.5  Change sAMAccountName Back to Original Computer Account:

MITRE ATT&CK: https://attack.mitre.org/techn...

After obtaining a TGT, we can change the sAMAccountName back to the original computer account.

3.0.5.1                 Attack Replay:

Figure 15 - Changing sAMAccountName Back to Original Computer Account via PowerMad

3.0.5.2                 Detection:

index=windows (EventCode=4742) OR (EventCode=4781)
| eventstats values(Security_ID),values(EventCode) as EventCode by Logon_ID 
| search EventCode=4742
| rex field=_raw "(Message=(?<Message>[a-zA-z ].*))" 
| eval datetime=strftime(_time, "%m-%d-%Y %H:%M:%S.%Q") 
| stats count values(datetime),values(Old_Account_Name),values(New_Account_Name),values(EventCode),values(MSADChangedAttributes),values(Message),values(Account_Domain),values(Security_ID),values(SAM_Account_Name) by Logon_ID 
| search count >=2 
| rename values(*) as * 
| eval Effecting_Account=mvindex(Security_ID,1) 
| eval Computer_Account_Impacted=mvindex(Security_ID,0) 
| table datetime,Account_Domain,Effecting_Account,Logon_ID,Computer_Account_Impacted,Message,MSADChangedAttributes,New_Account_Name,Old_Account_Name,EventCode
Figure 16 - Detection 5: Change sAMAccountName Back

3.0.6  S4U2Self:

MITRE ATT&CK: None

The last phase in this attack path involves performing S4U2Self. What is abnormal in this case is the mismatch between the Account Name and the Service Name. Specifically, the Account Name does not end with a '$', which is often used to denote the distinction between a computer account versus a regular user account, whereas the Service Name shows the account ending with a '$.' Note that the detection for this phase is different from what Elad Shamir (@elad_shamir) noted in the blog post, Wagging the Dog: Abusing Resource-Based Constrained Delegation to Attack Active Directory.

3.0.6.1                 Attack Replay:

Figure 17 - Using Rubeus to Perform S4U2Self

3.0.6.2                 Detection:

index=windows EventCode=4769 (Service_Name="*$*") Account_Name!="*$*" 
| eval alt_account_name=lower(mvindex(split(Account_Name,"@"),0)) 
| eval alt_service_name=lower(mvindex(split(Service_Name,"$$"),0)) 
| eval match = if(alt_account_name==alt_service_name,"true","false") 
| where match != "false" 
| eval Accounts = mvappend(Account_Name,Service_Name) 
| table _time,host,ComputerName,Client_Address,Account_Name,Service_Name,Accounts,match,EventCode
Figure 18 - Detection 6: S4U2Self

4.0 Final Attack Execution and Putting it All Together:

Figure 19 - Attack Execution With noPac.exe
Figure 20 - Detection Dashboard Before Attack Execution (1)
Figure 21 - Detection Dashboard Before Attack Execution (2)
Figure 22 - Populated Detection Dashboard (1)
Figure 23 - Populated Detection Dashboard (2)
Figure 24 - Populated Detection Dashboard (3)
Figure 25 - Populated Detection Dashboard (4)

5.0 Closing Notes:

I hope this blog can not only help share some of my detection engineering workflow that is 'attack path mapping,' but also insight into 'detection layering.'

Lastly, this blog would not have been possible without help from the following people:

Charlie Clark (@exploitph)

Greg Rivas (@_NOT_GREG)

Certi Coburn (@_EthicalChaos_)

Kevin Robertson (@kevin_robertson)

Elad Shamir (@elad_shamir)

Will Schroeder (@harmj0y)

Jonathan Johnson (@jsecurity101)

Randy Pargman (@rpargman)

Jean-Francois Maes (@Jean_Maes_1994)

Steve Erwin (@stevenerwin)

Ben Mauch (@Ben0xA)

Justin Vaicaro (@H3dTr1p)

Logan Sampson (@clandest1n)

Steve Neme

Amanda Mates (@amandamates)

6.0 References:

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

https://exploit.ph/more-samaccountname-impersonation.html

https://github.com/PowerShellMafia/PowerSploit/tree/master/Recon

https://github.com/Kevin-Robertson/Powermad

https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html

https://www.netspi.com/blog/technical/network-penetration-testing/machineaccountquota-is-useful-sometimes/

https://github.com/cube0x0/noPac

https://posts.specterops.io/introducing-the-funnel-of-fidelity-b1bb59b04036

https://medium.com/@mvelazco/hunting-for-samaccountname-spoofing-cve-2021-42287-and-domain-controller-impersonation-f704513c8a45

https://community.splunk.com/t5/Splunk-Search/Using-rex-command-to-extract-Message-field-in-Windows-Event-Logs/td-p/550597