24

It seems that starting with Windows Vista, processes with a lower integrity level (IL) cannot send messages to processes with higher integrity levels. This makes sense from a security standpoint, but it breaks some of our interprocess communication.

We have a legacy application (Process A) that unfortunately has to run with elevated "admin" privileges (accomplished by setting its shortcut to always run as administrator). At times it needs to instantiate a separate application (Process B). As a result, Process B inherits the same elevated privileges (and IL) as Process A. Therein lies the problem. There might be other independent instances of Process B that do not have elevated privileges, and all of these Process B instances need to be able to send messages to each other. This obviously fails if one instance of Process B is elevated and another is not.

I know we can open holes in the UIPI message filter using the ChangeWindowMessageFilter API method, but this doesn't seem like the ideal solution. Instead, I would much rather have Process A spawn Process B with reduced privileges, specifically so that it can communicate with the other Process B instances. I think by default the other Process B instances run at the "Medium" IL, so therefore I'd like Process A to spawn Process B instances with this same IL.

My searches have led me to the CreateProcessAsUser and CreateRestrictedToken API methods, but despite this documentation, all of the various facets of tokens and security descriptors and such is still very confusing to me.

I've also come across some threads here (Running a process with lowest possible privileges in winapi and Dropping privileges in C++ on Windows), but I can't find any good examples with code.

Can anyone provide me with some simple yet "correct" code that will help me spawn child processes using the appropriate Windows IL? Specifically, I'd like an example of how to take the existing Process A token and convert it so it has the reduced privileges (I'm pretty sure I can figure out the rest). I'm really unclear about whether I need to duplicate the process' token before modifying it as well.

Community
  • 1
  • 1
Jeremy
  • 934
  • 1
  • 10
  • 19
  • Would it be possible to have a proxy process running, in non-elevated context? If so, you could either get the proxy process to launch process B on behalf of process A as needed, or get process A to take the token from the proxy process and use it to launch process B. – Harry Johnston Mar 30 '12 at 02:30
  • @HarryJohnston – I have read of similar solutions elsewhere, so it definitely seems like that could work. However, unless this third proxy process (C) is already running (independent of Process A), we're back at square one (how to run a separate process using lower privileges). And if Process C *is* already running, then presumably it would have to be running the entire time a user was logged in, a scenario I would rather avoid. `CreateRestrictedToken` seems like it was provided specifically for this and similar types of situations, so how can we drop a token's IL? – Jeremy Apr 03 '12 at 17:32

3 Answers3

13

Warning! While this approach was probably more or less OK for the original poster, it isn't really a good idea in general. In particular, note (as per the comment thread) that artificially manipulated tokens have been reported to cause problems in more complicated applications, so if you are using them, be sure to stick to the basic Win32 API. There are of course also potential security implications.

In most scenarios similar to those of the OP, it would probably be preferable to replace the shortcut that launches the elevated application with a launcher application. The launcher can then remain running for as long as the elevated application is running, and provide a natural limited token for the elevated application to use to launch non-elevated processes.


There's code for launching a low integrity process, which is analogous to your case, in the Designing Applications to Run at a Low Integrity Level article in MSDN.

First, you duplicate the process token, since you can't (or at least shouldn't) mess about with a token that is already being used. Then you use SetTokenInformation with the TokenIntegrityLevel class to set the integrity level. There appears to be a bug in the example code, since the correct SID for low integrity level is S-1-16-4096 rather than S-1-16-1024, but you'll want medium integrity level anyway, which is S-1-16-8192. These can be found here.

Once you have this working (that is, once you are able to launch medium integrity processes from your high integrity process) you should try using CreateRestrictedToken to create the new token instead of DuplicateToken, and remove the Administrators token and all privileges (except SeChangeNotifyPrivilege). Otherwise, the new processes will have medium integrity but still have administrator privilege, which could make it easier for any malicious code that might be running in the same session to elevate its privileges.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • Perfect, just what I was looking for. I was able to use the MSDN code without too much modification, and that is working just as I hoped. I will have to try the second half soon, and if I can get that working I will post some code. Thanks! – Jeremy Apr 04 '12 at 20:32
  • 1
    You're welcome. You might want to use GetTokenInformation (with most or all of the available information classes) to compare a regular medium integrity process with an elevated process, so that you can work out what UAC actually does to create the restricted token. (The details, AFAIK, aren't well documented.) It might even be possible to write some code that compares all the properties of one of the tokens you've made to one made by UAC, so that you can see unambiguously whether you've overlooked anything or not. – Harry Johnston Apr 04 '12 at 20:43
  • @Jeremy have you got it working yet? If so, please do post your code here, thanks a lot in advance :) – Weipeng Jul 11 '13 at 04:07
  • @pongba Sorry for the delay.. I don't have any code to post, as I never completely finished implementing a solution. Instead I was able to reconfigure our legacy app (Process A) to run without admin privileges. However, I'm pretty sure the MSDN article posted in this answer should get you started. – Jeremy Sep 16 '13 at 23:03
  • 1
    The code from MSDN does not work on Windows 10 anymore. The new process will still be elevated. This does not happen on Windows 7. Compare GetTokenInformation(TokenElevation) on Win 7 and Win 10! – Elmue Feb 11 '16 at 00:56
  • @Elmue: in both Windows 7 and Windows 10 the child has elevated privileges - the MSDN code reduces the integrity level, not the privilege level. It sounds as if GetTokenInformation is giving you bad information in Windows 7, although really the expected behaviour of `TokenElevation` was always unclear when dealing with artificially manipulated tokens. See also http://stackoverflow.com/q/30970433/886887 – Harry Johnston Feb 11 '16 at 01:34
  • 1
    After experimenting many days with tokens I came to the concusion that how ever we modify a token, what comes out, will be corrupt. I tried everything to adapt all token properties of a High token to get a Medium token but when starting a process that has an embedded Internet Explorer control, this will behave strange. If you test your code only with Calculator all will be fine, but if you need a generic solution to work with ANY process, all token manipulation leads to a waste of time. The only reliable way to get a medium token is WTSQueryUserToken() but that requires a service. – Elmue Feb 12 '16 at 23:28
  • 3
    I also tested CreateRestrictedToken() and SaferComputeTokenFromLevel() but the result was always the same: The artificial token does not behave the same as a REAL medium token when starting my process with CreateProcessAsUser(). It is incredible that for such an important functionality (e.g. an Installer wants to start the installed application with medium IL) there is no SIMPLE and SAFE API to get a medium token in a High process! Something like ShellExecute(..., "asMedium",...). Not in Vista, nor in Win 7,8,10. I don't understand why Microsoft lets the programmers alone with that task? – Elmue Feb 12 '16 at 23:38
  • @Elmue: as you suggest, it would certainly be wise to avoid using artificial tokens whenever possible, and if you absolutely can't avoid them, to restrict yourself to basic Win32 functionality only in the affected process. (Probably not a problem for the OP in this case.) It should usually be possible to avoid them. If you have a specific scenario you're trying to deal with, and can't figure out a sensible approach, you should probably post a question about it. – Harry Johnston Feb 13 '16 at 12:01
  • I've added a warning note to my answer, along with a brief description of the approach I'd recommend instead in the OPs scenario. (Also, did you check out Adrian's answer? It sounds more like what you're after.) – Harry Johnston Feb 13 '16 at 12:09
  • I don't like very much the code from Adrian. Apart, his post are only some code snippets. Anybody has tried that? Does it really work? And on Windows 10? After investing many days in that topic I found the answer: I start the medium process in a service (WTSQueryUserToken, CreateProcessAsUser). The only correct way is NOT to use an artificial (= crippled) token. But I know that this is not a solution for everyone, because normally an installer does not have an own service running in the backgroud. But in my case the installer already installs my service and I use it for that. – Elmue Feb 16 '16 at 17:38
  • @Elmue I don't suppose you saved a small example that would show how the corrupt token behaves differently from a normal medium level token? It seems to work for me, but I'm worried that I'll find something broken down the line. – Roman Starkov Nov 18 '16 at 23:26
  • @romkyns: "It seems to work for me." So perfect for you! As I said: It works for some processes like Windows Calulator even with a crippled selfmade token. But I had severe problems when starting Internet Explorer which does not behave normal. This shows that the self made token is not the same as the original token. I suppose that Mircorosft stores internal data in the Token that we cannot manipulate with any API, because I tried all Token manipulation commands that exist to get an identical token, but it is still different. – Elmue Nov 19 '16 at 15:28
  • 1
    What I experienced was very weird. Here more details: I have an application with an embedded Internet Explorer control. This browser control is loaded with a HTML page that comes from a local URL. On all Windows versions this works perfect except on Windows 10 when starting my process with a self made Medium Token. Then Internet Explorer tells that he cannot access the URL. If I start my application normally by double clicking it in Explorer all works fine. On Windows 7 and 8 all fine - even with a self made token. But on WIndows 10 with self made token it shows an empty page. – Elmue Nov 19 '16 at 15:37
  • 3
    @Elmue I see, interesting. Well it's definitely true that the token is different - Process Hacker shows plenty of differences, at least the way I create it. Raymond Chen says [we really should be asking Explorer to run the app for us](https://blogs.msdn.microsoft.com/oldnewthing/20131118-00/?p=2643) to get an un-elevated version. – Roman Starkov Nov 19 '16 at 16:06
  • @romkyns, my best guess is that the problem is that the artificial token doesn't belong to the right logon session. I don't think there's any way to fix that. That shouldn't affect a self-contained application, but I gather IE does some weird stuff, trying to interface with the shell perhaps. – Harry Johnston Nov 19 '16 at 21:52
  • @romkyns: "Process Hacker shows plenty of differences, at least the way I create it." I modified all token properties that you can query via GetTokenInformation() to get an identical token and it was still different. Very interesting your link to the code from Raymond Chen. I didn't know that way to start a process via Explorer. It is really easy. You should post that as a separate answer. But what he says about Bob and Alice does not apply to my case because I created the Medium Token for the SAME user. – Elmue Nov 21 '16 at 12:38
3

I've used the approach described here to accomplish this. The basic idea is to ask Explorer to run Process B for you. Since Explorer typically runs at medium integrity level, this gives you what you want.

http://brandonlive.com/2008/04/26/getting-the-shell-to-run-an-application-for-you-part-1-why/ http://brandonlive.com/2008/04/27/getting-the-shell-to-run-an-application-for-you-part-2-how/

The first link will at least give you a good background.

We have a legacy application (Process A) that unfortunately has to run with elevated "admin" privileges (accomplished by setting its shortcut to always run as administrator).

A cleaner way to do that is set the requestedExecutionLevel to the manifest.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • I can't test this at the moment, but my biggest concern would be version compatibility. Does this work on all Windows versions starting with XP? Does it depend on anything that might break in future versions? It also looks like it uses libraries that are distributed with Internet Explorer (see [`IShellWindows`](http://msdn.microsoft.com/en-us/library/windows/desktop/cc836570.aspx)). What if users don't have IE installed? – Jeremy Mar 28 '12 at 20:16
  • Also, Process A is not a Visual Studio project, but rather an old C++ Builder application. I know how to set the application manifest in Visual Studio, but can you do that with other C++ projects as well? – Jeremy Mar 28 '12 at 20:20
  • 1
    In pre-XE2 versions of C++Builder, you can link an external .manifest file into the project's resources using an .rc file. C++Builder XE2 adds support for custom manifests in the Project Options. – Remy Lebeau Mar 28 '12 at 21:12
  • @RemyLebeau – That is great to know, so thanks! Incidentally, you answered a question for me long ago in the Borland forums. Luckily for me and everyone else you're still around to share your expertise. [= – Jeremy Mar 28 '12 at 21:44
  • 1
    @Jeremy: Those are valid concerns. For XP, you have to be prepared for it to fail, which is fine since XP doesn't have integrity levels, so if it fails, you just kick off Process B the way you normally would (e.g., CreateProcess). I'm surprised IShellWindows is tied to IE, as I thought it was provided by the shell (explorer) itself. I'm not sure if uninstalling IE actually removes the implementation for the object. There's a good chance it doesn't. – Adrian McCarthy Mar 28 '12 at 22:40
  • @AdrianMcCarthy: a lot of things in the Explorer shell are tied to IE, and have been since the days of Windows 95 and IE4. That is part of what the whole Antitrust lawsuit against MS was all about back in the 90s. Explorer and IE got a little TOO tied together. MS backed off over the years, but there are still some ties in place. – Remy Lebeau Mar 28 '12 at 22:48
-1

I may not be answering your complete question but as you have mentioned about CreateProcessAsUser and CreateRestrictedToken. I have a code which is working with this API. The code I wrote was written based on the following like.

source:[Windows Vista for Developers – Part 4 – User Account Control] (http://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-1320-Part-4-1320-User-Account-Control)!

The code is available in the following link.

Example Code : http://pastebin.com/XMUAehF9

samjeba
  • 136
  • 5