I brought this process into the world, and I can take it out!


Clipboard Gadget wants to know why normal processes can kill elevated processes via TerminateProcess, yet they cannot do a trivial Show­Window(hwnd, SW_MINIMIZE). "Only explorer seems to be able to do so somehow."

There are several questions packed into this "suggestion." (As it happens, most of the suggestions are really questions; the suggestion is "I suggest that you answer my question." That's not really what I was looking for when I invited suggestions, but I accept that that's what I basically get.)

First, why can normal processes kill elevated processes?

The kernel-colored glasses answer is "because the security attributes for the process grants the logon user PROCESS_TERMINATE access."

Of course, that doesn't really answer the question; it just moves it to another question: Why are elevated processes granting termination access to the logged-on user?

I checked with the security folks on this one. The intent was to give the user a way to terminate a process that they elevated without having to go through another round of elevation. If the user goes to Task Manager, right-clicks the application, and then selects "Close", and the application doesn't respond to WM_CLOSE, then the "Oh dear, this isn't working, do you want to try harder?" dialog box would appear, and if the user says "Go ahead and nuke it," then we should go ahead and nuke it.

Note that this extra permission is granted only if the process was elevated via the normal elevation user interface (which nitpickers will point out may not actually display anything if you have enabled silent elevation). The user was already a participant in elevating the process and already provided the necessary credentials to do so. You might say that elevating a process pre-approves it for being terminated. As Bill Cosby is credited with saying, "I brought you into this world, and I can take you out!"

If the process was elevated by some means other than the user interface (for example, if it was started remotely or injected by a service), then this extra permission is not granted (because it is only the elevation user interface that grants it), and the old rules apply.

Phew, that's part one of the question. Now part two: Why can't you do a trivial Show­Window(hwnd, SW_MINIMIZE)? Because that runs afoul of User Interface Privilege Isolation, which prevents low-integrity processes from manipulating the user interface of higher-integrity processes.

My guess is that Clipboard Gadget though that terminating a process is a higher-privilege operation than being able to manipulate it. It isn't. Terminating a process prevents it from doing anything, which is different from being able to make it do anything you want. You might hire a chauffeur to drive you all over town in a limousine, and you can fire him at any time, but that doesn't mean that you can grab the wheel and drive the limousine yourself.

Finally, Clipboard Gadget wants to know how Explorer can minimize windows. Explorer does not call Show­Window(hwnd, SW_MINIMIZE) to minimize windows, because Explorer is running at medium integrity and cannot manipulate the windows belonging to high-integrity processes. Instead, it posts a WM_SYS­COMMAND with the request SC_MINIMIZE. This does not minimize the window; it is merely a request to minimize the window. The application is free to ignore this request; for example, the application may have disabled its Minimize box. Most applications, however, accede to the request by minimizing the window. Just like how most chauffeurs will agree to take you to your destination along the route you specify. Unless your instructions involving going the wrong way down a one-way street or running over pedestrians.

But don't fool yourself into thinking that you're driving the limousine.

Comments (25)
  1. AC says:

    Doesn't the UI Privilege Isolation also prevent posting window messages to higher integrity processes? Otherwise you can fool other processes by posting WM_COMMAND messages etc.

    Are only certain messages allowed or is my initial assumption wrong?

  2. I was wondering the exact same thing the other week regarding part 1 – thanks for answering.  I think there are still bad security implications – for some elevated processes, having PROCESS_TERMINATE access would still be like "driving the limousine."  For example, imagine a batch script that invokes various helper programs and checks the return codes.  By forcibly terminating processes at key point in the script you could alter the script behavior in undesired ways.

    Then I realized that the non-elevated user needs to be able to log out, and that kills everything.  It would seem ridiculous to require elevation in order to log out, so that the elevated processes could be terminated, although it's arguably the correct approach – but probably wouldn't fly on a consumer operating system – only servers/workstations.

  3. Matt says:

    @AC:

    Yes UIPI limits which messages (and what parameters) are allowed to be sent to windows. For example, you can send a WM_SYSCOMMAND to a high window, but cannot send a WM_DROPFILES.

  4. Now the $65536 question:

    Why a low-privilege account process started from high-privileged account session by RunAs, has privileges to kill processes of the original account?

    To illustrate that:

    Log on as an administrator. Start CMD.EXE. By runas command, start taskmgr under any user-privileged account. Use low-privileged taskmgr to kill any Administrator process on your session.

  5. Avi says:

    @alegr1

    Why shouldn't a process which descends from privileged process be able to do so?  This seems to be an example of Raymond's "already on the other side of the hatch" maxim.  If you can start cmd.exe as Administrator, you can do whatever you want from a normal tskmgr session, so who cares, security-wise, if you do it from a lower-privileged process?

  6. Random832 says:

    @Avi the problem is, "using RunAs to change to a low-privileged account" sounds like it should be, in this metaphor, shoving the new process out of the airtight hatchway so it can't do exactly that sort of thing.

  7. Joshua says:

    There is an interesting puzzle — how do I spawn an interactive processes from a service. This breaks down into two further subproblems: how do I spawn an interactive process as the user, and how do I spawn an interactive process with high privileges. The first: steal explorer's token. The second: steal winlogon's token. Be careful with this one. This leaves you vulnerable to shatter attacks unless you specially addressed them.

    The second path results in a process the user cannot kill (as is to be expected from Raymond's post above). I'm not sure, but I believe a logout will kill it (I'm using the trick to spawn a feedback window that's going to close itself anyway so I never tested that).

    The UIPI restriction about signed binary with manifest for uiAccess=true is a waste of CPU power. Thread injection to any allowed process (I'm looking at you on-screen-keyboard) will bypass it. This does not appear to be avoidable. I'm not sure if uiAccess=true results in opening the floodgate again to shatter attacks. This may be significant.

  8. Avi says:

    @Random832

    It seems a logical extension of Raymond's post.  If the chain of (interactive) processes can be followed back to a(n interactive) process that has privileges to kill other processes, there's no security benefit in preventing any process on the chain from having the same privilege.

  9. @Joshua

    The problem is, stealing anyone's token can leave your service open to problems. The reason for this is because you would end up with you needing to use the Local System account and maybe even SeTcbPrivilege to get the token from elsewhere. This also suffers from the problem of 0 users logged on = no token, or multiple users logged on = which instance of Explorer. I also wonder why so many people think that services should have high privileges in the first place. I have seriously seen a service (with a massive security flaw) which had the TCB privilege solely for getting a token from elsewhere. The service would have worked perfectly fine with Local Service if it wasn't for this requirement of getting a token. Why did this person do it this way? Because it was easier.

    What I often recommend is for you to run an application at logon using the run key. If needed, this application can give credentials to the service via SSPI so the service can act on behalf of the user, and this application can be used for the notifications.

  10. Leo Davidson says:

    "User Interface Privilege Isolation, which prevents low-integrity processes from manipulating the user interface of higher-integrity processes."

    I feel that killing the process should count as manipulating its user interface.

    Causing all of the process's windows to close is surely manipulating its UI. If we're worried enough to protect elevated windows from being minimized then surely we should care about them being closed as well?

    UAC sure is a quirky design. :)

  11. Joshua says:

    UAC sure is a quirky design. :)

    Irrelevant to UIPI, which needed doing anyway.

  12. @Joshua

    Well, from that extra information, I still think that the service had too many privileges and could have gotten rid of that by fixing the design.

    @Leo

    If it was the other way around, wouldn't it just be replaced with people asking why they can't kill processes that they created?

  13. Joshua says:

    @Crescens2k: What I needed was a SUID-bit but it wasn't available.

  14. Random832 says:

    @Avi can't they all be traced back to winlogon, by that logic? There has to be some way to deliberately break the chain, or there would never be any such thing as an unprivileged process.

  15. jon says:

    So it sounds like the main reason low-integrity processes can kill high-integrity processes is to avoid annoying the user with a UAC prompt. That doesn't sound like a very well thought-through security policy.

  16. Joshua says:

    @jon: Windows Media Player is a super-high privileged process and it is elevated by kernel. Would you have it be unkillable?

  17. Faber says:

    I second the first question by AC: Why is posting SC_SYSCOMMAND by the explorer not prevented by UI privilege isolation? preventing messages from getting from your mid-integrity explorer to your high-integry banking app is the whole point of UI privilege isolation, is it not?  And it does not matter if the banking app may ignore the Message, since the low integrity internet download might be sending a keyboard message to fiddle with some Password entry box of the high integrity banking app, and i am sure the banking app honors keyboard input thinking it originated from the user directly.

    [WM_SYSCOMMAND is hardly the same as keyboard messages. You can't fiddle a password entry box with WM_SYSCOMMAND. -Raymond]
  18. Joshua says:

    [WM_SYSCOMMAND is hardly the same as keyboard messages. You can't fiddle a password entry box with WM_SYSCOMMAND. -Raymond]

    Keyboard messages are harmless compared to what WM_SETTEXT and EM_GETSEL can do.

  19. Joshua says:

    @Crescens2k: The code grabbed the active session ID to decide which session to create the process in. If no users were logged in, the code would have grabbed the service session, but due to other factors, that's unreachable anyway.

    This was *not* a process that ran at login. This was a process that ran occasionally in response to a user task, and needed to communicate to the user what is in essence a glorified progress bar. If it weren't for the fact the process also ran from non-service triggered events, breaking it into separate processes for worker and display would have been a better idea. The process had high privileges so it could write to the installation directory.

    But that's all wide of the point I was making.

  20. @Joshua:

    Keyboard messages are harmless compared to what WM_SETTEXT and EM_GETSEL can do.

    And that's the kind of messages stopped by UI isolation. And CBT hooks.

  21. Alex Sh says:

    >  As Bill Cosby is credited with saying, "I brought you into this world, and I can take you out!"

    Actually, this is a quote from 1962 movie "Taras Bulba" (Yul Brynner as Taras says this). And the movie itself is based on novella by Nikolai Gogol, published in 1842, which has this quote as well (in Russian, of course). Though in Russian it sounds more like "I brought you into this world, and I will kill you!", which suites this topic nicely.

    [Hence my very careful phrasing of the sentence. Thank you for nitpicking. -Raymond]
  22. @Alex Sh:

    The original Gogol quote is best translated as: "I begat you, and so I shall kill you".

  23. Douglas says:

    I loved the limo driver analogy. Spot on!

  24. @jon "That doesn't sound like a very well thought-through security policy."

    UAC is not a security boundary, it's a convenience tool. Security in Windows is still determined by sessions and their are always ways and means for a user to manipulate windows in the current session (and by extension, processes, which *are* the user).

    @Joshua

    Services should never, ever open an interactive session at all. In fact, they should always assume their isn't anybody logged in. If there does need to be some UI, it should be a separate application which is launched by the user that uses some form of IPC to communicate with the service. Each and every attempt everyone makes at trying to launch UI from within the service itself is always going to fall foul of the no user/multiple user issue.

  25. Richard Cox says:

    because the security attributes for the process grants the logon user PROCESS_TERMINATE access

    To be pedantic terminate rights is granted to the session (SID), and the logged in user has the session in their (default) token.

    Of course the difference only matters when you look at the process's ACL and don't see the user's login sid their.

Comments are closed.