Win32 Prevent Process from Seeing or Closing Processes from other sessions - windows

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

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.

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.

Sessions, Window stations and Desktops

Sessions, Window Stations and Desktops.
A session consists of all process and
other system objects that represent a
single user's logon session. Sessions
contain Window Stations and Window
Stations contain desktops.
The above is from http://blogs.technet.com/b/askperf/archive/2007/07/24/sessions-desktops-and-windows-stations.aspx
(Similar articles say the same thing, e.g. blogs.technet.com/b/askperf/archive/2007/04/27/application-compatibility-session-0-isolation.aspx and blogs.msdn.com/b/ntdebugging/archive/2007/01/04/desktop-heap-overview.aspx)
I've always understood Sessions and logon sessions to be one and the same thing.
However, reading p. 474, Russinovich and Solomon, Windows Internals, 5th edition, it says (penultimate paragraph):
The CreateProcessWithLogon function
also creates a token by creating a new
logon session with an initial process,
which is how the Runas command
launches processes under alternative
tokens.
So Runas creates a new logon session. If we use Runas to run Notepad under a different user's credentials we see it appear on the desktop. So Notepad is running on the same desktop as everything else. (This is, as far as I understand it, the default desktop in Window Station Winsta0; the interactive window station.) So what we have now are two logon sessions associated with the same Session (the thing that contains window stations). So Sessions and logon sessions would not appear to be one and the same.
Can anybody confirm this please?
Confused
There are indeed two types of sessions here. Here's my understanding of how they work.
A logon session[2] is managed by the Local Security Authority[2], and manages the scope of a user logon. These are created primarily by the winlogon process, but also secondarily by API functions such as LogonUser, CreateProcessAsUser, and CreateProcessWithLogonW, or by applications that use these functions, such as the runas command.
A logon session isn't tied to any specific Object Manager[2] concepts as window stations and desktops. It's basically just a block of information containing the logon SID and some cached security information about the account. This block of information, this logon session, is what an access token points to.
The other type of session is sometimes called a Terminal Services session, Terminal Server session, Remote Desktop session, logon session (as confusing as that is), user logon session, or user session. Usually, though, it's just called a "session", without further qualification.
This is the type of session that you'll usually hear about, and is what window stations belong to. This type of session came about to support multiple interactive GUI logons, as provided by Terminal Services (now known as Remote Desktop), and is now also used to support Fast User Switching. Sessions provide the necessary isolation between the Object Manager objects associated with each user logon.
I don't spend much time messing with all this stuff, so I'm a little fuzzy on some of the details, but I think this is a pretty fair representation of the overall picture. I hope it clears things up a bit.

What will report an account's permissions?

My service program executes another instance of itself with, essentially, CreateProcess(GetCommandLine()). The child process then uses OpenProcess to get a handle to its parent process (so it can detect when the parent has stopped running). For some customers, OpenProcess fails with ERROR_ACCESS_DENIED. I'm trying to determine the reason and reproduce the circumstances for in-house testing.
I want something I can send to customers (either a program, or instructions for using programs that are already commonly installed on Windows servers) for them to run and generate a report that includes what account the service runs as and what privileges that account and its groups have been granted or denied. How can I collect that information from customers?
Getting the account name is easy -- GetUserName. Getting the rights assigned to that account is a bit more work. If memory serves, the sequence runs something like:
GetKernelObjectSecurity(Current_oject, &security_descriptor)
GetSecurityDescriptorDacl(security_descriptor, &dacl)
GetEffectiveRightsFromAcl(dacl, user_name, &rights)
You might prefer to use GetExplicitEntriesFromAcl for that last step. There is one problem with all this: if they've restricted the user too much, some (or all) of it might fail as well.

CreateProcessAsUser vs ShellExecute

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.

Resources