GPOWrite
Summary
Description
Identification
PowerShell
function Find-GPOWrite {
[CmdletBinding()]
param([string]$Target = $null,[string]$OutputPath = "GPOWrite.csv",[switch]$ExcludeAdmins = $false)
Import-Module ActiveDirectory -ErrorAction Stop
Import-Module GroupPolicy -ErrorAction Stop
Write-Host "Gathering Group Policy Objects and inspecting AD ACLs for explicit write access..."
# Build exclusion SID list if requested
$ExcludedSIDStrings = @()
if ($ExcludeAdmins) {
Write-Host "Excluding default administrative groups and built-in accounts."
try {
$ExcludedSIDStrings += (New-Object System.Security.Principal.NTAccount "NT AUTHORITY\SYSTEM").Translate([System.Security.Principal.SecurityIdentifier]).Value
$ExcludedSIDStrings += (New-Object System.Security.Principal.NTAccount "SYSTEM").Translate([System.Security.Principal.SecurityIdentifier]).Value
$ExcludedSIDStrings += (New-Object System.Security.Principal.NTAccount "NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS").Translate([System.Security.Principal.SecurityIdentifier]).Value
} catch {}
# Creator Owner SID
$ExcludedSIDStrings += [System.Security.Principal.SecurityIdentifier]::new("S-1-3-0").Value
foreach ($grp in @(
"Domain Admins","Enterprise Admins","Schema Admins","Cert Publishers",
"Group Policy Creator Owners","Domain Controllers","Key Admins",
"Enterprise Key Admins","DnsAdmins","RAS and IAS Servers"
)) {
try { $ExcludedSIDStrings += (Get-ADGroup -Identity $grp -ErrorAction Stop).SID.Value } catch {}
}
}
$results = @()
$gposToScan = @()
try {
if ($Target) {
Write-Verbose "Target specified. Looking for GPO: '$Target'."
$gpoToScan = Get-GPO -Name $Target -ErrorAction Stop
if ($gpoToScan) {
$gposToScan += $gpoToScan
Write-Host "Inspecting explicit write permissions on GPO: '$Target'."
} else {
Write-Output "GPO '$Target' not found."
return
}
} else {
Write-Verbose "No target specified. Getting all GPOs."
$gposToScan = Get-GPO -All -ErrorAction Stop
Write-Host "Inspecting explicit write permissions on all GPOs in the domain."
}
if (-not $gposToScan) {
Write-Output "No Group Policy Objects found matching the criteria."
return
}
$domainDN = (Get-ADDomain).DistinguishedName
# Define which AD rights count as "write-like"
$ADR = [System.DirectoryServices.ActiveDirectoryRights]
$writeRightsList = @($ADR::WriteProperty,$ADR::GenericWrite,$ADR::WriteDacl,$ADR::WriteOwner)
foreach ($gpo in $gposToScan) {
Write-Verbose "Inspecting GPO '$($gpo.DisplayName)' (ID: $($gpo.Id))."
# AD object of the GPO (GPC)
$gpoDN = "CN={$($gpo.Id)},CN=Policies,CN=System,$domainDN"
$adPath = "AD:$gpoDN"
try {
$acl = Get-Acl -Path $adPath -ErrorAction Stop
$aces = $acl.Access
foreach ($ace in $aces) {
# Only consider Allow, non-inherited ACEs
if ($ace.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) { continue }
if ($ace.IsInherited) { continue }
# Exclusion filter (by SID)
$trusteeSidString = $null
try {
$trusteeSidString = $ace.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
} catch {
$trusteeSidString = "S-1-0-0" # Null SID fallback if translation fails
}
if ($ExcludeAdmins -and ($ExcludedSIDStrings -contains $trusteeSidString)) {
Write-Verbose "Trustee '$($ace.IdentityReference.Value)' is excluded. Skipping."
continue
}
# Check if ACE has any of the write-like rights
$matchedRights = @()
foreach ($wr in $writeRightsList) {
if (($ace.ActiveDirectoryRights -band $wr) -eq $wr) {
$matchedRights += $wr.ToString()
}
}
if ($matchedRights.Count -gt 0) {
# Record result; keep same CSV columns as your previous function
$results += [PSCustomObject]@{
GPOName = $gpo.DisplayName
GPOId = $gpo.Id
Trustee = $ace.IdentityReference.Value
Permission = ($matchedRights -join ",")
}
}
}
}
catch {
Write-Warning "Error retrieving ACL for GPO '$($gpo.DisplayName)' (ID: $($gpo.Id)): $($_.Exception.Message)"
}
}
}
catch {
Write-Error "Failed to retrieve Group Policy Objects: $($_.Exception.Message)"
return
}
# Export the results to CSV if any were found
if ($results.Count -gt 0) {
$exclusionMessage = if ($ExcludeAdmins) { " (excluding default admin groups and built-in accounts)" } else { "" }
Write-Host "Found $($results.Count) explicit write ACE(s) on GPO AD objects$exclusionMessage."
try {
$results |
Sort-Object -Unique GPOName, GPOId, Trustee, Permission |
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 explicit write ACEs found on GPO AD objects$exclusionMessage."
}
}Group Policy Management

Exploitation
Windows

Important Note

Linux

Mitigation

Detection
Event ID
Description
Fields/Attributes
References
References
Last updated
Was this helpful?