1

In my local system service I may start a UI process that should run with credentials of the logged on Windows user, but at times, when there's no logged on user, it should be able to start in the Winlogon or "secure desktop" as well.

Thus, I'm using the following construct to prep the user token for it:

//The following pseudo-code snippet is run from the local system service

HANDLE hSelfToken = NULL;
HANDLE hToken2 = NULL;

::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &hSelfToken);

//Remove most of privileges & create restricted token
::CreateRestrictedToken(hSelfToken, 
            DISABLE_MAX_PRIVILEGE | LUA_TOKEN,
            0, NULL,
            0, NULL, 0, NULL,
            &hToken2);

//Set user session ID for the token where the process will run
::SetTokenInformation(hToken2, TokenSessionId, &dwSessionId, sizeof(dwSessionId));

//The 'hToken2' is later used to call CreateProcessAsUser() to start a user UI process

This works great, except that in despite of having pretty much no privileges and no elevation, my UI process that is started with this method still receives too many of the system service's "rights." For instance, it can create a file in C:\ root folder, or open HKLM registry key for writing.

So I'm curious, what else shall I do to lower the child process' privileges?

c00000fd
  • 18,074
  • 19
  • 132
  • 318
  • 1
    It would probably be preferable to not describe access rights as "privileges" since that word has a specific meaning in the context of Windows access control. – Harry Johnston Aug 10 '15 at 01:02
  • I suspect the `LUA_TOKEN` flag sets the elevation type of the new token, but that in and of itself doesn't affect the token's access rights. [In this question and answer](http://stackoverflow.com/questions/30970433/do-high-integrity-tokens-have-to-have-the-administrators-group-enabled) I demonstrate that an "elevated" token does not necessarily have the Administrators group enabled, I think you've done the opposite - created a token with a "limited" elevation type but that belongs to the Administrators group. – Harry Johnston Aug 10 '15 at 01:07
  • CreateRestrictedToken() allows you to specify a list of restricting SIDs, that would probably be the safest approach. But you could instead disable those SIDs that you don't want, i.e., at least Administrators and SYSTEM, since they both appear in the DACL for C:\ and HKLM. However, I'm not sure there's any way to do this while still being able to display a GUI on the target desktop. – Harry Johnston Aug 10 '15 at 01:18
  • You are creating a restricted token based on the token of the user that the service itself is running as (SYSTEM?), instead of the token of the user who is logged in to the target session that you will be running the new process on. Use [`WTSQueryUserToken()`](https://msdn.microsoft.com/en-us/library/Aa383840.aspx) to get the token of the target session's user, if any. – Remy Lebeau Aug 10 '15 at 02:03
  • @HarryJohnston: Appreciate it! In reference to your suggestions: `LUA_TOKEN` flag actually removes elevation from the token. Adding Admin group SID (the one I get from doing `ConvertStringSidToSid(L"S-1-5-32-544", &pSidAdminGroup)`) as `SidsToDisable` parameter for CreateRestrictedToken **doesn't** change anything that I could notice. If I add the `SYSTEM` SID to deny list (that I get from `ConvertStringSidToSid(L"S-1-5-18", &pSidSYSTEM)`) it makes `CreateProcessAsUser` fail with `ERROR_ACCESS_DENIED`. So no luck so far... – c00000fd Aug 10 '15 at 20:56
  • @RemyLebeau: `WTSQueryUserToken()` was the first API I tried. It works fine for an interactive logged in user, but unfortunately when I use it for a "Secure desktop" session (or `Winlogon`), the child process that is started with that token starts up but then immediately fails with exit code set to `STATUS_DLL_INIT_FAILED` or `0xC0000142`. And unfortunately I couldn't find any explanation why, or how to fix it? – c00000fd Aug 10 '15 at 21:02
  • `WTSQueryUserToken()` fails with an `ERROR_NO_TOKEN` error if you call it on a session that does not have a user logged in, like the Winlogon session. Are you accounting for that? As for "secure" *desktops*, they run within a user's *session*. A secure desktop does not run in its own session, except for maybe the Winlogon session when no interactive user is logged in yet. So, use `WTSQueryUserToken()` to get the session's user token, falling back to your service's token if no user token is available. – Remy Lebeau Aug 10 '15 at 21:13
  • Or, use `WTSQuerySessionInformation()` to find out if the session has a user logged in, and if so then grab its user's token, otherwise use your service's token. – Remy Lebeau Aug 10 '15 at 21:16
  • @RemyLebeau: You know, I pretty much gave up on `WTSQueryUserToken`. Here's why. The token I get from it works if I run the child process in `Winsta0\Default`, but then if I need to run it in `Winsta0\Winlogon` or "secure desktop" I get all kinds of weird errors from the child proc initialization. (Like in this case, it would exit with `ERROR_ENVVAR_NOT_FOUND` code.) So to use that API, I need to know if the "secure desktop" is currently active, which just creates a new problem to solve... – c00000fd Aug 10 '15 at 21:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86648/discussion-between-remy-lebeau-and-c00000fd). – Remy Lebeau Aug 10 '15 at 21:53

0 Answers0