Welcome to the first episode of the ThreaD (Threat Detection) series. I am opening up the series with the spotlight on DCSync (T1003.006) attack in Active Directory (AD).
Intro
A ton of good resources on DCSync already exist such as [1], [2], and [3]. So, I will just attempt to summarize it.
Simply put, in a DCSync attack, an adversary will impersonate a domain controller and request password hash data from a domain controller in an AD domain. This is possible if an adversary has certain privileges that can request replication from a domain controller via the Directory Replication Service Remote Protocol (MS-DRSR).
And what are those privileges?
- DS-Replication-Get-Changes
- Replicating Directory Changes All
- Replicating Directory Changes In Filtered Set
Understandably, by default, only Domain Admins, Enterprise Admins, Administrators, and Domain Controllers groups have these privileges.
DCSync does not exploit any vulnerability since MS-DRSR is a legitimate protocol for replicating AD objects between domain controllers.
Detection
Network detection
Under the hood, the Directory Replication Service Remote Protocol (MS-DRSR) uses DRSUAPI. DRSUAPI exposes several functions such as DNSBind, DNSUnbind, DSReplicaSync, etc. Among them, DSGetNCChanges is the most important one for our use case.
DSGetNCChanges is responsible for replicating the necessary updates between domain controllers.
So can we detect the presence of DSGetNCChanges in network traffic coming from non domain controller IP addresses?
Fortunately, Zeek already has a parser (DCE_RPC) for DRSUAPI.
{
"_stream": "dce_rpc",
"_process": "zeek",
"ts": 1652285949.992177,
"uid": "C6jUuU2MFOyDBHScV7",
"id.orig_h": "192.168.2.63",
"id.orig_p": 34226,
"id.resp_h": "192.168.2.47",
"id.resp_p": 49157,
"rtt": 0.0017480850219726563,
"named_pipe": "49157",
"endpoint": "drsuapi",
"operation": "DRSGetNCChanges"
}
So, all we need is to create a list, WINDOWS_DC_IPS — containing IP addresses of all the domain controllers — to use in our SIEM query.
EventSource=Zeek endpoint=DRSUAPI operation=DRSGetNCChanges
NOT(id.orig_h IN WINDOWS_DC_IPS)
But, this approach only works if the enterprise has Zeek configured to monitor internal AD traffic. This may not be the case for many organizations.
Endpoint detection
Fortunately for enterprises that do not have access to Zeek logs, Windows can log the use of permissions (such as Replicating Directory Changes All) required for DCSync.
In fact, there already exists a sigma rule for detecting DCSync using Windows Event ID 4662. This requires administrators to have enabled the Audit Directory Service Access subcategory.
EventSource=Windows EventID=4662 AccessMask=0x100
Properties IN ("Replicating Directory Changes All",
"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2",
"1131f6aa-9c07-11d1-f79f-00c04fc2dcd2",
"9923a32a-3607-11d2-b9be-0000f87a36b2",
"89e95b76-444d-4c62-991a-0facbeda640c")
NOT ((SubjectDomainName="Window Manager") OR
((SubjectUserName="NT AUT" OR SubjectUserName="MSOL_")) OR
(SubjectUserName="*$")))
Let’s break down the query.
- Access mask of 0x100 refers to Control Access. This access allows the right to perform an operation (replication in our case) controlled by an extended access right.
- The Properties field contains the GUIDs of the permissions of interest.
SubjectUserName="*$"
filters out domain controllers since DCs are computer accounts.- The rest is for reducing false positives.
The GUID mappings have been traced to be as follows:
- DS-Replication-Get-Changes: {1131f6aa-9c07-11d1-f79f-00c04fc2dcd2}
- DS-Replication-Get-Changes-All: {1131f6ad-9c07-11d1-f79f-00c04fc2dcd2}
- DS-Replication-Get-Changes-In-Filtered-Set: {89e95b76-444d-4c62-991a-0facbeda640c}
- DS-Install-Replica: {9923a32a-3607-11d2-b9be-0000f87a36b2}
DS-Install-Replica permission is required for DCShadow attack. We can ignore it for now.
For testing, one can use Mimikatz which supports DCSync.
mimikatz "lsadump::dcsync /domain:mytestdomain.local /user:krbtgt"
So far, the sigma rule worked nicely for me without giving any false positives. Then, one day it didn’t work.
A day after the patch Tuesday of May, Oliver Lyak released his blog on a privilege escalation vulnerability (CVE-2022-26923) in AD domain services. Basically, that flaw allows a low-privilege authenticated user to acquire a certificate of privileged accounts such as domain controllers from AD Certificate Services, enabling elevation of privilege.
In that blog, Oliver — after exploiting the vulnerability to obtain the NT hash of the affected domain controller account — uses secretsdump.py to perform DCSync.
secretsdump.py [[domain/]username@]<target address> -hashes :[NTHASH]
I am aware that I cannot directly use the earlier sigma rule since I am impersonating the DC’s name in this case. But, Windows did not generate any EID 4662 event 🤔. This smells like a System Access Control List (SACL) issue.
ACL Review
Security Access Control List (SACL) is one of the two types of ACL, the other being Discretionary Access Control List (DACL). Check out this awesome guide on ACLs. Excerpts below are taken from that guide.
Essentially, ACL is a list of Access Control Entry (ACE). Each ACE in an ACL identifies a trustee and specifies the access rights allowed, denied, or audited for that trustee. A DACL identifies the trustees that are allowed or denied access to a securable object.
When a process tries to access a securable object, the system checks the ACEs in the object’s DACL to determine whether to grant access to it.
An ACE in a SACL can generate audit records when an access attempt fails, when it succeeds, or both
Troubleshooting
My lab only has a single domain controller that generates 4662 with DS-Replication-Get-Changes once every hour.

According to MSDN, DS-Replication-Get-Changes-All extended right allows the replication of secret domain data.
Thus, the DS-Replication-Get-Changes-All right seems to be the real deal for DCSync.
Using ADSIEdit, I can see that the Domain Controllers group only has the Replicating Directory Changes All permission.

Further, there is no ACE entry in SACL of the Domain Controllers group. This was the reason why the DC didn’t log my DCSync attempt.

Therefore, I added an ACE entry in SACL of the Domain Controllers group to log DS-Replication-Get-Changes-All invocations.

I repeat my DCSync and the DC is now able to log my DCSync (using that same domain controller’s hash). However, I do not know why Replicating Directory Changes is being logged by the DC account name. I will update the blog once I find the answer.
One thing to note is that 4662 does not have the IP address information. So, to hunt for systems that are simulating to be DCs, we need to correlate 4662 with 4624 using logon id.
Basically, we are looking for DC’s creating logon sessions from their “non-official” IP addresses shortly followed by 4662 with DS-Replication-Get-Changes-All permission access having the same username and logon id.
[EventSource=Windows EventID=4624 TargetUserName IN WINDOWS_DC_ACCOUNTS
=*IpAddress
NOT(IpAddress IN WINDOWS_DC
IpAddress
IN ["127.0.0.1", "::1"])]
as s1 followed by
[norm_id=WinServer EventID=4662 AccessMask="0x100" properties="1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" TargetUserName IN WINDOWS_DC_ACCOUNTS]
as s2 within 10 second on s1.TargetUserName=s2.TargetUserName and s1.TargetLogonId=s2.SubjectLogonId

DC’s are normally configured to have static IP addresses. So, seeing more than one IP address associated with a DC is worth checking over.
EventSource=Windows EventID=4624 TargetUserName IN WINDOWS_DC_ACCOUNTS
IpAddress=* NOT(IpAddress IN ["127.0.0.1", "::1"])
| stats distinct_list(IpAddress) as dc_ips by TargetUserName

192.168.2.47 is the DC’s IP address, while 192.168.2.63 is from where I initiated DCSync.
Conclusion
In real-world AD environments, I expect this method of modifying the SACL of the Domain Controllers group to maybe only slightly increase the log volume due to replications occurring between legitimate DCs.
To conclude, Zeek and sigma rules are awesome. More people should be using them!