> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/ad/writeaccountrestrictions.md).

# WriteAccountRestrictions

## Summary

|                            |                                           |
| -------------------------- | ----------------------------------------- |
| **FSProtect ACL Alias**    | WriteAccountRestrictions                  |
| **AD Alias**               | Write Account Restrictions                |
| **Affected Object Types**  | Users, Computer, Managed Service Accounts |
| **Exploitation Certainty** | Certain                                   |
| **AD Right**               | WriteProperty                             |
| **Permission Guid**        | 4c164200-20c0-11d0-a768-00aa006e0529      |

## Description

The `WriteAccountRestrictions` permission in Active Directory refers to the `User-Account-Restrictions` property set. This permission allows an account to modify restriction attributes on user accounts, such as *logon hours*, *permitted workstations*, and *account expiration dates*. Critically, it includes the ability to modify the `msDS-AllowedToActOnBehalfOfOtherIdentity` attribute, which is essential for configuring Kerberos `Resource-Based Constrained Delegation` (RBCD).

If misconfigured or exploited, an attacker with the `WriteAccountRestrictions` permission can alter the `msDS-AllowedToActOnBehalfOfOtherIdentity` attribute on target objects. This enables them to perform a Kerberos RBCD attack by allowing an account they control to act on behalf of other identities. As a result, the attacker can impersonate any user—including domain administrators—to access services on the target computer. This can lead to unauthorized access, privilege escalation, and compromise of the entire network.

## Identification

#### Active Directory Module

Using the ActiveDirectory PowerShell module, you can enumerate `WriteAccountRestrictions` entries.

### PowerShell

**1.** Find-WriteAccountRestrictions function

```powershell
function Find-WriteAccountRestrictions {
    [CmdletBinding()]
    param ([string]$SearchBase = $null,[string]$OutputPath = "WriteAccountRestrictions.csv")
    Import-Module ActiveDirectory -ErrorAction stop
    Write-Host "Gathering Active Directory objects and inspecting ACLs for explicit 'Write Account Restrictions' permissions..."
    $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow;
    $ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty;
    # Guid of Write Account Restrictions permission
    # https://learn.microsoft.com/en-us/windows/win32/adschema/r-user-account-restrictions
    $WriteAccountRestrictionsPermissionGuid = "4c164200-20c0-11d0-a768-00aa006e0529";
    $ExcludedSIDs = @()
    try { $ExcludedSIDs += (Get-ADGroup -Identity "Domain Admins").SID
    }catch {Write-Warning "Could not resolve SID for 'Domain Admins'. It might not be filtered from results." }
    $adObjectParams = @{
        Filter    = "*"
        Properties = "nTSecurityDescriptor"
        ErrorAction = "Stop"
    }
    if ($SearchBase) {
        $adObjectParams.Add("SearchBase", $SearchBase)
        Write-Host "Searching for objects within '$SearchBase'."
    } else { Write-Host "Searching for all objects in the domain." }
    $foundAcls = @()
    try {
        $objectsToScan = Get-ADObject @adObjectParams
        if (-not $objectsToScan) {
            Write-Output "No Active Directory objects found matching the criteria."
            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 # Use -band for bitwise comparison
                        ($ace.ObjectType -eq $WriteAccountRestrictionsPermissionGuid) -and
                        -not $ace.IsInherited -and
                        ($ExcludedSIDs -notcontains $ace.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]))) {
                        $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 explicit 'Write Account Restrictions' permissions (excluding 'Domain Admins')."
        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 explicit 'Write Account Restrictions' permissions (excluding 'Domain Admins')."
    }
}
```

**2.** Scan all objects in the domain

```powershell
Find-WriteAccountRestrictions
```

**3.** Using SearchBase to limit the searching Scope

```powershell
Find-WriteAccountRestrictions -SearchBase "CN=Computers,DC=Forestall,DC=Labs"
```

#### .NET Directory Services

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

**1.** Find-WriteAccountRestrictionsSimple function

```powershell
function Find-WriteAccountRestrictionsSimple {
    [CmdletBinding()]
    param([string]$Target,[string]$OutputPath = "WriteAccountRestrictions.csv")
    $guid      = [guid]"4c164200-20c0-11d0-a768-00aa006e0529"
    $allow     = [System.Security.AccessControl.AccessControlType]::Allow
    $writeProp = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
    $excludedSids = @()
    try {
        $root      = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
        $baseDN    = $root.Properties["defaultNamingContext"].Value
        $searchRoot= New-Object System.DirectoryServices.DirectoryEntry("LDAP://$baseDN")
        $ds        = [System.DirectoryServices.DirectorySearcher]::new($searchRoot)
        $ds.Filter = "(&(objectCategory=group)(sAMAccountName=Domain Admins))"
        $ds.PageSize = 1000
        [void]$ds.PropertiesToLoad.Add("objectSid")
        $res = $ds.FindOne()
        if ($res -and $res.Properties["objectsid"] -and $res.Properties["objectsid"][0]) {
            $excludedSids += New-Object System.Security.Principal.SecurityIdentifier($res.Properties["objectsid"][0],0)
        }
    } catch {}
    if ($Target) {
        try {
            $entries = @( New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target") )
        } catch { Write-Error "Failed to bind to '$Target': $_"; return }
    } else {
        try {
            $root      = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
            $baseDN    = $root.Properties["defaultNamingContext"].Value
            $searchRoot= New-Object System.DirectoryServices.DirectoryEntry("LDAP://$baseDN")
            $ds        = [System.DirectoryServices.DirectorySearcher]::new($searchRoot)
            $ds.Filter = "(objectClass=*)"
            $ds.PageSize = 1000
            [void]$ds.PropertiesToLoad.Add("distinguishedName")
            $hits = $ds.FindAll()
            $entries = foreach ($hit in $hits) {
                try { $hit.GetDirectoryEntry() } catch { $null }
            }
        } catch { Write-Error "LDAP enumeration failed: $_"; return }
    }
    $found = foreach ($entry in $entries) {
        if (-not $entry) { continue }
        $dn = $entry.distinguishedName
        try {
            $acl  = $entry.ObjectSecurity
            $aces = $acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])
        } catch { continue }
        foreach ($ace in $aces) {
            if ($ace.AccessControlType -ne $allow) { continue }
            if (($ace.ActiveDirectoryRights -band $writeProp) -eq 0) { continue }
            if ($ace.ObjectType -ne $guid) { continue }
            if ($ace.IsInherited) { continue }
            $sid = $ace.IdentityReference
            $skip = $false
            foreach ($ex in $excludedSids) { if ($sid -eq $ex) { $skip = $true; break } }
            if ($skip) { continue }
            $who = try { $sid.Translate([System.Security.Principal.NTAccount]).Value } catch { $sid.Value }
            [PSCustomObject]@{
                'Vulnerable Object' = $dn
                'Internal Threat'   = $who
            }
        }
    }
    if ($found) {
        $out = $found | Sort-Object -Unique 'Vulnerable Object','Internal Threat'
        try { $out | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 } catch { Write-Warning "CSV export failed: $_" }
        $out
    } else {  Write-Host "No explicit 'Write Account Restrictions' ACEs found (excluding 'Domain Admins')."}
}

```

**2.** Scan all objects in the domain

```powershell
Find-WriteAccountRestrictionsSimple
```

### Active Directory Users and Computers

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

**2.** Right-click on the Object name.

**3.** Select Properties from the context menu.

**4.** In the Properties window, navigate to the Security tab.

**5.** Click on the Advanced button to open the Advanced Security Settings dialog.

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

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

**8.** In the permissions list, locate and check the option `Write Account Restrictions`.

**9.** Click OK to save your changes and close the dialogs.

![ADUC](/files/Qxp2QekhBwQA1beMPHez)

## Exploitation

For exploitation information go to [AddAllowedToAct](https://docs.forestall.io/fsprotect/edges/ad/addallowedtoact)

## Mitigation

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

**1.** Open `Active Directory Users and Computers`, and activate `Advanced Features` option.

**2.** Double click the affected Object and open `Security` tab.

**3.** In this tab, click `Advanced` button and open the dangerous objects Access Control Entry.

**4.** Remove the `Write Account Restrictions` right.

**5.** Click OK and Apply buttons for saving changes.

![ADUC](/files/Qxp2QekhBwQA1beMPHez)

## Detection

Adding new Access Control Entries on the Active Directory objects changes the `ntSecurityDescriptor` attribute of the objects themselves. These changes can be detected with the 5136 and 4662 Event IDs to identify dangerous objects modifications.

| 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 (Resource-Based Constrained Delegation) - thehacker.recipes](https://www.thehacker.recipes/ad/movement/kerberos/delegations/rbcd)
* [Wagging the Dog: RBCD (Shenanigans Labs Blog)](https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html)
* [RBCD Abuse (Redfox Security Blog)](https://redfoxsec.com/blog/rbcd-resource-based-constrained-delegation-abuse/)
