Skip to Main Content
April 09, 2024

A Hitch-Hacker's Guide To DACL-Based Detections - The Addendum

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

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

1    Introduction

Last year, Andrew and I posted a four (4) part blog series covering various Active Directory (AD) attributes and how modifications made by an attacker with sufficient privileges can be detected.

After 40+ detections covering over 20 different AD attributes- naturally, you think we’d be done.

Nah.

Most attackers and defenders alike would agree that the attack surface within AD is nearly limitless, and, as such, we identified a few additional attributes that we thought had value, particularly regarding detection and baselining opportunities.

As a reminder, this blog (series) assumes an 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. We will not examine any post exploitation (i.e., forged Kerberos tickets, etc.) as our primary purpose is to build detections identifying when changes are made. 

Lastly, this post will provide classic Splunk SPL queries for detecting the attacks outlined, using only Windows Event IDs as described. Furthermore, this blog post only examines a subset of the Windows Event logging data, and not all possible telemetry within this data set have been analyzed.

2    Logging Setup

As noted in previous DACL posts (see Part 1A), 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:

4624

4662

4742

4781

5136

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

Though the length of this post is shorter than previous DACL posts, it is important to remember a couple of key formatting guidelines from Part 1A 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    Object Modifications & Detections

4.1      Operating-System

4.1.1     Background

The operatingSystem attribute stores the name of the machine operating system in AD.

While the operatingSystem attribute may have limited use in attack application, it can be used for baselining the operating systems within a given AD environment, potentially identifying vulnerable devices. Alternately, this value could be changed in order to create honeypots that may attract attackers seeking to exploit vulnerable hosts on the network.

4.1.2     Modifying the Attributes (Attack)

As done in Parts 1A-3 of the previously released DACL series (Part 1A, 1B, 2, 3), we will use Kevin Robertson’s Powermad to modify the attribute for the previously created IMPOSTER-GRANOLA machine account.

Set-MachineAccountAttribute -Attribute OperatingSystem -Value NetApp
Figure 1 - Modifying the Attribute

4.1.3     Building the Detection

4.1.3.1 Detection With Event IDs 5136, 4624, and 4662
index=main ((EventCode=5136 AND LDAP_Display_Name=operatingSystem)  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_Account=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_Account, AccessMask, Props, Object_Properties 
| where  len(Class)>0 
| stats values by _time, Changed_Account
Figure 2- Detection With Event IDs 5136, 4662, and 4624
Figure 3 - Detection With Event IDs 5136, 4662, and 4624

4.2      Operating-System-Version

4.2.1     Background

The operatingSystemVersion attribute contains the value for the operating system’s version string. Microsoft provides a table that links the string version number to the normalized operating system here.

There is additional benefit in using this attribute, in tandem with the operatingSystem attribute, to compare the explicitly named operating system within the operatingSystem attribute to the string contained within operatingSystemVersion, thus potentially identifying differences that may indicate the value was tampered with, providing a useful IOC for defensive security professionals.

4.2.2     Modifying the Attributes (Attack)

As previously, we will utilize PowerMad to modify the attribute.

Figure 4 - Modifying the Attack
Figure 5 - Object Post Modification

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=operatingSystemVersion)  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_Account=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_Account, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by _time, Changed_Account
Figure 6 - Detection With Event IDs 5136, 4662, and 4624
Figure 7 - Detection With Event IDs 5136, 4662, and 4624

4.2.4     A Note About operatingSystem and operatingSystemVersion

As an interesting note, while we were exploring modifications of the operatingSystem and operatingSystemVersion attributes, we also identified that when creating a machine account via ADUC, it will by default set both attributes for the machine as blank. This is different from when a real machine is created and added to the domain manually.

We tested this by building a new VM and adding it to the domain, and you can see that the operatingSystem and operatingSystemVersion attributes are populated.

Figure 8 - Domain Joined Server

As a direct comparison, we also created machine accounts using two (2) different tools—Impacket’s addcomputer.py and PowerMad. Both machine creations resulted in blank values for both attributes.

Figure 9 - Computer Account Created via Impacket
Figure 10 - Imacket Machine Account With Blank Attributes
Figure 11 - PowerMad Machine Account Creation
Figure 12 - PowerMad Test Machine Account With Blank Attributes

This provides some interesting value as another potential IOC that defenders can use with machine account creation events (and the detections within this post) for identifying machine accounts created by attackers.

4.3      dynamicObject/Entry-TTL

4.3.1     Background

Technically, dynamicObjects are not attributes, but rather are types of objects (user, group, or computer) that are impermanent within AD. This has significant value for an attacker with sufficient permissions to create objects on a domain, given that they can create an object with a set destruction time, making it more difficult for auditors and defenders to track attacker activity if the appropriate telemetry isn’t present.

This is done by way of an attribute set on the dynamic object created called EntryTTL. EntryTTL designates a set “time-to-live” for the object before the Domain will delete it, relocating it to /dev/null.

4.3.2     Modifying the Attributes (Attack)

In order to modify the attribute, we first need to create an ldf file on our machine.

The text of the file should contain the following (you will need to replace the DN and SAM Account Name with the appropriate values for your domain):

dn: CN=cold.cereal,CN=Users,DC=BREAKFASTLAND,DC=LOCAL
changetype: add
objectClass: user
objectClass: dynamicObject
entryTTL: 1800
sAMAccountName: cold.cereal

Next, we have to open up a command prompt and navigate to the path where the file is. The following command should be run:

Ldifde -v -i -f create_dynamic_object.ldf
Figure 13 - ldf File and Creation of User Account/EntryTTL
Figure 14 - Object Creation/Modification

4.3.3     Building the Detection

4.3.3.1 Detection With Event IDs 5136, 4624, and 4662
index=main ((EventCode=5136 AND LDAP_Display_Name=entryTTL)  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_Account=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_Account, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by _time, Changed_Account
Figure 15 - Detection With Event IDs 5136, 4662, and 4624
Figure 16 - Detection With Event IDs 5136, 4662, and 4624

4.4      User-Principal-Name

4.4.1     Background

The userPrincipalName attribute contains the UPN for a user’s login name based upon the standards described in RFC 822. This value typically maps to the user’s email address name.

4.4.2     Modifying the Attributes (Attack)

As previously, we will utilize PowerMad to modify the attribute.

Figure 17 - Modifying the Attribute
Figure 18 - Object Post Change

4.4.3     Building the Detection

4.4.3.1 Detection With Event IDs 5136, 4624, and 4662
index=main ((EventCode=5136 AND LDAP_Display_Name=userPrincipalName)  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_Account=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_Account, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by Changed_Account
Figure 19 - Detection With Event IDs 5136, 4662, and 4624
Figure 20 - Detection With Event IDs 5136, 4662, and 4624
4.4.3.2 Detection With 4742 and 4624
index=main ((EventCode=4742 AND User_Principal_Name!="-") 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))  
| eval Account_Info=if(EventCode==4742,mvindex(Message,-1), mvindex(Message,-1))
| rex field=Account_Info "(?<Changed_Account>(?ms)..........................................................................Account\s+Name.*?(Account\s+Name:\s+)(\w+..........))"
| join type=outer Logon_ID    
        [ search (EventCode=4742) 
        | stats count by Logon_ID, Account_Name
        | table Account_Name,Logon_ID ]  
| join type=outer Logon_ID    
        [ search (EventCode=4624)
        | stats count by Logon_ID, Account_Name, Source_Network_Address  
        | table Account_Name,Logon_ID, Source_Network_Address,]
| table _time, Mod_Account, Source_Network_Address, Logon_ID, User_Principal_Name
| stats values by  _time, Mod_Account,Source_Network_Address, Logon_ID, User_Principal_Name 
| table _time, Mod_Account, Source_Network_Address, Logon_ID, User_Principal_Name
Figure 21 - Detection With 4742

4.5      User-Certificate

The userCertificate attribute contains a DER-encoded X509v3 certificate that is assigned to a given user.

4.5.1     Modifying the Attribute

To modify this attribute, we utilized ADUC to manually add a hexadecimal string into the attribute editor. In this case, we are not submitting a DER-encoded string, but only a test value in order to trigger the change to the attribute we are targeting.

Figure 22 - Manually Modifying the Attribute
Figure 23 - Object Post Change

4.5.2     Building the Detection

4.5.2.1 Detection With Event IDs 5136, 4624, and 4662
index=main ((EventCode=5136 AND LDAP_Display_Name=userCertificate)  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_Account=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_Account, AccessMask, Props, Object_Properties
| where  len(Class)>0
| stats values by _time, Changed_Account
Figure 24 - Detection With Event IDs 5136, 4662, and 4624
Figure 25 - Detection With Event IDs 5136, 4662, and 4624

5    Conclusion

Once again, our hope is that from this series of blog posts, professionals and organizations are given detections and detection templates that can provide assistance in auditing baselines, and providing deeper coverage of AD attributes that may assist defenders in more quickly identifying abnormal or malicious activity.

Another key point to remember when trying to implement the detections provided in this post 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 best practice and preference may be 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.

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:

Jonathan Johnson (@jsecurity101)

Jim Sykora (@jimsycurity)

References

Operating System Attribute:

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

https://www.crowdstrike.com/blog/seven-common-microsoft-ad-misconfigurations-that-adversaries-abuse/

https://twitter.com/JosephRyanRies/status/1671655129290797056?s=20

https://twitter.com/JosephRyanRies/status/1648724225773953024?s=20

https://support.microsoft.com/en-us/topic/kb5021130-how-to-manage-the-netlogon-protocol-changes-related-to-cve-2022-38023-46ea3067-3989-4d40-963c-680fd9e8ee25

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-38023

https://www.cve.org/CVERecord?id=CVE-2022-38023

Operating System Version Attribute:

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

https://learn.microsoft.com/en-us/windows/win32/sysinfo/operating-system-version

https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/linux-accounts-cannot-get-aes-tickets?utm_source=pocket_saves

 https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/2e5dcf34-4b51-44a0-b45a-277ed616ca39

https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/1163bb03-7035-433e-b5a4-802247262d18#Appendix_A_62\

http://dloder.blogspot.com/2009/04/who-told-ad-what-os-im-running.html

Dynamic Object/EntryTTL Attribute:

https://learn.microsoft.com/en-us/archive/blogs/pfesweplat/ad-object-detection-detecting-the-undetectable-dynamicobject

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

4.14. Creating a Dynamic Object - Active Directory Cookbook [Book] (oreilly.com)

userPrincipalName Attribute:

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

UserCertificate:

https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adls/d66d1662-0b4f-44ab-a4c8-e788f3ae39cf

https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adls/d66d1662-0b4f-44ab-a4c8-e788f3ae39cf