A Hitch-hacker's Guide to DACL-Based Detections (Part 3)
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
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
Figure 2 - Detection Using Multiple Event IDs (1)
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.
Figure 4 - Modifying the Attribute
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
Figure 6 - Detecting Using Multiple Event IDs (1)
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
Figure 8 - Modifying the Attribute
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
Figure 10 - Detection With Event IDs 5136, 4662, and 4624 (1)
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.
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
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
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:
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
Figure 16 - Final Query for gPCMachineExtensionName Modification (1)
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.
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
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
Figure 20 - Final gPCFileSysPath Detection (1)
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.
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.
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
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.
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
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
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
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
Figure 29 - Detecting ntSecurityDescriptor Change via Event ID 4898 (1)
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
Figure 31 - Detecting With Event IDs 5136, 4624, and 4662 (1)
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
Figure 33 - Additional Object Change Detections (PKI Objects) (1)
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.
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.
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.
Figure 37 - CA Creation (1)
As stated in the blog from decoder, you can leave all fields blank, with the exception of “Common Name.”
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.
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.
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
Figure 41 - Detection with Event IDs 5136, 4662, and 4624 (1)
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
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
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
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:
msDS-SupportedEncryptionTypes:
https://learn.microsoft.com/en-us/windows/win32/adschema/a-msds-supportedencryptiontypes
msds-RevealOnDemandGroup:
https://eladshamir.com/2023/01/25/RODCs.html
gPCMachineExtensionNames:
https://www.trustedsec.com/blog/weaponizing-group-policy-objects-access/
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://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/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/