CreateProcessAsUser vs ShellExecute - winapi

I need to ShellExecute something as another user, currently I start a helper process with CreateProcessAsUser that calls ShellExecute, but that seems like too much of a hack (Wrong parent process etc.) Is there a better way to do this?
#PabloG: ImpersonateLoggedOnUser does not work:
HANDLE hTok;
VERIFY(LogonUser("otheruser",0,"password",LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hTok));
VERIFY(ImpersonateLoggedOnUser(hTok));
ShellExecute(0,0,"calc.exe",0,0,SW_SHOW);
RevertToSelf();
CloseHandle(hTok);
will just start calc as the logged in user, not "otheruser"
#1800 INFORMATION: CreateProcess/CreateProcessAsUser is not the same as ShellExecute, with UAC on Vista, CreateProcess is useless when you don't have control over what program the user is executing (CreateProcess will return with a error if you give it a exe file with a manifest marked as requireAdmin)
#Brian R. Bondy: I already know this info (And don't get me wrong, its good stuff), but it is off topic (IMHO) I am asking for a ShellExecuteAsUser, not about starting processes as another user, I already know how to do that.

The solution really depends on what your needs are, and can be pretty complex (Thanks fully to Windows Vista). This is probably going to be beyond your need, but this will help others that find this page via search.
If you do not need the process to run with a GUI and you do not require elevation
If the user you want to run as is already logged into a session
If you need to run the process with a GUI, and the user may, or may not be logged in
If you need to run the process with elevation
Regarding 1:
In windows Vista there exists something called session 0 isolation. All services run as session 0 and you are not supposed to have a GUI in session 0. The first logged on user is logged into session 1. In previous versions of windows (pre Vista), the first logged on user was also ran fully in session 0.
You can run several different processes with different usernames in the same session. You can find a good document about session 0 isolation here.
Since we're dealing with option 1), you don't need a GUI. Therefore you can start your process in session 0.
You'll want a call sequence something like this:
LogonUser, ExpandEnvironmentStringsForUser, GetLogonSID, LoadUserProfile, CreateEnvironmentBlock, CreateProcessAsUser.
Example code for this can be found via any search engine, or via Google code search
Regarding 2: If the user you'd like to run the process as is already logged in, you can simply use: WTSEnumerateSessions, and WTSQuerySessionInformation to get the session ID, and then WTSQueryUserToken to get the user token. From there you can just use the user token in the CreateProcessAsUser Win32 API.
This is a great method because you don't even need to login as the user nor know the user's username/password. I believe this is only possible via a service though running as local system account.
You can get the current session via WTSGetActiveConsoleSessionId.
Regarding 3:
You would follow the same steps as #1, but in addition you would use the STARTUPINFO's lpDesktop field. Set this to winsta0\Default. You will also need to try to use the OpenDesktop Win32 API and if this fails you can CreateDesktop. Before using the station and desktop handles you should use SetSecurityInfo on each of them with SE_WINDOW_OBJECT, and GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION.
If the user in question later tries to login, he will actually see the running process.
Regarding 4:
This can be done as well, but it requires you to already be running an elevated process. A service running as local system account does run as elevated. I could also only get it to work by having an authenticode signed process that I wanted to start. The process you want to start also must have a manifest file associated with it with the requestedExecutionLevel level="requireAdministrator"
Other notes:
You can set a token's session via SetTokenInformation and TokenSessionId
You cannot change the session ID of an already running process.
This whole process would be drastically more simple if Vista was not in the equation.

If you need ShellExecute semantics you can feed following:
C:\windwos\system32\cmd.exe /k" start <your_target_to_be_ShellExecuted>"
to CreateProcessAsUser and you are done.

You can wrap the ShellExecute between ImpersonateLoggedOnUser / RevertToSelf
links:
ImpersonateLoggedOnUser: http://msdn.microsoft.com/en-us/library/aa378612(VS.85).aspx
RevertToSelf: http://msdn.microsoft.com/en-us/library/aa379317.aspx
sorry, cannot hyperlink URLs with "()"

Why don't you just do CreateProcessAsUser specifying the process you want to run?
You may also be able to use SHCreateProcessAsUserW.

Related

Run .exe as very restricted user

I am trying to run program.exe in a very restricted mode (Windows). This means program.exe should have access to five .txt files, and no other permissions like staring new process, shutdown, edit other files, etc.
I've spent a month trying to achieve it, but there is still no results. I tried to run it as constrained user: runas /trustlevel:10000 "program.exe", and then added permissions:icacls program.exe /grant *S-1-5-12:F
But it seems such user still have some rights, for instace he can run notepad.exe and explorer.exe, and this is unacceptable in my case. Also I played with CreateProcessAsUser() winapi function, but it lead to same result.
So my question is: how can I run program.exe with this limitations, maybe I should create new user with restrictions? How?
P.S: Basically I want to get primitive sandbox.
Thank you.
So thanks to your help, I've written this code:
hToken = NULL;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
SID_AND_ATTRIBUTES sidsToDisable;
ConvertStringSidToSid(L"S-1-1-0", &sidsToDisable.Sid);
sidsToDisable.Attributes = NULL;
SID_AND_ATTRIBUTES sidsToRestrict;
ConvertStringSidToSid(L"S-1-1-0", &sidsToRestrict.Sid);
sidsToRestrict.Attributes = NULL;
CreateRestrictedToken(hToken,NULL,1,&sidsToDisable,0,NULL,1,&sidsToRestrict,&hToken);
PROCESS_INFORMATION pi;
if (CreateProcessAsUser(hToken, wszPath, NULL, NULL, NULL,TRUE, CREATE_NEW_CONSOLE /*| CREATE_SUSPENDED*/, NULL, NULL, &si, &pi))
{
//...
//ok process created
}
But it does not work. 0xc0000022 errors occur all the time, or 0xc00000142 when using admin SID. When I create hToken by SaferComputeTokenFromLevel() it works fine. What am I doing wrong?
Sandboxing that thorough is believed to be impossible. See also the linked PDF.
If this needs to be robust, your only option is a fully-fledged VM. Of course if the sandboxed application has to be a Windows executable, that has licensing implications.
If it doesn't need to be all that robust, it should be possible in principle to put something together that might be sufficient. You would need administrative privilege to do so, but a system service could launch the sandboxed application on a non-privileged user's behalf.
The hard part, I think, will be creating a suitable token. I think that ideally, you want a token that has a unique logon session SID but nothing else.
Using the LSA API is probably the best approach, since it allows you to synthesize an arbitrary token, but it is complicated and not terribly well documented.
You could experiment with duplicating and/or modifying the token you get from ImpersonateAnonymousToken, but I'm not sure whether it would be possible to include a unique logon session SID.
Combining LogonUser (against a user account created for the purpose) with CreateRestrictedToken should be both straightforward and adequate. This is probably the place to start.
You should also make sure the integrity level for the token is suitably low. I would assume that either S-1-16-0 or S-1-16-1 is the lowest level possible.
You'll want to create a new window station for the sandboxed process. That's why we need the logon session SID, so we can grant the token permissions to the window station. Otherwise, the process won't be able to run.
If the process generates a GUI, you'll have to remote it somehow. Ideally you would modify the process itself so that the GUI part is separate and does not need to be sandboxed. Failing that, it should be possible in principle to copy the bitmap from the sandboxed desktop, and forward mouse clicks and keystrokes back to it. I'm not sure how reliable this would be.
To prevent the sandboxed process from launching child processes, put it in a job object. You can set the active process limit for the job to 1 to prevent child processes from being created. (This isn't really necessary IMO since the child processes would have all the same restrictions as the parent, but it can be done.)
Your last comment suggests that perhaps you don't need even this level of robustness. If so, you could save yourself the trouble of remoting the GUI by running the process in the user's own desktop and window station.
You would still need to generate a suitable token, but you might not need to use a separate user account and logon session; if so, CreateRestrictedToken should be sufficient. You can use the interactive user's own token as the source.
You will still need to lower the token's integrity level to protect against shatter attacks.
You can still use a job object to prevent child processes being launched.
JOBOBJECT_BASIC_UI_RESTRICTIONS can be used to provide some level of protection against attacks on the user interface.
In this scenario, you might not even need admin access. I'm not sure offhand.
This approach is not robust enough for running potentially malicious executables, but if the sandbox is only intended as a backup precaution it may be adequate.

Win32 Prevent Process from Seeing or Closing Processes from other sessions

When running under Citrix or RDP as an admin, I can see, terminate or otherwise manipulate processes created by other users, or in other sessions. Admins need to be able to do this from time to time so I can't remove that capability from Admin logins, but I'd like to restrict it on certain processes.
Is there a way to change the security and/or privileges of the current process or a process I launch, so that it can no longer see or otherwise manipulate other user's processes? I'd be happy to either change the rights of the currently executing process (which is mine), or launch my process using some method that denies the privilege to the target executable. It's the inverse of Run as Admin.
I thought that AdjustTokenPrivileges might be the key, but I can't seem to find any privileges to remove that relate to seeing other users' processes in the Privilege Constants list.
CreateProcess with an appropriate lpSecurityDescriptor seems like it might do the trick, but I haven't been able to find a good example of the security descriptor, or anything that relates to other users' processes.
Anyone know the secret sauce? Thanks

How can I autostart a UI-application under windows that a non-admin user cannot close?

I have developed a C# Windows Forms application that runs in the background as a systray icon and does some stuff when it's clicked. It is supposed to launch when Windows starts and run continously, and a normal user without administrator rights shall not be allowed to shut it down.
What is the best way to do this? I initially intended to run it on the LocalSystem account through Task Scheduler, but then I learned (the hard way) about Session 0 isolation (i.e. the application will run but its UI elements do not show). When I run it with the logged in user, even if it runs elevated, the user can still shut it down through task manager (without requiring elevation).
Is there any way to give a process from LocalSystem access to the UI? I have a winlogon and a csrss process from LocalSystem running in session 1, so I guess it can be done, I just don't know how. Or is there maybe an easier way to disallow users to shut down a process of their own through the task manager? The only other option I can think of is to create an extra Windows Service that keeps polling if the app is running, and immediately launches it again if someone kills it - but that seems incredibly clumsy (also, I want it to stay dead when it crashed by itself, to avoid a single bug causing infinite loops of process creation).
Deponds on why they can't shut it down.
The natural way to go would to have created a service, started by a high priv account, and then had the desktop app just show what it was doing.
If there's something that they should see, but don't becasue they aren't running the service monitor app. (and acknowledge message back to the service), send them an email, send their boss an email, send yourself one and then go shout at them.....
Be a lot easier than trying to get the lid back on this tin of worms.
A nice way to make sure the desktop app is ruuning, would be simply to schedule it to run every X, but drop out immediately if it already is or the somethingwenthorriblywrong flkag is set.
Not worth writing a service to check if it's still there, as they could kill that as well, unless you want to make that a service they can't kill. :(
You are trying to be too draconian with this. Add some sort of auditing so you can see it dies or was shutdown, monitor that and deal with any adverse reports. It's a heck of a lot easier, and gives manage something to do...
You can run an administrative process in the user's logon session. One method would be to for a master process (a system service) to duplicate its own token, use SetTokenInformation to change the session associated with the token, and then call CreateProcessAsUser. The lpStartupInfo parameter can be used to associate the process with a particular window station and desktop. It may be necessary to explicitly change the permissions on the window station and desktop first.
HOWEVER, this is a bad idea from a security standpoint, because GUI applications are vulnerable to malicious messages sent from other processes on the same desktop ("shatter attacks").
It would be safer to run the process in the user's own context but apply an ACL to it. This can be done using the lpProcessAttributes parameter to CreateProcess or CreateProcessAsUser, or with the SetSecurityInfo function. I've never tried this, but it should in theory prevent the user from using task manager to close the process.
If you are creating the process from the user's context, then the user will be the owner, so a sufficiently knowledgeable person could change the permissions back in order to terminate the process. If you need to block this hole, using CreateProcessAsUser from a privileged master process (the token can be duplicated from one of the existing processes in the user's session) should (again, in theory) mean that the user is not the process owner.
Another potential issue: if you listen for messages indicating that the user is logging out (in order to exit) such a message could be faked. If you don't then you need to find some other way of exiting when the user logs out. You may need to experiment here, I'm not sure how the system will react.

Is passing a windows security token between processes permitted

Imagine I have an existing process running under windows as a particular user. Would it be permitted for that process to take its current token (with something like OpenThreadToken and DuplicateTokenEx), pass it to another process on the same machine (perhaps through a network socket or some other IPC), and then expect that process to be able to use it to call CreateProcessAsUser?
From the documentation I have read (http://msdn.microsoft.com/en-us/library/ms682429%28VS.85%29.aspx), I've seen nothing which prohibits this but perhaps the token can only be used by the thread or process which created it.
(Why would you? I want to has a web request come to IIS, be authenticated, have IIS arrange the impersonation of the remote user and then pass the impersonation token to another server process (on the same machine) so that the server process can perform some security checks in the context of the remote user)
Yes, that is possible. You can use DuplicateHandle to get a handle that is valid for the target process (send the new handle value to the target process, so it knows it.).
However, the target process must still have the privileges to use the token accordingly. E.g. SE_IMPERSONATE to impersonate the user and SE_ASSIGN_PRIMARY to be used by CPAU. Of course there are some exceptions that you can read in MSDN for ImpersonateLoggedOnUser and CPAU.
I haven't tried it, but it seems that this is the same question asked here. The description seems to make sense. Pass the process ID via whatever mechanism you choose (e.g., IPC), then call OpenProcess, OpenProcessToken, and finally ImpersonateLoggedOnUser. The resulting handle could be passed to CreateProcessAsUser. Well ... I know it could be passed to that function but whether it would have the desired result I do not know. Interesting question, though.
Why not just use named pipes, and then call ImpersonateNamedPipeUser() - it's safe and secure and easy! Note that the process doing the impersonation MUST have the Impersonation privilege.

Create a process with ordinary rights from an administrator process

There is any way to run another process with ordinary rights from a process running as administrator? From a process with elevated rights I want to start a process with ordinary rights like it were started from explorer. I tried impersonation but I didn't work.
Use CreateProcessAsUser(). Details are in the linked SDK docs.
No this is not possible (There are several hacky ways to do this (Inject into explorer, task scheduler, SaferAPI+MediumIL etc) but none of them work in all scenarios)
This seems like a nice way to do it, provided you don't care about situations where the Shell is not running (e.g. possibly some Terminal Services application-only setups, perhaps, though I'm not sure):
http://brandonlive.com/2008/04/27/getting-the-shell-to-run-an-application-for-you-part-2-how/
It gets an interface to Explorer.exe, which should be running in the user's normal context, and asks Explorer to execute a command in its behalf. This is done just using simple, documented COM interfaces and without having to mess around with process tokens or code/DLL injection.
The technique I've used with success is to use IShellDispatch2::ShellExecute to ask Explorer to launch the process. Since Explorer typically runs as normal integrity, this is effective. The trick is that it's a bit of work to get the IShellDispatch2 object. I followed the process outlined in this blog.
on linux you can use setuid to change the program user id
for windows
you can look at his:
https://serverfault.com/questions/16886/is-there-an-equivalent-of-su-for-windows
If at all possible, use Larry Osterman's solution (in the comments to Anders' answer) instead, i.e., have a parent process that runs without elevation, and launch both the elevated process and the non-elevated process from there.
If that isn't possible, there is an approach that should work in almost all cases, though it is usually more trouble than it's worth:
Install and start a system service, configured to run as local system. Give it your Remote Desktop session ID, e.g., via a command-line argument or a registry setting, as well as your process ID and the command line you want to run.
From the system service, use GetTokenInformation with TokenLinkedToken to get the linked token from the target process. You have to do this from a system service because you need SE_TCB_NAME to get a usable token. (This limitation does not appear to be documented but is consistent with the behaviour of tokens in Windows.)
Or, if there is no linked token, use WTSQueryUserToken to get a copy of the user's token. I believe this always gives you the limited token (if there is one) but if you want to be safe you can check whether it is an elevated or limited token using GetTokenInformation and TokenElevationType; if it is an elevated token, you can use TokenLinkedToken to get the limited one.
If there is no linked token (TokenElevationTypeDefault) you should use the token as-is. This may happen either because the user is not an administrator, or because UAC is disabled, either globally or because the user logged in using the built-in Administrator account. If the user is not an administrator then the token is already suitable. If UAC is disabled, you should respect the user's intent and use the administrative token.
You can then use CreateProcessAsUser or CreateProcessWithTokenW from the system service to launch the new process.
Finally, the service should delete itself and stop.
There is at least one edge case: if your elevated process is launched from a command window (or other process) which was launched using runas and non-administrative credentials. There is no split token in this case, and the original token may have already been deleted, so unless you captured a copy before elevation took place (Larry's solution, or variants thereof) there is no general way to run the child process in the same user context that the elevated process was originally launched from. The best you can do is the context of the logged on user (via WTSQueryUserToken as described above) which may not be the behaviour the end user would expect. (This may, however, be an acceptable limitation, depending on the scenario.)
When Explorer needs to de-elevate, it uses a scheduled task. This is probably a good solution in most cases, and much easier than my earlier answer.
A simple example in Powershell:
$action = New-ScheduledTaskAction -Execute 'notepad.exe'
Register-ScheduledTask -TaskName 'Launch Notepad' -Action $action -User $env:username
Start-ScheduledTask -TaskName 'Launch Notepad'

Resources