Copy Link
Add to Bookmark
Report
uninformed 08 03
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.