The Phantom Menace: Exposing hidden risks through ACLs in Active Directory
We often find and capitalize on these misconfigurations in our Red Team / Internal Pentests / Insider Threat assessments.
In this post, we will discuss, in a general overview, some concepts that will help us understand how Windows handles access relationships and privileges between objects and how to enumerate these relationships.
- Securable Objects
- Security Descriptors
- Access Control Lists (ACLs)
- Enumerating ACLs
- Abusing ACLs
- Summary
- Acknowledgements
Securable objects
In Active Directory, a securable object refers to any entity or resource that can be protected by applying access controls and security attributes (security descriptors). These objects can be found in the Active Directory structure, including users, groups, computers, organizational units, printers, files, folders, and other resources.
Each securable object has an associated Security Descriptor that defines who can access the object and what actions they can perform. The Security Descriptor contains information about the object's owner, the permissions and accesses granted through DACL (Discretionary Access Control List), and the events audited through SACL (System Access Control List).
Securable objects are organized in a hierarchical structure in Active Directory, allowing access controls to be applied at different levels, from the domain to the individual object. This facilitates security management and the implementation of role and group-based access policies.
Security Descriptors
Per Microsoft documentation, "A security descriptor contains the security information associated with a securable object". A security descriptor comprises a SECURITY_DESCRIPTOR
binary structure and its associated security information. This binary structure is stored as properties directly in the AD objects (nTSecurityDescriptor attribute). A security descriptor can include the following:
- Object Owner (SID)
- Discretionary Access Control List (DACL)
- System Access Control List (SACL)
- Set of control bits
The internal structure of a security descriptor is quite complex because several elements are nested within each other; for this reason, it uses its language that defines how each data block is stored within this structure, Security Descriptor Definition Language (SDDL).
Graphically, we can see all the information of a security descriptor through object properties --> Security --> Advanced.
Below is an example of the security descriptor of the Computer type object with the cname "EvilCorpCa", belonging to the EVILCORP domain.
The permissions tab refers to the DACL; all the object's DACL Access Control Entries (ACEs) appear in that table. These ACEs define which security principal (also called "trustee") has the rights on the object (here is the important point).
To keep our goal of providing an overview of the abuse and detection of misconfigurations in DACLs, we will not go deeper into SDDL and how to parse the value stored in the nTSecurityDescriptor
property. Therefore, to simplify our purpose, let's use pywerview, a Python version of Powerview, which allows us to obtain the ACLs of an object, returning the ACEs in a human-readable format.
Following is an example of an LDAP query to obtain the value of the nTSecurityDescriptor
the attribute of the domain object "EvilCorpCa$":
The LDAP query will return the value in binary and base64 format. To convert this value and retrieve the blocks we are interested in, the DACL list, for example, we should work with the SDDL language. We can use .NET classes to execute LDAP searches. The DirectorySearcher .NET class contains a property named SecurityMasks
that allows us to enumerate parts of the ntSecurityDescriptor
property of an Active Directory object, even as a non-privileged user. We'll use pywerview to avoid executing commands on a potentially monitored Windows system to help us retrieve the ACEs.
Access Control Lists (ACLs)
The Access Control List defines the permissions and rights granted or denied to users or groups. The ACL is associated with each object and contains a list of ACEs. Each ACE specifies a user or group and their corresponding permissions for that object.
Windows contains two types of ACLs: Discretionary access control lists (DACLs) and System Access Control lists (SACLs).
Access Control Entries (ACEs)
ACE is an element in an Access Control List (ACL). An ACL can have zero or more ACEs. Each ACE controls or monitors access to an object by a specified trustee.
All types of ACEs contain:
- A security identifier (SID) from the trustee (security principal) to which ACE applies.
- An access mask: specifies the access rights controlled by the ACE.
- A flag that indicates the type of ACE
- A set of bit flags that determine whether child containers or objects can inherit the ACE from the primary object.
We can find a list of access rights and permissions in the own Microsoft documentation.
Discretionary Access Control List (DACL)
The object DACL specifies the level of access a principal/trustee has over an object. The owner of the object controls the DACL by default.
As attackers, it is important to understand that once we compromise an object in the domain, we won't know what privileges we have over other objects until we enumerate the DACLs of those objects.
The object's owner has WRITE_DAC
permissions on the object by default, allowing it to modify the DACL of the object it owns. An object requires READ_CONTROL
permission to enumerate DACLs on an object.
System Access Control List (SACL)
As discussed above, security descriptors can contain a system access control list (SACL). These SACL entries contain access control entries (ACEs).
While an object's DACL define which principal has permissions over the concerned object through its ACEs, in the case of SACL, the ACEs specify the types of access attempts that generate audit logs in a Domain Controller's security event log.
From the attacker's perspective, the SACL object enumeration can be useful before any modification on that object to evaluate the level of auditing of that object.
SACLs can be a great auditing feature for Blue teams to detect misconfiguration abuse, such as trying to read a specific attribute or modify an object.
Below is an example of the enumeration of the SACL of the domain object "EvilCorpCa" from pyweview:
In this ACE, it is defined that all write attempts coming from the Everyone
group on the EvilCorpCa object will be audited.
We will discuss SACLs in the next Lares post about auditing ACLs.
Enumerating ACLs
Now that we have an overview of using the Access Control Model to secure and attribute objects in the active directory, it's time to enumerate possible misconfiguration.
- BloodHound:
Bloodhound maps relationships and dependencies between objects in Active Directory, such as users, groups, computers, Group Policy Objects (GPOs), and trust relationships visually and graphically:
With BloodHound, the Blue Team can quickly identify possible paths adversaries might utilize to compromise the entire domain.
In large environments where it may not be easy to digest all the information that BloodHound can give us, ADACLScanner allows us to configure many options and filters to audit ACLs (both DACLs and SACLs).
It is possible to filter by type of access (Allow/Deny), by type of permissions (GenericAll, WriteProperty...), and by Trustee. It also allows exporting the results and filtering by criticality, which gives us a clear view of the status of our environment:
Below is an example of a report analyzing an Organizational Unit (OU) in the domain, filtering by criticality:
- PowerShell:
We can enumerate the ACLs from PowerShell in a less graphical way from the Microsoft.PowerShell.Security module. We can leverage the Get-Acl function:
(Get-Acl "AD:$(Get-ADGroup DBOWNERS)").Access | Select-Object ActiveDirectoryRights,IdentityReference,AccessControlType | Select-String lares
Dsacls is the equivalent of the Security tab in the properties dialog box for an Active Directory object in tools such as Active Directory Users and Computers (ADUC). You can use either tool to view and change permissions to an Active Directory object:
dsacls "CN=DBOWNERS,OU=DBS,DC=evilcorp,DC=local" | Select-String "lares"
With Powerview, it is possible to enumerate the ACEs of an object through the Get-DomainObjectACL
function:
Get-DomainObjectAcl -Identity DBOWNERS | ? { ($_.SecurityIdentifier -match '^S-1-5-.*-[1-9]\d{3,}$')} | select SecurityIdentifier,ActiveDirectoryRights, @{name='trustee';expression= {Convert-SIDToName $_.SecurityIdentifier }} | fl
- Pywerview:
With Python we can use pywerview to obtain the DACL and SACL by parsing the GUID and SID for more convenience:
pywerview get-objectacl -u lares -w EvilPass1. -t 192.168.1.45 --resolve-sids --resolve-guids --name WSRV01
The wrirte_property
permission indicates we have access to modify a specific attribute on an object. We can refer to Microsoft's documentation to map the objectacetype
value to a human-readable Active Directory attribute:
Abusing ACLs
Below are some examples of ACLs misconfiguration abuses. This is not an exhaustive set of attack paths - it is up to the tester to be creative and consider operational security when attempting to exploit misconfigurations.
GenericAll
All possible access rights. All possible abuse cases :).
- Permission value:
ADS_RIGHT_GENERIC_ALL
.
Let's use as an example a misconfiguration in the ACLs of a Active Directory Certificate Services (ADCS) certificate template in which a user has GenericAll
permissions on a template (ESC4). We can modify the template using certipy:
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 5136 [A directory service object was modified]
- 4900 [ Certificate services template security was updated]
GenericWrite
Permission to write all properties of the object. The ObjectType
member of an ACE can contain a GUID that identifies a property set or property. If ObjectType
does not contain a GUID, the ACE controls the right to write all the object's properties.
- Permission value:
GENERIC_WRITE
In the following example, we will use pywhisker to write the msDS-KeyCredentialLink
the attribute of a target user/computer to gain full control over that object (ShadowCredentials):
pywhisker -d "EvilCorp.local" --dc-ip 192.168.1.45 -u "lares" -p "EvilPass1." --action "add" --target "EVILDC01$" --filename evildc01
Then request TGT to get a PAC for the current user using U2U (When the TGT was obtained using PKINIT, the PAC will contain the NT hash):
gettgtpkinit.py -cert-pfx evildc01.pfx -pfx-pass blablapass -dc-ip 192.168.1.45 EVILCORP.LOCAL/EVILDC01$ evildc01.ccache
Export the .cacche and get the NT hash:
export KRB5CCNAME=evildc01.ccache
getnthash.py -key 23123123.. EvilCorp.local/EVILDC01$ -dc-ip 192.167.1.45
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 5136 [A directory service object was modified]. (msDS-KeyCredentialLink).
- 4768 [A Kerberos authentication ticket (TGT) was requested].
- 4769 [A Kerberos service ticket was requested].
WriteProperty
Permission to write over specific attributes. The attribute can be found in the GUID ObjectType
GUID. Account Operators usually have this right.
- Permission value:
ADS_RIGHT_DS_WRITE_PROP
- Over computers: Shadow Credentials, RBCD, SPN Jacking.
- Over users: Shadow Credentials, Targeted Kerberoasting, Logon Script.
- Over Groups: AddMember.
For this example the lares
user has write permissions over the serviceprincipalname
(WriteSPN) the attribute of EvilDA. We can subsequently add SPN value to that account using targetedKerberoast.py, effectively making that account "Kerberoastable":
The retrieved hash is then run through hashcat to retrieve the plaintext password of the Domain Admin account.
hashcat -m 13100 --force -a 0 EVILDA.txt superPasswordList.txt
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 4768 [A Kerberos authentication ticket (TGT) was requested].
- 4769 [A Kerberos service ticket was requested].
- 5136 [A directory service object was modified].
WriteDacl
The right to modify the DACL in the object's security descriptor.
- Permission value:
ADS_RIGHT_WRITE_DAC
.
We can change the DACL of our target using ldap_shell
by specifying the value of the ACE we want, in this case, GenericAll (0x10000000)
.
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 4665 [An operation was performed on an object] (Object-Access, Read + Write_DAC).
- 4742 [A computer account was changed].
WriteOwner
The right to assume ownership of the object. The user must be an object trustee. The user cannot transfer ownership to other users.
- Permission value:
ADS_RIGHT_WRITE_OWNER
Using ldap_shell
again we can set ourselves, the lares
user, as the owner of the target object:
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 4665 [An operation was performed on an object] (Object-Access, Read + Write_DAC).
- 4737 [A security-enabled local group was changed].
Self (Self-Membership)
Validated write permission to update group membership regarding adding/removing one's account.
- Rights-GUID:
bf9679c0-0de6-11d0-a285-00aa003049e2
In this example, we add ourselves, lares
user to the target group using ldap_shell
via NTLM auth:
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 4665 [An operation was performed on an object] (Object-Access, Read + Write_DAC).
- 4737 [ A security-enabled global group was changed]
AllExtendedRights
Rights to perform operations controlled by an extended access right. If ObjectType
does not contain a GUID, the ACE controls the right to perform all extended rights operations. This permission allows for resetting passwords on User objects and for crafting a Resource-Based Constrained Delegation (RBCD) attack for Computer objects.
- Permission value:
ADS_RIGHT_DS_CONTROL_ACCESS
. - Over Group:
AddMember
. - Over User:
ForceChangePassword
. - Over Computers:
ReadLAPSPassword
. - If a domain object with
AllExtendedRights
permissions on the domain object itself is compromised, that domain object will have both theDS-Replication-Get-Changes
andDS-Replication-Get-Changes-All privilege
. Both rights allow a principal to replicate objects from the Domain (DCSync).
For this example, our lares
user has AllExtendedRights
over EvilCorp.local. We can use secretsdump to perform DCSync:
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 4662 [An operation was performed on an object].
ReadLAPSPassword (ExtendedRight)
ExtendedRight, right to read ms-mcs-admpwd
attribute over a computer configured for LAPS.
- Rights-GUID:`2096ca39-21e0-479a-86f1-d06725a9b5d9`.
Since the Local Administrator Password Solution (LAPS) password is stored in cleartext within the ms-mcs-admpwd
attribute, we only need to perform an LDAP query or use pywerview
to read that attribute value on the machine we have permissions on:
ldapsearch -H ldap://192.168.1.45 -D 'Lares@evilcorp.local' -w 'EvilPass1.' -b "dc=EVILCORP,dc=LOCAL" "(ms-MCS-AdmPwd=*)" ms-MCS-AdmPwd
Events:
- 4776 [The computer attempted to validate the credentials for an account].
- 4624 [An account was successfully logged on].
- 4662 [An operation was performed on an object.] (ReadProperty).
ForceChangePassword (ExtendedRight)
Right to reset a password on a user account.
- Rights-GUID:
00299570-246d-11d0-a768-00aa006e0529
We can modify the target user's password using ldap_shell
with Kerberos auth:
- 4768 [A Kerberos authentication (TGT) was requested].
- 4769 [ A Kerberos service ticket was requested].
- 4624 [An account was successfully logged].
- 4724 [ An attempt was made to reset an account's password].
Summary
ACLs misconfigurations are among the most common lateral movement and privilege escalation methods in Active Directory. Our goal with this post was to summarize how permissions are related between objects in Active Directory, what ACLs are and how they work, and how they can be abused during an assessment.
From a defensive perspective, auditing ACL misconfigurations can be difficult, particularly in large environments. Many events generated by abusing these misconfigurations are not logged by default in Windows, so configuration and tuning will be required.
In our next post, we will talk specifically about how to audit your Active Directory environment to avoid misconfigurations and abuse of ACLs.