Skip to main content

Active Template Library Security Update for Developers

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.

Determining if the control is affected

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:

ATL Security Update 

Step 1: If your COM component is an ActiveX control, check if your ActiveX control is marked as safe for initializing (SFI) or not. If the control is meant to be loaded on a device with a version of Internet Explorer earlier than version 6 or with Pocket IE, a control marked as Safe for Scripting is equally vulnerable.

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.

Step 2: If your control inherits from IPersistStreamInitImpl or calls AtlIPersistStreamInit_Load (much less likely), check if your control uses the vulnerable property map macros.

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()

Step 3: If your control calls CComVariant::ReadFromStream(pStream), check if non-trusted data can be passed.

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.

Fixing the affected control

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:

  1. Rebuild your control to not advertise itself as “Safe for initializing” and remove support for IPersistStreamInit & IPersistStorage.
  2. If you require either or both of these interfaces then you will need to upgrade to a supported version of Visual Studio and follow the steps listed for Visual Studio 2003 or later.

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.

Overall summary of steps

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:

  1. If you mark your component as safe for initializing then you should remove either the advertising of “Safe for initializing” or if your control  inherits IPersistStreamInitImpl, or calls AtlIPersistStreamInit_Load, then you should fix the vulnerable macro (PROP_ENTRY/ PROP_ENTRY_EX) used in property declaration.
  2. If your control calls CComVariant::ReadFromStream then you should update the ReadFromStream call to include the vtExpected parameter.
  3. If these apply to you then you will need to:
    1. Apply the ATL header and library update.
    2. Modify your property map from using PROP_ENTRY to PROP_ENTRY_TYPE (or the _EX implementations).  Do not use VT_EMPTY for the vt argument.  Avoid use of VT_DISPATCH and VT_UNKNOWN as these are unsafe.
    3. If you must use VT_DISPATCH or VT_UNKNOWN variant types then you will need to implement CLSID filtering.
    4. If you can’t use CLSID filtering then you will need to re-engineer your solution to remove this dependency.  This would be a good opportunity to review whether IPersistStreamInit and IPersistStorage are required or if the class really should be safe for initializing.
  4. 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.

Detailed Guidance

Step 1: Apply the updates

Microsoft has released an ATL Header and Library update that addresses these problems. Developers can download the update bulletin located here.

Step 2: Check the supported interfaces

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.

Step 3: Fix vulnerable macros if they are present in your control code

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:

  • vt: Expected variant type
  • rgclsidAllowed: LIST of allowed CLSIDs , rgclsidAllowed should be an array of the form:

const CLSID rgclsidAllowed[] =

{

   CLSID_Foo,

   CLSID_Bar,

};

  • pfnFunc: A function pointer, which returns an HRESULT to indicate whether to allow the CLSID passed to it. Its basic functionality should be to identify if the given CLSID can be allowed to load or not. The implementation of the callback function should return E_ACCESSDENIED for disallowed CLSIDs and S_OK for allowed ones.  Other errors that occur in the course of operation may be returned as long as any failure HRESULT indicates disallow and any success HRESULT indicates allow.
  • cclsidAllowed: Count of allowed CLSIDs

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:

  1. For each vulnerable macro, identify your variant type to be used for that property.
  2. If your expected variant type is not VT_DISPATCH and not VT_UNKNOWN, update your code by using the safe macros, PROP_ENTRY_TYPE or PROP_ENTRY_TYPE_EX specifying the expected variant type (vt).
  3. If your expected variant type is VT_DISPATCH or VT_UNKNOWN, use PROP_ENTRY_INTERFACE or PROP_ENTRY_INTERFACE_EX specifying either the array of allowed CLSIDs or the callback function that performs this check.

Step 4: For controls that use CComVariant::ReadFromStream, fix the call to ReadFromStream(pStream).

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:

  • vt:  Expected Variant Type
  • rgclsidAllowed: LIST of allowed CLSIDs or function pointer
  • cclsidAllowed: Count of allowed CLSIDs.

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.

More Information

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.