This article describes a PowerShell script to find all orphaned objects in Active Directory. The script also documents all security principals protected by SDProp and AdminSDHolder. The script is linked here: Find Orphaned Objects in Active Directory


Introduction

Certain highly privileged security principals in Active Directory are protected. Protected objects are direct or transitive members of default highly privileged groups. A process called SDProp (Security Descriptor Propagator) runs once an hour (by default) on the domain controller with the PDC Emulator role. SDProp compares the permissions of all protected objects to those assigned to the AdminSDHolder object. If they are different, SDProp overwrites the permissions so they match those of the AdminSDHolder object. By default, the AdminSDHolder object has inheritance of permissions disabled, so SDProp also enforces this on the protected objects. Finally, SDProp assigns the value 1 to the adminCount attribute. The AdminSDHolder object is in the cn=System container of the domain.

This process ensures that permissions on privileged security principals are enforced, even if the objects are moved to another organizational unit. Permissions are protected from manual or unintentional changes.

When a security principal is removed from all protected groups, it no longer is protected by SDProp. However, the value of the adminCount attribute is not changed. In addition, inheritance remains disabled. This is a source of confusion and a possible security concern. These objects are called orphaned. Without inheritance, permissions applied to the parent organizational unit are not applied to such objects. It is important to review these objects. Some people recommend that they be deleted or disabled. At the very least, inheritance should be enabled. Clearing the adminCount attribute would avoid confusion. Many people use this attribute as a quick way to find protected objects.

↑ Return to Top


Default Protected Objects

A default set of privileged groups and users is protected automatically. The list of default protected objects in the domain is determined by the operating system of the domain controller with the PDC Emulator (PDCe) FSMO role.

PDCe Operating System Service Pack Default Protected Objects
Windows 2000 Server Administrators
Domain Admins
Enterprise Admins
Schema Admins
Windows 2000 Server SP 4 (or Hotfix 327825) Administrators
Domain Admins
Enterprise Admins
Schema Admins
Cert Publishers
Replicator
Account Operators
Server Operators
Print Operators
Backup Operators
Administrator
krbtgt
Domain Controllers
Windows Server 2003 Administrators
Domain Admins
Enterprise Admins
Schema Admins
Cert Publishers
Replicator
Account Operators
Server Operators
Print Operators
Backup Operators
Administrator
krbtgt
Domain Controllers
Windows Server 2003 SP 1 or 2 Administrators
Domain Admins
Enterprise Admins
Schema Admins
Replicator
Account Operators
Server Operators
Print Operators
Backup Operators
Administrator
krbtgt
Domain Controllers
Windows Server 2008 or above Administrators
Domain Admins
Enterprise Admins
Schema Admins
Replicator
Account Operators
Server Operators
Print Operators
Backup Operators
Administrator
krbtgt
Domain Controllers
Read-only Domain Controllers

↑ Return to Top


dSHeuristic Attribute and Operator Groups

If the operating system of the PDC Emulator is Windows Server 2003 or above, or if Service Pack 4 or Hotfix 327825 is applied to Windows 2000 Server, four Operator groups are included in the list of default protected objects. These are the Account Operators, Server Operators, Print Operators, and Backup Operators groups. These are all in the cn=Builtin container. However, you can exclude any combination of these groups from protection by modifying the dSHeuristics attribute of the cn=Directory Service object. This object is in the cn=Windows NT,cn=Services container in the Configuration partition of the forest. The value of this attribute, and the Operator groups excluded from protection, will apply to all domains in the forest.

The dSHeuristics attribute has string syntax. Each character of the value represents a different setting. In this case, the 16th character is interpreted as a hexadecimal value indicating which combination of the four Operator groups is excluded from protection. If the value of dSHeuristics is more than 10 characters long, the 10th character must be a "1". If the value is more than 20 characters long, the 20th must be a "2".

If you modify dSHeuristics to exclude one or more Operator groups, any existing characters should be left unchanged. They represent other settings. If the existing value is blank (not set), or is less than 10 characters long, make sure the 10th character is a "1". The 16th character is interpreted as follows:

16th Groups Excluded
Character Account Operators Server Operators Print Operators Backup Operators
0 (or missing)
1 X
2 X
3 X X
4 X
5 X X
6 X X
7 X X X
8 X
9 X X
A X X
B X X X
C X X
D X X X
E X X X
F X X X X

For example, if there is no existing value of dSHeuristics, and you wish to exclude the Print Operators and Backup Operators groups, you would assign the following value.

000000000100000C

The 10th character is a "1" and the 16th is the hexadecimal value "C". Be aware that if you exclude any Operator groups, and they have members, you will have created orphaned objects.

↑ Return to Top


Other Protected Objects

Any members of the default protected groups will also be protected. This includes direct and transitive membership in any protected groups. It also includes users and computers where the primary group is any protected group. Protected objects can be users, computers, or groups. Both security and distribution group members can be protected.

↑ Return to Top


The SDPROP Process

The Security Descriptor Propagator (SDProp) process runs once an hour (by default) on the PDC Emulator. It compares the permissions of every protected object in the domain to those assigned to the AdminSDHolder object. If the permissions do not match, SDProp overwrites the permissions so they match those of the AdminSDHolder object. By default, this includes disabling inheritance. Then the value 1 is assigned to the adminCount attribute of the protected object. Note that clearing the adminCount attribute of any protected object (or assigning a value other than 1) does not trigger SDProp to modify the object. Adding or changing any Access Control Entry (ACE) in the Discretionary Access Control List (DACL), or enabling inheritance, will trigger SDProp to correct the permissions.

↑ Return to Top


The AdminSDHolder Object

The AdminSDHolder object is in the cn=System container of the domain. The permissions assigned to this object will be applied to all protected objects in the domain by the SDProp process. By default this includes disabling the inheritance of permissions from any parent organizational units. Although it is generally not recommended, an administrator can modify the default permissions assigned to this object.

↑ Return to Top


Orphaned Objects

When a protected security principal is no longer a member of any protected group, it becomes orphaned. Unless the situation is corrected, the permissions assigned to the object will remain the same, including the disabling of inheritance. Also, the value of the adminCount attribute will not be changed. Besides creating confusion, this situation prevents permissions applied to organizational units from being inherited by the orphaned objects. Orphaned objects can be users, computers, or groups.

The script described in this article considers any object that is not a member of any protected group, but either has adminCount equal to 1 or has inheritance disabled as orphaned. These objects may have never been members of any protected group. There is no way to tell. The adminCount attribute could have been modified. Inheritance could have been disabled for some other reason. But these objects still should be investigated. The same concerns apply.

↑ Return to Top


Script Features

The script has lots of comments to explain the various steps. Key features are as follows:

  • Default protected groups and users are identified by well-known RID. This allows them to be identified even if they have been renamed.
  • A recursive function is used to retrieve all nested groups of the default protected groups.
  • A function is used to determine membership in a protected group. This is used to document which group membership accounts for an object being protected. Any object can be a member of more than one protected group, but this function only returns the first one found.
  • The operating system and service pack of the PDC Emulator in each domain is used to determine the default protected groups and users.
  • The dSHeuristics attribute of the cn=Directory Service object is used to determine if any Operator groups are not protected.
  • Primary group membership is considered when determining if user or computer objects are protected or orphaned.
  • The script does not depend on the adminCount attribute alone to determine if objects are protected or orphaned.
  • Protected objects and orphaned objects can be users, computers, or groups.

↑ Return to Top


Difficulties Encountered

When searching for objects in Active Directory there is no way to filter on the RID of the objects. This means that the query for protected groups in the script must retrieve all groups, then calculate the RID from the value of the objectSID attribute, which is a byte array. The code to calculate the RID of an object from the objectSID is similar to below, where $ADObject is a reference to the object:

$SID = $ADObject.objectSID
$arrSID = ($SID.ToString()).Split()
$k = $arrSID.Count
$RID = [Int32]$arrSID[$k - 4] `
    + (256 * [int32]$arrSID[$k - 3]) `
    + (256 * 256 * [Int32]$arrSID[$k - 2]) `
    + (256 * 256 * 256 * [Int32]$arrSID[$k - 1])

There is also no way to filter on whether or not inheritance is enabled. The script must retrieve all objects meeting other conditions, then bind to each object found and check the ntSecurityDescriptor attribute. The script uses the .NET function AreAccessRulesProtected for this. The code is similar to below:

$Entry = $ADObject.GetDirectoryEntry()
# The use of psbase is required to support PowerShell V1.
$ObjInherit = $Entry.psbase.ObjectSecurity.AreAccessRulesProtected
If ($ObjInherit -eq "True") {$Inherit = "Inheritance disabled"}
Else {$Inherit = "Inheritance enabled"}

This last difficulty makes the final query in the script slow. To find all objects that might be orphaned, we must filter on all security principals that are not members of any protected group. Then we need to bind to each and determine if inheritance is enabled. This can be very slow, even if there are only a few thousand security principals. But there is no alternative, since we cannot depend on the adminCount attribute.

↑ Return to Top


Unresolved Issue

The Domain Controllers group is protected by default, as long as the PDCe operating system is Windows 2000 Server SP4 or above. However, testing in a lab environment reveals that the members of this group, the domain controller objects, are not protected. If permissons of the group are modified, the SDProp process corrects the situation. But permissions of the members are not corrected. Also, permission inheritance remains enabled and the adminCount attribute is not set. No documentation or explanation has been found for this anomaly.

↑ Return to Top


Recommendations

Consider the following recommendations:

  • The domain controller with the PDC Emulator FSMO role should have the latest operating system supported by the domain functional level. Because of the affect it has on the user experience, it should also be well connected and highly available.
  • The PDC Emulator role should not be transferred to a domain controller with a lower level operating system. This can result in orphaned objects no longer protected by SDProp and the AdminSDHolder object.
  • Any orphaned objects should either be deleted, or investigated thoroughly. At the very least, inheritance should be enabled so that permissions applied to the parent organizational unit will be inherited. The adminCount attribute should be cleared to avoid confusion.
  • The security applied to the AdminSDHolder object should be modified with great care. This is seldom a good idea. In particular, it is not recommended that inheritance be enabled, except perhaps for testing in a lab environment.

↑ Return to Top


See Also

↑ Return to Top


Other Resources

↑ Return to Top