GenericWrite

Summary

FSProtect ACL Alias

GenericWrite

AD Alias

Write

Affected Object Types

Objects

Exploitation Certainty

Certain

AD Right

GenericWrite

AD Permission Guid

00000000-0000-0000-0000-000000000000

Description

The GenericWrite permission in Active Directory allows a user to modify certain attributes of an object, such as user accounts, groups, or computer objects. This permission is versatile and facilitates various administrative tasks, enabling administrators to update object properties without granting full control over the objects. Proper configuration of GenericWrite ensures that administrators can efficiently manage directory objects while maintaining security.

However, if misconfigured, the GenericWrite permission may allow an attacker to alter critical attributes, such as modifying group memberships, resetting passwords, or modifying security settings. For example, an attacker could add themselves to privileged groups like Domain Admins and weaken security configurations, enabling further exploitation. Proper assignment, monitoring, and auditing of the GenericWrite permission are essential to maintain directory security.

Identification

PowerShell

Active Directory Module

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

1. Find-GenericWrite function

function Find-GenericWrite {
    [CmdletBinding()]
    param ([string]$Target = $null,[string]$SearchBase = $null,[string]$OutputPath = "GenericWrite.csv",[switch]$ExcludeAdmins = $false)
    Import-Module ActiveDirectory
    Write-Host "Gathering Active Directory objects and inspecting ACLs for explicit GenericWrite or broad WriteProperty permissions..."
    $ExcludedSIDs = @()
    if ($ExcludeAdmins) {
        Write-Host "Excluding default administrative groups and built-in accounts."
        $ExcludedSIDs += (New-Object System.Security.Principal.NTAccount "NT AUTHORITY\SYSTEM").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "NT AUTHORITY\SELF").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "BUILTIN\Account Operators").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "BUILTIN\Administrators").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "BUILTIN\Terminal Server License Servers").Translate([System.Security.Principal.SecurityIdentifier]),(New-Object System.Security.Principal.NTAccount "NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS").Translate([System.Security.Principal.SecurityIdentifier]) 
        $ExcludedSIDs += [System.Security.Principal.SecurityIdentifier]::new("S-1-3-0");
        try {$ExcludedSIDs += (Get-ADGroup -Identity "Domain Admins").SID;$ExcludedSIDs += (Get-ADGroup -Identity "Enterprise Admins").SID;$ExcludedSIDs += (Get-ADGroup -Identity "Schema Admins").SID;$ExcludedSIDs += (Get-ADGroup -Identity "Cert Publishers").SID;$ExcludedSIDs += (Get-ADGroup -Identity "Group Policy Creator Owners").SID;$ExcludedSIDs += (Get-ADGroup -Identity "Domain Controllers").SID;$ExcludedSIDs += (Get-ADGroup -Identity "Key Admins").SID;$ExcludedSIDs += (Get-ADGroup -Identity "Enterprise Key Admins").SID;$ExcludedSIDs += (Get-ADGroup -Identity "DnsAdmins").SID;$ExcludedSIDs += (Get-ADGroup -Identity "RAS and IAS Servers").SID}
        catch {Write-Warning "Could not resolve one or more default domain admin groups. They might not be filtered from results."}}
    $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow;$GenericWriteRight = [System.DirectoryServices.ActiveDirectoryRights]::GenericWrite;$WritePropertyRight = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
    $foundAcls = @();$objectsToScan = @()
    try {
        if ($Target) {
            Write-Host "Searching for permissions on specific object: '$Target'."
            $specificObject = Get-ADObject -Identity $Target -Properties nTSecurityDescriptor -ErrorAction Stop
            if ($specificObject) {$objectsToScan += $specificObject} else {Write-Output "Object '$Target' not found.";return}
        } else {
            $adObjectParams = @{Filter    = "*"
                Properties = "nTSecurityDescriptor"
                ErrorAction = "Stop"}
            if ($SearchBase) {
                $adObjectParams.Add("SearchBase", $SearchBase)
                Write-Host "Searching for all objects within '$SearchBase'."
            } else {$adObjectParams.Add("SearchBase", (Get-ADRootDSE).DefaultNamingContext);Write-Host "Searching for all objects in the domain."}
            $objectsToScan = Get-ADObject @adObjectParams
        }
        if (-not $objectsToScan) {Write-Output "No 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) {
                $isExcluded = $false
                    if ($ExcludeAdmins) {
                        try {if ($ExcludedSIDs -contains $ace.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier])) {$isExcluded = $true}}
                        catch {Write-Warning "Could not translate SID for exclusion check: $($ace.IdentityReference.Value). Error: $($_.Exception.Message)"}}
                    if ($ace.AccessControlType -eq $AccessControlType -and-not $ace.IsInherited -and  -not $isExcluded) {
                        if (($ace.ActiveDirectoryRights -eq $GenericWriteRight) -or
                            (($ace.ActiveDirectoryRights -band $WritePropertyRight) -and ([Guid]::Empty -eq $ace.ObjectType))) { # $null -eq $ace.ObjectType checks if no specific property GUID is set
                            $foundAcls += [PSCustomObject]@{
                                'Vulnerable Object' = $ObjectDistinguishedName
                                'Permission Type'   = if ($ace.ActiveDirectoryRights -eq $GenericWriteRight) { 'GenericWrite' } else { 'Broad WriteProperty' }
                                'Permission Holder' = $ace.IdentityReference.Value # Get the string representation of IdentityReference
}}}}}
            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) {
        $exclusionMessage = if ($ExcludeAdmins) { " (excluding default admin groups and built-in accounts)" } else { "" }
        Write-Host "Found $($foundAcls.Count) object(s) with explicit GenericWrite or broad WriteProperty permissions$exclusionMessage."
        try {
            $foundAcls | Sort-Object -Unique 'Vulnerable Object', 'Permission Holder' | 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 {
        $exclusionMessage = if ($ExcludeAdmins) { " (excluding default admin groups and built-in accounts)" } else { "" }
        Write-Output "No Active Directory objects found with explicit GenericWrite or broad WriteProperty permissions$exclusionMessage." }}

2. Scan all domain objects

Find-GenericWrite

3. Scan a specific object

Find-GenericWrite -Target "CN=Users,DC=forestall,DC=labs"

4. To exclude default admin ACLs to improve visibility

Find-GenericWrite -ExcludeAdmins

5. Using SearchBase to limit the scope

Find-GenericWrite -SearchBase 'CN=Users,Dc=Forestall,DC=Labs' 

.NET Directory Services

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

1. Find-GenericWriteSimple function

function Find-GenericWriteSimple {
    [CmdletBinding()]
    param ([string]$Target = $null,[string]$SearchBase   = $null,[string]$OutputPath   = "GenericWrite.csv",[switch]$ExcludeAdmins)
    $GenericWriteRight = [System.DirectoryServices.ActiveDirectoryRights]::GenericWrite
    $WritePropertyRight = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
    $Allow = [System.Security.AccessControl.AccessControlType]::Allow
    try {$baseDN = if ($SearchBase) { $SearchBase } else { ([ADSI]"LDAP://RootDSE").defaultNamingContext }} catch { Write-Error "Failed to read RootDSE: $_";return}
    $ExcludedSidStrings = @()
    if ($ExcludeAdmins) {
        $wellKnown = @("NT AUTHORITY\SYSTEM","NT AUTHORITY\SELF","BUILTIN\Account Operators","BUILTIN\Administrators","NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS","CREATOR OWNER","BUILTIN\Terminal Server License Servers")
        foreach ($acct in $wellKnown) { try {$sid = (New-Object System.Security.Principal.NTAccount($acct)).Translate([System.Security.Principal.SecurityIdentifier]);$ExcludedSidStrings += $sid.Value} catch {}}
        foreach ($name in @(
            "Domain Admins","Enterprise Admins","Schema Admins","Cert Publishers","Domain Controllers","Key Admins","Enterprise Key Admins","DnsAdmins", "RAS and IAS Servers","Group Policy Creator Owners"
        )) {
            try {$grp = [ADSI]"LDAP://CN=$name,CN=Users,$baseDN";$bytes = $grp.Properties["objectSid"][0];if ($bytes) {$sid = New-Object System.Security.Principal.SecurityIdentifier($bytes,0);$ExcludedSidStrings += $sid.Value}} catch {}
        } $ExcludedSidStrings = $ExcludedSidStrings | Sort-Object -Unique }
    function _Get-DN {
        param($searchResult, $entry)
        $dn = $null
        if ($searchResult -and $searchResult.Properties.Contains("distinguishedname")) {$dn = $searchResult.Properties["distinguishedname"] | Select-Object -First 1 }
        if (-not $dn -and $entry) {try { $dn = $entry.Properties["distinguishedName"] | Select-Object -First 1 } catch {}}
        return $dn}
    $rows = New-Object System.Collections.Generic.List[object]
    if ($Target) {
        try {$entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target");$null = $entry.RefreshCache(@("ntSecurityDescriptor","distinguishedName"))} catch {Write-Error "Failed to bind to target '$Target' : $($_.Exception.Message)";return}
        $dn = $entry.Properties["distinguishedName"] | Select-Object -First 1
        if (-not $dn) { $dn = $Target }
        try {$aces = $entry.ObjectSecurity.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])} catch {Write-Warning "Failed to read ACL for $dn : $($_.Exception.Message)";return}
        foreach ($ace in $aces) {
            if ($ace.AccessControlType -ne $Allow -or $ace.IsInherited) { continue }
            $hasGenericWrite   = ($ace.ActiveDirectoryRights -band $GenericWriteRight)
            $hasWriteProperty  = ($ace.ActiveDirectoryRights -band $WritePropertyRight) -and ([Guid]::Empty -eq $ace.ObjectType) # Broad WriteProperty only
            if (-not ($hasGenericWrite -or $hasWriteProperty)) { continue }
            $sidValue = $ace.IdentityReference.Value
            if ($ExcludeAdmins -and $ExcludedSidStrings -contains $sidValue) { continue }
            $who = $sidValue
            try { $who = $ace.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value } catch {}
            $rows.Add([PSCustomObject]@{
                'Vulnerable Object' = $dn
                'Permission Type'   = if ($hasGenericWrite) { 'GenericWrite' } else { 'WriteProperty' }
                'Permission Holder' = $who})}}else {
        try {
            $root = [ADSI]"LDAP://$baseDN";$srch = New-Object System.DirectoryServices.DirectorySearcher($root)
            $srch.Filter = "(objectClass=*)";$srch.PageSize = 1000
            $srch.SearchScope = [System.DirectoryServices.SearchScope]::Subtree;$srch.SecurityMasks = [System.DirectoryServices.SecurityMasks]::Dacl
            foreach ($p in @("distinguishedName","ntSecurityDescriptor")) { [void]$srch.PropertiesToLoad.Add($p) }
        } catch {Write-Error "LDAP search setup failed: $($_.Exception.Message)";return}
        try {$results = $srch.FindAll()} catch {Write-Error "LDAP search failed: $($_.Exception.Message)";return}
        foreach ($res in $results) {
            $entry = $null
            try { $entry = $res.GetDirectoryEntry() } catch { continue }
            $dn = _Get-DN -searchResult $res -entry $entry
            if (-not $dn) { continue }
            try {$aces = $entry.ObjectSecurity.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])} catch {continue}
            foreach ($ace in $aces) {
                if ($ace.AccessControlType -ne $Allow -or $ace.IsInherited) { continue }
                $hasGenericWrite   = ($ace.ActiveDirectoryRights -eq $GenericWriteRight)
                $hasWriteProperty  = ($ace.ActiveDirectoryRights -band $WritePropertyRight) -and ([Guid]::Empty -eq $ace.ObjectType) # Broad WriteProperty only
                if (-not ($hasGenericWrite -or $hasWriteProperty)) { continue }
                $sidValue = $ace.IdentityReference.Value
                if ($ExcludeAdmins -and $ExcludedSidStrings -contains $sidValue) { continue }
                $who = $sidValue
                try { $who = $ace.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value } catch {}
                $rows.Add([PSCustomObject]@{
                    'Vulnerable Object' = $dn
                    'Permission Type'   = if ($hasGenericWrite) { 'GenericWrite' } else { 'WriteProperty' }
                    'Permission Holder' = $who})}}}
if ($rows.Count -gt 0) {$rows = $rows | Sort-Object 'Vulnerable Object','Permission Type','Permission Holder' -Unique;$rows | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8;Write-Host "Exported $($rows.Count) entr$(if ($rows.Count -eq 1){'y'}else{'ies'}) to $OutputPath"} else {Write-Host "No objects found with explicit GenericWrite or WriteProperty permissions."}}

2. Scan all domain objects

Find-GenericWriteSimple

3. Scan a specific object

Find-GenericWriteSimple -Target "CN=Users,DC=forestall,DC=labs"

4. To exclude default admin ACLs to improve visibility

Find-GenericWriteSimple -ExcludeAdmin

Active Directory Users and Computers

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

2. Right-click on the domain name.

3. Select Properties from the context menu.

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

5. Locate and select the relevant Access Control Entry (ACE) for the user or group you wish to configure.

6. Click Edit to modify the selected ACE.

7. In the permissions list, locate and check the option Write.

8. Click Apply to save your changes and close the dialogs.

Exploitation

Users

When an attacker possesses GenericWrite permissions over a user object in Active Directory, they can exploit a vulnerability by writing to the msds-KeyCredentialLink attribute.

This action allows the attacker to create "Shadow Credentials" for that user object. Shadow Credentials enable an attacker to authenticate as the compromised principal using Kerberos Public Key Cryptography for Initial Authentication (PKINIT).

For more detailed information on this attack vector, refer to the AddKeyCredentialLink edge documentation in Active Directory security assessments.

Alternatively, with appropriate write permissions, you can modify the servicePrincipalNames (SPN) attribute of a user object. This modification can then facilitate a targeted Kerberoasting attack against the account. For more details on the abuse scenarios related to this, see theWriteSPN edge documentation.

Groups

If you have GenericWrite permissions on a group, you can add yourself or any other controlled principal to that group. Further details on this form of abuse can be found under the AddMember edge documentation.

Computers

When GenericWrite permissions are held over a computer object, an attacker can manipulate the msds-KeyCredentialLink attribute. This manipulation enables the creation of "Shadow Credentials" on the computer object, allowing an adversary to authenticate as that computer principal via Kerberos PKINIT. For further details on this attack technique, refer to the AddKeyCredentialLink edge documentation.

Alternatively, you can perform a resource-based constrained delegation attack against the computer. See the AddAllowedToAct edge abuse information for more information about that attack.

GPO

When GenericWrite permissions are held over a GPO it's possible to take full control of a GPO to modify its settings, which will then apply to its targeted users and computers. Select the target object, open the gpedit GUI, and inject a malicious policy—such as an immediate scheduled task with item‑level targeting—into the GPO. Wait at least two hours for the Group Policy Client to retrieve and execute the new policy. See the GPOWrite edge documentation for abuse details.

Certificate Template

When an attacker has GenericWrite permissions over a Certificate Template, they can modify its settings to introduce the ESC1 vulnerability. This misconfiguration can then be exploited to escalate privileges and ultimately gain Domain Admin rights.

For more details, see WriteCertificateTemplates.

Delegated Managed Service Account

If an attacker has GenericWrite permissions over a dMSA object, they can modify the PrincipalsAllowedToRetrieveManagedPassword property to include their own account. This grants the ability to retrieve the managed service account’s password.

Example:

Set-ADServiceAccount -Identity MyAppSvc -PrincipalsAllowedToRetrieveManagedPassword adam

Once added, the attacker can read the password. For more abuse details, see the ReadGMSAPassword edge.

Mitigation

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

1. Open Active Directory Users and Computers.

2. Double-click the affected Object and open Security tab.

3. In this tab, click the dangerous Access Control Entry.

4. Remove the Write right.

5. Click OK and Apply to save changes.

Detection

Adding new Access Control Entries to 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 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

References

Last updated

Was this helpful?