Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support sudo <PowerShell cmdlet> 2 #11343

Closed
iSazonov opened this issue Dec 14, 2019 · 30 comments
Closed

Support sudo <PowerShell cmdlet> 2 #11343

iSazonov opened this issue Dec 14, 2019 · 30 comments
Labels
Issue-Enhancement the issue is more of a feature request than a bug WG-Engine core PowerShell engine, interpreter, and runtime
Projects
Milestone

Comments

@iSazonov
Copy link
Collaborator

iSazonov commented Dec 14, 2019

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).
@iSazonov iSazonov added Issue-Enhancement the issue is more of a feature request than a bug WG-Engine core PowerShell engine, interpreter, and runtime labels Dec 14, 2019
@iSazonov
Copy link
Collaborator Author

@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.

@rjmholt
Copy link
Collaborator

rjmholt commented Dec 15, 2019

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.

@iSazonov
Copy link
Collaborator Author

@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.
  • We can not use Process.Start() (or Start-Process) even for native commands because on Windows new windows will be opened.
  1. We should run script blocks that assumes having PowerShell context
  • We can not use single elevated process because we need to have per user/per session/per runspace/per scope powershell context

@rjmholt
Copy link
Collaborator

rjmholt commented Dec 15, 2019

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

@iSazonov
Copy link
Collaborator Author

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.

@rjmholt
Copy link
Collaborator

rjmholt commented Dec 15, 2019

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

@gerardog
Copy link

gerardog commented Jan 13, 2020

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'.

@SteveL-MSFT
Copy link
Member

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.

@vexx32
Copy link
Collaborator

vexx32 commented Feb 24, 2020

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

@gdoenlen
Copy link

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

@sba923
Copy link
Contributor

sba923 commented Feb 24, 2020

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

@SteveL-MSFT
Copy link
Member

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.

@SteveL-MSFT SteveL-MSFT added this to the 7.1-Consider milestone Feb 27, 2020
@SteveL-MSFT SteveL-MSFT added this to To do in Shell via automation Feb 27, 2020
@iSazonov
Copy link
Collaborator Author

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)

@PaulHigin
Copy link
Contributor

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() }

@JamesWTruher
Copy link
Member

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.

@iSazonov
Copy link
Collaborator Author

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.

@gerardog
Copy link

gerardog commented Feb 28, 2020

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.

@agowa
Copy link

agowa commented Feb 28, 2020

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:
#3232 (comment)
As well as: PowerShell/Win32-OpenSSH#1308 (comment)

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

@PaulHigin
Copy link
Contributor

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.

@SeeminglyScience
Copy link
Collaborator

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".

@agowa
Copy link

agowa commented Mar 5, 2020

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 "-Confirm".
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.

@minecraftchest1
Copy link

minecraftchest1 commented Mar 5, 2020

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 security**Best 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.

@minecraftchest1
Copy link

Reading about UAC elevating the current shell, what about creating a new subshell, elevating that shell, running the specified command, then exiting that shell.

@agowa
Copy link

agowa commented Apr 16, 2021

@minecraftchest1 UAC doesn't allow you to pass input into the new shell. You could only spawn a new window. Or alternatively you would need to go through e.g. IPC to let both processes talk to each other. It's not as simple as "just spawning" another shell.

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

It won't, I was talking about why UAC is NOT a security boundary and related security concerns...

@minecraftchest1
Copy link

minecraftchest1 commented Apr 16, 2021 via email

@agowa
Copy link

agowa commented Apr 17, 2021

@minecraftchest1 The cleanest implementation would be an addition to windows internals it self. It would provide the most standardized way of handling this especially because this is a core functionality that not only PowerShell is looking for.

But that said there are different ways to implement it.

  1. The pipe based one I explained above.
  2. Create a service that either runs with LocalSystem privileges or with an user having SeTcbPrivilege (Act as part of the operating system) and that service than could perform the call to GetTokenInformation/SetTokenInformation with TokenLinkedToken (or even full impersonation) on behalf of the actual application and therefore bypass UAC (the official way) and elevating the already running application/thread (after checking permissions of course).

Edit:
After thinking a bit more about this. It doesn't work quite that easily as windows doesn't allow to change/overwrite the primary token of a process after it was launched (maaybe a kernel driver could do that though). But even that we could work around. But than we'd have two use cases:

  1. Elevating native applications
  2. Elevating PowerShell cmdlets/functions/Win32-Api-Calls/…

For the 1st one we could just use:

  1. Invoker process calls "sudo notepad.exe"
  2. sudo creates a new but suspended process for notepad.exe and invokes an IPC call to the helper process (running with SeTcbPrivileges, SeDebugPrivilege, SeCreateTokenPrivilege and SeAssignPrimaryTokenPrivilege).
  3. The helper process than duplicates the token of the calling process checks its privileges according to predefined rules for elevation (like /etc/sudoers on linux) than either crafts a new one (if another user should be impersonated) or just uses the linked token (if the user is an administrator but the process is currently running as invoker).
  4. The helper process than injects the token into the suspended process using the token acquired in the previous step.
  5. The helper process than resumes the suspended process and returns control back to the caller.
  6. The sudo process now acts as a proxy in between the unprivileged and the privileged process and forwards stdin/stdout/stderr.

For the 2nd one we would have to do a bit more advanced handling:

  1. Modify the PSSession stuff in powershell to be an interface (e.g. so that *-PSSession is transport-agnostic and that anyone can implement new kinds of transportation).
  2. Create a new kind of PSSession (in addition to SSH and WinRM) that will connect two PowerShell processes one being elevated and the other one not.
  3. Call sudo powershell-IPCSession.exe (for details about what sudo does see above) in the background
  4. powershell-IPCSession.exe once started spawns a new powershell session and connects it to the named pipe created by the non privileged process.
  5. The non privileged process now can perform elevated actions within this session via the IPC pipe.

@minecraftchest1
Copy link

I understand. I shared this with the ReactOS project to ask what they could do. I personally don't have the patience to do things like this.

@Akarinnnnn
Copy link

Akarinnnnn commented May 27, 2022

I perfer to open UAC popup on Windows personally, just like the installers.
In my envisage, pssudo have following design:

  • Start an evaluated pwsh process by sudo pwsh ... or RunAs equalvent, and specify IPC channel, lifetime(if we have subsequent command to sudo), communication token(maybe not necessary), etc. on command line parameters. For example sudo pwsh -SudoMode -SudoLifetime 5min -SudoToken 1234567890abcdef -SudoIpcPipe pipename
  • Send variables and command to elevated instance through IPC channel, and receive result.
  • Signal the elevated instance to exit, if subsequnt elevated commands are executed.

@gerardog
Copy link

gerardog commented May 27, 2022

Hi! just wanted to let you know that gsudo ships with an Invoke-gsudo cmdlet to elevate like the pssudo proposal.

It runs a ScriptBlock on an elevated PS instance, so it is a different lexical scope: the ScriptBlock can't access your $variables, but you can use $using:variable to apply it´s serialized value. Pipeline inputs and outputs are automatically serialized and deserialized.

Example usage from the docs.

# Accepts pipeline input.
Get-process SpoolSv | Invoke-gsudo { Stop-Process -Force }

# Variable usage
$folder = "C:\ProtectedFolder"
Invoke-gsudo { Remove-Item $using:folder }

# The result is serialized (PSObject) with properties.
(Invoke-gsudo { Get-ChildItem $using:folder }).LastWriteTime

(The cmdlet is optional, you can still just prepend gsudo to elevate a command string)

Sorry if I am spamming this thread. I thought that mentioning it here once was on-topic. My apologies if it's not.
I created this discussion at gsudo's repo for anyone interested in discussing the cmdlet design, usage, troubleshooting, etc.

Shell automation moved this from To do to Done Apr 23, 2023
@RokeJulianLockhart

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Enhancement the issue is more of a feature request than a bug WG-Engine core PowerShell engine, interpreter, and runtime
Projects
Shell
  
Done
Development

No branches or pull requests