Skip to content

WriteAccountRestrictions

Copy page

Summary

Forestall 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

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

Find-WriteAccountRestrictions

3. Using SearchBase to limit the searching Scope

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

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

Find-WriteAccountRestrictionsSimple

Exploitation

For exploitation information go to 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

ADUC

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

Was this page helpful?

Thank you for your feedback.