Powershell: Support sudo <PowerShell cmdlet> 2

Created on 14 Dec 2019  ·  22Comments  ·  Source: PowerShell/PowerShell

Summary of the new feature/enhancement

Summarize #3232 to get working _in the same way on all platforms_:

sudo Remove-Item foo.txt where foo.txt is owned by root

See also

We have two common scenario for sudo - run a native command and run a script.

  1. Run a native command
    This can be easily implemented like:
Start-Process <nativecommand> -ArgumentList … -Credential $userCred -Wait

Side effect on Windows: new window will be opened because:

  • Windows does not allow elevate current process
  • Windows does not allow attach elevated process to current console
    .Net Core (and I guess Windows API too) doesn't allow to run new process attached to the same console.
    So we can not get elevated Start-Process pwsh -NoNewWindow -Credential $cred
  1. Run a script
    2.1. We could implement this as for native command:
Start-Process pwsh -ArgumentList "-c","<scriptblockstring>" -Credential $userCred -Wait

2.2. We could implement this in most power way using PowerShell remoting:

Invoke-Command -ComputerName LocalHost -ScriptBlock <scriptblock> -Credential $userCred -ArgumentList ...

Current implementation proposal is 2.2.

Alternative proposal - start another pwsh process

Proposed name

  • __pssudo__ to avoid a conflict with Unix native sudo.
  • Invoke-AsUser

Proposed UX behavior

  • interactively ask for credentials at first run
  • store credentials for a time and doesn't ask in follow runs

It is still not clear:

  • PowerShell remoting to localhost by default has disabled elevated accounts in WinRM loopback - how could we address this in the context sudo? Even if PowerShell remoting does not allow us to do sudo by default, we could implement it turned off by default or allow only for an interactive session.
  • PS sudo implementation should works over any transport - WinRM and SSH. But again there is a question about elevation in loopback - regardless of a protocol, security should remain at the same level, which implies that SSH loopback should behave the same as for WinRM taking in account how Windows internals work but currently SSH loopback works (with manual connection).
Issue-Enhancement WG-Engine

Most helpful comment

We definitely want pipeline support and also between cmdlets and native tools to work as expected. We could add a -Elevated type switch to Invoke-Command and have a wrapper pssudo function make use of that since PowerShell aliases are a bit limited today rather than having a separate cmdlet just for sudo-type operation. We should also keep in mind that this should work on non-Windows as well where we would have to sudo the pwsh process and handle redirection. The limitation of deserialized objects in the pipeline vs live objects is an acceptable limitation in my mind.

All 22 comments

@SteveL-MSFT @joeyaiello My current conclusion is that we have only single way to implement cross-planform sudo - using PowerShell remoting like @anmenaga implemented Windows Compatibility.
We gathered a lot of information in #3232 and now MSFT team could have been brainstorming to make a conclusion.

This has been discussed on and off.

There are a few thoughts that I haven't seen in the other thread. Since that one is focused more on the UX, I'll do the dump on implementation here.

Basically because PowerShell runs everything except for native commands in-process, this is very tricky.

There are essentially two options, both with significant drawbacks.

  1. sudo becomes an alias to a remote/subprocess invocation in PowerShell to an Administrator/root process, where it runs the given command and gives back the result. This works fine for native commands, but for cmdlets only gives back deserialised objects. It also means that to make things performant we basically need to set up a persistent remote root session to host those commands. The main drawback is then the deserialisation (sudo cmdlets work subtly differently from non-sudo), with the two other drawbacks of having to override sudo (makes sense, but not ideal) and having to manage the security boundary and lifetime of the administrator remote host.

  2. PowerShell itself becomes a setuid command and sudo works in process as a builtin (again we must override the util), becoming an administrator process when a sudo command is run. The major drawbacks here are (1) being a setuid command makes PowerShell quite dangerous and requires authentication to use in whatever scenario and (2) upgrading the entire process to root does not cater to the abstraction of runspaces or indeed any threading at all.

Given the two options, I really don't like either of them, but certainly think (2) is the more flawed and should be excluded at the outset.

This is just a braindump off the top of my head, so feel free to share, expand on or poke holes in implementation ideas here.

@rjmholt If you continue your thinking you will finish on a kind (JEA?) of "PowerShell remoting" as implementation like me.

  1. We should have the same UX on all platforms.
  2. We can not use Process.Start() (or Start-Process) even for native commands because on Windows new windows will be opened.
  3. We should run script blocks that assumes having PowerShell context
  4. We can not use single elevated process because we need to have per user/per session/per runspace/per scope powershell context

Yeah I think we agree that the best path would be an implementation using a kind of “remoting as security boundary” architecture.

The part I don’t like about it is the lifetime of the elevated session. If we spin a new one up every time, it could be terribly slow. If we keep one open indefinitely, it leaves open a large attack surface.

Anyway, @jameswtruher @paulhigin @stevel-msft might have thoughts here

If we spin a new one up every time, it could be terribly slow.

Yes, it is the same as "ForEach -Parallel" - it is slow for inappropriate scenarios but works great for target scenarios.
I guess we get acceptable response time for interactive sessions (pwsh startup time is ~400 ms, with -NonInteractive it is less). For background, non-interactive scenarios I believe that using of pssudo is not right thing.

Sounds like we want sudo to be an entry point into a wider effort to allow offloading to and management of an elevated PowerShell worker process

Windows does not allow attach elevated process to current console
.Net Core (and I guess Windows API too) doesn't allow to run new process attached to the same console.
So we can not get elevated Start-Process pwsh -NoNewWindow -Credential $cred

Sudo implementations in the wild resolve this by spawing a new hidden process, then using the AttachConsole Api to attach the elevated process to the non-elevated caller. Check Luke Sampson's sudo.ps1 implementation. This doesnt work if you want to redirect StdIn/StdOut. So on my own implementation gsudo (windows-only) I did a workaround for this scenario capturing StdIn/Out/Err and streaming via RPC to the non-elevated instance (when redirected, as in 'gsudo dir > somefile'.

Looks like @gerardog is willing to contribute his code to a cmdlet in PowerShell (for Windows). We should discuss the cmdlet design first. I'm ok with a pssudo alias, but the cmdlet still needs a verb-noun and adding a new parameterset to Start-Process might not be the way to go.

I'd go for something like Invoke-ElevatedCommand perhaps?

Is there any thought of an additional common parameter to elevate a cmdlet instead of a specific cmdlet that does it?

The hidden elevated helper process should allow getting a close as possible to 'same UX on all platforms', shouldn't it?

We definitely want pipeline support and also between cmdlets and native tools to work as expected. We could add a -Elevated type switch to Invoke-Command and have a wrapper pssudo function make use of that since PowerShell aliases are a bit limited today rather than having a separate cmdlet just for sudo-type operation. We should also keep in mind that this should work on non-Windows as well where we would have to sudo the pwsh process and handle redirection. The limitation of deserialized objects in the pipeline vs live objects is an acceptable limitation in my mind.

The limitation of deserialized objects in the pipeline vs live objects is an acceptable limitation in my mind.

sudo scenario assumes executing "a whole job". So I guess the serialization may not be a problem because we must send/receive only data but not elevated methods .

Thinking about security I believe PSSudo implemented by remoting should allow only to connect to __local__ endpoint. (In the case we could use binary serialization for _data_)

This looks very cool to me, but I am unclear as to what the design will be. Will it be based on remoting or @gerardog spawning a new process? Should we have an RFC to discuss? I would prefer not using remoting since it adds overhead. The serialization system can be used outside of remoting.

Also I don't like the idea of using Invoke-Command since it has so many parameter sets already. I would prefer a new cmdlet like 'Invoke-AsElevated'.

I am more concerned than @SteveL-MSFT about returning deserialized PSObjects, and I think it may trip up a lot of users, e.g.,

psudo Get-Process "pwsh" | Foreach-Object { $_.Kill() }

I agree that this is very nice, but I'm trying to wrap my head around a cmdlet implementation. Would there be an issue with a cmdlet using dotnet core (rather than 4.7.1) due to the "can't attach to the console" notes above, or would that just dictate the specific implementation.

Perhaps we could find how implement "process fork" version on both platforms but it causes huge problems. 1. It is double work. 2. It is obviously impossible to get the same behavior on all platforms and as result 3. we will get many negative feedbacks (bug issues).

If we look .Net Core team experience they re-write some native code things on C# get same behavior on all platforms (ex., libcurl history).
I would like to avoid different implementations for each platformю

I am more concerned than @SteveL-MSFT about returning deserialized PSObjects, and I think it may trip up a lot of users, e.g.,
psudo Get-Process "pwsh" | Foreach-Object { $_.Kill() }

pssudo should accept a script block and execute it elevated.

Yes, we need double implementations (unless the double code is moved to .Net Core):

On Unix/Linux

  • using sudo means an interactive password text prompt will appear. (IMHO the preferred, standard Unix way). The user can still opt-in to configure sudoers for silent elevation.
  • using setuid on a helper process would be an administrative nightmare: (configuration, controlling who can and can't use the pssudo, requiring root access during installation to set setuid...)

On Windows:

  • Correct me if I am wrong but elevation with user and password (no UAC popup) is not possible (shouldn't be, right, otherwise would be a UAC Bypass?), unless some change is done on WinRM/loopback, and I believe that is undesirable. (I would like to hear a word from Microsoft/PowerShell team about this). And then: Is it ok/desirable to just ask for user password on a console?
  • A simple Start-Process -Verb RunAs to launch the helper process will show one UAC pop-up. Given that Remote PS Sessions already can run as admin and (i think) we are targeting local system interactive sessions only, this looks acceptable to me.

That being said, starting the elevated session will be interactive (either text or UAC), so I wonder how people will be using elevation on scripts and handle the 'interactive step'. Probably providing an easy way to fail fast if the user refuses to elevate would be a good idea:

Start-PSElevatedSession -NonInteractive --> Test if the elevated session is already available. Provides a way to throw 'This script requires admin privileges'.

I think Microsoft should update us here on a list of what is acceptable or not to do from a security compliance perspective. That is required to define the user experience specs first. The technical challenges should be addressed later (e.g. if we hypothetically can't launch an elevated process in the current console we can do a workaround to attach the non-elevated into the elevated one). This team can probably get help from other MS teams to find new ways to overcome those limitations.

Correct me if I am wrong but elevation with user and password (no UAC popup) is not possible (shouldn't be, right, otherwise would be a UAC Bypass?), unless some change is done on WinRM/loopback, and I believe that is undesirable. (I would like to hear a word from Microsoft/PowerShell team about this). And then: Is it ok/desirable to just ask for user password on a console?

I want to point you towards this:
https://github.com/PowerShell/PowerShell/issues/3232#issuecomment-565839733
As well as: https://github.com/PowerShell/Win32-OpenSSH/issues/1308#issuecomment-448430464

KB4480116 introduced this limitation only for powershell, 3rd party libraries are not affected, therefore I'd say we should just undo that KB.

I spent some time reading through the various links. This is great investigatory work. AFAIK there is no way to bypass UAC other than disabling it system wide, so for Windows this will have to be part of the experience.

I don't immediately see a security issue with passing an elevated console through the un-elevated console, when it is the same user only elevated. Only admin users can elevate. I don't know about supporting a RunAs option, where alternate credentials would be passed in, and could be a security concern. But in any case a full security review would have to be done on the final design.

At this point, I think an RFC document should be created that outlines the feature, user experience and different design approaches.

Only admin users can elevate. I don't know about supporting a RunAs option, where alternate credentials would be passed in, and could be a security concern.

Worth noting that UAC can prompt for credentials depending on how UAC: Behavior of the elevation prompt for standard users is configured. It's pretty common to be configured as something other than "Automatically deny elevation requests".

I don't see a problem by attaching an elevated command prompt to an non elevated one in general. Also the UAC is not a security feature. There are multiple ways to bypass it in a network. Even without credential delegation.

  1. Do powershell remoting from A to B.
  2. Create a scheduled task at B (you're full admin at B, even though you're invoker at A) that runs with Administrative rights and that connects back to A and performs your action...
  3. Wait for that scheduled task to be executed.

Or simpler using a non standard winrm client to connect via loopback to it, as mentioned in one of the links I attached earlier.

Also another thing to consider: If there are two persons, one with a limited user account and the other with an administrative one, why should the 2nd person enter his credentials into a non trusted session of the first one? This is the only case where detaching the terminal really would change anything, but the 1st user could as well just intercept the keyboard using a hardware accessory.

_of topic: He has physical access and regarding Microsoft security concepts it is game over, he could even get System rights by just applying windows updates, even if bitlocker disk encryption is on, yes that vulnerability is publicly known and Microsoft refused to patch it..._

back to topic what are possible threads:
For the scenario where the user is compromised using e.g. (what is currently popular?) lets say Emoted, but it was not able to get system rights yet, what could it do? Right, it could connect to the command prompt running with the same user credentials in the same session. So if we attach the administrative one it could hijack the connection. But with that permissions if we spawn a new console/window it would just intercept the creation of that process and append some parameters to it so that the administrator is tricked into acknowledging it...

Now see how unix systems handle these threads:
Processes with user rights cannot initiate communication with the sudo process (except for stdin) which is only attached to the terminal. Therefore the administrator has to check before he grants access using his credentials that:

  1. The sudo binary is not overwritten (or use full path)
  2. The Terminal, or more specific the terminal emulator software is trustworthy.
  3. The command that he wants to elevate is trustworthy.

What was uac designed for regarding enterprises?
From what I've read it was designed to help admins to "not shoot them selves into the food that easily", e.g. "del /S /Q C:\" in the search bar... Basically it provides a kind of "-WhatIf".
And regarding private and/or standalone computers?
It just inadvertently helps to obey good security habits of two separate user accounts for non administrative and administrative tasks. For the advantage of having separate contexts at all, because most people just have used the administrative account for everything including browsing the internet and so on.

I'd be totally fine with the attach to unprivileged process solution, as it provides the same guarantee uac currently does, but also for comandline applications. Also it is a feature that could be made to be disableable, like uac.

But anyway, we do not need to start from scratch for the security part, there is already something similar that we can hook onto its called UIAccess Applications: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-account-control-allow-uiaccess-applications-to-prompt-for-elevation-without-using-the-secure-desktop

Best practices and more for the policy setting, User Account Control Allow UIAccess applications to prompt for elevation without using the secure desktop.

I don't see a problem by attaching an elevated command prompt to an non elevated one in general. Also the UAC is not a security feature. There are multiple ways to bypass it in a network. Even without credential delegation.

  1. Do powershell remoting from A to B.
  2. Create a scheduled task at B (you're full admin at B, even though you're invoker at A) that runs with Administrative rights and that connects back to A and performs your action...
  3. Wait for that scheduled task to be executed.

Or simpler using a non standard winrm client to connect via loopback to it, as mentioned in one of the links I attached earlier.

Also another thing to consider: If there are two persons, one with a limited user account and the other with an administrative one, why should the 2nd person enter his credentials into a non trusted session of the first one? This is the only case where detaching the terminal really would change anything, but the 1st user could as well just intercept the keyboard using a hardware accessory.

_of topic: He has physical access and regarding Microsoft security concepts it is game over, he could even get System rights by just applying windows updates, even if bitlocker disk encryption is on, yes that vulnerability is publicly known and Microsoft refused to patch it..._

back to topic what are possible threads:
For the scenario where the user is compromised using e.g. (what is currently popular?) lets say Emoted, but it was not able to get system rights yet, what could it do? Right, it could connect to the command prompt running with the same user credentials in the same session. So if we attach the administrative one it could hijack the connection. _But_ with that permissions if we spawn a new console/window it would just intercept the creation of that process and append some parameters to it so that the administrator is tricked into acknowledging it...

Now see how unix systems handle these threads:
Processes with user rights cannot initiate communication with the sudo process (except for stdin) which is only attached to the terminal. Therefore the administrator has to check _before_ he grants access using his credentials that:

  1. The sudo binary is not overwritten (or use full path)
  2. The Terminal, or more specific the terminal emulator software is trustworthy.
  3. The command that he wants to elevate is trustworthy.

What was uac designed for regarding enterprises?
From what I've read it was designed to help admins to "not shoot them selves into the food that easily", e.g. "del /S /Q C:" in the search bar... Basically it provides a kind of "-WhatIf".
And regarding private and/or standalone computers?
It just inadvertently helps to obey good security habits of two separate user accounts for non administrative and administrative tasks. For the advantage of having separate contexts at all, because most people just have used the administrative account for everything including browsing the internet and so on.

I'd be totally fine with the attach to unprivileged process solution, as it provides the same guarantee uac currently does, but also for comandline applications. Also it is a feature that could be made to be disableable, like uac.

But anyway, we do not need to start from scratch for the security part, there is already something similar that we can hook onto its called _UIAccess_ _Applications_: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-account-control-allow-uiaccess-applications-to-prompt-for-elevation-without-using-the-secure-desktop

User Account Control Allow UIAccess applications to prompt for elevation without using the secure desktop (Windows 10) - Windows securityBest practices and more for the policy setting, User Account Control Allow UIAccess applications to prompt for elevation without using the secure desktop.

So, how does this enable us to elevate a running powershell session?

Thought. Is their a command we can run (such as "Powershell /elevated /no-new-window) that would allow us to do the *nix command su root to start an elevated sub-session?

Best practices and more for the policy setting, User Account Control Allow UIAccess applications to prompt for elevation without using the secure desktop.
Was this page helpful?
0 / 5 - 0 ratings