Run Commands As NT AUTHORITY\SYSTEM User without PsExec
So you're an IT Administrator and you need to run something as the SYSTEM user. The go-to simplest option is to use PsExec with the -s flag. You go to do this and you quickly find out your security team blocked PsExec because "bad guys use it". It's an unfortunate situation because PsExec is an excellent tool for troubleshooting deployments and things like Assigned Access kiosk configurations.
That's the exact situation I found myself in, which led me to figure out another way to run commands and scripts as the SYSTEM user. The trick here is, scheduled tasks can easily be set to run as the SYSTEM user. So what do we do? Simply make a script that creates a scheduled task to run the desired commands.
At the bottom of this page, you can find the script I use. Simply save it as a .ps1 and here is an example of using it:
Example usage:
$scriptBlock = {
# Put your commands here
Write-Host "Hello world!"
Write-Host " - from NT AUTHORITY\SYSTEM user"
}
.\Run-AsSystem.ps1 -ScriptBlock $scriptBlock
Here is the actual script:
param (
[Parameter(Mandatory)]
[ScriptBlock]$ScriptBlock,
[string]$LogFile = "C:\Logs\RunAsSystem.log"
)
mkdir C:\Logs -Force
$TaskName = "RunAsSystem_$([guid]::NewGuid())"
# Encode scriptblock safely
$ScriptText = $ScriptBlock.ToString()
$InnerScript = @"
`$ErrorActionPreference = 'Stop'
`$log = '$LogFile'
'===== SYSTEM POWERSHELL START =====' | Out-File `$log -Append
'Time: ' + (Get-Date) | Out-File `$log -Append
'User: ' + (whoami) | Out-File `$log -Append
'' | Out-File `$log -Append
try {
'--- OUTPUT ---' | Out-File `$log -Append
`$sb = [ScriptBlock]::Create(@'
$ScriptText
'@)
`$result = & `$sb
`$result | Out-File `$log -Append
`$exitCode = 0
}
catch {
'--- ERROR ---' | Out-File `$log -Append
`$_ | Format-List * -Force | Out-File `$log -Append
`$exitCode = 1
}
'' | Out-File `$log -Append
'ExitCode: ' + `$exitCode | Out-File `$log -Append
'===== SYSTEM POWERSHELL END =====' | Out-File `$log -Append
exit `$exitCode
"@
$Encoded = [Convert]::ToBase64String(
[Text.Encoding]::Unicode.GetBytes($InnerScript)
)
$Action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -EncodedCommand $Encoded"
$Principal = New-ScheduledTaskPrincipal `
-UserId "SYSTEM" `
-LogonType ServiceAccount `
-RunLevel Highest
$Settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Minutes 10)
$TaskDescription = "We have to run as SYSTEM this way because IT Security likes to make things difficult."
Register-ScheduledTask `
-TaskName $TaskName `
-Action $Action `
-Principal $Principal `
-Settings $Settings `
-Description $TaskDescription
-Force | Out-Null
Start-ScheduledTask -TaskName $TaskName
# Wait for completion
do {
Start-Sleep -Seconds 1
$State = (Get-ScheduledTask -TaskName $TaskName).State
} while ($State -eq "Running")
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
Write-Host "SYSTEM execution complete"
Write-Host "Log file: $LogFile"
Get-Content $LogFile
Once you save it, you'll make