FalconFriday — Detecting malicious modifications to Active Directory — 0xFF1D

Recently, we are seeing more and more threat actors and red teams move to using relay attacks, often combined with the ability of users to add or modify data in Active Directory.

The first ability that is often misused in these attacks, is that in most environments any user has the rights to add a new computer account and modify the properties of the newly created account. For example:

  • The Certifried (CVE-2022–26923) attack released this month that allowed any user to escalate to domain admin.
  • The SAM the Admin attack (CVE-2021–42278) that allowed escalating from a regular user to domain admin.

Another ability that is abused is modification of properties on computer accounts that allow gaining access to the machine. For example:

  • Kerberos Resource-Based constrained delegation by modifying the ‘msDS-AllowedToActOnBehalfOfOtherIdentity’ property, as abused by tools such as ntlmrelayx and KrbRelayUp.
  • Adding Shadow Credentials by modifying the ‘msDS-KeyCredentialLink’ property as abused by tools such as Whisker.

In this FalconFriday we will focus on how to identify a number of these attacks by using relatively simple detections based on the Windows Event Logs. In most environments, these modifications are rare and made only by a specific group of admin users. Therefore, provided detections can be valuable in identifying known and unknown attacks that rely on these changes being made. Hope this blog gives you a practical head start!


Identifying changes to Active Directory properties

A valuable data source in analyzing changes to properties in Active Directory is Event ID 4662 in the Windows Security log on Domain Controllers, which logs access to Active Directory objects.

This event is not enabled by default, but can be enabled using the System Audit Policies under Directory Service Access, using the option ‘Audit object access’.

Note that this can generate a quite substantial amount of logging when enabled on a Domain Controller. However, in our experience, these logs can be quite valuable, also in detecting other attacks — such as users extracting large numbers of objects from Active Directory using tools like Sharphound. We might come back to this topic in a future blogpost. 🙂

These log entries might look a bit puzzling at first since most fields are logged as GUIDs and numbers instead of human-readable names.

Below is an example of a log entry where the msDS-AllowedToActOnBehalfOfOtherIdentity property of a computer account has been updated.

The ObjectType is represented using a GUID. In this case bf967a86–0de6–11d0-a285–00aa003049e2 ,which is the GUID for the Computer Class as defined in the Active Directory schema.

The properties that were updated are stored in the Properties field. Again, these are stored as GUIDs that can be found on the same Active Directory schema documentation. In the above example case, three GUIDs are referenced:

The %%7685 part at the start of the Properties values refers to “Write Property” as can be observed in this post on the Microsoft Technet forum. This is one way to distinguish read events from write events. Another way is using the AccessMask which is specified as a hex-encoded integer referring to the Active Directory access mask, as documented in the iads.h header file. In this example case, 0x20 refers to ADS_RIGHT_DS_WRITE_PROP. We found the method using the access mask to be the most reliable.

Knowing the meaning of these fields, we can write a simple KQL query that identifies write events to the ms-DS-Allowed-To-Act-On-Behalf-Of-Other-Identity attribute of a computer account in Sentinel:

let timeframe=30d;
SecurityEvent
| where ingestion_time() >= ago(timeframe)
| where EventID == 4662
| where ObjectType =~ "%{bf967a86-0de6-11d0-a285-00aa003049e2}"
// GUID for Computer.
| where Properties contains "{3f78c3e5-f79a-46bd-a0b8-9d18116ddc79}"
// GUID for msDS-AllowedToActOnBehalfOfOtherIdentity.
| extend AccessMaskParsed=toint(AccessMask)
| where binary_and(AccessMaskParsed,0x20) == 0x20
// Check if the access mask contains ADS_RIGHT_DS_WRITE_PROP (0x20).

We have seen in production environments that writes to this property are typically rare. More, they are mainly performed by specific service accounts that can be filtered based on the Account property of the event (which contains the username of the user that performed the update to Active Directory).

We can use the same query with a different GUID f3a64788–5306–11d1-a9c5–0000f80367c1 to look for changes to the servicePrincipalName property, which is one of the properties that is abused by the recent Certifried vulnerability (CVE-2022–26923).

In addition the GUID 5b47d60f-6090–40b2–9f37–2a4de88f3063 can be used to monitor the ‘msDS-KeyCredentialLink’ property, which can be used to add Shadow Credentials to an existing machine.

In our testing we say that these changes are more frequent than changes to msDS-AllowedToActOnBehalfOfOtherIdentity, but are still rare enough to identify anomalies. For example, low-privileged users performing these actions.


Monitoring the creation of computer accounts

Active Directory has a setting that determines whether regular users are allowed to create computer accounts called ms-DS-MachineAccountQuota. The value of this property in a given domain can be obtained using PowerShell:

Get-ADObject ((Get-ADDomain).distinguishedname) -Properties ms-DS-MachineAccountQuota | select ms-DS-MachineAccountQuota

By default, this value is set to 10 and in our experience during Red Teaming engagements it is rarely set lower than that. This means that any user is allowed to create up to 10 computer accounts in the domain.

The creation of a new computer account by an unprivileged user is a step that is used in various privilege escalation attacks, such as relaying attacks.

The creation of a new computer account is logged by default on Domain Controllers, using Event ID 4741 in the Windows Security log. Out-of-the-box Sentinel does not fully parse all the relevant fields from the XML data in the event, requiring us to write some KQL to parse these fields.

A nice way to parse XML into a dictionary (or ‘bag’, as this is officially called in KQL), is to use the parse_xml function combined with mv-apply and summarize:

let timeframe=30d;
SecurityEvent
| where ingestion_time() >= ago(timeframe)
| where EventID == 4741
| mv-apply t=parse_xml(EventData).EventData.Data on (
project kv=pack(tostring(t["@Name"]),tostring(t["#text"]))
| summarize EventDataParsed=make_bag(kv)
)

This will return the key value pairs of the XML in a Kusto bag that can then be easily accessed in the query. The most interesting fields are the SubjectUserName and TargetUserName which contain the user creating the computer account and the name of the computer account respectively.

In most environments the creation of computer accounts is quite common, but typically it is always done by a limited number of known admin accounts and service accounts. These accounts can be filtered using the SubjectUserName property.

Knowledge center

Other articles

FalconHound, attack path management for blue teams

FalconHound, attack path management for blue teams

[dsm_breadcrumbs show_home_icon="off" separator_icon="K||divi||400" admin_label="Supreme Breadcrumbs" _builder_version="4.18.0" _module_preset="default" items_font="||||||||" items_text_color="rgba(255,255,255,0.6)" custom_css_main_element="color:...

Together. Secure. Today.

Stay in the loop and sign up to our newsletter

FalconForce realizes ambitions by working closely with its customers in a methodical manner, improving their security in the digital domain.

Energieweg 3
3542 DZ Utrecht
The Netherlands

FalconForce B.V.
[email protected]
(+31) 85 044 93 34

KVK 76682307
BTW NL860745314B01