Getting out of Jail: Escaping Internet Explorer Protected Mode September, 2007 Skywing Skywing@valhallalegends.com http://www.nynaeve.net Abstract: With the introduction of Windows Vista, Microsoft has added a new form of mandatory access control to the core operating system. Internally known as "integrity levels", this new addition to the security manager allows security controls to be placed on a per-process basis. This is different from the traditional model of per-user security controls used in all prior versions of Windows NT. In this manner, integrity levels are essentially a bolt-on to the existing Windows NT security architecture. While the idea is theoretically sound, there does exist a great possibility for implementation errors with respect to how integrity levels work in practice. Integrity levels are the core of Internet Explorer Protected Mode, a new "low-rights" mode where Internet Explorer runs without permission to modify most files or registry keys. This places both Internet Explorer and integrity levels as a whole at the forefront of the computer security battle with respect to Windows Vista. 1) Introduction Internet Explorer Protected Mode is a reduced-rights operational mode of Internet Explorer where the security manager itself enforces a policy of not allowing write access to most file system, registry, and other securable objects by default. This mode does provide special sandbox file system and registry space that is permitted to be written to by Internet Explorer when operating in Protected Mode. While there exist some fundamental shortcomings of Protected Mode as it is currently implemented, such as an inability to protect user data from being read by a compromised browser process, it has been thought to be effective at blocking most write access to the system from a compromised browser. The benefit of this is that if one is using Internet Explorer and a buffer overrun occurs within IExplore.exe, the persistent impact should be lessened. For example, instead of having write access to everything accessible to the user's account, exploit code would instead be limited to being able to write to the low integrity section of the registry and the low integrity temporary files directories. This greatly impacts the ability of malware to persist itself or compromise a computer beyond just IExplore.exe without some sort of user interaction (such as persuading a user to launch a program from an untrusted location with full rights, or other social engineering attacks). 2) Protected Mode and Integrity Levels Internally, Protected Mode is implemented by running IExplore.exe as a low integrity process. With the default security descriptor that is applied to most securable objects, low integrity processes may not generally request access rights that map to GENERIC_WRITE for a particular object. As Internet Explorer does need to be able to persist some files and settings, exceptions can (and are) carved out for low integrity processes in the form of registry keys and directories with special security descriptors that grant the ability for low integrity processes to request write access. Because the IExplore process cannot write files to a location that would be automatically used by a higher integrity process, and it cannot request dangerous access rights to other running processes (such as the ability to inject code via requesting PROCESS_VM_WRITE or the like), malware that runs in the context of a compromised IExplore process is (theoretically) fairly contained from the rest of the system. However, this containment only holds as long as the system happens to be free of implementation errors. Alas, but perhaps not unexpectedly, there are in fact implementation problems in the way the system manages processes running at differing integrity levels that can be leveraged to break out of the Protected Mode (or low integrity) jail. To understand these implementation errors, it is first necessary to gain a basic working understanding of how the new integrity-based security model works in Windows. The integrity model is key to a number of Windows Vista features, including UAC (User Account Control). When a user logs on to a computer in Windows Vista with UAC enabled, their shell is normally started as a ``medium'' integrity process. Integrity levels are integers and symbolic designations such as ``low'', ``medium'', ``high'', or ``system'' are simply used to indicate certain well-known intermediate values). Medium integrity is the default integrity level even for built-in administrators (except the default ``Administrator'' account, which is a special case and is exempted from UAC). Most day to day activity is intended to be performed at medium integrity; for instance, a word processor program would be expected to operate at medium integrity, and (theoretically) games would generally run at medium integrity as well. Games tend to be rather poorly written in terms of awareness of the security system, however, so this tends to not really be the case, at least not without added help from the operating system. Medium integrity roughly corresponds to the environment that a limited user would run as under previous versions of Windows. That is to say, the user has read and write access to their own user profile and their own registry hive, but not write access to the system as a whole. Now, when a user launches Internet Explorer, an IExplore.exe process is launched as low integrity. The default security descriptor for most objects on Windows prevents low integrity processes from gaining write access to medium integrity securable objects, as previously mentioned. In reality, the default security descriptor denies write access to higher integrities, not just to medium integrity, though in this case the effect is similar in terms of Internet Explorer. As a result, the IExplore.exe process cannot write directly to most locations on the system. However, Internet Explorer does, in certain cases, need to gain write to locations outside of the low integrity (Protected Mode) sandbox. For this task, Internet Explorer relies on a helper process, known as ieuser.exe, which runs at medium integrity level. There is a tightly controlled RPC interface between ieuser.exe and IExplore.exe that allows Internet Explorer, running at low integrity, to request that ieuser.exe display a dialog box asking the user to, say, choose a save location for a file and then save said file to disk. This is the mechanism by which one can save files in their home directory even under Protected Mode. Because the RPC interface only allows IExplore.exe to use the RPC interface to request that a file to be saved, a program cannot directly abuse the RPC interface to write to arbitrary locations, at least not without user interaction. Part of the reason why the RPC interface cannot be trivially abused is that there also exists some protection baked into the window manager that prevents a thread at a lower integrity level from sending certain, potentially dangerous, messages to threads at a higher integrity level. This allows ieuser.exe to safely display user interface on the same desktop as the IExplore.exe process without malicious code in the Internet Explorer process simply being able to simulate fake keystrokes in order to cause it to save a dangerous file to a dangerous location without user interaction. Most programs that are integrity-level aware operate with the same sort of paradigm that Internet Explorer does. In such programs, there is typically a higher integrity broker process that provides a tightly controlled interface to request that certain actions be taken, with the consent of the user. For example, UAC has a broker process (a privileged service) that is responsible for displaying the consent user interface when the user tries to perform an administrative task. This operates similar in principal to how Internet Explorer can provide a security barrier through Protected Mode because the lower privileged process (the user program) cannot magically elevate itself to full administrative rights in the UAC case (which runs a program at high integrity level, as opposed to the default medium integrity level). Instead, it could only ask the service to display the consent UI, which is protected from interference by the program requesting elevation due to the window manager restrictions on sending dangerous messages to a higher integrity level window. 2) Breaking the Broker If one has been using Windows Vista for some time, none of the behavior that has just been described should come across as new. However, there are some cases that have not yet been discussed which one might have observed from time to time with Windows Vista. For example, although programs are typically restricted from being able to synthesize input across integrity levels, there are some limited circumstances where this is permitted. One easy to see instance of this is the on-screen keyboard program (osk.exe) which, despite running without a UAC prompt, can generate keyboard input messages that are transmitted to other processes, even elevated administrative processes. This would at first appear to be a break in the security system; questions along the lines of "If one program can magically send keystrokes to higher integrity processes, why can't another?" come to mind. However, there are in fact some carefully-designed restrictions that are intended to prevent a user (or a program) from arbitrarily being able to execute custom code with this ability. First of all, in order to request special access to send unrestricted keyboard input, a program's main executable must resolve to a path within the Program Files or Windows directory. Although the author feels that such a check is essentially a giant hack at best, it does effectively prevent a "plain user" running at medium integrity from being able to run custom code that can synthesize keystrokes to high integrity processes, as a plain user would not be able to write to any of these directories. Additionally, any such program must also be signed with a valid digital signature from any trusted code signing root. This is a fairly useless check from a security perspective, in the author's opinion, as anybody can pay a code signing authority to get a code signing certificate in their own name; code signing certificates are not a guarantee of malware-free (or even bug-free) code. Although it would be easy to bypass the second check with a payment to a certificate issuing authority, a plain user cannot so easily bypass the first check relating to the restriction on where the program main executable may be located. Even if a user cannot launch custom code directly as a program with access to simulate keystrokes to higher integrity processes (known as "uiaccess" internally), one would tend to get the impression that it would be possible to simply inject code into a running osk.exe instance (or other process with uiaccess). This fails as well, however; the process that is responsible for launching osk.exe (the same broken service that is responsible for launching the UAC consent user interface, the "Application Information" (appinfo) service) creates osk.exe with a higher than normal integrity level in order to use the integrity level security mechanism to block users from being able to inject code into a process with access to simulate keystrokes. When the appinfo service receives a request to launch a program that may require elevation, which occurs when ShellExecute is called to start a program, it will inspect the user's token and the application's manifest to determine what to do. The application manifest can specify that a program runs with the user's integrity level, that it needs to be elevated (in which case a consent user interface is launched), that it should be elevated if and only if the current user is a non-elevated administrator (otherwise the program is to be launched without elevation), or that the program requests the ability to perform keystroke simulation to high integrity processes. In the case of a launch request for a program requesting uiaccess, appinfo!RAiLaunchAdminProcess is called to service the request. The process is then verified to be within the (hardcoded) set of allowed directories by appinfo!AiCheckSecureApplicationDirectory. After validating that the program is being launched from within an allowed directory, control is eventually passed to appinfo!AiLaunchProcess which performs the remaining work necessary to service the launch request. At this point, due to the "secure" application directory requirement, it is not possible for a limited user (or a user running with low integrity, for that matter) to place a custom executable in any of the "secure" application directories. Now, the appinfo service is capable of servicing requests from processes of all integrity levels. Due to this fact, it needs to be capable of determining the correct integrity level to create a new process from at this point. Because the new process is not being launched as a full administrator in the case of a process requesting uiaccess, no consent user interface is displayed for elevation. However, the appinfo service does still need a way to protect the new process from any other processes running as that user (as access to synthesize keystrokes is considered sensitive). For this task, the appinfo!LUASetUIAToken function is called by appinfo to protect the new process from other plain user processes running as the calling user. This is accomplished by adjusting the token that will be used to create the new process to run at a higher integrity level than the caller, unless the caller is already at high integrity level (0x3000). The way LUASetUIAToken does this is to first try to query the linked token associated with the caller's token. A linked token is a second, shadow token that is assigned when a computer administrator logs in with UAC enabled; in the UAC case, the user normally runs as a restricted version of themselves, without their administrative privileges (or Administrators group membership), and at medium integrity level. If the calling user does indeed have a linked token, LUASetUIAToken retrieves the integrity level of the linked token for use with the new process. However, if the user doesn't have a linked token (i.e. they are logged on as a true plain user and not an administrator running without administrative privileges), then LUASetUIAToken uses the integrity level of the caller's token instead of the token linked with the caller's token (in other words, the elevation token). In the case of a computer administrator this approach would normally provide sufficient protection, however, for a limited user, there exists a small snag. Specifically, the integrity level that LUASetUIAToken has retrieved matches the integrity level of the caller, so the caller would still have free reign over the process. To counteract this issue, there is an additional check baked into LUASetUIAToken to determine if the integrity level that was selected is at (or above) high integrity. If the integrity level is lower than high integrity, LUASetUIAToken adds 16 to the integrity level (although integrity levels are commonly thought of as just having four values, that is, low, medium, high, and system, there are 0x1000 unnamed integrity levels in between each named integrity level). So long as the numeric value of the integrity level chosen is greater than the caller's integrity level, the new process will be protected from the caller. In the case of the caller already being a full, elevated administrator, there's nothing to protect against, so LUASetUIAccess doesn't attempt to raise the integrity level above high integrity. After determining a final integrity level, LUASetUIAToken changes the integrity level in the token that will be used to launch the new process to match the desired integrity level. At this point, appinfo is ready to create the process. If needed, the user profile block is loaded and an environment block is created, following which advapi32!CreateProcessAsUser is called to launch the uiaccess-enabled application for the caller with a raised integrity level. After the process is created, the output parameters of CreateProcessAsUser are marshalled back into the caller's process, and AiLaunchProcess signals successful completion to the caller. If one has been following along so far, the question of ``How does all of this relate to Internet Explorer Protected Mode'' has probably crossed one's mind. It turns out that there's a slight deficiency in the protocol outlined above with respect to creating uiaccess processes. The problem lies in the fact that AiLaunchProcess returns the output parameters of CreateProcessAsUser back to the caller's process. This is dangerous, because in the Windows security model, security checks are done when one attempts to open a handle; after a handle is opened, the access rights requested are forever more associated with that handle, regardless of who uses the handle. In the case of appinfo, this turns out to be a real problem because appinfo, being the creator of the new process, is handed back a thread and process handle that grant full access to the new thread and process, respectively. Appinfo then marshals these handles back to the caller (which may be running at low integrity level). At this point, a privilege escalation problem has occured; the caller has been essentially handed the keys to a higher integrity process. While the caller would never normally be able to open a handle to the new process on its own, in this case, it doesn't have to, as the appinfo service does so on its behalf and returns the handles back to it. Now, in the ShellExecute case, the client stub for the appinfo AiLaunchAdminProcess routine doesn't want (or need) the process or thread handles, and closes them immediately after. However, this is obviously not a security barrier, as this code is running in the untrusted process and could be patched out. As such, there exists a privilege escalation hole of sorts with the appinfo service. It can be abused to, without user interaction, leak a handle to a higher integrity process to a low integrity process (such as Internet Explorer when operating in Protected Mode). Furthermore, even Internet Explorer in Protected Mode, running at low integrity, can request to launch an already-existing uiaccess-flagged executable, such as osk.exe (which is conveniently already in a "secure" application directory, the Windows system directory). With a process and thread handle as returned by appinfo, it is possible to inject code into the new process, and from there, as they say, the rest is history. 3) Caveats Although the problem outlined in this article is indeed a privilege escalation hole, there are some limitations to it. First of all, if the caller is running as a plain user instead of a non-elevated administrator, appinfo creates the uiaccess process with integrity level 0x1010 (low integrity + 16). This is still less than medium integrity (0x2000), and thus in the true limited user case, the new process, while protected from other low integrity processes, is still unable to interfere with medium integrity processes directly. In the case where a user is running as an administrator but is not elevated (which happens to be the default case for most Windows Vista users), it is true that appinfo.exe returns a handle to a process running at high integrity level. However, only the integrity level is changed; the process is most certainly not an administrator (and in fact has BUILTIN\Administrators as a deny only SID). This does mean that the new process is quite capable of injecting code into any processes the user has started though (with zero user interaction). If the user happens to already have a high integrity process running on the desktop as a full administrator, the new process could be used to attack it as the process would be running at the same integrity level and it would additionally be running as the same user. This means that in the default configuration, this issue can be used to escape from Protected Mode, but one is still not given full-blown administrative access to the system. However, any location in the user profile directory could be written to. This effectively eliminates the security benefit of Protected Mode for a non-elevated administrator (with respect to treating the user as a plain user). Source code to a simple program to demonstrate the appinfo service issue is included with the article. The problem is at this point expected to be fixed by Windows Vista Service Pack 1 and Windows Server 2008 RTM. The sample code launches osk.exe with ShellExecute, patches out the CloseHandle calls in ShellExecute to retain the process and thread handles, and then injects a thread into osk.exe that launches cmd.exe. The sample program also includes a facility to create a low integrity process to verify correct function; the intended use is to launch a low integrity command shell, verify that directories such as the user profile directory cannot be written to, and then use the sample program from the low integrity process to launch a medium integrity cmd.exe instance without user interaction, which does indeed have free reign of the user profile directory. The same code will operate in the context of Internet Explorer in Protected Mode, although in the interest of keeping the example clear and concise, the author has not included code to inject the sample program in some form into Internet Explorer (which would simulate an attack on the browser). Note that while the uiaccess process is launched as a high integrity process, it is configured such that unless a token is explicitly provided that requests high integrity, new child processes of the uiaccess process will launch as medium integrity processes. It is possible to work around this issue and retain high integrity with the use of CreateProcessAsUser by code injected into the uiaccess process if desired. However, as described above, simply retaining high integrity does not provide administrative access on its own. If there are no other high integrity processes running as the current user on the current desktop, running as high integrity and running as medium integrity with the non-elevated token are functionally equivalent, for all intents and purposes. 4) Conclusion UAC, Internet Explorer Protected Mode, and the integrity level model represent an entirely new way of thinking about security in the Windows world. Traditionally, Windows security has been a user-based model, where all processes that execute as a user were considered equally trusted. Windows Vista and Windows Server 2008 are the first steps towards changing this model to support the concept of a untrusted process (as opposed to an untrusted user). While this has the potential to significantly benefit end user security, as is the case with Internet Explorer Protected Mode, there are bound to be bumps along the way. Writing an integrity level broker process is difficult. It is very easy to make simple mistakes that compromise the security of the integrity level mechanism, as the appinfo issue highlights. The author would like to think that by shedding light on this type of programming error, future issues of a similar vein may be prevented before they reach end users.