Skip to content

Instantly share code, notes, and snippets.

@a-gu
Last active August 9, 2023 13:29
Show Gist options
  • Save a-gu/61cf7e030b3235ed03373ba2b8016e1c to your computer and use it in GitHub Desktop.
Save a-gu/61cf7e030b3235ed03373ba2b8016e1c to your computer and use it in GitHub Desktop.
Invoke a PowerShell command string on a remote system using a Scheduled Task and network share access (to retrieve output). Other WMI/CIM permissions not required.
Function Invoke-RemoteCommand {
[CmdletBinding()]
Param(
[string]$ComputerName = '',
[string]$Command = { Get-ChildItem env: | Select-Object Key,Value | ConvertTo-CSV },
[boolean]$ReturnOutput = $True,
[string]$Prefix = 'Invoke-RemoteCommand'
)
# Prepare command
$Nonce = -join ((48..57)+(65..90)+(97..122) | Get-Random -Count 8 |% { [char]$_ })
$Nonce = "$Prefix`_$Nonce`_$(Get-Date -Format FileDateTimeUniversal)"
Write-Debug "Executing command with nonce `"$Nonce`""
$AssembledCommand = @()
If ($ReturnOutput) {
$AssembledCommand += @(
"Get-SmbShare |? { `$_.CurrentUsers -le 0 -and `$_.Name.StartsWith('$Prefix`_') -and [DateTime]::ParseExact(((`$_.Name -split '_')[-1] -replace '\$+$', ''), 'yyyyMMddTHHmmssffffZ', `$null) -le (Get-Date) } |% { Remove-SmbShare -Name `$_.Name -Force }",
"schtasks /Query /FO csv | ConvertFrom-Csv |? { `$_.Status -notin @('Status', 'Running') -and `$_.TaskName.StartsWith('$Prefix`_') -and [DateTime]::ParseExact((`$_.TaskName -split '_')[-1], 'yyyyMMddTHHmmssffffZ', `$null) -le (Get-Date) } |% { schtasks /Delete /F /TN `$_.TaskName; }",
"New-SmbShare -Temporary -Name '$Nonce`$' -Path `"`$env:WINDIR\Temp\`" -FullAccess `"$env:USERDOMAIN\$env:USERNAME`" -EncryptData `$True -FolderEnumerationMode AccessBased -AsJob",
"Start-Transcript -Append -LiteralPath `"`$env:WINDIR\Temp\$Nonce.tmp`""
)
}
$AssembledCommand += @"
Try { $Command }
Finally {
$(If ($ReturnOutput) { 'Stop-Transcript -ErrorAction SilentlyContinue;' })
schtasks /Delete /F /TN `"$Nonce`";
$(If ($ReturnOutput) { "While (Test-Path -LiteralPath `"`$env:WINDIR\Temp\$Nonce.tmp`") { Start-Sleep -Milliseconds 100; }; Remove-SmbShare -Name `"$Nonce`$`" -Force" })
}
"@
$AssembledCommand = $AssembledCommand -join ';'
$EncodedCommand = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($AssembledCommand))
# Create task
$TaskXML = New-TemporaryFile
Set-Content $TaskXML "<?xml version=`"1.0`" encoding=`"UTF-16`"?><Task version=`"1.4`" xmlns=`"http://schemas.microsoft.com/windows/2004/02/mit/task`"><Triggers><RegistrationTrigger><Enabled>true</Enabled></RegistrationTrigger></Triggers><Principals><Principal id=`"Author`"><LogonType>S4U</LogonType><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><MultipleInstancesPolicy>StopExisting</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>true</AllowHardTerminate><StartWhenAvailable>true</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable><IdleSettings><StopOnIdleEnd>false</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>true</Hidden><RunOnlyIfIdle>false</RunOnlyIfIdle><DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession><UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine><WakeToRun>false</WakeToRun><ExecutionTimeLimit>PT0S</ExecutionTimeLimit><Priority>5</Priority></Settings><Actions Context=`"Author`"><Exec><Command>powershell.exe</Command><Arguments>-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -EncodedCommand $EncodedCommand</Arguments></Exec></Actions></Task>"
If ($ComputerName.Length -gt 0) {
schtasks /Create /TN "$Nonce" /XML $TaskXML.FullName /S $ComputerName | Write-Debug
} Else {
schtasks /Create /TN "$Nonce" /XML $TaskXML.FullName | Write-Debug
}
Remove-Item $TaskXML
# Handle output
If ($ReturnOutput) {
Try {
# Read output file
$ReadPath = "\\$(If ($ComputerName.Length -eq 0) { 'localhost' } Else { $ComputerName })\$Nonce`$\$Nonce.tmp"
$PatternSOF = "^\*{22}(`r?`n.*)*?`r?`n\*{22}`r?`n(Transcript started,.*?`r?`n)?"
$PatternEOF = "\*{22}(`r?`n.*)*?`r?`n\*{22}(`r?`n)?$"
$OutputText = ''
Write-Debug "Waiting for output…"
Do {
Try {
Start-Sleep -Milliseconds 100
$OutputText = Get-Content -Raw -LiteralPath $ReadPath -ErrorAction Stop
} Catch { $OutputText = '' }
} While ($OutputText -notmatch $PatternEOF)
$OutputText = $OutputText -replace $PatternSOF, ''
$OutputText = $OutputText -replace $PatternEOF, ''
Write-Output $OutputText
}
Finally {
# Cleanup
Write-Debug "Cleaning up output…"
While ($True) {
Try {
Remove-Item -LiteralPath $ReadPath -ErrorAction Stop
Break
} Catch { Start-Sleep -Milliseconds 100 }
}
}
}
Write-Debug "Done."
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment