Case Study: Internet Explorer
Unfortunately for Internet Explorer, it's time for it to once again
dawn the all-too-exploitable hat and tell us about how it can be
used as a medium to gain arbitrary code execution with all otherwise
non-exploitable bugs. In this approach, Internet Explorer is used
as a medium for causing DLLs that register and deregister top-level
UEFs to be loaded and unloaded. One way in which an attacker can
accomplish this is by using Internet Explorer's facilities for
instantiating COM objects from within the browser. This can be
accomplished either by using the new ActiveXObject
construct in JavaScript or by using the HTML OBJECT tag.
In either case, when a COM object is being instantiated, the DLL
associated with that COM object will be loaded into memory if the
object instance is created using the INPROC_SERVER. When
this happens, the COM object's DllMain will be called. If
the DLL has an unhandled exception filter, it may be registered
during CRT initialization as called from the DLL's entry point. This
takes care of the registering of UEFs, so long as COM objects that
are associated with DLLs that set UEFs can be found.
To control the deregister phase, it is necessary to somehow cause
the DLLs associated with the previously instantiated COM objects to
be unloaded. One approach that can be taken to do this is attempt
to leverage the locations that
ole32!CoFreeUnusedLibrariesEx is called from. One
particular place that it's called from is during the closure of an
Internet Explorer window that once hosted the COM object. When this
function is called, all currently loaded COM DLLs will have their
DllCanUnloadNow routines called. If the routine returns
S_OK, such as when there are no outstanding references to
COM objects hosted by the DLL, then the DLL can be unloaded.
Now that techniques for controlling the loading and unloading of
DLLs that set UEFs has been identified, it's necessary to come up
with an implementation that will allow the deregister phase to occur
asymmetrically. One method that can be used to accomplish this
illustrated by the registration phase in figure and
the deregistration phase in figure .
Figure:
Registering Top-Level UEFs through COM Objects
|
In the example described in figure , two windows are
opened, each of which registers a UEF by way of a DLL that
implements a specific COM object. In this example, the first window
instantiates COMObject1 which is implemented by DLL #1.
When DLL #1 is loaded, it registers a top-level UEF Fx.
Once that completes, the second window is opened which instantiates
COMObject2, thus causing DLL #2 to be loaded which also
registers a top-level UEF, Gx. Once these operations
complete, DLL #1 and DLL #2 are still resident in memory and the
top-level UEF points to Gx.
To gain control of the top-level UEF, Fx and Gx
will need to be deregistered asymmetrically. To accomplish this,
DLL #1 must be unloaded before DLL #2. This can be done by
closing the window that hosts COMObject1, thus causing
ole32!CoFreeUnusedLibrariesEx to be called which results in
DLL #1 being unloaded. Following that, the window that hosts
COMObject2 should be closed, once again causing unused
libraries to be freed and DLL #2 unloaded. The diagram in figure
illustrates this process.
Figure:
Deregistering Top-Level UEFs through COM Objects
Asymmetrically
|
After the process in figure completes, Fx
will be the top-level UEF for the process, even though the DLL that
hosts it, DLL #1, has been unloaded. If an exception occurs at
this point in time, the unhandled exception filter will make a call
to a function that now points to an invalid region of memory.
At this point, an attacker now has reasonable control over the
top-level UEF but is still in need of some approach that can used to
place his or her code at the location that Fx resided at.
To accomplish this, attackers can make use of the
heap-spraying[8,7] technique that has been
commonly applied to browser-based vulnerabilities. The purpose of
the heap-spraying technique is to consume an arbitrary amount of
memory that results in the contents of the heap growing toward a
specific address region. The contents, or spray data, is arbitrary
code that will result in an attacker's direct or indirect control of
execution flow once the vulnerability is triggered. For the purpose
of this paper, the trigger is the generation of an arbitrary
exception.
As stated above, the heap-spraying technique can be used to place
code at the location that Fx resided. However, this is
limited by whether or not that location is close enough to the heap
to be a practical target for heap-spraying. In particular, if the
heap is growing from 0x00480000 and the DLL that contains
Fx was loaded at 0x7c800000, it would be a
requirement that roughly 1.988 GB of data be placed in the
heap. That is, of course, assuming that the target machine has
enough memory to contain this allocation (across RAM and swap). Not
to mention the fact that spraying that much data could take an
inordinate amount of time depending on the speed of the machine. For
these reasons, it is typically necessary for the DLL that contains
Fx in this example scenario to be mapped at an address that
is as close as possible to a region that the heap is growing from.
During the research of this attack vector, it was found that all of
the COM DLLs provided by Microsoft on XPSP2 are compiled to load at
higher addresses which make them challenging to reach with
heap-spraying, but it's not impossible. Many 3rd party COM DLLs,
however, are compiled with a default load address of
0x00400000, thus making them perfect candidates for this
technique. Another thing to keep in mind is that the preferred load
address of a DLL is just that: preferred. If two DLLs have the same
preferred load address, or their mappings would overlap, then
obviously one would be relocated to a new location, typically at a
lower address close to the heap, when it is loaded. By keeping this
fact in mind, it may be possible to load DLLs that overlap, forcing
relocation of a DLL that sets a UEF that would otherwise be loaded
at a higher address.
It is also very important to note that a COM object does not have to
be successfully instantiated for the DLL associated with it to be
loaded into memory. This is because in order for Internet Explorer
to determine whether or not the COM class can be created and is
compatible with one that may be used from Internet Explorer, it must
load and query various COM interfaces associated with the COM class.
This fact is very useful because it means that any DLL that hosts a
COM object can be used -- not just ones that host COM objects that
can be successfully instantiated from Internet Explorer.
The culmination of all of these facts is a functional proof of
concept exploit for Windows XP SP2 and the latest version of
Internet Explorer with all patches applied prior to MS06-051. Its
one requirement is that the target have Adobe Acrobat installed.
Alternatively, other 3rd party (or even MS provided DLLs) can be
used so long as they can be feasibly reached with heap-spraying
techniques. Technically speaking, this proof of concept exploits a
NULL pointer dereference to gain arbitrary code execution.
It has been implemented as an exploit module for the 3.0 version of
the Metasploit Framework.
The following example shows this proof of concept in action:
msf exploit(windows/browser/ie_unexpfilt_poc) > exploit
[*] Started reverse handler
[*] Using URL: http://x.x.x.x:8080/FnhWjeVOnU8NlbAGAEhjcjzQWh17myEK1Exg0
[*] Server started.
[*] Exploit running as background job.
msf exploit(windows/browser/ie_unexpfilt_poc) >
[*] Sending stage (474 bytes)
[*] Command shell session 1 opened (x.x.x.x:4444 -> y.y.y.y:1059)
msf exploit(windows/browser/ie_unexpfilt_poc) > session -i 1
[*] Starting interaction with 1...
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\mmiller\Desktop>
|