Diving into DACLs and ACEs with DACLSearch
In Active Directory, permissions are enforced through "Access Control Entries" (ACEs) stored in "Discretionary Access Control Lists" (DACLs). When misconfigured, ACEs can grant principals (users or groups) excessive rights, enabling lateral movement and privilege escalation.
Motivation
The motivation to develop a new tool came from CTFs and HackTheBox machines, where I noticed that some ACEs were not collected by "BloodHound", because they aren’t considered dangerous. For example, rights to reanimate deleted objects or specific write attributes that enable exploitation of ESC14.
A second motivation was research. When I was investigating on Abusing sAMAccountName Hijacking in "GPP: Local Users and Groups", I needed to identify user objects that could be created in containers or OUs. I couldn’t do that from BloodHound data collected during past engagements.
Don’t get me wrong, BloodHound is an incredible tool. But for the sake of exhaustivity, I wanted a tool that could give me all ACEs that a principal has on any AD object. This article will explain how DACLs and ACEs work at a level that makes DACLSearch output meaningful.
Security descriptors
In Windows, every securable object (files, registry keys, AD objects, etc.) has a security descriptor, a binary structure that describes the rights a principal has on an object. For AD objects, the security descriptor is written in the nTSecurityDescriptor LDAP attribute.
Here are the key elements of a security descriptor:
- Control Flags: Flags that define the security descriptor behavior.
- Owner SID: The object's owner security identifier.
- DACL: Grants or denies permissions to principals on the object.
- SACL: Specifies auditing rules.
ACE structure
In this article we will focus on ACEs contained in DACLs, as SACLs are only used to generate records in the security event log when access attempts are made by a specified trustee on DACLs. There are two categories of ACEs in DACLs.
Generic ACEs, which apply simple rights directly to an object. A generic ACE contains:
- SID
- AceSize
- AceType
- AceFlags
- AccessMask
Object-specific ACEs, used in Active Directory to provide more granular control. If the ACE is object-specific, the following elements are added:
- ObjectFlags
- ObjectType
- InheritedObjectType
Decrypting ACEs
This section describes the main components of ACEs and the values they can hold, which will help you understand the output of DACLSearch.
Security Identifier (SID)
This component holds the "Security Identifier" of the user or group object that will be granted the rights specified in the ACE. It can be any SID, even from deleted or foreign domain principals. The SID name can be resolved if it is associated with a sAMAccountName.
Access Type (AceType)
A mask that defines whether the entry allows or denies rights.
| Readable Name | Microsoft Enum | Hex Value | Description |
|---|---|---|---|
| Allowed | ACCESS_ALLOWED_ACE_TYPE | 0x00 | Grants access rights to a trustee on the object itself. |
| Denied | ACCESS_DENIED_ACE_TYPE | 0x01 | Denies access rights to a trustee on the object itself. |
| Allowed Object | ACCESS_ALLOWED_OBJECT_ACE_TYPE | 0x05 | Grants access rights to a trustee on an object, property, or property set. |
| Denied Object | ACCESS_DENIED_OBJECT_ACE_TYPE | 0x06 | Denies access rights to a trustee on an object, property, or property set. |
Flags (AceFlags)
A mask that defines inheritance behavior for the ACE.
| Readable Name | Microsoft Enum | Hex Value | Description |
|---|---|---|---|
| Object Inherit | OBJECT_INHERIT_ACE | 0x01 | ACE is inherited by child objects. |
| Container Inherit | CONTAINER_INHERIT_ACE | 0x02 | ACE is inherited by child containers. |
| No Propagate Inherit | NO_PROPAGATE_INHERIT_ACE | 0x04 | ACE will not be propagated to child objects. |
| Inherit Only | INHERIT_ONLY_ACE | 0x08 | ACE is not applied on the object itself. |
| Inherited | INHERITED_ACE | 0x10 | ACE was inherited, not explicitly set. |
The Container Inherit flag can apply even to objects that are not defined as container object. According to Microsoft’s Containers and Leaves documentation, any object class listed in the possSuperiors or systemPossSuperiors attributes of another schema class is effectively a container, since it can hold child objects. For example, user objects are considered containers because they can have child objects.
Access Rights (AccessMask)
A mask that defines which actions the trustee can perform.
| Readable Name | Microsoft Enum | Hex Value | Description |
|---|---|---|---|
| Create Child | CREATE_CHILD | 0x00000001 | Create new objects under this container. |
| Delete Child | DELETE_CHILD | 0x00000002 | Delete child objects. |
| List Children | ACTRL_DS_LIST | 0x00000004 | Enumerate objects under this container. |
| Self | SELF | 0x00000008 | Validated writes. |
| Read Prop | READ_PROP | 0x00000010 | Read properties or property sets. |
| Write Prop | WRITE_PROP | 0x00000020 | Write properties or property sets. |
| Delete Tree | DELETE_TREE | 0x00000040 | Delete all child objects. |
| List Object | LIST_OBJECT | 0x00000080 | List the object itself. |
| Extended Right | CONTROL_ACCESS | 0x00000100 | Perform extended operations. |
| Delete | DELETE | 0x00010000 | Delete this object. |
| Read Control | READ_CONTROL | 0x00020000 | Read the security descriptor. |
| Write DACL | WRITE_DACL | 0x00040000 | Modify permissions. |
| Write Owner | WRITE_OWNER | 0x00080000 | Take ownership of the object. |
| Access System Security | ACCESS_SYSTEM_SECURITY | 0x01000000 | View/edit the SACL. |
| Maximum Allowed | MAXIMUM_ALLOWED | 0x02000000 | Grants all permissions permitted by the ACL. |
| Generic Read | GENERIC_READ | 0x00020094 | Composite: Read Control + List Children + List Object + Read Props. |
| Generic Write | GENERIC_WRITE | 0x00020028 | Composite: Write Props + Self + Read Control. |
| Generic Execute | GENERIC_EXECUTE | 0x00020004 | Composite: Read Control + List Children. |
| Generic All | GENERIC_ALL | 0x000F01FF | Composite: Full control over the object. |
Object Type and Inherited Object Type
Object-specific ACEs allow permissions to be applied to specific child objects, property sets, validated writes, or extended rights. These ACEs use GUIDs to precisely define the scope of access in two separate components:
- Object Type: Specifies the type of object, property set, validated write, or extended right that the ACE controls.
- Inherited Object Type: Determines which child objects can inherit the ACE. This is particularly useful for delegating permissions to specific object classes.
ACE Inheritance and precedence
In Active Directory, permissions applied to a parent object (like an OU or container) can be inherited by child objects. This makes delegation easier but also means multiple ACEs may apply to the same object. When this happens, the system must decide which ACEs take precedence.
The rules are:
- Within the same DACL, "Denied" ACEs take precedence over "Allowed" ACEs.
- Child ACEs have precedence over inherited parent ACEs. For example, explicit ACEs (set directly on the object) always override inherited ACEs.
Note that user and group ACEs have equal weight. If a user has a direct "Allowed Full Control" ACE but is also a member of a group that has a "Denied Full Control" ACE on the same object, the deny takes precedence and the user will have no rights on the object.
How DACLSearch Works
With the model above in mind, here is how DACLSearch operates:
-
DACLSearch dumps LDAP objects with a limited set of attributes to reduce data volume. It’s possible to dump the LDAP data to JSON with the
--jsonargument and export all LDAP attributes with the--fullargument for future use. -
The tool iterates over all AD objects and retrieves each object’s
nTSecurityDescriptor, storing parsed ACEs, object metadata, and group membership into a SQLite database. -
The CLI lets you query the SQLite database to recover any ACE a principal has on any object. You can filter by principal, target object, ACE type, access mask, object type, and more.
Filtering
ACL data contains many benign, default ACEs that can crowd results. DACLSearch supports filters to reduce noise.
There are two filter types:
- Search filters: Run independently and return separate result sets. If you select multiple search filters, results from each are returned separately and are merged if you choose multiple results.
- Merge filters: Combined with each search filter before querying. If you select multiple merge filters, each one is applied to every search filter.
Some built-in filters are included based on common ACE exploitation. In the example below, we use all built-in search filters and also merge filters that exclude self-ACEs and ACEs from built-in groups such as "Administrators", "Domain Admins", "Authenticated Users", etc.
Editing and creating filters
Filters are fully editable in the CLI. You can create custom filters that include or exclude results based on:
- Principal names
- Principal object classes
- Target object DNs
- Target object classes
- ACE types
- ACE access masks
- ACE object types
- ACE inherited object types
- ACE flags
- Owners
You can also toggle the following special options:
- Recursive search: recursively include ACEs applied via group membership (enabled by default).
- Ownership search: search for objects where a given user is the owner (enabled by default).
- Self rights removal: remove ACEs that a principal has on themselves (enabled by default).
- Merge: mark a filter as a merge filter (disabled by default).
Saving filters
Created filters can be saved as YAML files for future reuse. For example, here is a filter created to help abuse ESC14:
title: Weak explicit mapping (ESC14)
exclude_ace_flags:
- Inherit Only
include_target_object_classes:
- computer
- user
include_access_masks:
- Write Prop
include_object_types:
- E-mail-Addresses
- Common-Name
- RDN
- DNS-Host-Name
- DNS-Host-Name-Attributes
ownership_search: false
Closing Thoughts
I hope this article was a good introduction to DACLs and ACEs and how DACLSearch works. If you have questions, don’t hesitate to contact me on X: @Toffyrak.
References
- https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_aceflag_enum
- https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_acetype_enum
- https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_rights_enum
- https://learn.microsoft.com/en-us/windows/win32/secauthz/object-specific-aces
- https://learn.microsoft.com/en-us/windows/win32/ad/control-access-rights
- https://learn.microsoft.com/en-us/windows/win32/ad/containers-and-leaves