The Active Template Library (ATL) is a set of template-based C++ classes that simplify the development of Component Object Model (COM) objects. These objects have a wide range of use in Windows and Web solutions and, as such, their safe use and construction is a key requirement for developers delivering these solutions.
On July 28 2009, Microsoft announced that ATL was impacted in Security Advisory (973882) and provided updated versions of ATL with the release of security bulletin MS09-035. This bulletin addresses an issue with ATL that may affect developers who built ActiveX components using ATL. An additional bulletin MS09-034 addresses changes to Internet Explorer that help mitigate the risks from vulnerable ActiveX controls.
Note: The Internet Explorer bulletin provides more detail on new defense in depth options for this issue and we recommend that you install the update.
Microsoft has released an ATL header and library update that addresses these problems for Visual Studio 2003 and later versions. Developers can download the update from the security bulletin located here. Depending on the version of Visual Studio or Visual C++ that a developer is using, there are various steps that must be taken to correct the issue.
Note: Visual Studio 2002 and earlier products are not supported and we recommend that you move to a mainstream supported release. Visual Studio 2003 is in Extended Support and again we recommend moving to a mainstream supported release. More information on support for Microsoft developer tools can be found at the Microsoft Support Developer Tools page.
Note: The Windows WDK includes a source code version of ATL that is also impacted by this issue. You should apply the updates and use the Visual Studio version of ATL to implement this guidance.
To ask questions about how to use the Visual Studio ATL headers and libraries with your SDK/DDK, please use the Visual C++ Language Forum.
You need to review the source code of your control in order to assess whether your control is impacted. The following is a step-by-step guide for the code review process.
First, while we believe that most affected COM components would be ActiveX controls, we want to emphasize here that this ATL vulnerability applies to any COM component built with ATL if the necessary conditions are present.
The diagram below provides an easy way of identifying vulnerable controls:
For more information about what it means to be SFI (or Safe for Scripting for devices as above), please refer to Not safe = not dangerous? How to tell if ActiveX vulnerabilities are exploitable in Internet Explorer and MSDN: Safe Initialization and Scripting for ActiveX Controls.
If your ActiveX control is not marked as SFI (or Safe for Scripting for devices, as above), there is no attack vector to your ActiveX control from IE.
If PROP_ENTRY/ PROP_ENTRY_EX are used, or if PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX are used with the VT_EMPTY, VT_DISPATCH or VT_UNKNOWN types, then your control might be vulnerable.
Example:
BEGIN_PROP_MAP(CMSWebDVD)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
PROP_ENTRY("DisableAutoMouseProcessing", 70, CLSID_NULL) // vulnerable
PROP_ENTRY("BackColor", DISPID_BACKCOLOR, CLSID_StockColorPage)
PROP_ENTRY("EnableResetOnStop", 66, CLSID_NULL)
PROP_ENTRY("ColorKey", 58, CLSID_NULL)
PROP_ENTRY("WindowlessActivation", 69, CLSID_NULL)
PROP_ENTRY_TYPE("Param5", 5, CLSID_NULL, VT_DISPATCH) // vulnerable, VT_DISPATH used
PROP_ENTRY_TYPE("Param7", 7, CLSID_NULL, VT_UNKNOWN) // vulnerable, VT_UNKNOWN used
END_PROP_MAP()
Any use of CComVariant::ReadFromStream(pStream) method with non-trusted data is unsafe. While calling CComVariant::ReadFromStream would most likely be part of an implementation of IPersistStreamInit, it can also be used elsewhere.
Unsupported versions of Visual Studio, Visual C++, and ATL may be affected by this issue. If you are using an unsupported version, you should move to a fully supported version of Visual Studio, such as Visual Studio 2008. However, if you are using Visual Studio 6.0, Visual C++ 6.0 or earlier, you may also be able to address the vulnerability by:
Microsoft recommends using a version of Visual Studio that is in current, mainstream support. This ensures that you have the broadest array of support options available to you.
While the next section provides more detail on how to address the issue, the basic outline of the steps you will need to take is:
Microsoft has released an ATL Header and Library update that addresses these problems. Developers can download the update bulletin located here.
In most cases the actual developer requirement for the control is to instantiate the SFI control via PARAM in HTML. For example the HTML below shows 2 examples of a control being instantiated:
<object id="X" classid="CLSID:<Your CLSID>" >
<param name="FOO" value="FOO">
</object>
<object classid=”CLSID:<Your CLSID>” data="stream.bin" </object>
If this is the case then only IPersistPropertyBag or IPersistPropertyBag2 need to be supported. Support for IPersistStreamInit, IPersistStream, or IPersistStorage is not required and is in fact not desirable. Consider removing the support of these interfaces.
Note: If your control implements IPersisteStorage interface using IPersistStorageImpl, it won’t work after you remove the implementation of IPersistStreamInit.
If your control needs to inherit from IPersistStreamInitImpl (i.e. for support of IPersistStreamInit), or call AtlIPersistStreamInit_Load, then you should fix the vulnerable macros used in property declaration. The older vulnerable macros PROP_ENTRY/ PROP_ENTRY_EX are deprecated, and should be replaced by the safe macros PROP_ENTRY_TYPE & PROP_ENTRY_TYPE_EX.
OLD MACROS:
#define PROP_ENTRY (szDesc, dispid, clsid)
#define PROP_ENTRY_EX (szDesc, dispid, clsid, iidDispatch)
NEW MACROS:
#define PROP_ENTRY_TYPE (szDesc, dispid, clsid, vt)
#define PROP_ENTRY_TYPE_EX (szDesc, dispid, clsid, iidDispatch, vt)
As seen above, the new macro (PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX) allows the expected type to be provided so that if, for example, a VT_BSTR is expected and a VT_UNKNOWN is read from the stream, the call will fail. You should never use VT_EMPTY in PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX of a safe for initializing class since this means there is no expected type and any type, safe or unsafe, may be read from the stream.
The macros PROP_ENTRY_TYPE and PROP_ENTRY_TYPE_EX can be used safely except in the case where VT_UNKNOWN or VT_DISPATCH are the expected types. If your control needs to support these types Microsoft recommends that you carefully review this need to ensure that there are valid user scenarios for this and remove this support if the scenarios don’t require it. Additionally you should review the need to mark the control SFI in these scenarios and remove the marking if not required.
If your SFI control does need to support VT_DISPATCH or VT_UNKNOWN property, you could consider using the following new macro provided by this ATL update for CLSID filtering.
Note: CLSID filtering in the DLL version of ATL (ATLxx.DLL). If your project uses the ATL.DLL (ATLxx.DLL) then you will need to re-engineer your code to either remove the requirement for CLSID filtering as above or switch from the DLL version of ATLxx.DLL by removing the definition for _ATL_DLL.
These macros, whose names all start with PROP_ENTRY_INTERFACE, allow the caller to specify not only the expected VT_ type but also the valid CLSIDs that can be read from a stream. If the CLSID that is read from the stream doesn't match the ones provided in the macro, then the call fails.
#define PROP_ENTRY_INTERFACE(szDesc, dispid, clsid, rgclsidAllowed, cclsidAllowed, vt)
#define PROP_ENTRY_INTERFACE_CALLBACK(szDesc, dispid, clsid, pfnFunc, vt)
#define PROP_ENTRY_INTERFACE_EX(szDesc, dispid, clsid, iidDispatch, rgclsidAllowed, cclsidAllowed, vt)
#define PROP_ENTRY_INTERFACE_CALLBACK_EX(szDesc, dispid, clsid, iidDispatch, pfnFunc, vt)
Additional notes on the macros above:
const CLSID rgclsidAllowed[] =
{
CLSID_Foo,
CLSID_Bar,
};
The following are some examples about how to use these new macros. Again, please review careful if your class really needs to have a VT_DISPATCH or VT_UNKNOWN property:
PROP_ENTRY_INTERFACE("PROP_ENTRY_INTERFACE", 0, CLSID_PropPage, &CLSID_Allowed, 1, VT_DISPATCH)
PROP_ENTRY_INTERFACE_EX("PROP_ENTRY_INTERFACE_EX", 0, CLSID_PropPage, __uuidof(IAlternate), &CLSID_Allowed, 1, VT_DISPATCH)
PROP_ENTRY_INTERFACE_CALLBACK("PROP_ENTRY_INTERFACE_CALLBACK", 0, CLSID_PropPage, AllowedCLSID, VT_DISPATCH)
PROP_ENTRY_INTERFACE_CALLBACK_EX("PROP_ENTRY_INTERFACE_EX", 0, CLSID_PropPage, __uuidof(IAlternate), AllowedCLSID, VT_DISPATCH)
HRESULT AllowedCLSID(const CLSID& clsid, REFIID iidInterface, void** ppvObj);
Note: The ppvObj parmeter is optional. If the callback function needs to instantiate the class as part of its operation, it should return a pointer to the interface given by iidInterface otherwise it should set *ppvObj = NULL. If *ppvObj is not set by the callback, ATL will instantiate the class itself. You may want to make additional checks on the object to ensure that it is safe for use (i.e. Check if object is safe for initialization by calling IObjectSafety interface). In such a case the instance created in the callback must be returned to ATL so that any safety settings are used. This functionality gives the developer better control of loaded interfaces.
To summarize:
If your control calls CComVariant::ReadFromStream passing non-trusted data, you should update the call from ReadFromStream(pStream) to ReadFromStream(pStream, vtExpected), where vtExpected is the expected variant type for the value to be read.
For Example:
Old:
hr = var.ReadFromStream(pStm);
Fix:
hr = var.ReadFromStream(pStm, vtExpected);
In the case where your control has a variant type VT_DISPATCH or VT_UNKNOWN, update your call ReadFromStream(pStream) to ReadFromStream(pStream, vt, rgclsidAllowed, cclsidAllowed)
Where:
Example:
const CLSID rgclsidAllowed[] =
{
…
}
ClassesAllowedInStream allowed = { rgclsidAllowed };
hr = var.ReadFromStream(pStm, VT_UNKNOWN, allowed, _countof(rgclsidAllowed));
or
HRESULT HrAllowClsid(const CLSID& clsid, REFIID iidInterface, void **ppvObj);
ClassesAllowedInStream allowed = { (const CLSID *) & HrAllowClsid };
hr = var.ReadFromStream(pStm, VT_UNKNOWN, allowed, 1));
Again, treat VT_DISPATCH and VT_UNKNOWN very cautiously.
For more information on what developers need to consider and do if they need to mark controls as safe, see MSDN: Safe Initialization and Scripting for ActiveX Controls.
For more information on the use of Killbits in this situation, refer to the knowledge base article on the issue.
View a Channel 9 Video on this ATL issue.
For questions regarding ATL and Visual C++ please visit the Visual C++ Language Forum on MSDN.
Microsoft has released an ATL Header and Library update that addresses these problems. Developers can download the update here.
Note: To install this patch on Windows Server 2003 you may need to first install the updates described in KB925336 or KB973825
Note: Visual Studio and Windows both ship an ATL.DLL. You will need to ensure that both ATL.DLL and ATLXX.DLL (where XX is related to the version of Visual Studio installed) are updated correctly.
Further information can be found at the Protect your computer from the Active Template Library (ATL) security vulnerability page.