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