IE8 behavior notes
MS09-019 contains the fix for the IE8 vulnerability responsibly disclosed by Nils at the CanSecWest pwn2own competition (CVE-2009-1532).
Nils exploited this vulnerability on an IE8 build that did allow .NET assemblies to load in the Internet Zone. The final, released build of IE8 does not allow .Net assemblies to load in the Internet Zone. IE8’s behavior around .NET assemblies is significant because the Dowd/Sotirov ASLR+DEP bypass [DowdSotirov08] required .NET to place shellcode at a known location with correct page permissions. We posted a blog entry about this bypass in March.
We do not expect to see this vulnerability exploited in the wild on Vista or above because (a) the released build of IE8 does not allow .NET assemblies to load from the Internet Zone, and (b) IE8 has DEP enabled by default.
You can change IE8’s behavior around loading .NET assemblies in different zones. By default, they are not allowed to load in the Internet Zone but they are allowed in the Intranet Zone. If you’d like to disable .NET assemblies in the Intranet Zone, set this regkey to 3 (disabled):
If you’d like to allow IE8 to load .NET assemblies in the Internet Zone, you can change this regkey to 0 (enabled):
As discussed above, if you need to re-enable IE8 loading .NET assemblies in the Internet Zone, we encourage you to do so only as a short term workaround due to the attack surface it exposes. If you use applications that require .NET in the Internet Zone, we recommend that you migrate them to either ClickOnce deployment or the Windows Presentation Foundation (WPF).
The root cause of this vulnerability was the use of an object after deleting it (EIP is pointing to heap). Due to a failure to check an error code we end up calling this already freed object.
The root cause of this vulnerability was the use of an object after deleting it (EIP is pointing to heap). Due to a failure to check an error code we end up calling this already freed object. Let’s focus on the following snipped of code to understand the error code check failure:
HRESULT CElementCollectionBase::VersionedGetDispID(BSTR bstrName, DWORD grfdex, __out DISPID *pdispidMember, __in IUnknown *pUnkContext, OMVersion version, OMVersionFlag versionFlags, __in PFNGETDISPID *pfnTearoffGetDispID)
HRESULT hr = S_OK;
hr = THR(_pCollectionCache->EnsureAry(_lIndex)); -- hr will be FALSE due to various reasons and since the object is not available
-- please note S_FALSE = 1 and FALSE = 0
if (hr) -- However we check if it is S_FALSE=1
goto Cleanup; -- and we continue without going directly to the cleanup section
hr = DispatchGetDispIDCollection(this,
// we pass in NULL to the above and then handle this ourselves in a versioned fashion.
if ( hr || (!hr && *pdispidMember == DISPID_UNKNOWN) )
hr = super::VersionedGetDispID(bstrName, grfdex, pdispidMember, pUnkContext, version, versionFlags, NULL);
IE7 returns S_FALSE (1) instead of FALSE (0) on the above call when the object is no longer available. With S_FALSE IE7 correctly goes into the cleanup code without referencing this already deleted object so it is not affected.
In order to clean the area for similar issues, we developed some static analysis tools to detect this kind of error check failures (S_FALSE != FALSE). These analysis tools are going to be included in future security testing stages on the next versions of different products so we do not make the same mistake again.
Reference: [DowdSotirov08] Bypassing browser memory protections in Windows Vista, Alexander Sotirov and Mark Dowd
Fermin J. Serna and Chengyun, MSRC Engineering
*Postings are provided "AS IS" with no warranties, and confers no rights.*