Windows : How to run a program with a different user (equivalent of linux 'sudo -u user cmd')?

Context

In a setup program for Windows, which is executed with administrator privilege, I want to configure a service, which will be run with a dedicated user (let's call this user serviceUser).

For the purposes of the setup, I want to run another program (one that checks that the configuration is ok) as serviceUser.

I'm more of a linux user, and I am trying to run the equivalent of :

sudo -u serviceUser myProgram -check

What I tried

From what I understood, the goto utility to run a program as another user on Windows seems to be RunAs.

From a shell, I was able to start a command using RunAs, e.g : RunAs /user:DOMAIN\serviceUser "cmd.exe /C dir"

But I noticed some issues for my use case :

  • the command is started in another shell, and asynchronously (e.g : RunAs ... returns right away, while the command is executed in the background).
    In my use case : I intended to check the exit code of my program, and to read its output

  • RunAs first starts by asking the password for serviceUser, even if I start RunAs with administrator privileges

  • I haven't understood if RunAs is always available, or if I need to activate some service (the "Secondary Logon" service ?) on the target Windows system

Questions

  • Is there a way to wait for the program executed by RunAs to exit, and to capture its output ?
  • Is there a way to provide the credentials for the user in a non interactive way, or to bluntly say "I'm an admin, don't ask for the password" ?
  • Do I have to run some checks on the host system, to see if RunAs is available ?

or alternatively : when running from an administrator shell, is there a more direct way to execute a program as a non administrator user ?

2 Answers

RunAs has to start another shell, because it needs to switch the attached permissions token of the shell, which otherwise is not possible with Windows API.

Answers:

Is there a way to wait for the program executed by RunAs to exit, and to capture its output ?

See this answerfor how to wait for a program to terminate.

To capture its output it needs to be directed to a file, or if you were willing to study PowerShell, use theTee-Object command(abbreviable to tee).

Is there a way to provide the credentials for the user in a non interactive way, or to bluntly say "I'm an admin, don't ask for the password" ?

You always need to specify the password. However, to avoid it being asked interactively, you may use instead of RunAs the freePsExecwhich also allows specifying the password on the command line (not a secure practice).

Do I have to run some checks on the host system, to see if RunAs is available ?

RunAs is part of Windows so is always available.

I'm going to answer the question of the original subject and partially answer the 2nd and 4th questions because I am unusually qualified to do so. (I'm probably one of a handful of software developers on the planet who can.)

These three questions are roughly related to each other:

Windows: How to run a program with a different user (equivalent of linux 'sudo -u user cmd')?

Is there a way to provide the credentials for the user in a non interactive way, or to bluntly say "I'm an admin, don't ask for the password"?

When running from an administrator shell, is there a more direct way to execute a program as a non administrator user?

And the answer you are basically looking for is this tool I wrote:

I'll get to the tool itself in a bit.

I highly recommend watching the video, which will give you a crash course/primer on how Windows functions under the hood to implement system security. But I'll do my best here to summarize the contents of that video.

The problem is that Windows and Linux are vastly different under the hood when it comes to users and user accounts. The fundamental security object in Windows is the "security token" which is a superstructure of a hodgepodge of other structures. To start a process in Windows, you have to have a security token. By default, the current process' or, in some cases, thread token is copied. A security token is made up of different components like the user Security IDentifier or SID, owner SID, primary group SID, a list of group SIDs the token is a part of, the privileges assigned to the token, the default DACL, elevation status, and a host of miscellaneous stuff (some of which is VERY undocumented). The problem is that saying you want to run as a different user is a lot more complicated than it sounds. Even within your current user as Administrator, you actually hold two security tokens: Your non-elevated token and your "linked" elevated token. And Windows has no straightforward mechanism by which to switch between tokens (UAC is a system service that aids in starting processes with the elevated token). And once a process starts running (this is key later!), the process token cannot be changed. In Linux, if you are root, you can simply say, "I am now effectively this UID" and Linux will verify root and switch the user context because the UID + GID is the superstructure of security in the vast majority of Linux deployments and those are simple integers, making switching contexts relatively easy. SIDs in Windows, on the other hand, are binary objects of variable size, there are many of them for every single token, and Token Privileges are the proverbial monkey wrench. There is no comparison/equivalence to be made. Windows security objects are fundamentally more complex (and I'd argue not in a good way).

Barring all that, let's suppose you want to go ahead and make your own security tokens. There is only one official authority in Windows that can make security tokens, which is the Local Security Authority Subsystem Service (LSASS) and every exposed interface into it requires credentials to create a token (i.e. a password, hardware token, or biometric input). That service ultimately calls the undocumented NtCreateToken API to create tokens. The NtCreateToken API requires the SeCreateTokenPrivilege privilege to call that API in the first place. Very few processes have SeCreateTokenPrivilege. And obtaining a privilege you don't already have on an existing process token is somewhere between "complicated" and "impossible." At this point, you have entered a rabbit hole from which few have returned unscathed. Put succinctly, there is no way to, as a regular user or even an elevated Administrator, directly start a process as another user without credentials. At least not officially.

However, you might note that the CreateProcess tool claims that it can start processes as other users without credentials. But how it works is most definitely NOT direct, requires insanely long command-lines, and there are some other pretty big caveats. The process to starting a child process as another user, especially if you are starting it in a non-elevated environment, is long and convoluted on the technical front. In short, the tool works by starting a new child process but suspended. Then in a temporary NT System Service (I won't even go into how that gets set up), NtSetInformationProcess (another very undocumented Windows kernel API) is called to assign the desired token to the suspended child process. Only before a process starts running but after its creation can a token be assigned (create suspended process, assign token, start primary thread).

You can pass /createtoken with a set of serialized options to create a brand new token from scratch (i.e. without the user's password) but the started process might not load due to this Windows loader issue with imm32.dll. So while Command Prompt (cmd.exe) itself starts without issue as another user (can be verified with SysInternals Process Explorer), starting other processes as that user is problematic and may or may not work due to unexpected errors. Something is wrong with the token or the process context that imm32.dll doesn't like. Running as NT AUTHORITY\SYSTEM or copying an existing token from another running process doesn't seem to have issues though. That said, /createtoken does successfully create tokens for most users without requiring that user's credentials and that token can be used to start SOME processes. So in a way this maybe answers your second question. Sort of. More research is required.

For the last question, technically a similar mechanism that the CreateProcess tool uses for the /elevatedtoken option could also possibly be used to select and assign the non-elevated token to the target process. The tool just doesn't have such simplified support built into it. I can see a use-case where you are running as an elevated Administrator but want to dip back down to a non-elevated state for a short bit and then return to your elevated state later without having to go through a UAC dialog. So maybe a /regulartoken option could be added to the tool in the future to accommodate that.

Keep in mind that all of this is highly experimental because it calls undocumented Windows kernel APIs. And could break at any time as a result (e.g. a Windows Update). The tool has already been flagged as "malware" at least once by Windows Defender probably because of all the unusual APIs that it calls. (Windows Defender is not what I think of as particularly intelligent software.) It's not malware though. Just somewhat unusual. I recommend building it with Visual C++ if there is any concern since the source code is right there for anyone to see/build.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like