Skip to Main Content
May 20, 2022

Splunk SPL Queries for Detecting gMSA Attacks

Written by Andrew Schwartz

1    Introduction

What is a group Managed Service Account (gMSA)? If your job is to break into networks, a gMSA can be a prime target for a path to escalate privileges, perform credential access, move laterally or even persist in a domain via a 'golden' opportunity. If you’re an enterprise defender, it’s something you need to lock down and keep a close eye on to prevent abuse. That’s why I decided to build Splunk SPL queries to detect attacks against gMSAs. I first became aware of the gMSA account when BloodHound 3.0 was released. I just never had a chance to simulate the attacks, examine the artifacts, and build detections until now.

The two main attacks I examined are reading the gMSA Password and the Golden gMSA Attack. Fantastic detection guidance on configuring the SACLs on the necessary Active Directory accounts/attributes to leverage Windows Event ID 4662 was done by both Stealthbits and Semperis. Additionally, Rob Fuller (@mubix) tweeted about a more obscure Windows Event that can also be used in detecting this ominous sounding event: 'A caller successfully fetched the password of a group managed service account.' Lastly, this blog post would not be possible without the initial detection research provided by Stealthbits, Semperis, and Rob Fuller.

The main objective of this blog post is to provide Splunk SPL queries for detecting the attacks described, 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. Additionally, not all the possible telemetry data points within this data set have been analyzed.

A few things to be mindful of:

  • The difference between standalone Managed Service Accounts (sMSAs) and gMSAs
  • The difference between msDS-ManagedPassword and msDS-ManagedPasswordId
  • Understanding what the KDS Root Key is, how it is used, and why it is valuable to an attacker

1.1   sMSA vs. gMSA

The TL;DR difference of a 'standalone Managed Service Account (sMSA)' and a gMSA can be summarized by the official Microsoft documentation:

An sMSA is a "managed domain account that provides automatic password management, simplified service principal name (SPN) management and the ability to delegate the management to other administrators."

The gMSA "provides the same functionality within the domain but also extends that functionality over multiple servers."

1.2   msDS-ManagedPassword vs. msDS-ManagedPasswordId

While looking into all the elements that went into this blog post, one of the things that repeatedly confused me was the difference between the msDS-ManagedPassword and msDS-ManagedPasswordId Active Directory attributes. These are two completely different attributes and will be evident respective to the technique that is used.

1.3      Lab Setup

This is something I have not touched upon in my previous blog posts, but for this post I thought it would be helpful for others who may want to simulate these attacks or investigate the detection artifacts further. For this blog post, I used Marvel-Lab by Jonathan Johnson (@jsecurity101) and Ben Shell (@UsernameIsBen) to help me create an Active Directory environment and setup/configure Splunk (SIEM). I then created both a gMSA and a KDS Root Key, which I had never done before. After some Googling and checking the Microsoft documentation, I was able to create both. Just an FYI—I am doing this in a lab and was on my Domain Controller.

Figure 1 - gMSA Service Account Properties
Figure 2 - Creating and Testing KDS Root Key

1.4      Reading msDS-ManagedPassword

MITRE ATT&CK: TA0006 (This is possibly the closest category but not officially or directly mapped. As of writing this blog post, a MITRE ATT&CK sub-technique is also not mapped.)

1.4.1   Domain Controller Events and Detection

As both the Stealthbits and Semperis posts note, a SACL to audit the account that has access to read the gMSA password should be configured. This will allow necessary Windows Event ID 4662 to be generated. Configuring the SACL is an additional step that must be taken even if Windows Event ID 4662 is currently being ingested.

Figure 3 - Lack of Event ID 4662 in Windows Security Logs
Figure 4 - SACL Auditing Setup (1)
Figure 5 - SACL Auditing Setup (2)

This will then generate the 4662 with the data that we need to build an SPL query.

Figure 6 - Successful Auditing of Windows Security Event ID 4662

Additionally, when the gMSA msDS-ManagedPassword is successfully read, a Windows Event ID 2946 will also be generated. It should be noted that a failure Windows Event ID 2947 will be generated if the attempt was unsuccessful.

Figure 7 - Windows Domain Service Event ID 2946

Interestingly, I did not have this Windows Event ID in my Splunk inputs.conf file. After speaking with Anton Ovrutsky (@Antonlovesdnb), he advised me to add the following stanza to my Splunk inputs.conf file:

[WinEventLog://Directory Service]
index = ad_test
disabled = 0
evt_resolve_ad_obj = 1
checkpointInterval = 5
renderXml=true 
Figure 8 - Successful Splunk Ingestion of Windows Event ID 2946

However, due to the Event message not being parsed in Splunk, we must create custom fields to properly extract the necessary data to build detections from.

Figure 9 - Creating New Fields in Splunk
Figure 10 - Successful Creation of New Fields

We can now create a detection for reading of msDS-ManagedPassword, which can be attempted remotely or via a .NET binary. Using the OLAF ‘WARM HUGS’ QUERY by Olaf Hartong (@olafhartong), we can build a detection to correlate on both Windows Events 4662 and 2946:

| multisearch 
  [search index=windows EventCode=4662 sourcetype=Security TaskCategory="Directory Service Access" Object_Server=DS Object_Type="msDS-GroupManagedServiceAccount" Accesses="Read Property" Access_Mask=0x10 (sAMAccountName AND msDS-GroupManagedServiceAccount AND msDS-GroupMSAMembership)]
  [search index="ad_test" EventCode=2946]
| transaction maxspan=5s maxpause=5s
| eval EventCode_4662_Message=mvindex(Message,0)
| eval EventCode_2946_DataXml=mvindex(EventData_Xml,0)
| table _time,Account_Name,Account_Domain,Logon_ID,Object_Type,Object_Name,EventCode_4662_Message,EventCode_2946_DataXml,EventCode,eventcodes
| eval eventcodes=mvcount(EventCode)
| where eventcodes >1

I used the OLAF ‘WARM HUGS’ QUERY as I had difficulty finding a correlating field in Splunk for both Windows Events. However, because Windows Event ID 4662 has a Logon ID parsed in Splunk, we can use this field to search for any correlating Windows Event ID 4624 that will provide us context with a remote logon to our Domain Controller. To help build this query, I turned back to Greg Rivas' (@_NOT_GREG) I WANT TO GO FAST query.

index=windows (EventCode=4624 Logon_Type=3 sourcetype=Security) OR (EventCode=4662 sourcetype=Security TaskCategory="Directory Service Access" Object_Server=DS Object_Type="msDS-GroupManagedServiceAccount" Accesses="Read Property" Access_Mask=0x10 (sAMAccountName AND msDS-GroupManagedServiceAccount AND msDS-GroupMSAMembership))
| eval logon_id=if(EventCode=4624,mvindex(Logon_ID,1),mvindex(Logon_ID,0))
| eventstats values(Account_Name),values(Account_Domain),values(Object_Name),values(Object_Type),values(Operation_Type),values(Properties),values(EventCode) as EventCode by logon_id
| search EventCode=4662
| rename values(*) as *
| eval account_name=mvindex(Account_Name,1)
| eval account_domain=mvindex(Account_Domain,1)
| search EventCode=4624
| eval datetime=strftime(_time, "%m-%d-%Y %H:%M:%S.%Q")
| stats count values(datetime),values(account_name),values(account_domain),values(logon_id),values(Object_Name),values(Object_Type),values(Operation_Type),values(Properties),values(EventCode)
| search count >=2 
| table values(datetime),values(account_name),values(account_domain),values(logon_id),values(Object_Name),values(Object_Type),values(Operation_Type),values(Properties),values(EventCode)

Special shout-out to Ben Mauch (@Ben0xA) as I forgot to thank him fully in my previous blog post for helping with the datetime and stats count[ing] logic.

1.4.1.1 GMSAPasswordReader.exe Attack Execution

Figure 11 – Attack Execution With GMSAPasswordReader.exe

1.4.1.2 GMSAPasswordReader.exe Detection

Figure 12 - GMSAPasswordReader.exe Splunk Detection (Olaf Query)

1.4.1.3 gMSADumper.py Reader Attack Execution/Detection

Figure 13 - gMSADumper.py Attack Execution and Splunk Detection (Olaf Query)
Figure 14 - Reading msDS-ManagedPassword Splunk Detection (Greg Query)

An interesting difference between the two attack tools was found in the Windows Event ID 4662 log output. When I executed gMSADumper.py from my attacker system, in the Splunk Properties field is the msDS-ManagedPassword attribute. However, the attribute was not present in the 4662 Event Properties field when I utilized GMSAPasswordReader.exe from a domain-joined system. Therefore, correlating on Windows Event ID 2946 proved to be useful to build an SPL query to provide additional context.

Figure 15 - gMSADumper.py Event 4662 Properties
Figure 16 - GMSAPasswordReader.exe Event 4662 Properties

1.5      GoldenGMSA

1.5.1   Domain Controller Events and Detection

1.5.1.1 Reading msDS-ManagedPasswordId

MITRE ATT&CK: TA0006 (This is possibly the closest category but not officially or directly mapped. As of writing this blog post, a MITRE ATT&CK sub-technique is also not mapped.)

The main difference for this detection will be hunting for the msDS-ManagedPasswordId attribute. Almost everything else is identical to the query used with the msDS-ManagedPassword attribute (as shown above). Interestingly, this time a Windows Event ID 2946 was not generated on the Domain Controller, as was in the other use case. Lastly, as Semperis noted in their post, a SACL must be created to generate the 4662 Event ID.

Because I had the SACL previously created and set to Read All Properties, I did not have to recreate or update/modify the SACL previously in place. Because Windows Event ID 4662 has a Logon ID field that is parsed in Splunk, we can use this field to search for any correlating Windows Event ID 4624s that will provide us context with a remote logon to our Domain Controller. To help build this query, I turned back to Greg Rivas' (@_NOT_GREG) I WANT TO GO FAST query.

index=windows (EventCode=4624 Logon_Type=3 sourcetype=Security) OR (EventCode=4662 sourcetype=Security TaskCategory="Directory Service Access" Object_Server=DS Object_Type="msDS-GroupManagedServiceAccount" Accesses="Read Property" Access_Mask=0x10 ("sAMAccountName" AND "msDS-ManagedPasswordId" AND "msDS-GroupManagedServiceAccount"))
| rex field=_raw ".*(?<ManagedPasswordProp>msDS-ManagedPasswordId).*" 
| eval logon_id=if(EventCode=4624,mvindex(Logon_ID,1),mvindex(Logon_ID,0))
| eventstats values(Account_Name),values(Account_Domain),values(Object_Name),values(Object_Type),values(ManagedPasswordProp),values(Operation_Type),values(Properties),values(EventCode) as EventCode by logon_id
| search EventCode=4662
| rename values(*) as *
| eval account_name=mvindex(Account_Name,1)
| eval account_domain=mvindex(Account_Domain,1)
| search EventCode=4624
| eval datetime=strftime(_time, "%m-%d-%Y %H:%M:%S.%Q")
| stats count values(datetime),values(account_name),values(account_domain),values(logon_id),values(Object_Name),values(Object_Type),values(ManagedPasswordProp),values(Operation_Type),values(Properties),values(EventCode)
| search count >=2 
| table values(datetime),values(account_name),values(account_domain),values(logon_id),values(Object_Name),values(Object_Type),values(Operation_Type),values(Properties),values(ManagedPasswordProp),values(EventCode)
Figure 17 - GMSAInfo (msDS-ManagedPasswordId) Attack Execution
Figure 18 - GMSAInfo (msDS-ManagedPasswordId) Splunk Detection

1.5.1.2 Reading msKDS-ProvRootKey

MITRE ATT&CK: TA0006 (This is possibly the closest category but not officially or directly mapped. As of writing this blog post, a MITRE ATT&CK sub-technique is also not mapped.)

Just like being able to log the msDS-ManagedPassword or the msDS-ManagedPasswordId attribute activity, we must create a SACL on the msKDS-ProvRootKey, which Semperis also brilliantly noted in their post. Because Windows Event ID 4662 has a Logon ID field that is parsed in Splunk, we can use this field to search for any correlating Windows Event ID 4624s that will provide us context with a remote logon to our Domain Controller. To help build this query, I turned back to Greg Rivas's (@_NOT_GREG) I WANT TO GO FAST query.

Figure 19 - SACL Auditing Setup (3)
index=windows (EventCode=4624 Logon_Type=3 sourcetype=Security) OR (EventCode=4662 sourcetype=Security TaskCategory="Directory Service Access" Object_Server=DS Object_Type="msKds-ProvRootKey" Accesses="Read Property" Access_Mask=0x10) 
| eval logon_id=if(EventCode=4624,mvindex(Logon_ID,1),mvindex(Logon_ID,0)) 
| eventstats values(Account_Name),values(Account_Domain),values(Object_Name),values(Object_Type),values(Operation_Type),values(Properties),values(EventCode) as EventCode by logon_id 
| search EventCode=4662 
| rename values(*) as * 
| eval account_name=mvindex(Account_Name,1) 
| eval account_domain=mvindex(Account_Domain,1) 
| eval datetime=strftime(_time, "%m-%d-%Y %H:%M:%S.%Q") 
| stats count values(datetime),values(account_name),values(account_domain),values(logon_id),values(Object_Name),values(Object_Type),values(Operation_Type),values(Properties),values(EventCode) by datetime 
| search count >=3 
| table values(datetime),values(account_name),values(account_domain),values(logon_id),values(Object_Name),values(Object_Type),values(Operation_Type),values(Properties),values(EventCode)
Figure 20 - KDSInfo (msKDS-ProvRootKey) Attack Execution
Figure 21 - KDSInfo (msKDS-ProvRootKey) Splunk Detection

It was discovered that two Event ID 4662s were logged, each containing different Properties attributes.

Figure 22 - KDSInfo (msKDS-ProvRootKey) Splunk Detection (Event 1)
Figure 23 - KDSInfo (msKDS-ProvRootKey) Splunk Detection (Event 2)

1.6      Closing Thoughts

While this post covers the attack simulation and builds the SPL detection queries, this post did not go in depth into the background as much regarding gMSA, KDS Root Key, and their respective attributes. That contextual information was extensively articulated by Stealthbits and Semperis, as noted above. Sean Metcalf (@Pyrotek3) also has a fantastic post on the subject of gMSA accounts. They set an extremely high bar, and I did not want to regurgitate or incorrectly replicate their work.

Regarding 'other' events, one could further supplement the above SPL detections with Windows Event ID 5156. Using Jonathan Johnson's (@jsecurity101) IPC research, this event would be logged on the Domain Controller, with the following attributes of an Inbound direction, LSASS.exe for Application Name, and Port 389 (LDAP) for the Destination Port. On the workstation, if Microsoft-Windows-LDAP-Client/Debug events are collected, these logs would complement the Windows Event ID 5156 activity, by showing the respective LDAP query used. Though, LDAP obfuscation maybe possible. Lastly, monitoring and alerting on the creation of new sMSA or gMSA accounts being added to the domain outside of standard operating Identity and Access Management (IAM) procedures.

It is important to note that the detections in this post were built in a 'lab' environment. A real-world production environment will certainly require some tuning to remove false positives. I try to stay away from excluding Machine Accounts (‘usually’ denoted by ending in $) as the source Account Name, as these accounts can also be the source of attack. Therefore, creating lookups excluding domain controllers as the source may be more feasible. Additionally, I did not employ granular SACL auditing for the attributes described in this post. This is something that can be considered, with caution, if licensing levels need to be taken into account.

I also did not use Windows Event ID 4688 or Sysmon Event ID 1. Please do not get me wrong, the telemetry for these events is extremely valuable. However, these additional events may be better served to expand upon the detection capabilities at various layers of the attack chain (e.g., a 'Layer 1' detection or used in 'reactive' defensive operations, such as Incident Response or 'reactive hunting').

To reiterate, the purpose of this post was to build the SPL queries for the attacks noted and by no means show any weakness of employing gMSA accounts. gMSA accounts are still an effective mechanism when coupled with other deterrence controls. Nonetheless, gMSA accounts are a prime target for attackers given the immense high value they return if compromised and targeting the KDS Root Key may afford a greater level of OPSEC as an alternative to targeting the KRBTGT account. It is important to take note of the attributes and who has the respective access/rights and to have the necessary detections as a means of defense-in-depth.

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

Andy Robbins (@_wald0)

Anton Ovrutsky (@Antonlovesdnb)

Ben Mauch (@Ben0xA)

Ben Shell (@UsernameIsBen)

Charlie Bromberg (@_nwodtuhs)

Charlie Clark (@exploitph)

Elad Shamir (@elad_shamir)

Greg Rivas (@_NOT_GREG)

Guy Teverovsky (@GuyTeverovsky)

Jonathan Johnson (@jsecurity101)

Julie Daymut

Justin Vaicaro (@H3dTr1p)

Kevin Joyce

Martin Sohn

Micah Van Deusen (@micahvandeusen)

Mike Spitzer (@__spitzer__)

Nathan Noll

Oddvar Moe (@Oddvarmoe)

Olaf Hartong (@olafhartong)

Randy Pargman (@rpargman)

Rohan Vazarkar (@CptJesus)

Sean Metcalf (@Pyrotek3)

Semperis

Stealthbits

Yuval Gordon (@YuG0rd)

1.7      References

https://sabrinaksy.com/2021/02/04/azure-atp-how-to-setup-a-gmsa-account/

https://posts.specterops.io/introducing-bloodhound-3-0-c00e77ff0aa6

https://syfuhs.net/how-managed-service-accounts-in-active-directory-work

https://improsec.com/tech-blog/sid-filter-as-security-boundary-between-domains-part-5-golden-gmsa-trust-attack-from-child-to-parent

https://adsecurity.org/?p=4367

https://www.hub.trimarcsecurity.com/post/webcast-securing-active-directory-resolving-common-issues

https://docs.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview

https://stealthbits.com/blog/securing-gmsa-passwords/

https://www.dsinternals.com/en/retrieving-cleartext-gmsa-passwords-from-active-directory/

https://www.thehacker.recipes/ad/movement/access-controls/readgmsapassword

https://github.com/rvazarkar/GMSAPasswordReader

https://github.com/micahvandeusen/gMSADumper

https://docs.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/create-the-key-distribution-services-kds-root-key

https://timwappat.info/post/2018/09/15/Remove-or-delete-KDSRootKey-(KDS-Root-Key)

https://ipc-research.readthedocs.io/en/latest/subpages/RPC.html

https://attack.mitre.org/tactics/TA0006/

https://github.com/jsecurity101/Marvel-Lab

https://docs.microsoft.com/en-us/powershell/module/kds/?view=windowsserver2022-ps

https://youtu.be/vga7A2tYejE?t=1903