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

Sudo/command line only elevation? #146

Open
parkovski opened this issue Apr 3, 2018 · 16 comments

Comments

@parkovski
Copy link

commented Apr 3, 2018

I'm not sure this is the right place to ask about this, but I can't think of where the right place would be, so I'm just going to list my thoughts on this.

  • As Windows command line functionality improves to the point that the system can be used remote/headless, this is becoming more of a necessity than an annoyance. Actually, this really comes down to having an official SSH client: SSH connections have to open as Admin, potential security hole.
  • This is not some black magic thing or something that needs a UAC redesign or kernel work. I've experimented a little, and you can create a local service running as the system account which will give you elevated processes from the command line. I didn't pursue this any further though, because I don't have the time to commit to securing and testing the service, but I at least know it's doable.
  • Even if I did want to publish a fully-featured sudo service, many organizations are not going to trust a third-party utility that is that big of a potential security hole, and it's unlikely the official SSH distribution would come configured for it, so now we're back at point one again.
  • There is auto-elevation built into UAC, but only for the Windows publisher. So if this was a system utility, it's not even necessary to run it as a separate service.
  • I think for the vast majority of users, this is entirely unnecessary, and I'd be fine with something that's enabled as a part of dev mode or when installing SSH. Those two cases do need it though.
  • With WSL now we have a weird [restricted token, elevated token] x [regular account, root] scenario going on, where you have to be in the right kind of command prompt to get the correct privileges. If we had Windows sudo, I don't think it'd be too hard to unify it with the WSL sudo command.

Anyways, these are just my thoughts that I wanted to get out there. Happy to hear about any plans/concerns/a better place to have this discussion.

@zadjii-msft

This comment has been minimized.

Copy link
Member

commented Apr 3, 2018

So I know there have been a LOT of discussions about getting a "sudo for Windows" built. I'm not sure that we have any concrete plans in place, but I know it's something a lot of us want. I'm certainly not the person to be asking about this - perhaps @bitcrazed or @yodurr can comment more?

@zadjii-msft zadjii-msft added this to the Backlog milestone Apr 3, 2018

@parkovski

This comment has been minimized.

Copy link
Author

commented Apr 3, 2018

Thanks for the quick response! If I had to guess I'd say this probably would go to either whoever deals with UAC/token management or dev mode, but I don't know of any other Windows team that's as community friendly and easily reachable, so I figured it's worth a shot starting a discussion.

It's really conflicting, because I've seen so many scripts that just pop up the UAC dialog, and I could throw something together for myself that doesn't, but I can only fix the annoyance issue, not the "things being designed around the platform not supporting this" one.

@bitcrazed

This comment has been minimized.

Copy link
Contributor

commented Apr 3, 2018

While we're not able to give you a firm timeline right now, I can echo @zadjii-msft's comment - we are sooooooooo keen for "Add sudo" to bubble up our priority list so we can get to work on it :)

As with anything to do with security, we will be working with the Windows core security & management teams to make sure that we "do it right".

@parkovski

This comment has been minimized.

Copy link
Author

commented Apr 3, 2018

Good to know it's on the list, that's about as much as I can ask for. Thanks!

@bitcrazed

This comment has been minimized.

Copy link
Contributor

commented Apr 5, 2018

@parkovski - If that's all you can ask for, I fear for your imagination :D ;)

@parkovski

This comment has been minimized.

Copy link
Author

commented Apr 5, 2018

Hey now. I can ask for chocolate and berries to be a healthy diet or for politicians to stop pitting people against each other, but realistically all I'm gonna get is command line elevation.

@bitcrazed

This comment has been minimized.

Copy link
Contributor

commented Apr 6, 2018

Chocolate isn't healthy? Pfffft!

@parkovski

This comment has been minimized.

Copy link
Author

commented Apr 6, 2018

Uh oh, my cover's blown :(

@ExE-Boss

This comment has been minimized.

Copy link

commented Aug 29, 2018

This will likely depend on ConPTY or something, as it’s currently impossible to get the STDOUT, STDERR or STDIN of an elevated process from a non‑elevated process :( (I know as I tried to implement a sudo cmdlet for PowerShell).

@Magissia

This comment has been minimized.

Copy link

commented May 13, 2019

We need a full sudo, not just a "run as admin in terminal".

Sudo allows to run arbitrary commands as arbitrary users, following it's configuration.

@vsalvino

This comment has been minimized.

Copy link

commented Jun 10, 2019

Not terminal related, but I have a pretty good PowerShell sudo that has been working well for me in Windows.

Edit your profile:

PS > notepad $PROFILE

Add the following sudo function:

function sudo {
    Start-Process -Verb RunAs -FilePath "pwsh" -ArgumentList (@("-NoExit", "-Command") + $args)
}

Then invoke in your shell. Supports cmdlets, executables, anything that could normally be typed into a PS prompt:

PS > sudo Remove-Item .\test.txt  # Remove a file
PS > sudo Copy-Item .\test.txt C:\  # Copy a file
PS > sudo net start w3svc  # Start IIS

If you want to pass in a variable or expression that will be evaluated at run time, rather than pre-evaluated, wrap it in braces. For example:

PS > $myvar = "a"
PS > sudo echo $myvar  # $myvar is pre-evaluated, so the command reads: sudo echo "a"
PS > sudo { $PSVersionTable }  # with braces, $PSVersionTable is not evaluated until it is run as administrator

Remove "-NoExit" from the sudo function if you'd prefer to have the administrator window close when complete.

@parkovski

This comment has been minimized.

Copy link
Author

commented Jun 10, 2019

@vsalvino Thanks, but this still requires UAC which is a no-go for some use cases (ssh). Unfortunately the only real solution to this is going to require a pretty decent amount of work.

@fcharlie

This comment has been minimized.

Copy link
Contributor

commented Jul 29, 2019

I don't guarantee that my opinion is absolutely correct. If there is something wrong with this, please remind me.

Implementing sudo without sharing terminals is very easy in Windows. For example, I have developed Privexec (Run the program with the specified permission level Trust Installer, System, MIC, AppContainer ...) to support the process of starting the administrator rights and can wait.

wsudo -A -w pwsh

But the difficulty is that Windows RunAs does not support inheriting standard output.

So how to implement a sudo program, after creating a new process, enables the privileged process to inherit the existing terminal?

Premise

Regardless of the operating system, the act of escalation is actually a process that requests a high-privileged process to start a low-privileged process.

Linux sudo internal

We look at the file permissions of the sudo program exa -g -h -l --time-style long-iso /usr/bin/sudo (exa is a replacement for ls written in Rust.):

2019-07-31 11-21-46屏幕截图

This means that when we execute sudo,Since the owner of sudo is root. and contains the S_ISUID privilege, sudo can set itself to root, and after verifying that the user credentials are valid, the user-entered command is initiated. The child process just inherits the permissions of sudo.

I executed top and sudo top in the same terminal, and I found that the input and output are consistent:

2019-07-29 09-21-23屏幕截图

Windows sudo design and ideas

Use UAC to grant administrator privileges from standard users in Windows. The process is as follows ShellExecute Internals::

  1. AppInfo goes and talks to the Local Security Authority to get the elevated token of the logged in user of Session 1.
  2. AppInfo loads up a STARTUPINFOEX structure (new to Vista), and calls the brand new Vista API InitializeProcThreadAttributeList() with room for one attribute.
  3. OpenProcess() is called to get a handle to the process that initiated the RPC call.
  4. UpdateProcThreadAttribute() is called with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, and uses the handle retrieved in step 3.
  5. CreateProcessAsUser() is called with EXTENDED_STARTUPINFO_PRESENT and the results of steps 1 and 4.
  6. DeleteProcThreadAttributeList() is called.
  7. Results are gathered, and handles are cleaned up.

Therefore, there are several ideas for implementing sudo that can inherit input and output on Windows. The first one is Windows improvement. When the AppInfo service creates a process, if the process's subsystem is CUI and needs to inherit input and output, the process will be called. The console lets the child process inherit, but here I am not sure if the console can inherit from different permission levels.

Another solution is the C/S architecture, which is divided into sudo and sudo-srv. sudo is the client that privates the privilege. Sudo-srv acts as a service running with System privileges. When the user runs sudo, sudo interacts with the user. The command line, the credential is sent to sudo-srv through the encryption code, sudo-srv uses LogonUserW to verify that the user credentials are valid, When the starting process subsystem is CUI, sudo-srv uses the conpty+CreateProcessAsUserW method to start the child process, and then the conpty The output interacts with sudo and sudo is output to the terminal. The benefits of this approach are not required for Windows updates.

In the code below, we need to replace CreateProcessW with CreateProcessAsUserW in sudo-srv to start the admin process:

__declspec(noinline) inline HRESULT CreateConPty(const std::wstring& cmdline,
                                                 const unsigned short w,
                                                 const unsigned short h,
                                                 HANDLE* const hInput,
                                                 HANDLE* const hOutput,
                                                 HANDLE* const hSignal,
                                                 PROCESS_INFORMATION* const piPty)
{
    // Create some anon pipes so we can pass handles down and into the console.
    // IMPORTANT NOTE:
    // We're creating the pipe here with un-inheritable handles, then marking
    //      the conhost sides of the pipes as inheritable. We do this because if
    //      the entire pipe is marked as inheritable, when we pass the handles
    //      to CreateProcess, at some point the entire pipe object is copied to
    //      the conhost process, which includes the terminal side of the pipes
    //      (_inPipe and _outPipe). This means that if we die, there's still
    //      outstanding handles to our side of the pipes, and those handles are
    //      in conhost, despite conhost being unable to reference those handles
    //      and close them.
    // CRITICAL: Close our side of the handles. Otherwise you'll get the same
    //      problem if you close conhost, but not us (the terminal).
    HANDLE outPipeConhostSide;
    HANDLE inPipeConhostSide;
    HANDLE signalPipeConhostSide;

    SECURITY_ATTRIBUTES sa;
    sa = { 0 };
    sa.nLength = sizeof(sa);
    sa.bInheritHandle = FALSE;
    sa.lpSecurityDescriptor = nullptr;

    CreatePipe(&inPipeConhostSide, hInput, &sa, 0);
    CreatePipe(hOutput, &outPipeConhostSide, &sa, 0);

    // Mark inheritable for signal handle when creating. It'll have the same value on the other side.
    sa.bInheritHandle = TRUE;
    CreatePipe(&signalPipeConhostSide, hSignal, &sa, 0);

    SetHandleInformation(inPipeConhostSide, HANDLE_FLAG_INHERIT, 1);
    SetHandleInformation(outPipeConhostSide, HANDLE_FLAG_INHERIT, 1);

    std::wstring conhostCmdline = L"conhost.exe";
    conhostCmdline += L" --headless";
    std::wstringstream ss;
    if (w != 0 && h != 0)
    {
        ss << L" --width " << (unsigned long)w;
        ss << L" --height " << (unsigned long)h;
    }

    ss << L" --signal 0x" << std::hex << HandleToUlong(signalPipeConhostSide);
    conhostCmdline += ss.str();
    conhostCmdline += L" -- ";
    conhostCmdline += cmdline;

    STARTUPINFO si = { 0 };
    si.cb = sizeof(STARTUPINFOW);
    si.hStdInput = inPipeConhostSide;
    si.hStdOutput = outPipeConhostSide;
    si.hStdError = outPipeConhostSide;
    si.dwFlags |= STARTF_USESTDHANDLES;

    std::unique_ptr<wchar_t[]> mutableCommandline = std::make_unique<wchar_t[]>(conhostCmdline.length() + 1);
    if (mutableCommandline == nullptr)
    {
        return E_OUTOFMEMORY;
    }
    HRESULT hr = StringCchCopy(mutableCommandline.get(), conhostCmdline.length() + 1, conhostCmdline.c_str());
    if (!SUCCEEDED(hr))
    {
        return hr;
    }

    bool fSuccess = !!CreateProcessW(
        nullptr,
        mutableCommandline.get(),
        nullptr, // lpProcessAttributes
        nullptr, // lpThreadAttributes
        true, // bInheritHandles
        0, // dwCreationFlags
        nullptr, // lpEnvironment
        nullptr, // lpCurrentDirectory
        &si, // lpStartupInfo
        piPty // lpProcessInformation
    );

    CloseHandle(inPipeConhostSide);
    CloseHandle(outPipeConhostSide);
    CloseHandle(signalPipeConhostSide);

    return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}

Simple flow chart:

2019-07-29 10-29-48屏幕截图

There is also a simpler solution, that is, using an intermediate program, ShellExecute starts an intermediate program, and then the intermediate program will reattach to the console of the parent process according to the command line parameters, thus implementing a shared console like sudo. Refer to @lukesampson's psutils/sudo here. But this solution still requires UI interaction.

Now wsudo implements this mechanism in the dev branch (Windows Terminal Dev test OK). see: M2Team/Privexec@ead21177d6a83368f82aeda40bf3adf597ecc173
wsudo-tie source

wsudo create a administrator wsudo. administrator wsudo create TrustedInstaller process:

屏幕截图(9)

End

In Windows, similar to sudo is the TrustedInstaller service, and the TrustedInstaller service runs the TrustedInstaller.

Reference

Create AppContainer Process

Create NoElevated, MIC Process

Create System, TrustedInstaller Process

@bitcrazed

This comment has been minimized.

Copy link
Contributor

commented Jul 30, 2019

Thanks for sharing your detailed thoughts @fcharlie. It turns-out that when considering a sudo-like feature as part of an operating system like Windows, that there are a lot of edge cases, and threat model issues to sift through and resolve. Providing a sudo-like capability requires a great deal of care to make sure that one doesn't, for example, accidentally introduce a weakness or vulnerability into the system, while also ensuring that the solution provides sufficient utility and behaves as users would expect.

Do bear with us - we will be taking a more detailed look into this feature request in the future, once we have the resources & intent necessary to perform the detailed research and analysis.

@bgshacklett

This comment has been minimized.

Copy link

commented Aug 3, 2019

Is there anything that the community can do to work with you to help get those resources assigned? Perhaps there's a uservoice issue which can be up-voted, etc.?

@MouriNaruto

This comment has been minimized.

Copy link

commented Aug 8, 2019

I will create a new POC (Prove Of Concept) project and push to GitHub recently. (With elevation and drop right support.)

I think sudo-like implementations in windows should be different from implementations in POSIX. So I think it is OK for this project to add some features which NSudo (https://github.com/M2Team/NSudo, the project created by myself) have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
10 participants
You can’t perform that action at this time.