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.