The App Compat Guy

Chris Jackson's Semantic Consonance

January, 2007

  • The App Compat Guy

    Applying Mitigations for UAC Issues (LUA Bugs) on Windows Vista with Standard User Analyzer

    • 9 Comments

    The story is all too familiar. Developing software as a standard user on your computer can be challenging until you know all of the tricks (see Aaron Margosis' Blog for many of these tricks). Consequently, all too often developers end up running as local administrator on their computers. Without having any ACL or privilege restrictions, they end up opening file or registry handles that require an Administrator ACE, they create objects in the global namespace, they use high-powered privileges, etc. And, naturally, they don't end up noticing these bugs, because it runs fine on their computers. They then hand the application off to test, and they don't notice anything either since they are also running as local administrator. They then distribute the software, and because so much software has the same affliction (LUA Bugs), the software ends up distributed across an organization full of local administrators because running in a standard user environment breaks too many of their applications. This just adds one more to the pile.

    Windows Vista changes all of that with the introduction of User Account Control (UAC) - a suite of technologies that not only make it easier to run as a standard user, but that actually run a local administrator account with greatly reduced rights most of the time (until you elevate). And, despite many of the technologies that we put in place to help mitigate these issues (such as file and registry virtualization), some software still doesn't work without elevation.

    As a software developer, there are a number of tools and resources available to help you with modifying your application to behave correctly when running as a standard user or restricted administrator on Windows Vista. (For example: http://www.microsoft.com/downloads/details.aspx?FamilyID=BA73B169-A648-49AF-BC5E-A2EEBB74C16B&displaylang=en.)

    We have also just shipped ACT 5.0 (http://www.microsoft.com/downloads/details.aspx?FamilyID=24da89e9-b581-47b0-b45e-492dd6da2971&displaylang=en), which includes Standard User Analyzer - a runtime analysis tool you can use to pinpoint LUA Bugs and address them in your code.

    However, not everyone is fortunate to have access to the source code to an application to be able to modify it. If you are planning a migration to Windows Vista, you may have a whole pile of software which your testing (either manual or through using the UAC Compatibility Evaluator in ACT 5.0) indicates will not behave correctly when running as a standard user. If you want to keep this software working, what can you do?

    There is a great new feature in Standard User Analyzer to address exactly this issue. Some UAC issues can be addressed with shims, and others with modifications to ACLs. You can't fix them all (for example, privilege use is not something you can shim - if you don't have the privilege, we can't add it), but may of them you can. However, understanding all of the shims and knowing when to use which one can be a challenging task, and requires spending quite a bit of quality time with Compatibility Administrator. So Standard User Analyzer now includes the ability to recommend mitigations.

    To explore this, I took an application with some known LUA bugs in it. Specifically, it tries to do the following things:

    • Create a file with write access in c:\windows
    • Create a registry key with write access in HKLM
    • Write to win.ini using the profile APIs
    • Perform a hard administrator check
    • Add the SeDebugPrivilege
    • Create a memory mapped file in the global namespace
    • ACL a mutex requiring the Administrator ACE, and then attempt to open that mutex
    • Attempt to launch a process that is manifested as requireAdministrator using the CreateProcess API

    Let's take SUA for a test drive on this application and pick apart the recommended mitigations.

    I ran the application through a test pass that exercised all of these bugs. SUA detected all of the known issues, and reported on them. If I then take a look at the mitigations it recommends, we can then evaluate the suggestions.

    • Create a file with write access in c:\windows: loosen ACLs on the specific file (to write to the file), and loosen ACLs on the Windows directory to create it.

    Personally, I think SUA is a bit overly aggressive here. Loosening ACLs is not something you really want to do until you have conducted a thorough security review. This will, indeed, get the application working. However, I wish it had taken into account the fact that the Windows directory is one of the three directories that is virtualized, and not make this recommendation, and I wish there were more guidance around the security implications for changing ACLs.

    • Create a registry key with write access in HKLM: loosen ACLs on the specific registry key

    Here we are targeting the specific registry key, which is good. Except, of course, that the registry key doesn't exist until the application creates it, so you can't apply an ACL until you create the key. And, again, virtualization would mitigate it. (However, if the appliation depends on the value here being shared between users, virtualization would lead to a change in behavior.)

    • Write to win.ini using the profile APIs: loosen ACLs on win.ini

    Again, too heavy handed with ACLs. While targeted, win.ini would be virtualized, so unless not sharing values between users effectively breaks the application (even if it no longer crashes), and unless you have considered the full impact of allowing write access to win.ini, you may want to be really sure this is something you want to do.

    • Perform a hard administrator check: Apply ForceAdminAccess shim

    What a beautiful shim this is! All it does is lie. The application asks if you are an administrator, and it just goes ahead and says yes. (Typically, a restricted administrator account is going to say no.) Pair this shim with virtualization, and it gets a huge number of applications working that ask first, and then try to do restricted stuff. Good choice.

    • Add the SeDebugPrivilege: Nothing

    That's right. Can't fix this one. If you need SeDebugPrivilege, you'll have to elevate.

    • Create a memory mapped file in the global namespace: Apply LocalMappedObject shim

    Another elegant redirection solution. When you create an object, you specify the name of the object using a string. You can prefix the string with "global\" or with "local\". Now, since this is just a string, it's easy enough for a shim to just take that string, replace global with local, and have the object live in a namespace that doesn't require administrator rights! Again, this can change the behavior of the application if the intent is to share this object with processes in other sessions, but it's certainly worth a try.

    • ACL a mutex requiring the Administrator ACE, and then attempt to open that mutex: Nothing

    We can't virtualize arbitrary kernel objects. So, since we need that ACE and we don't have it, the application is just going to fail.

    • Attempt to launch a process that is manifested as requireAdministrator using the CreateProcess API: Apply ElevateCreateProcess shim

    Another elegant solution. Why is this necessary? CreateProcess is fairly low in the layers on Windows, and we have done a ton of work to get to the point where code in a low layer does not take any dependencies on a code in a higher layer. You really can't do much useful stuff on Windows without creating a process, but you can do quite a bit without parsing an XML manifest or running setup detection heuristics in a compatibility layer. So, guess what? Calling CreateProcess doesn't read this stuff. (ShellExecute does.) But a shim can intercept and do that work, and that's what it does here.

    So, let's take stock of where we are. Standard User Analyzer pinpointed the issues which would potentially be mitigated, and it suggested a mitigation for them. When it comes to shims, this can be enormously useful as you start to navigate the list of shims for addressing UAC issues in Vista - this can pinpoint the shims that are useful for the specific problems you have. Fantastic.

    I was a bit ... erm ... less than complementary about the ACL relaxing we have here. But that's probably not particularly fair of me. You see, if we don't loosen the ACLs, the best we can do is either run as admin (which we don't want to do unless we absolutely have to, so we can potentially support a standard user desktop) or redirect to a per-user location (either through virtualization or shims). Redirecting to a per-user location can potentially affect the behavior of the application, particularly on a multi-user machine. So, loosening the ACLs will do a better job of approximating the original behavior when this is the case, while still limiting the surface area. So they had a tough choice to make, since they don't know if the file they are grabbing a write handle for is intentionally for sharing, or if it is just accidentally for sharing between users. The choice they made is to fix it up the best they possibly could with the least amount of security surface area, instead of minimizing surface area and leaving more stuff potentially broken. But I will still recommend a thorough security review on every ACL relaxation you put in place - it's just too easy to end up in a situation where you grant permissions where harm can be done - don't just assume that this is the best possible way to do it.

    A better way, perhaps, would be to recommend one setting, take your vote as to if it works yet, then try the next step, try the application again, and so forth. See how that goes. If all else fails, run as admin. But that's kind of a tough UI to put together, and a potentially long iterative testing process. So, instead, you just get a starting point from which you can tighten or loosen, but is our best guess as to what we might be able to do in order to get stuff working.

    Now that we've been through all of that, what do I do with these recommendations?

    Well, SUA will apply them to the local machine for testing. It will loosen ACLs for you. It will create a custom shim database and install it locally so you can try these shims out. But what it doesn't do is create the scripts and redistributable sdb you can use to deploy across an organization. You get the chance to test it on your computer, but then you need to roll these changes into either an existing script or shim database, or else use it as a starting point. So, it's really there to inform your decision, not to fully automate it. And, with security decisions like this, that may be kind of a good idea.

  • The App Compat Guy

    Why is Windows Vista Ignoring my UAC Manifest?

    • 2 Comments

    One of the many reasons why I love my job so much is that I am so consistently faced with problems that are difficult to sort out. Well, not that I like the sensation of being stumped, but I sure do like the sensation of figuring out what was happening. (Obviously even more so if the solution is non-trivial.) And one of my goals is to share this knowledge here, so you don't have to spend as much time being stumped as I did.

    Working with a customer application, they had found a number of LUA bugs, and with UAC enabled on Windows Vista they were going to present a serious problem. Unfortunately, they depend on the makers of third party components who also had LUA bugs, and the schedule for updated UAC-compliant components did not align with their own. So, in the interim, they just had to manifest their application as requireAdministrator, which will cause it to prompt when you want to run it. Yes, of course they weren't excited about doing that (who is?) but with Windows Vista's consumer release just around the corner there is a serious time constraint.

    So, we just cooked up our UAC XML manifest and applied it to the binary. The shell immediately responded, as it should have - it began showing a shield icon on top of the application icon. Yet, myseriously, when we double clicked on the application to launch it, we didn't see the secure desktop asking us to approve the authorization / provide credentials. We were just launching the application non-elevated. Huh? I scratched my head. A lot. Then I scratched it some more. Obviously I had the manifest syntax correct, or else it wouldn't have shown the shield icon in the shell. So what was going on here? More head scratching.

    As it turns out, if you start poking around the binary itself, I found a little flag set. The ALLOWISOLATION:NO flag. This is a flag that really should be avoided in just about every circumstance. It tells the loader specifically not to process the manifest for side-by-side (or UAC, which is new in Vista). So, the loader wasn't ever reading the manifest because the binary itself was telling it not to. So, we just used editbin.exe /ALLOWISOLATION to reset this flag to its normal state, and things were back to normal. Everything was fine.

    Which then brought us to the next question - why was the shell reading the manifest, but not the loader? I went back and forth on this one for a bit, but in the end I believe it is doing the right thing. The shield icon is saying that this executable will require elevation immediately. And, in fact, it does. Just because we directed the loader to ignore the manifest does not negate this simple fact.

    So, if you run across a scenario where Explorer is showing the shield icon, you have UAC enabled, but you don't see a consent dialog, take a look at the ALLOWISOLATION:NO flag - this may be your culprit!

  • The App Compat Guy

    Configuring Application Verifier as a Testing Tool for Windows Vista Compatibility

    • 1 Comments

    When I speak with customers about compatibility testing in preparation for an upgrade to Windows Vista, one of the tools I always introduce is Application Verifier.

    Application Verifier is a great tool for developers. It runs a number of runtime checks to see if you are doing anything that may comprimise the reliability or compatibility of your application. (Consequently, as a runtime analysis tool, it depends on you having a thorough test plan to ensure adequate code coverage.) When AppVerifier runs across a noteworthy issue, it throws a stop error, which breaks you into a debugger right at that line of code, which is extremely useful for a developer.

    However, throwing a stop error is significantly less useful for a tester who is not familiar with, and may not have access to, the code. If you don't have a debugger attached, that error will frequently just bubble up, never caught, and eventually lead to a crash of the application. Not particularly helpful!

    Of course, you can configure Appliation Verifier not to break into the debugger, but the user experience there is not optimized for configuring in bulk. If you start up Application Verifier, and then click on one of the test types, you can go to Edit -> Verifier Stop Options. You then see a list of all of the verifier stops, and whether or not you will throw an error. However, the first thing you will note is that you can't multi-select the Verifier Stop codes. You have to go one by one. Second, if you select one, configure it, and then move to the next one - try going back. You'll find that the configuration change you just made has gone away. You have to press OK after configuring each and every stop error and then re-open the dialog box to configure the next one. Some of these tests have quite a few stop codes, so that can be quite time consuming!

    There are a couple of ways to simplify that process. If you have already done it once, all you need to do is grab the registry key that was created (under Image File Execution Options) and export them. Change the name for a new application, and voila - the new application is configured. However, if you haven't configured it once already, the description above isn't likely to sound particularly tempting.

    Fortunately, Application Verifier has a command line interface, so you can script that process. Even more fortunately, only one person has to write that into a little command fine - and I figured that person may as well be me. You can use the following script to enable and configure a subset of Application Verifier tests that will be interesting in terms of collecting data to support compatibility and reliability testing. (You can always add or remove based on your needs.)

    Just copy this text and paste it into Notepad, and save with a .cmd file extension. I call mine AppVerifier Test Config.cmd.

    @echo off
    if (%1)==() goto NoArgument
    @echo on
    appverif.exe -enable Exceptions Handles Heaps Locks -for %1
    appverif.exe -configure 0x0650 -for %1 -with ErrorReport=0
    appverif.exe -enable Handles -for %1
    appverif.exe -configure 0x0300 0x0301 0x0302 0x0303 0x0304 0x0305 -for %1 -with ErrorReport=0
    appverif.exe -enable Heaps -for %1
    appverif.exe -configure 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007 0x0008 0x0009 0x000A 0x000B 0x000C 0x000D 0x000E 0x000F 0x0010 0x0011 0x0012 0x0013 0x0014 -for %1 -with ErrorReport=0
    appverif.exe -enable Locks -for %1
    appverif.exe -configure 0x0201 0x0202 0x0203 0x0204 0x0205 0x0206 0x0207 0x0208 0x0209 0x0210 0x0211 0x0212 0x0213 0x0214 0x0215 -for %1 -with ErrorReport=0
    appverif.exe -enable Memory -for %1
    appverif.exe -configure 0x0601 0x0602 0x0603 0x0604 0x0605 0x0606 0x0607 0x0608 0x0609 0x060A 0x060B 0x060C 0x060D 0x060E 0x060F 0x0610 0x0612 0x0613 0x0614 0x0615 0x0616 0x0617 0x0618 0x0619 0x061A 0x061B 0x061C 0x061D 0x061E -for %1 -with ErrorReport=0
    appverif.exe -enable Threadpool -for %1
    appverif.exe -configure 0x0700 0x0701 0x0702 0x0703 0x0704 0x0705 0x0706 0x0707 0x0708 -for %1 -with ErrorReport=0
    appverif.exe -enable TLS -for %1
    appverif.exe -configure 0x0350 0x0351 0x0352 -for %1 -with ErrorReport=0
    appverif.exe -enable FilePaths -for %1
    appverif.exe -configure 0x2400 0x2401 0x2402 0x2404 0x2405 0x2406 0x2407 0x2408 0x240A -for %1 -with ErrorReport=0
    appverif.exe -enable HighVersionLie -for %1
    appverif.exe -configure 0x2200 0x2201 0x2202 0x2203 0x2204 -for %1 -with ErrorReport=0
    appverif.exe -enable InteractiveServices -for %1
    appverif.exe -configure 0x2800 0x2801 0x2802 0x2803 0x2804 0x2805 0x2806 0x2807 -for %1 -with ErrorReport=0
    appverif.exe -enable KernelModeDriverInstall -for %1
    appverif.exe -configure 0x2305 0x2306 0x2307 0x2308 0x2309 0x230A 0x230B 0x230C 0x238D -for %1 -with ErrorReport=0
    appverif.exe -enable LuaPriv -for %1
    appverif.exe -configure 0x3300 0x3301 0x3302 0x3303 0x3304 0x3305 0x3306 0x3307 0x3308 0x3309 0x330A 0x330B 0x330C 0x330D 0x330E 0x330F 0x3310 0x3311 0x3312 0x3313 0x3314 0x3315 0x3316 0x3317 0x3318 0x3319 0x331A 0x331B 0x331C 0x331D 0x331E 0x331F 0x3320 0x3321 0x3322 0x3323 0x3324 0x3325 0x3326 0x3327 0x3328 0x3329 0x332A 0x332B 0x332C 0x332D 0x332E 0x332F 0x3330 0x3331 0x3332 0x3333 0x3334 0x3335 0x3336 0x3337 0x3338 -for %1 -with ErrorReport=0
    appverif.exe -enable DangerousAPIs -for %1
    appverif.exe -configure 0x0100 0x0101 0x0102 0x0103 0x0104 -for %1 -with ErrorReport=0
    appverif.exe -enable DirtyStacks -for %1
    goto End
    :NoArgument
    echo Please specify the application name (without path)
    :End

    Now, I just need to run AppVerifier Test Config "My Application Name Without Path". Much easier than going through all of that clicking!

    Keep in mind that Application Verifier is a "set and forget" tool. You will be monitoring that application every single time that you run it from now until the end of time (or until you go into Application Verifier and remove it). When you want to see the data that you collected, just launch the Application Verifier GUI, from the View menu select Logs, and then select the test run that you want to review data for. You can then either press View to open the data in your default XML editor, or you can press the Save As button and save it somewhere as XML, and then use another tool to open it up and review. Personally, I prefer to save it and open that XML inside of Microsoft Office Excel.

    When you are done testing that application, you can right click on it and select Delete Application to stop monitoring. Of course, you can script this as well, but since it's only a couple of mouse clicks away I typically end up going through the UI.

    The output from Application Verifier is quite raw. It has a lot of data. Standard User Analyzer uses Application Verifier, and it performs a lot of work on the output of LuaPriv to make it easier to understand and to understand how you can mitigate the issues that it detected. We are not so lucky with the rest. However, while it is a fairly big step down from many of the other tools included in the Application Compatibility Toolkit 5.0, it is not nearly as big of a step down as going all of the way to a debugger. The less time you spend in a debugger, the more productive you will be. Hopefully this technique can help you track down more quality and compatibility issues without resorting to scrolling through page after page of symbol-free assembler for somebody else's code!

  • The App Compat Guy

    Creating a Process in Protected Mode on Windows Vista

    • 13 Comments

    Today we were having a discussion around Internet Explorer in Protected Mode, and the question came up regarding how to launch an arbitrary process with a low integrity level. This is fairly straightforward to do in code.

    First, you duplicate the handle of the medium integrity process (the default) that is launching the application. The OpenProcessToken API will find the existing token, and you can then call DuplicateTokenEx to create a copy.

    Next, you use SetTokenInformation to lower the duplicate token to low integrity. The SID for low integrity is S-1-16-4096, and the attributes of the TOKEN_MANDATORY_LABEL's label should be set to SE_GROUP_IDENTITY.

    Finally, you call CreateProcessAsUser to launch the process with the new integrity level.

    This is described, with sample code, on MSDN. See: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/dnwebgen/ProtectedMode.asp.

    Note that you don't have to use a bootstrapper to do this. Rather, you can call CreateProcessAsUser and re-launch yourself. In fact, this is exactly what iexplore.exe does when it is launching. We can watch this with the debugger by setting a few breakpoints.

    First, we'll set a breakpoint on ADVAPI32!OpenProcessToken:

    0:000> kp
    ChildEBP RetAddr
    001ff71c 773a3d59 ADVAPI32!OpenProcessToken
    001ff748 773a3d0b iertutil!GetTokenIntegrityLevel+0x46
    001ff75c 773a4154 iertutil!GetProcessIntegrityLevel+0x21
    001ff76c 773a5fa4 iertutil!IsMICLowProcess+0x11
    001ff774 013813b8 iertutil!IsProtectedModeProcess+0x19
    001ffbac 0138131a iexplore!wWinMain+0x85
    001ffc40 75e03833 iexplore!_initterm_e+0x1b1
    001ffc4c 771fa9bd kernel32!BaseThreadInitThunk+0xe
    001ffc8c 00000000 ntdll!_RtlUserThreadStart+0x23

    Here, we are checking our integrity level - obviously we only want to launch a new process if the current one isn't already running in low IL or we'd be launching new processes forever!

    Later, we call SetTokenInformation:

    0:000> k
    ChildEBP RetAddr
    001ff6b8 773a631b ADVAPI32!SetTokenInformation
    001ff6f4 773a6066 iertutil!IESetTokenIntegrityLevel+0xb1
    001ff714 773a600c iertutil!CreateMICIEProcess+0x4c
    001ff72c 01384815 iertutil!LaunchIEInProtectedMode+0x2a
    001ff774 01381488 iexplore!LaunchLoRIEModeIE+0x5e
    001ffbac 0138131a iexplore!wWinMain+0x253
    001ffc40 75e03833 iexplore!_initterm_e+0x1b1
    001ffc4c 771fa9bd kernel32!BaseThreadInitThunk+0xe
    001ffc8c 00000000 ntdll!_RtlUserThreadStart+0x23

    After we have configured the new token, we launch ourselves again, this time with low IL, using CreateProcessAsUser:

    0:000> k
    ChildEBP RetAddr
    001fe3ec 773a61e1 ADVAPI32!CreateProcessAsUserW
    001ff6ec 773a6079 iertutil!LaunchProtectedModeIEWithToken+0x163
    001ff714 773a600c iertutil!CreateMICIEProcess+0x5f
    001ff72c 01384815 iertutil!LaunchIEInProtectedMode+0x2a
    001ff774 01381488 iexplore!LaunchLoRIEModeIE+0x5e
    001ffbac 0138131a iexplore!wWinMain+0x253
    001ffc40 75e03833 iexplore!_initterm_e+0x1b1
    001ffc4c 771fa9bd kernel32!BaseThreadInitThunk+0xe
    001ffc8c 00000000 ntdll!_RtlUserThreadStart+0x23

    After we do this, a new iexplore.exe is launched, and it's running with a restricted token. You can certainly do the same with your code, and may want to consider this if you are looking to implement and secure an Internet-facing application that does not require access to local resources.

Page 1 of 1 (4 items)