Problems of Privilege: Find and Fix LUA Bugs
At a Glance:
- Least-Privilege User Accounts
- Determining a true LUA bug
- Techniques for fixing LUA bugs
You’re probably familiar with the Principle of Least Privilege, which states that a user should be given only the privileges necessary to accomplish his task. It’s an important rule to keep in mind when managing security, and TechNet Magazine has discussed it a number of times. LUA can also introduce some unexpected challenges.
The acronym LUA generally refers to Least-Privilege User Account, but is sometimes defined as Limited User Account, Least User Access, and several other variations. But whatever the letters stand for, the concept is the same. LUA is a computer user account that cannot make changes that affect other users of the system or the operating system itself. In Windows®, these are typically members of the built-in Users group. Members of this group are explicitly not members of powerful groups (such as Administrators, Power Users, and Backup Operators) and they do not hold elevated privileges (like Load and unload device drivers, and Act as part of the operating system). Unfortunately, LUA can surface a number of issues.
A LUA bug occurs when an application—or a feature of an application—works correctly when run with elevated privileges but fails to work for a LUA user when there is no technical or business reason for requiring elevated privileges. A common example is when an application saves its settings to a registry key under HKEY_LOCAL_MACHINE (which is read-only to LUA users), instead of to HKEY_CURRENT_USER.
Another problem arises with system time. Windows XP doesn’t allow LUA users to change the system time. This is not a LUA bug, because changing the system time has security implications with respect to auditing and the Kerberos protocol. The fact that Windows XP doesn’t allow LUA users to change the time zone, however, is arguably a LUA bug. So is the fact that double-clicking the clock in the taskbar’s notification area gives you an error message instead of a read-only view of the Date and Time applet. (Note that Windows Vista™ is heavily focused on a more seamless LUA experience, including improvements to the Date and Time applet.)
By far, the majority of LUA bugs are due to registry and file system access permissions. A program might, for instance, try to save its settings into its installation folder under %ProgramFiles%, or it might try to open a key under HKLM for "All-Access" even if it only needs Read access. Other types of LUA bugs include attempts to start or stop a service, load a device driver, access hardware resources directly, create or manage file shares, or even explicitly check whether the current user is a member of the Administrators group.
There are always one or more low-level operations (API calls) that succeed when performed as admin but fail when performed as LUA. You can see some of these yourself using tools like Regmon and Filemon from SysInternals. Is every one of these really a LUA bug? It depends on how the application responds to the failure. The responses I have seen can be categorized into three groups:
Fire and forget: The application invokes the operation, doesn’t check the result, but doesn’t depend on the operation having succeeded in order to continue working correctly. Not a LUA bug.
Gracefully degrade: The application invokes the operation, checks whether it succeeded, and handles failure in an appropriate way. Not a LUA bug.
True LUA bug: The application invokes the operation, assumes it succeeded, and depends on the operation having succeeded in order to continue working correctly. A variation on this is that the app checks whether the operation succeeded, but handles the failure inappropriately, such as by displaying an error message and falling over dead.
If you’ve ever used Regmon to monitor a GUI app running as LUA, you’ve probably seen an example that can be categorized as fire-and-forget—a failed attempt to open HKLM\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\Joystick\Winmm for All-Access. This occurs during initialization of the joystick subsystem for the process. The operation fails, but it does not affect the correct behavior of your application. However, I have seen advice on the Web (no doubt from people who have misinterpreted Regmon output) claiming that to fix some particular application you need to grant the user full access to this key. Absolutely not! This is not a true LUA bug. You should never need to change permissions on this key!
Before you make changes to security settings, verify that you’re fixing a true LUA bug and not just a phantom, making sure there aren’t better ways to correct the problem without increasing exposure.
How Not to Fix LUA Bugs
You have a standard application that you—or your users—need to run. The application isn’t designed to perform any system administration tasks on your computer, but for some unknown reason, it doesn’t work correctly unless it’s run from an account that has administrator-level access. You need to give your users access to this application, but you don’t want them running as administrators. What should you do?
The most common workaround is simply to add the user to the Administrators group. Sometimes this is done by a helpdesk technician seeing if that fixes the problem. The technician then forgets to remove the user from the Admins group, inevitably leading to another helpdesk call within a few weeks, when the user asks "Why is my computer running so slowly, and why are all these ads popping up whenever I log on?" Let’s just call this workaround a nonstarter and not give it any further consideration.
Two other common (and less than ideal) workarounds are to run just the one program as administrator, or to run the program as a regular user but after granting Everyone "Full Control" over the program’s installation folder, all of its registry keys under HKEY_LOCAL_MACHINE, and all of HKEY_CLASSES_ROOT.
Oh, and while you’re at it, you should remember to grant the user the Debug, "Take ownership", and "Act as part of the operating system" privileges (I’m kidding of course—these are seriously high-risk and should be avoided).
OK, so what should you do? In the rest of this article, I’ll lay out a systematic approach for working around LUA bugs while at the same time minimizing exposure. I’ll discuss approaches from most-preferred to least-preferred, listing some of the pros and cons of each. By the way, while the guidance in this article is primarily geared towards Windows XP, the techniques will also work on Windows Vista.
It’s a Bug—Treat it Like One and Make the Developers Fix It!
This is the best approach. If there is no legitimate reason (business or technical) for the app to require admin privileges, then failure of the app to work for a regular user account is a serious bug that compromises system security, stability, and manageability. Seriously consider having the developers fix the problem directly in the code.
If the development team responds to your request by saying something like "it’s mission-critical so it has to run as admin" or "the app has to run as admin because it writes to HKEY_LOCAL_MACHINE," you should respond by saying "you’re talking nonsense," and insist that they fix the bug.
Benefits: Once the development team has fixed the bug directly in the application code, you don’t need to carry forward any shims, tweaks, or workarounds. In addition, the developers may learn from the experience and avoid creating new LUA bugs. (Note that the number 1 cause of LUA bugs is developers running as admin when writing their code! I discuss this more in the "Loosen Access Control Lists" section.)
Drawbacks: The expense in both time and money can be prohibitive, especially if you have limited resources and a lot of apps that need to be fixed. The app may need to be rearchitected, and new bugs may be introduced in the process.
Another hurdle is that the developers or the source code may not be available. You could be dealing with third-party code from a company that no longer exists. The developers may be in rehab. Or in jail. Or, worse yet, working for your competitor. You get the idea.
Use the Application Compatibility Toolkit
The Application Compatibility Toolkit (ACT) offers useful LUA Mode shims. These shims detect attempts to write to systemwide locations in the file system and registry and silently redirect them to per-user locations. (Windows Vista includes a better equivalent called File and Registry Virtualization.) You can find the Application Compatibility Toolkit at "Application Compatibility: Overview
Benefits: The really compelling reason for using the LUA Mode shims found in the Application Compatibility Toolkit is that it’s easy. This also doesn’t require you to elevate privileges.
Drawbacks: On Windows XP, the LUA Mode shims often do not work. (File and Registry Virtualization in Windows Vista is a complete rewrite and will have much higher compatibility marks than ACT LUA Modes in Windows XP.)
The added complexity of the underlying operations can make troubleshooting more complicated when things don’t work.
Copy Specific HKCR Keys to HKCU\Software\Classes
Prior to Windows 2000, HKCR was just a symbolic link to HKLM\Software\Classes that only administrators could write to. This meant that operations performed on HKCR\.txt actually occur in HKLM\Software\Classes\.txt. Windows 2000 introduced per-user registration data, so now HKCR is a merged view of HKLM\Software\Classes and HKCU\Software\Classes (which the user can write to). If a key exists in the latter, it takes precedence. So now an operation on HKCR\.txt occurs in HKCU\Software\Classes\.txt if that key already exists; if it doesn’t, the operation occurs in HKLM\Software\Classes\.txt as it had in the past.
The problem is that a number of applications write to HKCR at run time to reinforce their file associations, COM registration data, and so on. An error is raised if the write fails, even if the data to be written is already there. The same data is written every time the app runs. If that same registration data were stored in HKCU\Software\Classes, then the write operations would succeed, without changing program behavior.
To fix this, first you must identify the keys under HKCR that the application is trying to write to. Export those keys to one or more .reg files (in the Registry Editor, select File | Export and choose Selected branch). Then using a text editor, replace all instances of [HKEY_CLASSES_ROOT\ with [HKEY_CURRENT_USER\Software\Classes\ and save your changes. When you’re finished, import the edited .reg file into the registry of the user who needs to run the program.
Benefits: This technique fixes issues where applications perform operations in HKCR that should have been done only during installation. This is a better approach than loosening access control on systemwide resources under HKCR. Malware overwriting keys under HKCU will not affect operating system components or other users of the computer.
Drawbacks: Tools have only recently become available for identifying HKCR writes as the source of LUA bugs and isolating which keys are involved. LUA Buglight makes this task much easier.
Back in the days of Windows 3.x, before the advent of the registry we know and love, the OS and applications stored configuration and preference data to .ini files, such as win.ini. Windows did and still does offer API-level support for .ini files via the Profile APIs (such as WritePrivateProfileString). Many apps, including some Windows applets, still use these APIs to try to write to .ini-formatted files, often in folders where users are not supposed to write.
Windows NT® 3.1 encouraged the migration from .ini files to the more scalable and manageable registry, and provided a means for automatically redirecting .ini file reads and writes to registry keys. The internal implementation of the Profile APIs was augmented to use mappings found under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\. If a mapping for an .ini file is not found under that key, then the operation is performed in the file system as before.
If access to an .ini-formatted file—via the Profile APIs—is the cause of a LUA bug, it can be corrected by adding a key under the IniFileMapping key to redirect access to HKCU. Note that IniFileMapping is under HKLM, and to configure this you’ll need administrative privileges. The configuration specifics are described in the documentation for the Profile APIs.
Benefits: This approach avoids loosening access control on systemwide resources in the file system. And again, malware overwriting keys under HKCU will not affect operating system components or other users of the computer.
Drawbacks: IniFileMapping entries specify the file name only and will affect all Profile API access to any file with that name, in any path. This can have side effects on other apps that have their own ini files with the same name.
Update the SafeDisc Driver
A number of games depend on the secdrv device driver—also known as SafeDisc—that comes from Macrovision. The SafeDisc driver that ships with Windows XP is a demand-start driver; users are not allowed to stop and start it, and this results in errors when programs attempt to access it. There is an update (available from Microsoft and from the Macrovision site) that configures the driver to load when the system starts so the user does not need permission to start it. This change allows some games to work correctly for a non-admin user.
(Note that at the time of writing, the Microsoft download page for this update said that this software "will not alter or patch any component on your system; it will only change the startup state of the system component." This is not true—it installs an updated driver.)
Benefits: This update is easy to implement, and there’s no need to make Access Control List (ACL) changes to systemwide resources.
Drawbacks: There aren’t really any drawbacks, except that it is primarily used for correcting problems with games (not productivity apps).
Loosen Access Control Lists
The most common underlying cause of LUA bugs is that developers (and often testers) typically run as admin. A developer may not intentionally require that the end user run as admin, but things that depend on admin access can creep into the code (for example, writing to files in the root folder of the C: drive, in the app’s installation folder under %ProgramFiles%, or in %windir%). The app works fine for the developers and testers, but then you try to run it as a regular user and it crashes.
One option is to change the access control list (ACL) on objects to grant your user the access that the program requires. Typically, the objects that need tweaking are in the registry or in the file system (if using NTFS). This approach requires serious consideration and you should note some caveats.
First, this approach should only be chosen after you’ve tried all of the more preferred options and confirmed that they don’t fix the LUA bug you’re dealing with. If you proceed, be very careful.
ACL changes should only be considered on application-specific resources, not on OS-wide resources. While it might be OK to change the ACL on %ProgramFiles%\VendorX\AppX\DataFolder, you should never change the ACL on %SystemRoot%\System32—to loosen or to tighten access. (For more about this, see the Knowledge Base article "Security configuration guidance support
".) In addition, avoid changing ACLs on program code if at all possible. This is to prevent malware from infecting or replacing application files.
Also avoid changing ACLs on resources (particularly .exe and .dll files) that are used by administrators or services. Doing so increases the risk of elevation of privilege, which can lead to compromising the entire system. (Even so, the attack surface remains far smaller than in a situation where everything always runs as admin.)
Ideally, the resource should only ever be accessed by a single user. If the resource is accessed by multiple non-admin users, there is increased risk of one user causing another user’s account to be compromised.
Finally, you should grant the least amount of additional access that is required to the smallest possible number of resources and to the smallest possible number of users in order to allow the application to work. Granting full control to Everyone on a big chunk of the file system or registry should never be necessary.
Granting additional access only to the computer’s primary user is optimal, but that may be difficult to manage across a large number of systems (for instance, granting MARY permissions on one system, STEVE on another, and so on). If you can define a set of users who need to use the program, you should add them to a group and then grant access to that group.
Alternatively, you can grant access to the built-in INTERACTIVE pseudo group. This will grant the additional access only to whoever is interactively logged on at the time, without granting additional remote access to the resource. Note that in a terminal server or Fast User Switching scenario, there can be multiple simultaneous users on the computer with INTERACTIVE in their tokens.
Benefits: This technique offers a big return on the investment of your time. Most of the LUA bugs that my colleagues and I have seen revolve around file and registry permissions. Tweaking Access Control Lists will probably fix a larger share of your LUA bugs than any other approach.
Drawbacks: This approach is less-preferred, because ACLs are set this way for a reason. Changing them allows otherwise constrained users to change shared resources—for good or for bad—and it makes it easier for one user (or malware unintentionally run by that user) to affect others. If an admin becomes affected, the entire system can be compromised.
In addition, it is not easy to identify precisely which resources should be opened up and by how much. And it’s difficult to know for certain whether opening access to a resource will inadvertently expose an avenue for elevation of privilege, allowing system takeover.
Run Just the One Problematic App with Elevated Privileges
Some apps address LUA bugs by explicitly checking for admin group membership on startup and displaying an error message insisting that you simply have to be an admin to use the program. This may be due to developer laziness, incompetence, or arrogance (or all three), but these apps will be resistant to any other workarounds available to you. As a last resort, after you’ve confirmed that all else fails, you can consider running the one problematic app with elevated groups or privileges.
Instead of running the app as admin, try running it elevated but as less than full admin. You could run the app as a member of Power Users or with a specific privilege, such as SeLoadDriverPrivilege. But with just a little more work, many of these other groups and privileges can be used to take over an entire system.
So how do you do this? If you trust the user with the admin password (or you trust the user to make security decisions) you have four options:
- You can use RunAs. (For more details on this, see my postings ""RunAs" basic (and intermediate) topics" and "RunAs with Explorer".)
- You can use MakeMeAdmin. This is a batch file, which you can easily customize to run something other than a command shell. You can also tweak it to make the elevated context less than full-admin. (For more information on this, see my blog posting.)
- SysInternals offers PsExec and Process Explorer. These apps provide various RunAs-like options.
- Finally, you can use RunAsAdmin. This is an interesting and useful open source utility by Valery Pryamikov. RunAsAdmin takes an approach that is a little bit like the Windows Vista User Account Control feature (UAC), elevating the current user in place without requiring a password.
On the other hand, if you don’t trust the user with the admin password, there are a couple of third-party options that are worth considering.
- PolicyMaker Application Security by DesktopStandard uses a Group Policy extension to configure rules for modifying process tokens. It mitigates some of the drawbacks mentioned below. It can be configured so that child processes launched by a targeted app do not inherit its modified token. And it can perform granular token modification, to raise (or lower) permissions or add (or remove) privileges.
- Winternals (the commercial side of SysInternals) offers Protection Manager, a tool that uses a lightweight client-server application and a whitelist technique to block all untrusted applications. Protection Manager allows applications to have their process tokens and privileges elevated to that of an administrator or reduced to that of a user (in cases where end users are non-administrators or administrators, respectively). Protection Manager doesn’t allow a child process of an elevated app to run elevated unless it is also explicitly configured as an elevated app. All children of reduced privilege processes are reduced automatically. Applications can be allowed, blocked, elevated, or reduced as specified by an administrator via digital signatures, hashes, NTFS file ownership, or path.
PolicyMaker Application Security and Protection Manager both determine in kernel-mode code whether, when, and how to modify a process token. Since passwords are not used, they’re not at risk, and the modification decision cannot be interfered with by non-admins.
Other tools are available that perform RunAs-like operations with the admin account credentials encrypted—or sometimes just obfuscated. Even though this technique raises the bar and will stop some users from getting the admin credentials, those passwords still have to be decrypted within the user’s security context and therefore may be exposed to an attacker with the right tools.
A question I’ve heard asked frequently is whether the RunAs.exe /savecred option can be used as a shortcut to let a user run a single app as admin using a saved password (without requiring further password entry). But this can cause unanticipated problems and there are a number of issues you should be aware of. The credentials are not tied to any one shortcut; once the credentials have been saved they can be used to start any app. While the password is securely encrypted with a user-specific key, it will still be decrypted in the user’s security context and briefly exposed. And the /savecred option is not available on Windows XP Home Edition.
Benefits: With this approach, you are able to avoid always running everything as admin. Unfortunately, that’s about the only benefit for this one.
Drawbacks: Running an application with elevated privileges is much riskier than any of the other options I’ve already described. It’s very difficult to defend a system against a malicious user or a malicious app when there’s an application running as admin. Here’s a simple example. If you run Notepad as admin, and then choose File | Open, you are presented with a little Explorer-like window. You’re still in Notepad, which means you have full admin-level access to the entire file system—you even have the ability to launch programs as admin from here. This simple technique can be exploited by a malicious user or by malware pumping keystrokes or window messages into the elevated program.
The Least-Privilege User Account can be a handy option in your security strategy, but it can present some interesting challenges. Workarounds for these bugs vary greatly in effectiveness and security. Though it can be difficult, it’s important to determine the exact cause of the bug and then address it with the best solution. If you use one of the less preferred techniques, you should understand how to protect your systems as much as possible.
Aaron Margosis is a Senior Consultant with Microsoft Consulting Services. The author of the popular MakeMeAdmin and PrivBar tools, he is a passionate evangelist for the use of "least privilege" on Windows, and has been called "the God of non-admin" by Mark Russinovich. Aaron has been with Microsoft since 1999. Take a look at his "non-admin" blog.
© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited
This article was adapted from posts that have been published on my blog
. There you’ll find more information about the concept of running with least privilege and how to take advantage of the benefits as well as deal with the challenges.