Using conflicting objects in Active Directory to gain privileges
Summary
Active Directory (AD) uses a multi-master replication model to have a decentralized infrastructure. As long as one domain controller (DC) is up and reachable, services such as authentication are available. The replication process is a complex topic having multiple concepts such as: site, connection object or site topology. Consider reviewing Microsoft documentation: Active Directory Replication Concepts if those notions are new to you.
Over the past few years, I’ve sometimes seen conflicting objects known as “CNF” objects. But I’ve never had a chance to play with them, let’s change that😏!
This replication process generally works well and can automatically manage some conflict cases. But when the conflict is hard to resolve, especially when two objects are created with the same name, in the same container and within a short period of time, Active Directory will rename one of these objects by adding the suffix \0ACNF:<objectGuid attribute value>
to keep all distinguishedName (DN) unique. In this kind of situation, those conflicts should be manually resolved.
Can we create such objects on demand? Yes of course😉! But what is the process? How does it work? There are a few articles on the subject (but not a lot) that I will refer to in the first part to have a good understanding of the topic. And then, we will see how we can obtain admin access to machines that newly join the domain.
This was discovered by Tenable Research while working on identity security.
TL;DR
For those who can’t read the whole post:
- An attacker authenticated as a standard domain user can listen and be notified of any new legitimate machine joining the domain, and then create a conflicting object. As long as the ms-DS-MachineAccountQuota attribute value is greater than zero (default case), they can create the same machine account on another domain controller before the replication occurs.
- By duplicating this conflicting machine account, the normal machine cannot contact the domain and thus cannot be used to log in with a domain user. Only a local user can be used.
- If the administrator wants to fix it without removing the machine from the domain, they will use a local administrator account to repair the secure channel.
- If the attacker is lucky (50–50 chance), the fake machine account will be selected by the Active Directory as the authentic account.
- Once the secure channel between the machine and the domain is fixed, the attacker who created the fake machine account has privileges on this machine account through the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. It can be used by the attacker to abuse Resource-Based Constrained Delegation (RBCD) and gain administrator privileges on the authentic newly joined machine.
A PowerShell script is publicly available on GitHub to detect a new machine (1) and create a duplicate account (2).
Definitions
We will focus our research on security principals, which are AD objects from the following classes: user, computer and group.
Security principals can be uniquely identified with different attributes:
- distinguishedName: ”The distinguished name is a string that includes the location of the object and is formed by concatenating the relative distinguished name of the object and each of its ancestors all the way to root.” More details here.
- objectGUID: “This attribute specifies the unique identifier for an object.”
Although it cannot be read directly on the object as an attribute, Microsoft defines the RDN as:
The relative distinguished name is the name defined by an object’s naming attribute […]. Most object classes use the cn (Common-Name) attribute as the naming attribute. An object’s relative distinguished name must be unique in the container where the object resides. There can be many object instances with the same relative distinguished name, but no two can be in same container.
You may be surprised not to see other attributes🤔. Microsoft indicates that:
[…] other attributes can be used for identification by applications such as userPrincipalName, sAMAccountName, and objectSid attributes. These attributes are very important “names” for Windows 2000, but these are not part of the object identity from the directory’s perspective.
But if we refer to another Microsoft page about the user naming attributes, it indicates that the value of the 3 aforementioned attributes is unique in a domain🤨. This is not what we’ve read just before!
Let’s define them too:
- objectSID: “This attribute specifies a binary value that specifies the security identifier (SID) of a security principal object.”
- sAMAccountName: “This attribute specifies the logon name used to support clients and servers running LAN manager and older versions of the operating system […]. This attribute has to be less than 20 characters to support older clients and must be unique among all security principal objects within the domain.”
- userPrincipalName: “This attribute specifies the user principal name (UPN) that is an Internet-style logon name for a user […]. The UPN is shorter than the distinguished name and easier to remember. By convention, the UPN maps to the user email name.”
We can use the following table to summarize what we’ve just read:
Creating conflicting objects
First, let me clarify that this kind of object should be rare in a normal production AD. When adding a new object that conflicts with an existing object, the system should block its creation.
There are at least 3 different methods to create conflicting objects nonetheless:
- Create the same object on multiple domain controllers at the same time. Caution: you need to be fast to add them before the replication occurs (by default, the intra-site replication interval is 15 seconds but the real interval to create conflicting objects is shorter, only 1 or 2 seconds).
- (💡it could work) Isolate domain controllers from the network before creating the objects. Once created, reconnect the network.
- (💡it could work) Using the tombstone reanimation.
We only used the first method.
Basis of CNF objects
Based on the definitions and especially the RDN, we understand that from the Active Directory perspective, the location of the objects matters and the conflict will not be processed the same way if 2 objects added at the same time are located in the same container or not.
➡️As noted in the definitions, we will focus our research on the security principals, which are LDAP objects. SYSVOL objects and other LDAP objects are out of scope.
Case 1: within the same container
Because the two objects will be located in the same container, there will be a conflict on the DN. The AD identifies and immediately resolves this conflict. One of the objects is renamed: its new RDN will be <Old RDN>\0ACNF:<objectGUID>
.
➡️This action is called “mangling the RDN” in certain documents.
For example, let’s assume that the user “CN=John Doe” is created in the organizational unit “OU=Office” in the “Example.com” domain on two distinct domain controllers around the same time. After replication, the two objects will look like:
The Microsoft Active Directory: Duplicate Object Name Resolution page describes this process well:
The sAMAccountName values are still the same, but one of the objects is renamed. The special character “\0A” is a line feed character (the backslash escape character followed by the 2 character hexadecimal representation of the line feed character). Of course, the two objects have different values for objectGUID and objectSID.
[…]
When a domain controller recognizes the conflict, it decides which object to rename based on the following:1. If the version numbers of the two objects are different, the object with the lowest version number is renamed.
2. If the object version numbers are the same, the object with the oldest timestamp is renamed.
3. If the timestamps are the same, the object with the lower GUID is renamed.
The important points are:
- One of the two objects is renamed.
- But the sAMAccountName attribute is not modified and values are the same.
So it is possible to find two objects with the same sAMAccountName value in the same AD domain 💥. This was unexpected and very interesting🫠!
Case 2: in distinct containers
Let’s continue our example with John Doe. Considering the second object was added into a different organizational unit or container, what’s going to happen? They both give the same results: it will be fine from DN/RDN perspective, and for the sAMAccountName they will be the same (similar to case 1). The mechanism that identified the RDN issue and acted immediately does not check the sAMAccountName.
That means those two objects will exist in the AD, without the “CNF” tag and they will have the same sAMAccountName value😯! So not all conflicting objects can be identified with “CNF” in the DN.
For instance, the following situation can exist:
When same sAMAccountName values are renamed?
You may wonder: what happens next to the sAMAccountName duplicated values? Are they here to stay forever? In a Microsoft blog it is mentioned:
Security Accounts Manager (SAM) service checks periodically for duplicate sAMAccountName values. […] When the process detects duplicate sAMAccountName values, one of the values is changed to the string “$DUPLICATE-xxx”, where xxx is the RID of the object in hexadecimal.
But it never did it periodically in our lab🤔. The only actions that triggered this renaming were those mentioned in the great blog post (sAMAccountName is always unique in a Windows domain… or is it? — by Joe Richards):
- When trying to authenticate as the user using the sAMAccountName (using the UPN will have no effect) and the correct password, one account will keep the original value and the others will be renamed.
- If we use a wrong password, then all objects having a sAMAccountName equals to the one we used will be impacted by the renaming.
- The same behavior can be observed if, later (after the replication propagates it), we try to create again an account that has been duplicated before.
The important points are:
- sAMAccountName renaming is done following a manual action and may occur automatically after a long period of time. Once again, not seen in our test environment but mentioned in a Microsoft blog.
- We might think that as soon as the system identifies conflicting objects, for instance when the DN is renamed with “CNF”, the sAMAccountName would also be updated. But that is not the case.
- Not all conflicting objects can be identified with “CNF” in the DN if we include objects having a duplicated sAMAccountName or the value
$DUPLICATE-xxx
, where xxx is the RID of the object in hexadecimal.
So until here nothing new, but it’s important to have this refreshment before the next part.
Attack scenario
The attack that we want to test is: what happens if a real computer is added to the domain, and if, at the same time, an attacker with a standard account detects it (step 1🕵️♂️) and adds the same machine account by targeting another domain controller (step 2📨). Can the attacker control the real machine (step 3🥷) and elevate their privileges thanks to this conflict?
Lab architecture
The minimum requirements for this scenario are:
- 2 domain controllers, to make the conflict possible:
- DC-999, having the PDC emulator role.
- DC-1001, targeted by the attacker. - (optional, but useful for this scenario) 1 member machine, compromised and used by the attacker:
- WIN10–01, sometimes named AttackMachine in the blog. The attacker has local administrative rights on this machine. - 1 standard Active Directory user account used by the attacker from WIN10–01.
- std-user-1. - 1 new machine that is joining the domain
- NEWMACHINE
The lab architecture and flow of the attack can be illustrated with the following schema:
Here are the 4 machines just before the scenario:
🕵️♂️Step 1 — Detect the addition of a new computer
To react as soon as possible, the attacker used the same method described for the UncoverDCShadow tool: the LDAP server control LDAP_SERVER_NOTIFICATION_OID (1.2.840.113556.1.4.528) to receive in real-time the notification about any newly created computer. The privileges of a standard AD account are “high” enough to subscribe. They can get more information if they wanted to, but here the need is only about the notification of the creation. They can subscribe to any domain controller they want. Normally, if you want to monitor an AD domain for legitimate purposes, and so by choosing only one domain controller, it’s a good habit to select the one holding the PDC emulator role to have more information. But to maximize their chances of catching the notification very quickly, whatever the domain controller where the join occurs, it’s better for the attacker to subscribe to all domain controllers.
📝Note: The Lab schema that was presented before only details the attacker flow. In this schema, the attacker subscribes only to the PDC from the AttackMachine.
Also, we assume that other methods might be used by the attacker to detect a new computer. For example, an aggressive loop with Get-ADComputer
doing the request every second could work under some circumstances.
As soon as the addition of the machine is detected on the domain controller they’ve subscribed to, the next step is to immediately create on a different domain controller a computer account having the same sAMAccountName. In this scenario, the attacker wants to create the maximum confusion for the administrator, and ideally replace the original account. So they will choose the Case 1 described in the first part of the blog: the fake object will be created in the same container.
📨Step 2 — Add the fake conflicting computer
To add the fake computer, the attacker still wants to do it with its standard account used before, and considering the ms-DS-MachineAccountQuota attribute still has its default value allowing any user to add up to 10 machine accounts in a domain.
🤓The MachineAccountQuota topic has already been explored in other blog posts, and as suggested in this one, the attacker used the Powermad tool to create the fake machine account.
➡️I’ve also learned a lot about pre-created computer accounts thanks to the Diving into Pre-Created Computer Accounts blog by Oddvar Moe and I recommend it.
Actions described in Step 1 and Step 2 can be scripted (cf. GitHub). The attacker can run it on the AttackMachine (WIN10–01) with a standard account. It will subscribe to events from DC-999 and target DC-1001 to create the fake computer. Numbers in the list below can be associated with the numbers in the screenshots.
- NewMachine computer is added to the “example.com” domain.
- The script detects it with a notification coming from DC-999, and immediately tries to create the fake similar machine account on DC-1001. The fake machine account DN has not been renamed (i.e.; mangled) and looks like a normal account. Whereas the authentic machine account DN has been renamed. At this stage, both objects have the same sAMAccountName value, we need to wait for NewMachine to reboot.
- During the reboot, NewMachine (highlighted in 🟩) will try to authenticate with its machine account to the domain. This is the moment when the sAMAccountName will be renamed. AD could decide here to select either the authentic or the fake account (highlighted in 🟧), the attacker needs a bit of luck🤞. We can see the sAMAccountName of the fake machine account has not been renamed and our fake object looks really like an authentic object so that’s a win 💪.
- The reboot is complete and a prompt for login is displayed.
- As expected when creating a machine account through the MachineAccountQuota, the mS-DS-CreatorSID attribute (highlighted in 🟥) contains the objectSid of the account that created it, i.e. std-user-1.
- Finally, we can confirm the sAMAccountName renaming has not impacted the fake machine account, but the authentic one.
📝Note: When trying to add the same machine account there are 3 possibilities:
- 😢 It fails because the commands to add the fake account were not fast enough.
- 😞 It works, but the renamed account is the fake account we added. Not good, it’s better to have the other case.
- 😀 It works, and the renamed account is the original! This means that the fake account looks like the real machine account.
In our tests, the first was rare but the second and the third were 50–50🎲.
As mentioned in Step 1, the chances can be increased by subscribing to multiple domain controllers. But this is not over yet! Even if the first challenge is passed, the next one is as important: the sAMAccountName renaming. Here also some luck is required to have the good case, 50–50 chance once again 🎲.
Let’s go back to our scenario: what happens if a user tries to authenticate from the NewMachine?
This is the first issue the user can see when typing the correct password:
The security database on the server does not have a computer account for this workstation trust relationship.
❌At this time, the machine is unusable with domain accounts and so the attacker has caused a partial Denial-of-Service (DoS) on the new machine. However, if a wrong password is typed, then the error will be different:
The user name or the password is incorrect. Try again.
This means that, contrary to the previous error, the account is found. So what does this error mean 🤔?
Thanks to Wireshark, we can deep dive into the discussion between NewMachine (192.168.109.70) and the domain controllers (here only DC-99, so 192.168.109.69).
We can see a Kerberos error “KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN” for “newmachine.example.com”.
Let’s compare the two objects side by side:
Even with those differences, we can identify the following attributes, which should be unique but are duplicated. This is the root cause of the Kerberos error:
In this scenario where the renamed DN having “CNF” is the authentic account, it is more than likely that the administrator will delete the authentic computer account. Let’s remove it and try again to authenticate from NewMachine. Is it better on the login screen?
❌There is still an issue, but we cannot see a Kerberos error in Wireshark this time.
Deleting the CNF object was quite logical for the administrator. However, at this stage there are two possibilities:
- The administrator will delete the other object, remove the machine, and try again from scratch.
- Or the administrator is stubborn, or this is not the first time the issue occurs and is determined to make it work! We like this spirit and will continue with this case😊.
After typing this error “The security database on the server does not have a computer account for this workstation trust relationship.” or even the previous one in various search engines on the Internet, a lot of results encourage us to reset the machine account password. To do that, first we need to log in the machine with a local account, and then open a privileged console/PowerShell. The command to type is:
netdom resetpwd /server:<DNS hostname of a DC> /userd:<Domain Admin user> /passwordd:'<Domain Admin password>'
📝Note: Other commands such as Test-ComputerSecureChannel -Repair -Credential (Get-Credential)
, can be found in the Internet but the result will be the same.
➡️Because of that requirement, this cannot be used when a new DC is added. The creation of the conflicting object even in the Domain Controllers OU will work (and the DoS). But this command fails when trying to execute it with the DSRM account. The situation can be resolved as soon as the fake account is deleted, and if the remaining account has a coherent sAMAccountName value.
Let’s retry to authenticate from NewMachine with a domain user account.
✅Finally, it worked! We have successfully authenticated on NewMachine with a domain user account, but remember: the machine account in the Active Directory is the fake one created by the attacker with EXAMPLE\std-user-1. This means that the attacker using this account has more privileges on NewMachine than they have on other machines. Let’s check that:
As expected, EXAMPLE\std-user-1 (the attacker) has more privileges on the newly joined machine because they created the machine account. Nevertheless, the owner of the machine account is EXAMPLE\Domain Admins.
😎There are interesting attributes and property sets that can be used for many purposes, such as abusing the Resource-Based Constrained Delegation (RBCD), and so, the attribute msDS-AllowedToActOnBehalfOfOtherIdentity! We can now refer to this article (Wagging the Dog: Abusing Resource-Based Constrained Delegation to Attack Active Directory — by E.Shamir) and see that, combined with S4U we can obtain a privileged session on the targeted machine.
So, with the method described in it, the attacker has enough rights to impersonate a privileged user on the target machine (NewMachine)💣.
🥷Step 3 — Take control of the real machine
First, the two computer objects do not have the property PrincipalsAllowedToDelegateToAccount. When the attacker tries to add a value on the computer where they are logged (WIN10–01), they can’t because of insufficient access rights. However, because they have more rights than usual on the NewMachine machine account, they can change this property without any issues.
The next steps will be straightforward, they can get the hash of the machine account with Mimikatz thanks to the local administrative rights.
And use them with Rubeus to request TGT and TGS:
The attacker can access files of NewMachine in the PowerShell window where the session has been previously opened, thanks to the forged ticket. In the other PowerShell windows on the right, the same command fails as a local administrator or as a standard user because by default (and without the forged ticket😈) the access to this machine is not allowed💥.
Recommendations
🚩The easiest way to prevent this (and other attacks!) is to authorize only administrators to add computers to the Active Directory domain. In our scenario, the attacker would have been blocked very early if the ms-DS-MachineAccountQuota attribute was well set. Privileged users are not subject to the maximum quota of the domain, it is unnecessary to set a value higher than 0 for that attribute. To remove the permission on regular users set the value to 0 using the following PowerShell command:
Set-ADDomain (Get-ADDomain).distinguishedname -Replace @{"ms-ds-MachineAccountQuota"="0"}
🧹Conflicting objects must be analyzed and cleaned. How did this conflict occur? What is the purpose of this object? Take the time to investigate the root cause. Perhaps it’s not due to malicious action, but replication issues. The safest way is to clean both objects and restart with a new object that has no conflict.
🛡️You can also look for evidence of this attack flow in the Windows Event logs or use solutions such as Tenable Identity Exposure which offers Indicators of Exposure that can help to identify the issue:
- Users Allowed to Join Computers to the Domain
- Conflicting Security Principals
- Dangerous Kerberos Delegation
Conclusion
There is no direct way to gain privileges on the targeted machine. However, an attacker can generate a DoS on a new machine joining the domain and expect the administrator to run the usual commands to fix the problem. This action will also allow an attacker to gain privileged rights via RBCD on the machine.