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