Press "Enter" to skip to content

Abusing GPO Permissions

A friend (@piffd0s) recently ran into a specific situation I hadn’t encountered before: the domain controllers and domain admins of the environment he was assessing were extremely locked down, but he was able to determine that a few users had edit rights on a few specific group policy objects (GPOs). After a bit of back and forth, he was able to abuse this to take down his target, and we were able to integrate some new functionality into PowerView that facilitates this process.

This post will cover these new features and demonstrate how to enumerate and abuse misconfigured GPOs in case you encounter a similar situation. I also covered a bit of this material in my recent Troopers16 presentation “I Have the Power(View): Offensive Active Directory with PowerShell“. Sidenote: I can’t say enough good things about Troopers– if you haven’t been I definitely recommend checking it out! Also, this new functionality is in the development branch of PowerSploit and should be merged into master soon(ish).

GPO Background

Group Policy Objects are Active Directory containers used to store groupings of policy settings. These objects are then linked to specific sites, domains, or most commonly specific organizational units (OUs). According to Microsoft, “By default, computer Group Policy is updated in the background every 90 minutes, with a random offset of 0 to 30 minutes.“, which forces the application of specific settings to any machines in an OU/site where the GPO is linked. Sean Metcalf also just posted a great article explaining GPOs from an offensive perspective, which I highly recommend reading.

With PowerView, the Get-NetGPO cmdlet allows for the easy enumeration of all current GPOs in a given domain. We can also easily figure out what OUs a policy is applied to by searching for a the GPO GUID in the gPLink attribute of any OU objects (this also works with Get-NetSite). We can then track this back to specific computers, as the “GPP and PowerView” post demonstrated. If we want to go the opposite direction and determine what GPOs a specific computer has applied, we can feed Get-NetGPO the -ComputerName COMPUTER argument, and all GPOs for the target system (applied by OU or site) will be returned:


The gpcfilesyspath field shows you where the configuration for the policy resides. All of this will matter shortly- the key here is to be able to quickly find the GPOs that apply to specific machines (starting from either the GPO name or the machine name) and where the actual GPO configuration files reside.

Enumerating GPO Permissions

I covered some similar information in my “Abusing Active Directory Permissions with PowerView” post, but I’ll reiterate a bit here. Active Directory objects (like files) have permissions associated with them. These can sometimes be misconfigured, and can also be backdoored for persistence (as shown in the abuse post). This includes GPOs. The key PowerView function we can use here for enumeration is Get-ObjectAcl.

Let’s enumerate all the permissions for all GPOs in the current domain:

Note: you can also use PowerView’s Invoke-ACLScanner to speed up your search. This will search the ACLs for ALL domain objects, and returns results where the IdentityReference RID is -1000 or above and also has some times of modification rights on the given object.

Either way, you will get a big chunk of data, with most of the entries likely being groups like Enterprise and Domain Admins. Here’s what a misconfiguration might look like:


And here’s how that misconfiguration looks through the Group Policy Management console:


So the ‘TESTLAB\will’ user has modification rights on the GPO with the GUID of “{3EE4BE4E-7397-4433-A9F1-3A5AE2F56EA2}” and display name of “SecurePolicy”. Let’s track this back and see what systems this GPO is applied to:


So now we now know the specific policy our user can edit and the machines this policy is applied to. And with edit rights to the GPO, we can force code execution on these machines!

Weaponizing GPO Edit Rights

Group Policy has a huge number of settings to manipulate, giving you a few ways to go about compromising machines/users touched by a compromised GPO. You could push out specific startup scripts, backdoor Internet Explorer settings, push out a .MSI under ‘Software installation’, add your domain account to the local administrators/RDP group, force the mounting of a network share (where you control the endpoint and can relay any specific credentials), or several other approaches I’m sure I’m not realizing.

My preference for immediate code execution would be to push out an ‘Immediate’ Scheduled task, which instantly runs and then removes itself, every time group policy refreshes. This part is pretty simple- we just need to build a schtask .XML template to substitute in our appropriate configuration/commands and then copy it to <GPO_PATH>\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml of the GPO we can edit. After waiting 1-2 hours for the group policy refresh cycle, we can remove the .xml to minimize our footprint.

PowerView’s new New-GPOImmediateTask function should take care of all this for you. The -TaskName argument is required, -Command specified the command to run (which defaults to powershell.exe), and -CommandArguments specifies the arguments for the given binary. The task description, author, and modification date can also optionally be modified with the appropriate parameters. A schtask .xml is built according to your specifications and is copied to the appropriate location determined by the -GPOname or -GPODisplayname arguments. By default the function will prompt you before copying, but this can be suppressed with -Force.

For example, let’s use New-GPOImmediateTask to push an Empire stager out to machines where this ‘{3EE4BE4E-7397-4433-A9F1-3A5AE2F56EA2}’ GPO (display name of ‘SecurePolicy’) is applied:



You can remove the schtask .xml after you get execution by supplying the -Remove flag:

Have fun!


  1. Teek Teek March 17, 2016

    I was just wondering what size environments you run this against? When I’ve tried using this for some of my engagements the length of time for it to complete stretches to hours or days and the volume of output is … high.
    When you are dealing with environments with hundreds of thousands of endpoints what do you do differently?

    • harmj0y harmj0y March 18, 2016

      Which exact function and syntax are you referring to, Invoke-ACLScanner or something like Get-NetGPO | %{Get-ObjectAcl -ResolveGUIDs -Name $_.Name} ?

      As these functions are relatively new, I haven’t tested them in networks with hundreds of thousands of endpoints. Unfortunately, I can’t think of ways off the top of my head to really speed up Get-ObjectACL as it’s more or less a modified LDAP query.

  2. Cyraxe Cyraxe June 19, 2017

    I’ve been trying to deploy the scheduled task as a starting point for my test deployment on deploying a file to a domain. I can’t seem to get it to work on Windows Server 2012 R2.

    It seems if i run New-GPOImmediateTask against the “Default Domain Controllers Policy” as an example or on a brand new GPO nothing seems to happen.
    However when I navigate in the GPO MMC it will appear as a setting ONLY if i navigate into the GPM editor window, double click the scheduled task and click ok. It seems that behind the scenes something is changing something in registry or on disk somewhere but i can’t seem to find it (api monitor crashes :( ).

    Is New-GPOImmediateTask meant to work off the bat without changes made in the GUI? just CLI only?

    This is the exact command that i ran: New-GPOImmediateTask -TaskName Testsched -GPODisplayName "Default Domain Controllers Policy" -CommandArguments '-c "testoutput | Out-File C:\test.txt"' -Force

    My end goal is to deploy a file instead of a scheduled task, but this is my starting point.

    • harmj0y harmj0y June 19, 2017

      So I removed New-GPOImmediateTask because of its inconsistencies- it ended up only really working as a PoC. Your best bet is likely to manually create the scheduled task in your lab, and editing the resulting XML that’s stored off in SYSVOL.

Leave a Reply

Your email address will not be published. Required fields are marked *