Windows Unprivileged - Windows Equivalent of Unix sudo
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 RunAsAdmin with the following configuration and role capability:
RunAsAdmin.pssc
SessionType = 'RestrictedRemoteServer'
RunAsVirtualAccount = $true
RoleDefinitions = @{ 'JEA_RunAsAdmin'= @{ RoleCapabilities = 'RunAsAdmin' }}
Create the appropriate group and add the appropriate users.
RunAsAdmin.psrc
ModulesToImport = 'Microsoft.PowerShell.LocalAccounts','ScheduledTasks'
VisibleProviders='FileSystem'
FunctionDefinitions = @{
Name='sudo';
ScriptBlock={
param (
[int]$sessionid,
[string]$pathImage
)
if ((!$sessionid) -or (!$pathImage)) {
Write-Host "sudo sessionid pathImage"
return
}#if
$pathAllowedImages = "HKLM:\SOFTWARE\AB\sudo\"
$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}
if (!$jobprocesslimit) {$jobprocesslimit = 0}
$pathRunJob = "C:\Program Files\ABTokenTools\RunJob.exe"
if (!(Test-Path $pathRunJob)) {
Write-Host "Cannot find [$pathRunJob]."
return
}#if
$sDescription = "Temporary sudo user"
$sUserName = "SudoAdmin"
$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
$action = New-ScheduledTaskAction -Execute $pathRunJob -Argument "/Image ""$pathImage"" /User $sUserName /Password $sPassword /SessionId $sessionid /JobProcessLimit $jobprocesslimit"
$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
}
Note the sudo function. It allows running all images whose paths are listed in
HKLM:\SOFTWARE\AB\sudo\
which might look like this:
PS C:\Program Files\WindowsPowerShell\Modules\JEA> dir HKLM:\SOFTWARE\AB\sudo\
Hive: HKEY_LOCAL_MACHINE\SOFTWARE\AB\sudo
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 sudo function you have to give it your session id and the path to the image you want to start. You can get your session id from PowerShell:
PS C:\> (Get-Process -Id $pid).SessionId
1
PS C:\>
This means you can also start this for a user in a different session if you are so inclined.
If you have registered the JEA configuration and imported it, you can run the sudo function with the session id and the path to the program:
PS C:\> Import-PSSession (New-PSSession -ConfigurationName RunAsAdmin) -AllowClobber
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 1.0 tmp_eueyzgkc.dyt {Clear-Host, Exit-PSSession, Get-Command, Measure-Object...}
PS C:\> sudo 1 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 sudo command.
If you try to start a program you are not allowed to start, sudo will return an error 5:
PS C:\> sudo 1 C:\windows\system32\cmd.exe
5
PS C:\>
If you start cmd.exe using sudo, 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 SudoAdmin
PS C:\>
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.
RunAsAdmin.pssc
RunAsAdmin.psrc
Next: Service Control Manager