Windows Unprivileged - Scheduled Task to Start Privileged Application
Updated 2026-01-09
Note that absolutely none of this is authoritative or directly based on relevant documentation. It’s mostly what I found and figured out and guessed and (in some cases) made up. Some of it may be wrong or dangerous or lead to disaster or confusion. I am not taking responsibility here for anything. Read and act on it at your own peril!
Also note that this implementation of sudo has absolutely nothing to do with Windows 11’s alleged “sudo” command which does not do anything like the Unix sudo.
Using JEA you can run any non-interactive command line tool with administrator privileges.
But what if you need to run an interactive or even a GUI program as an administrator?
This is really not recommended but it can be done.
And this is where it gets complicated.
Windows does not have a setuid bit which causes images to be started in the security context of their owners rather than their callers. And neither can Windows simply log on an account without credentials just like that.
This means that if we want a program to run using another user’s credentials (in this case an administrator’s credentials) somebody has to start it (and know those credentials).
Luckily the CreateProcessAsUser() API can not only start a process as another user but also in any available user’s session. For this SeTcbPrivilege is needed which only LocalSystem should have. So the goal is to have LocalSystem start a program as an administrator in the session of the calling user.
This can be done via scheduled task. And the scheduled task can be created by a JEA function.
For this you need RunJob.exe from ABTokenTools. See introduction for where to get those.
To do this, create a JEA configuration AdminMode with the following configuration and role capability:
RunAsAdmin.pssc
ModulesToImport = 'Microsoft.PowerShell.LocalAccounts','ScheduledTasks'
VisibleProviders='FileSystem'
FunctionDefinitions = @{
Name='Start-ElevatedProcess';
ScriptBlock={
param (
[Parameter(mandatory = $true)]
[string]$Image
)
$sessionid = (Get-Process -IncludeUserName | Where-Object {$_.UserName -eq $PSSenderInfo.ConnectedUser})[0].SI
$pathImage = $Image
$pathAllowedImages = "HKLM:\SOFTWARE\AB\AdminMode\"
$aAllowedImageKey = Get-Childitem $pathAllowedImages
$aAllowedImageKey | ForEach-Object {
$properties = Get-ItemProperty $_.PSPath
if ($properties.image -eq $pathImage) {
$ok = $true
$jobprocesslimit = $properties.jobprocesslimit
}#if
}#foreach
if (!$ok) {return 5}
$pathRunJob = "C:\Program Files\ABTokenTools\RunJob.exe"
if (!(Test-Path $pathRunJob)) {
Write-Host "Cannot find [$pathRunJob]."
return
}#if
$sDescription = "Temporary sudo user"
$sUserName = $PSSenderInfo.ConnectedUser -replace ".*\\" -replace "$","-admin"
$sPassword = [Guid]::NewGuid().Guid
$secPassword = ConvertTo-SecureString $sPassword -AsPlainText -Force
if (Get-LocalUser $sUserName 2> $null) {
Set-LocalUser -Name $sUserName -AccountExpires ((Get-Date).AddHours(1)) -Password $secPassword -Description $sDescription
} else {
New-LocalUser -Name $sUserName -AccountExpires ((Get-Date).AddHours(1)) -Password $secPassword -Description $sDescription
}#if
Add-LocalGroupMember -Group "Administrators" -Member $sUserName 2> $null
$sTaskName = "sudo in session $sessionid"
Unregister-ScheduledTask -TaskName $sTaskName -Confirm:0 2> $null
if ($jobprocesslimit) {
$action = New-ScheduledTaskAction -Execute $pathRunJob -Argument "/Image ""$pathImage"" /User $sUserName /Password $sPassword /SessionId $sessionid /JobProcessLimit $jobprocesslimit"
} else {
$action = New-ScheduledTaskAction -Execute $pathRunJob -Argument "/Image ""$pathImage"" /User $sUserName /Password $sPassword /SessionId $sessionid"
}#if
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM"
$task = New-ScheduledTask -Action $action -Principal $principal -Settings (New-ScheduledTaskSettingsSet)
Register-ScheduledTask -TaskName $sTaskName -InputObject $task -User "SYSTEM"
Start-ScheduledTask -TaskName $sTaskname
}#scriptblock
}You can find this code in my GitHub repo.
Note the Start-ElevatedProcess function. It allows running all images whose paths are listed in
HKLM:\SOFTWARE\AB\AdminMode\
which might look like this:
PS C:\Program Files\WindowsPowerShell\Modules\JEA> dir HKLM:\SOFTWARE\AB\AdminMode\
Hive: HKEY_LOCAL_MACHINE\SOFTWARE\AB\AdminMode
Name Property
---- --------
InetMgr.exe image : C:\windows\system32\inetsrv\InetMgr.exe
jobprocesslimit : 1
PS C:\Program Files\WindowsPowerShell\Modules\JEA>(Note that allowing InetMgr.exe even with a job process limit is not a good idea. Don’t do this in real life!)
Register the JEA configuration as usual and stir patiently until it is warm enough for consumption.
To call the Start-ElevatedProcess function you have to give it the image you want to start.
If you have registered the JEA configuration and imported it, you can run the Start-ElevatedProcess function with the path to the program:
PS C:\> Import-PSSession (New-PSSession -ConfigurationName AdminMode) -AllowClobber
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 1.0 tmp_eueyzgkc.dyt {Clear-Host, Exit-PSSession, Get-Command, Measure-Object...}
PS C:\> Start-ElevatedProcess C:\windows\system32\inetsrv\InetMgr.exe
TaskPath TaskName State
-------- -------- -----
\ sudo in session 1 3
PS C:\>
Note that IIS Manager is not themed. That’s a good way of identifying programs started by the Start-ElevatedProcess command.
If you try to start a program you are not allowed to start, Start-ElevatedProcess will return an error 5:
PS C:\> Start-ElevatedProcess C:\windows\system32\cmd.exe
5
PS C:\>If you do start cmd.exe using Start-ElevatedProcess (after allowing it in the registry), you will notice that it looks like this:

That’s because permissions are missing on your “window station” (your GUI) for console output. You can set them for the account running using RunJob.exe
PS C:\> & 'C:\Program Files\ABTokenTools\RunJob.exe'
#0: RunJob /PId pid /JobProcessLimit limit (appplies quota to running process)
#1: RunJob /Image pathImage [/JobProcessLimit limit] [[/Domain sDomain] /User sUser] [/Password sPassword] [/args ...] (creates a process with various attributes)
#2: RunJob /Image pathImage [/JobProcessLimit limit] [/Domain sDomain] /User sUser [/Password sPassword] /SessionId sessionid [/LoadProfile] [/args ...] (creates a process in another session)
#3: RunJob /Image pathImage [/JobProcessLimit limit] /SessionId sessionid [/args ...] (creates a process in another session as that session's user)
#4: RunJob /Image pathImage /UseRunAs [/args ...] (spawns a process using RunAs verb)
#5: RunJob /WindowStationPermission [/Domain sDomain] /User sUser (allows user access to session window station, use before #2)
PS C:\> & 'C:\Program Files\ABTokenTools\RunJob.exe' /WindowStationPermission /User benoit-admin
PS C:\>This assumes your original user name is “benoit”. The sudo user name will be “benoit-admin”
Also note that if jobprocesslimit is set to 1 (as it should be for this trick) you cannot start any program from cmd.exe.

You have to set jobprocesslimit to a larger number (or 0 to remove the process quota) to allow child processes.
You can obviously adapt the list of allowed programs and/or the method of allowing programs. I recommend against using my two examples. You probably don’t want a user to have an admin shell (even with the process quota they can still delete random files). And you probably don’t want a user to have complete access to IIS so they could create an application pool that runs under LocalSystem.
But for stubborn GUI tools by vendors that think they own your IT infrastructure, this is a possible solution, if a complicated one.
P.S.: Check out the command line parameters of RunJob.exe. Perhaps you need to add “/LoadProfile” to the RunJob call in the scheduled task.
Next: Service Control Manager