Like many Windows related technologies Active Directory uses a security descriptor and the access check process to determine what access a user has to parts of the directory. Each object in the directory contains an nTSecurityDescriptor attribute which stores the binary representation of the security descriptor. When a user accesses the object through LDAP the remote user's token is used with the security descriptor to determine if they have the rights to perform the operation they're requesting.
Weak security descriptors is a common misconfiguration that could result in the entire domain being compromised. Therefore it's important for an administrator to be able to find and remediate security weaknesses. Unfortunately Microsoft doesn't provide a means for an administrator to audit the security of AD, at least in any default tool I know of. There is third-party tooling, such as Bloodhound, which will perform this analysis offline but from reading the implementation of the checking they don't tend to use the real access check APIs and so likely miss some misconfigurations.
I wrote my own access checker for AD which is included in my NtObjectManager PowerShell module. I've used it to find a few vulnerabilities, such as CVE-2021-34470 which was an issue with Exchange's changes to AD. This works "online", as in you need to have an active account in the domain to run it, however AFAIK it should provide the most accurate results if what you're interested in what access an specific user has to AD objects. While the command is available in the module it's perhaps not immediately obvious how to use it an interpret the result, therefore I decide I should write a quick blog post about it.
A Complex Process
- ACTRL_DS_CREATE_CHILD (CreateChild) - Create a new child object
- ACTRL_DS_DELETE_CHILD (DeleteChild) - Delete a child object
- ACTRL_DS_LIST (List) - Enumerate child objects
- ACTRL_DS_SELF (Self) - Grant a write-validated extended right
- ACTRL_DS_READ_PROP (ReadProp) - Read an attribute
- ACTRL_DS_WRITE_PROP (WriteProp) - Write an attribute
- ACTRL_DS_DELETE_TREE (DeleteTree) - Delete a tree of objects
- ACTRL_DS_LIST_OBJECT (ListObject) - List a tree of objects
- ACTRL_DS_CONTROL_ACCESS (ControlAccess) - Grant a control extended right
- The list of groups granted to a local user is unlikely to match what they're granted on the DC where the real access check takes place.
- AccessCheckByType only returns a single granted access value, if we have a lot of object types to test it'd be quick expensive to call 100s if not 1000s of times for a single security descriptor.
- Enumerate the user's group list for the DC from the AD. Local group assignments are stored in the directory's CN=Builtin container.
- Build an Authz security context with the group list.
- Read a directory object's security descriptor.
- Read the object's schema class and build a list of specific schema objects to check:
- All attributes from the class and its super, auxiliary and dynamic auxiliary classes.
- All allowable child object classes
- All assignable control, write-validated and property set extended rights.
Using Get-AccessibleDsObject and Interpreting the Results
- GrantedAccess - The granted access when only specifying the object's schema class during the check. If an access is granted at this level it'd apply to all values of that type, for example if WriteProp is granted then any attribute in the object can be written by the user.
- WritableAttributes - The list of attributes a user can modify.
- WritablePropertySets - The list of writable property sets a user can modify. Note that this is more for information purposes, the modifiable attributes will also be in the WritableAttributes property which is going to be easier to inspect.
- GrantedControl - The list of control extended rights granted to a user.
- GrantedWriteValidated - The list of write validated extended rights granted to a user.
- CreateableClasses - The list of child object classes that can be created.
- DeletableClasses - The list of child object classes that can be deleted.
- DistinguishedName - The full DN of the object.
- SecurityDescriptor - The security descriptor used for the check.
- TokenInfo - The user's information used in the check, such as the list of groups.