Uninformed: Informative Information for the Uninformed

Vol 4» 2006.Jun



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
Image iereg

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
Image iedereg

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>