¿Cómo determinar si un control ATL es vulnerable (y como arreglarlo)?

Hola, les escribe Roberto Arbeláez

La semana pasada, Microsoft liberó el boletin de seguridad MS09-035 en el cual se describe una vulnerabilidad sobre aplicaciones desarrolladas con Visual Studio, que utilicen las librerías ATL.

El diagrama de flujos a continuación les permitirá determinar de manera rápida si sus aplicaciones son vulnerables o no:

image

Paso 1 - Determine si su control ActiveX fué marcado como SFI: Para saber si su control fué marcado como SFI (Safe for initializing), haga click aqui.

Si su control ActiveX NO está marcado como SFI, no hay vector de ataque a su control desde Internet explorer.

Paso 2 – Si su control hereda de IPersistStreamInitImpl o hace llamados a AtlIPersistStreamInit_Load (mucho menos probable), revise si su control usa las macros de mapas de propiedades vulnerables:

Si se usa PROP_ENTRY/ PROP_ENTRY_EX o si se usa PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX con los types VT_EMPTY, VT_DISPATCH o VT_UNKNOWN, su control puede ser vulnerable.

Ejemplo:

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

Paso 3 – Si su control hace llamados a CComVariant::ReadFromStream(pStream), revise si se pueden pasar datos no confiables (no validados).

Cualquier uso del método CComVariant::ReadFromStream(pStream) con datos no validados es inseguro. Aunque un llamado a CComVariant::ReadFromStream usualmente  hace parte de una implementación de IPersistStreamInit, también puede ser usado en otras instancias.

 

¿Que hago si mi control es vulnerable?

Paso 1: Aplique las actualizaciones

Microsoft liberó actualizaciones para las librerías y los encabezados de ATLque soluciona estos problemas. Los desarrolladores pueden obtener el boletin de seguridad que contiene la actualización aqui.

Paso 2: Revisar las interfaces soportadas

En la mayoría de los casos, el requerimiento que se le hizo al desarrollador para el control fué instanciar el control SFI a través de PARAM en HTML. Por ejemplo, el HTML a continuación muestra 2 ejemplos de un control siendo instanciado:

<object id="X" classid="CLSID:<Your CLSID>" >

  <param name="FOO" value="FOO">

</object>

<object classid=”CLSID:<Your CLSID>” data="stream.bin" </object>

Si este es el caso, entonces solamente IPersistPropertyBag o IPersistPropertyBag2 necesitan ser soportadas.  Soporte para IPersistStreamInit, IPersistStream, o IPersistStorage no es requerido y de hecho, no es deseable. Considere remover el soporte de estas interfaces.

Nota:  Si su control implementa la interfaz IPersisteStorage usando  IPersistStorageImpl, dejará de funcionar después de que elimine la implementación de IPersistStreamInit.

Paso 3: Arregle las macros vulnerables si están presentes en el código de su control

Si su control necesita heredar de  IPersistStreamInitImpl  (por ejemplo, para soportar IPersistStreamInit), o si hace llamados a AtlIPersistStreamInit_Load, entonces deberá arreglar las macros vulnerables usadas en la declaración de propiedades. Las macros vulnerables, PROP_ENTRY/ PROP_ENTRY_EX fueron declaradas como DEPRECATED, y deberán ser reemplazadas por las macros seguras PROP_ENTRY_TYPE y  PROP_ENTRY_TYPE_EX.

MACROS VIEJAS (INSEGURAS) :

#define PROP_ENTRY      (szDesc, dispid, clsid)

#define PROP_ENTRY_EX   (szDesc, dispid, clsid, iidDispatch)

MACROS NUEVAS (SEGURAS) :

#define PROP_ENTRY_TYPE       (szDesc, dispid, clsid, vt)

#define PROP_ENTRY_TYPE_EX    (szDesc, dispid, clsid, iidDispatch, vt)

La nuevo macro (PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX) permite que se provean types inesperados, de tal manera que si se espera un VT_BSTR y se lee un VT_UNKNOWN del stream, el llamado fallará. Nunca se debe usar VT_EMPTY en PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX en una clase SFI (safe for initializing) debido a que esto significa que no hay types inesperados, y cualquier type, seguro o inseguro, podrá ser leido del stream.

Los macros PROP_ENTRY_TYPE y PROP_ENTRY_TYPE_EX pueden ser usados de manera segura, excepto en el caso en el que VT_UNKNOWN o VT_DISPATCH sean los types esperados. Si su control necesita soportar estos types, Microsoft recomienda que usted revise cuidadosamente este requerimiento para asegurarse de que hay escenarios válidos de usuario que lo requieren, y remueva el soporte para los escenarios que no lo requieran. Adicionalmente, deberá revisar la necesida de marcar controles como SFI en este escenario y remover la marca si no es requerida.

Si su control SFI no necesita soportar las propiedades VT_DISPATCH o VT_UNKNOWN, debería considerar usar la siguiente macro ATL update for CLSID filtering.

Nota: Filtrado de CLSID en la versión DLL de ATL (ATLxx.DLL): Si su proyecto usa ATL.DLL (ATLxx.DLL) entonces es necesario que haga reingeniería de su código para remover los requerimientos de filtrado CLSID, o deberá evitar continuar usando la versión de DLL de ATLxx.DLL, eliminando la definición para _ATL_DLL.

Estas macros, cuyos nombres empiezan con PROP_ENTRY_INTERFACE, permiten que quien las llama pueda especificar no solamente el VT_type esperado, sino también los CLSIDs válidos que pueden ser leidos de un stream. Si el CLSID que es leído del stream no concuerda con los provistos por la macro, la llamada falla.

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

Notas adicionales sobre las macros anteriores:

  • vt: Tipo de variante esperada (Expected variant type)
  • rgclsidAllowed: Lista de CLSIDs permitidos, rgclsidAllowed deberá ser un array con la siguiente forma:

const CLSID rgclsidAllowed[] =

{

   CLSID_Foo,

   CLSID_Bar,

};

  • pfnFunc: Un apuntador a función, que retorna un HRESULT para indicar si el CLSID que le fue pasado (como parámetro) debe ser permitido o no. Su funcionalidad básica consiste en identificar si a un CLSID dado se le debe permitir ser  cargado. La implementación del function callback deberá retornar E_ACCESSDENIED para CLSIDs no permitidos, y S_OK para CLSIDs permitidos. Otros errores que puedan ocurrir durante la operación podrán ser retornados, desde que cualquier fallo HRESULT siga representando CLSID no permitido, y cualquier HRESULT exitoso indique CLSID permitido.
  • cclsidAllowed: Número de CLSIDs permitidos

Los siguientes son ejemplos sobre cómo usar estas nuevas macros. Nuevamente, por favor revise cuidadosamente si su class realmente necesita tener una propiedad VT_DISPATCH o VT_UNKNOWN:

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

Nota: El parámetro ppvObj es opcional. Si el function callback necesita instanciar la clase, como parte de su operación, deberá retornar un apuntador a la interfaz dada por iidInterface, de lo contrario, deberá ser definida como *ppvObj = NULL.  Si *ppvObj no es definida por el callback, ATL instanciará la clase directamente. Puede que desee hacer validaciones adicionales sobre el objeto para asegurarse de que es seguro de usar (por ejemplo, revisar si esta marcado como SFI, llamando a la interfaz IObjectSafety). En ese caso, la instancia creada en el callback deberá ser retornada a ATL para que se utilicen las configuraciones de seguridad. Esta funcionalidad le da al desarrollador mejor control de las interfaces cargadas.

Para resumir:

  1. Para cada macro vulnerable, identifique el variant type que debe ser usado para cada propiedad.
  2. Si el variant type esperado NO es VT_DISPATCH ni VT_UNKNOWN, actualice su código usando las macros seguras PROP_ENTRY_TYPE o PROP_ENTRY_TYPE_EX especificando los variant type esperados (vt).
  3. Si su variant type esperado es VT_DISPATCH o VT_UNKNOWN, use PROP_ENTRY_INTERFACE o PROP_ENTRY_INTERFACE_EX especificando bien sea el arreglo de CLSIDs permitidos, o la callback function que hace esta validación.
Paso 4: Para controles que usan CComVariant::ReadFromStream, arregle el llamado a ReadFromStream(pStream).

Si su control hace llamados a CComVariant::ReadFromStream pasando datos no confiables (no validados), deberá actualizar la llamada de ReadFromStream(pStream)ReadFromStream(pStream, vtExpected) , donde vtExpected es el variant type esperado para el valor a ser leído.

Por ejemplo:

Antiguo (inseguro):

hr = var.ReadFromStream(pStm);

Nuevo (seguro):

hr = var.ReadFromStream(pStm, vtExpected);

En el caso en el que el control tenga un variant type VT_DISPATCH o VT_UNKNOWN, actualice el llamado ReadFromStream(pStream)  a  ReadFromStream(pStream, vt, rgclsidAllowed, cclsidAllowed)

Donde:

  • vt:  Expected Variant Type
  • rgclsidAllowed: Lista de CLSIDs permitidos o apuntador a función
  • cclsidAllowed: Numero de CLSIDs permitidos

Ejemplo:

const CLSID rgclsidAllowed[] =

{

}

ClassesAllowedInStream allowed = { rgclsidAllowed };

hr = var.ReadFromStream(pStm, VT_UNKNOWN, allowed, _countof(rgclsidAllowed));

o

HRESULT HrAllowClsid(const CLSID& clsid, REFIID iidInterface, void **ppvObj);      

ClassesAllowedInStream allowed = { (const CLSID *) & HrAllowClsid };

hr = var.ReadFromStream(pStm, VT_UNKNOWN, allowed, 1));

Nuevamente, trate VT_DISPATCH y VT_UNKNOWN con mucho cuidado.

 

Si desea hacer una validación automática de su código, en el sitio https://codetest.verizonbusiness.com/ encontrarán una herramienta en la que pueden cargar sus controles, y les dirá si son vulnerables o no.

 

Saludos,

 

Roberto Arbeláez

CSS Security Program Manager for Latin America

Microsoft Corp.

 

Technorati Tags: MS09-035,973882,ATL,vulnerabilidad,vulnerability

del.icio.us Tags: MS09-035,973882,ATL,vulnerabilidad,vulnerability