DirSync: Leveraging Replication Get-Changes and Get-Changes-In-Filtered-Set
DCSync can be categorized as one of the most effective technique on a domain, since retrieving the krbtgt
NT hash allows to forge a valid ticket-granting ticket for any domain user. To manage to do so, a principal must possess the right privileges over the root of a domain: DS-Replication-Get-Changes and DS-Replication-Get-Changes-All. The combination renders it possible to successfully call MS-DRSR’s GetNCChanges to replicate secret attributes.
Recently, I stumbled upon a principal with only DS-Replication-Get-Changes
over a domain, which lead to investigating the impact, along with its sibling DS-Replication-Get-Changes-In-Filtered-Set.
This post is dedicated to presenting the consequences of delegating these privileges, and includes a proof of concept PowerShell module to demonstrate the technique for defensive and adversarial simulation purposes.
A Quick Glance at What Can Be Done
DS-Replication-Get-Changes
allows to read the value of confidential attributes.DS-Replication-Get-Changes-In-Filtered-Set
, coupled withDS-Replication-Get-Changes
, allows to read the value of confidential and Read-Only Domain Controller (RODC) filtered attributes, such as Local Administrator Password Solution’s (LAPS)ms-Mcs-AdmPwd
.
Describing Concepts
Before we move on, some Active Directory concepts must first be described.
The searchFlags Attribute
The Schema Naming Context, located at CN=Schema,CN=Configuration,DC=contoso,DC=com
contains an object for all attributes that can be set on objects found in Active Directory. Just as regular objects, these objects also have attributes set on them, effectively having attributes on an attribute object.
To make sense out of this, let us look at this concrete example.
At CN=ms-Mcs-AdmPwd,CN=Schema,CN=Configuration,DC=contoso,DC=com
resides the object of an attribute that you may be familiar with: ms-Mcs-AdmPwd
. This attribute is used to store the password of the local administrator managed by LAPS. When viewing its object, we can see some attributes that are either set or unset, and more specifically in our case, searchFlags:
searchFlags
(CN=Search-Flags
) includes an important feature: it can dictate whether an attribute is to be shown to a user that requests to see its value. Indeed, when the searchFlags
attribute contains the flag fCONFIDENTIAL
(0x00000080), a user requesting to see the value must have the explicit privileges to read it (RIGHT_DS_READ_PROPERTY and RIGHT_DS_CONTROL_ACCESS).
Going back to our example, since ms-Mcs-AdmPwd
’s searchFlags
has the flag fCONFIDENTIAL
, a user that wants to read the attribute ms-Mcs-AdmPwd
on a computer object must have RIGHT_DS_READ_PROPERTY
and RIGHT_DS_CONTROL_ACCESS
on it; otherwise, the attribute will be returned as empty. By doing so, LAPS ensures that only principals with the right privileges delegated over computer objects can read the attribute.
One last flag supported by searchFlags
worth mentioning is fRODCFilteredAttribute
(0x00000200). Essentially, this states that an attribute cannot be replicated to a RODC. Note that ms-Mcs-AdmPwd
also has this flag, and will come into play later on.
LDAP Extended Controls
LDAP Extended Controls, often simply named LDAP Controls, is a feature that was introduced in LDAPv3. By specifically crafting a request message with an object identifier (OID), a Domain Controller (DC) can be asked to perform a certain set of operations. For instance, the control named LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID (OID 1.2.840.113556.1.4.521) is used to request that a DC moves an object to another domain.
LDAP_SERVER_DIRSYNC_OID (1.2.840.113556.1.4.841)
This control is used to retrieve from a DC any changes made to Active Directory objects since the last time a synchronization was requested, and is the technique that will be used to read the values of attributes with the flags fCONFIDENTIAL
and fRODCFilteredAttribute
.
The documentation also contains a valuable piece of information: pseudocode of the security check performed when a directory synchronization is requested:
The Technique
Experimenting With LDAP_SERVER_DIRSYNC_OID
After going through some documentation, interesting points arise:
- .NET’s System.DirectoryServices.Protocols.DirSyncRequestControl is an easy way to request a directory synchronization, and an example script can be found here.
- The flag
LDAP_DIRSYNC_OBJECT_SECURITY
can be used to request a synchronization without having theDS-Replication-Get-Changes
privileges, but only returns attributes that the requester can read. Since we wish to read attributes that we normally cannot, we will avoid sending this. - An LDAP filter can be specified to only synchronize objects that match it.
- We can also request a specific set of attributes to be synchronized.
Before executing the aforementioned example script, the following has been altered:
- The script handles a cookie to only get the changes made since our last request, but has been removed since this serves us no purpose.
- Filtering is done on
(samaccountname=Administrator)
. - We only print a few attributes from the response for the sake of clarity, despite requesting all of them.
Once the executing user has been delegated DS-Replication-Get-Changes
, the directory synchronization succeeds:
Without these delegated privileges, the SendRequest
function fails, and returns the error message The user has insufficient access rights
, as we also saw from the security check pseudocode.
So far so good, but nothing impressive; these can be retrieved with a standard LDAP query.
Accessing Confidential Attributes With DS-Replication-Get-Changes
In the previous searchFlags
section, we have established that some attributes are confidential, and cannot be read without having explicit privileges over the object. While some attributes are confidential by default, it is possible to alter existing searchFlags
to set some other attributes to confidential, or to create your own confidential attribute.
You can query for confidential attributes like so:
1.2.840.113556.1.4.804
is a bitwise OR operator ensuring that we look for the flagfCONFIDENTIAL
within all the other flags, and not just this value.searchFlags
128
(0x00000080) refers tofCONFIDENTIAL
.- We exclude
512
(0x00000200) indicatingfRODCFilteredAttribute
, requiring more privileges.
As a proof of concept, the attribute unixUserPassword
will be used. It is sometimes used to store a user’s UNIX password hash.
In our example, when querying for this attribute through a regular LDAP query for the user Administrator
, nothing is returned, confirming that we do not have the right privileges over the object:
However, if we modify our script to print this attribute, we see that it was indeed synchronized:
Accessing RODC Filtered Attributes With DS-Replication-Get-Changes-In-Filtered-Set
In order to successfully use these privileges, one must also have DS-Replication-Get-Changes
, otherwise the LDAP control will deny the request, as denoted in the pseudocode.
Similarly to the last section, we can query for attributes with the flag fRODCFilteredAttribute
. We do not need to exclude confidential attributes; we also have the required privileges for these:
Other than ms-Mcs-AdmPwd
, these attributes should all be present in a modern, vanilla installation of Active Directory.
Let us focus on ms-Mcs-AdmPwd
. As briefly mentioned in the impact section, this attribute is used by LAPS to store the current password of the managed local administrator, which will either be the default Administrator
account, or a custom one.
With our new privileges, we attempt to retrieve the attribute of a LAPS-enabled computer object through a LDAP query, but to no avail:
What if we mimic the same procedure as before, and simply print the attribute through our script? The value of the attribute turns out to be empty.
To successfully get the value of a fRODCFilteredAttribute
attribute, we must explicitly specify that we wish to synchronize it when constructing the SearchRequest
. It can be done by replacing the $null
value in the SearchRequest
object of our previous script to an array containing the list of attributes, for instance ('ms-Mcs-AdmPwd')
, that we desire to fetch.
Once edited and executed again, the password is printed:
If attempting to run this modified script without DS-Replication-Get-Changes-In-Filtered-Set
, we will be greeted with the same error message The user has insufficient access rights
as before, due to attempting to synchronize a specific attribute with fRODCFilteredAttribute
. Again, this is on par with what was written in the security check pseudocode.
DirSync: A Proof of Concept Tool
DirSync is a simple proof of concept PowerShell module to demonstrate the impact of delegating these privileges. It offers two functions: Sync-LAPS
to directly focus on LAPS, and Sync-Attributes
to synchronize any desired attributes. Usage can be found in the README.md file, and via the usual Get-Help
PowerShell command.
A Remark for Blue Teamers
Keep in mind that this technique uses an LDAP request, and not RPC like DCSync
. While the tool is designed by default to communicate over plain text for compatibility reasons, it also supports LDAPS. If your DCs have a certificate installed allowing to use LDAPS, do not rely on over-the-wire detection, unless you hold decryption capabilities.
The Wireshark website contains an example capture file of the DirSync
control over plain LDAP, filtering on (ObjectClass=*)
and requesting no specific attributes.
Some Unorganized Notes
- It might very well be possible to achieve the same result with these privileges using
MS-DRSR
’sGetNCChanges
. - I did not manage to read secret attributes such as unicodePwd using
DS-Replication-Get-Changes-All
through theDirSync
LDAP control. - As mentioned in the pseudocode,
DS-Replication-Get-Changes-All
can be used instead ofDS-Replication-Get-Changes-In-Filtered-Set
to read RODC filtered attributes. As you can also perform a full-blownDCSync
with those privileges, I do not expectDirSync
to be the technique of choice, unless it offers some operational security. - There are multiple ways to track changes in a directory.
- I have not investigated the significance of being able to read the built-in confidential and RODC filtered attributes.
To Conclude
While clearly not as impactful as DCSync
, DirSync
presents an alternative to synchronize confidential and RODC filtered attributes. The demonstrated LAPS scenario could be used to introduce a new edge into BloodHound, and is a good candidate to look for during Active Directory privileges assessments.
A thank you to @joewaredotnet for the article Replicating Changes Control Access Right series that contributed to my curiousness.