Internet Explorer URLMON class factory uninitialized memory vulnerability

CVE-2007-0218

Vendor notification by iDefense: Oct 24, 2006
Independently rediscovered by me: Jan 18, 2007

Vendor patch: MS07-033

Public disclosure: Jun 12, 2007

Systems affected

  • Internet Explorer 5
  • Internet Explorer 6

Not affected

  • Internet Explorer 7

Overview

There is a memory corruption vulnerability in Internet Explorer. This vulnerability can be exploited by a malicious web page and results in remote code execution with the privileges of the logged-on user. The vulnerable code is present in Internet Explorer 5 and 6.

Unfortunately, it turned out that iDefense had already reported the same vulnerability to Microsoft before it was rediscovered by me. They got the credit for it in the MS07-033 bulletin.

Technical details

The vulnerability is a result of an uninitialized pointer returned by the CreateKnownProtocolInstance function in URLMON.DLL. This function is called by the CUrlClsFact class factory to instantiate COM objects implementing protocol handlers.

HRESULT CUrlClsFact::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
{
    if (class is a protocol handler)
        return CreateKnownProtocolInstance(type, classid, pUnkOuter, riid, ppvObject);
}

HRESULT CreateKnownProtocolInstance(unsigned long type, REFCLSID classid,
                                    IUnknown* pUnkOuter, REFIID riid, IUnknown **ppvObject)
{
    CINet* proto;

    switch (type) {
        case 1: proto = (CINet*)new CInetHttp(CLSID_HttpProtocol, pUnkOuter);
                break;
        ...
    }

    if (riid == IID_IUnknown) {
        if (pUnkOuter != NULL)
            *ppvObject = proto->GetIUnkInner(1);

        // ppvObject is left uninitialized if pUnkOuter is NULL
        return S_OK;
    }

    ...

    // return the new COM object
    *ppvObject = proto;
    return S_OK;
}

The expected behavior of the function is to create a new object and return a pointer to it in the ppvObject output parameter. However, if the pUnkOuter argument is NULL and riid is IID_IUnknown, the function returns S_OK without writing the object pointer in ppvObject. The caller of CUrlClsFact::CreateInstance will try to access the returned COM object pointer, but in fact it will be accessing the previous contents of the ppvObject variable.

If we try to instantiate one of the protocol handler CLSIDs in Internet Explorer, the vulnerable function will be called from MSHTML.DLL with pUnkOuter set to NULL, riid set to IID_IUnknown and ppvObject pointing to a NULL field in the COleSite object. These arguments will trigger the bug and the function will return without changing the value of ppvObject. Since its value was NULL before the call, it will remain NULL. Fortunately the MSHTML.DLL code checks for a NULL pointer and aborts the class instantiation without crashing.

To exploit this vulnerability, the protocol handler object has to be instantiated on a page that uses the Microsoft ActiveX License Manager, a rarely used component of Internet Explorer. The license manager implements a licensing scheme for controling the redistribution of ActiveX controls. To load the license manager, a web page instantiates the License Manager ActiveX control and supplies it with a license package file. Once it is loaded, the license manager intercepts all class instantiations and verifies that the license package contains a valid key for the new ActiveX control.

Instantiating a protocol handler CLSID on a page with an active license manager leads to a call to licmgr10!CLicenseManager::CreateInstance, which calls CoCreateInstnace in OLE32.DLL. This function creates a CUrlClsFact class factory and calls its CreateInstance method to create the protocol handler object. The arguments of CUrlClsFact::CreateInstance are the same as when it was called directly from Internet Explorer, with one difference: ppvObject points to a stack variable that contains a pointer to the CUrlClsFact class factory. When the function returns, the ppvObject variable still points to the class factory, which is freed before CoCreateInstance returns.

The end result is that CLicenseManager::CreateInstance returns a pointer to a freed chunk of memory that is used as a COM object by the caller. Since the size of the chunk is only 32 bytes, it is placed on the lookaside linked list and its first four bytes are overwritten with a pointer to the next chunk on the list. These four bytes are used as a vtable pointer when the object is accessed. By setting up a fake vtable on the lookaside list, it becomes possible to take control of the code execution and jump to shellcode.

Proof of concept

The following .HTML file will trigger the vulnerability:

<html>
<body>
    <object classid="CLSID:5220cb21-c88d-11cf-b347-00aa00a28331"></object>
    <object classid="CLSID:79eac9e2-baf9-11ce-8c82-00aa004ba90b"></object>
</body>
</html>

The 5220cb21-c88d-11cf-b347-00aa00a28331 CLSID instantiates the License Manager control. The second CLSID can be any of the following protocol handler classes:

79eac9e2-baf9-11ce-8c82-00aa004ba90b
79eac9e2-baf9-11ce-8c82-00aa004ba90b
79eac9e3-baf9-11ce-8c82-00aa004ba90b
79eac9e4-baf9-11ce-8c82-00aa004ba90b
79eac9e5-baf9-11ce-8c82-00aa004ba90b
79eac9e6-baf9-11ce-8c82-00aa004ba90b
79eac9e7-baf9-11ce-8c82-00aa004ba90b

Solution

To fix this vulnerability, the CreateKnownProtocolInstance function should be modified to return the correct interface pointer when IID_IUnknown is requested and pUnkOuter is NULL. The changes are marked with '+' signs in the pseudocode below:

   if (riid == IID_IUnknown) {
       if (pUnkOuter != NULL)
           *ppvObject = proto->GetIUnkInner(1);
+      else
+          proto->QueryInterface(riid, ppvObject);   // TODO: proper reference counting

       return S_OK;
   }

In Internet Explorer 7, the entire if statement for IID_IUnknown has been removed and the function always returns a pointer to the CINet object. This is another option for fixing the vulnerability.

Credit

Originally discovered by an anonymous iDefense contributor.

Independently rediscovered by Alexander Sotirov.