Análisis binario del 0-day de IEPeers

El pasado Martes 9 de Marzo, Microsoft anunció la existencia de una vulnerabilidad de tipo ‘0-day’ en Internet Explorer 6 y 7.

Esta vulnerabilidad fue descubierta en Internet analizando varios ataques dirigidos y se trata de un ‘use-after-free’ (referencia no valida a un puntero) que afecta a la librería Iepeers.dll: se intenta acceder a un objeto liberado previamente por Internet Explorer pudiendo llegar a conseguir la ejecución remota de código con los privilegios del usuario logueado en la máquina.

Poco después del anuncio de Microsoft, los detalles de la vulnerabilidad fueron publicados y se creó un módulo en Metasploit.


Para provocar esta vulnerabilidad y analizarla, se ha utilizado el exploit de Metasploit que podéis encontrar en:

http://www.metasploit.com/redmine/projects/framework/repository/entry/modules/exploits/windows/browser/ie_iepeers_pointer.rb

Este análisis fue realizado en una máquina con Windows XP SP3 e Internet Explorer 7, donde se analizaron las siguientes librerías:

• mshtml.dll 7.0.6000.16981
• iepeers.dll 7.0.6000.16981
• jscript.dll 5.7.6002.22145

• oleaut32.dll 5.1.2600.5512

Comenzamos: cuando se ejecuta el exploit, se produce una redirección a un shellcode dentro de la rutina TearoffThunk7():

.text:3DAD9500 loc_3DAD9500:
.text:3DAD9500 add eax, 0Ch

.text:3DAD9503 mov ecx, [eax]

.text:3DAD9505 mov [esp+4+arg_0], ecx

.text:3DAD9509 mov ecx, [eax+4]

.text:3DAD950C mov ecx, [ecx+1Ch]

.text:3DAD950F pop eax

.text:3DAD9510 mov dword ptr [eax+20h], 7

.text:3DAD9517 jmp ecx

La rutina de arriba es llamada desde IDispatchExGetDispID():

.text:75C73055 loc_75C73055:
.text:75C73055 push [ebp+arg_10]

.text:75C73058 mov eax, [ebp+arg_4]

.text:75C7305B push [ebp+arg_C]

.text:75C7305E mov ecx, [eax]

.text:75C73060 push [ebp+arg_8]

.text:75C73063 push eax

.text:75C73064 call dword ptr [ecx+1Ch]

La rutina TearoffThunk7() está dentro de una función con una tabla enorme de punteros que a su vez contiene múltiples rutinas idénticas. Esta función es referenciada en la rutina CreateTearOffThunk() que es un constructor usado para crear nuevos interfaces al vuelo tipo ‘tear-off’. Durante la explotación, COmWindowProxy::GetSecurityThunk() llama a CreateTearOffThunk().

Los interfaces ‘tear-off’ se utilizan en desarrollos COM cuando no tiene sentido crear inmediatamente un interfaz. Normalmente esto es porque el interfaz se utiliza raramente o se necesita demasiado tiempo para crearlo. Particularmente en esta implementación, el interfaz ‘tear-off’ mantiene la función con la enorme tabla de rutinas idénticas, como TearOffThunk7(). El interfaz toma la forma de un objeto en C++ que contiene un puntero a el objeto padre, un puntero a la tabla virtual de funciones y un contador de referencia. Cuando se complete la referencia al objeto ‘tear-off’, se llama a la rutina PlainRelease() para disminuir el contador de referencia. Cuando el contador llega a cero, la función virtual en PlainRelease() destruirá el objeto.


Examinando la parte del exploit que provoca la vulnerabilidad encontramos un comportamiento interesante:

foo.addBehavior('#default#userData');
for (i=0; i<10;>
foo.setAttribute('s',window);

}

El exploit primero añade una asociación Userdata al objeto foo. Luego el exploit en un bucle llama diez veces a setAttribute(). Este comportamiento será importante para entender cómo se explota la vulnerabilidad.

El interface ‘tear-off’ más interesante se inicializa en COmWindowProxy::GetSecurityThunk():

.text:3DA3A7CB push offset ?s_apfnIDispatchEx@COmWindowProxy@@2QBQ8CVoid@@AGJXZB; void *
.text:3DA3A7D0 push esi ; void *

.text:3DA3A7D1 call ?CreateTearOffThunk@@YGJPAXPBXPAUIUnknown@@PAPAX00KPBQBU_GUID@@0@Z ; CreateTearOffThunk(void *,void const *,IUnknown *,void * *,void *,void *,ulong,_GUID const * const *,void *)

.text:3DA3A7D6 cmp eax, ebx

.text:3DA3A7D8 jz loc_3DA

El interfaz ‘tear-off’ con la tabla virtual de funciones se inicializa aquí:

.text:3DAD2C26 mov edx, [ebp+arg_1C]
.text:3DAD2C29 test edx, edx

.text:3DAD2C2B mov dword ptr [esi], offset off_3DD2F218

.text:3DAD2C31 mov [esi+0Ch], eax

.text:3DAD2C34 mov [esi+10h], ecx

Este objeto termina teniendo nueve referencias a él cuando ocurre la explotación de la vulnerabilidad. El contador de referencia se sitúa en el segundo DWORD del objeto ‘tear-off’.

La rutina que maneja la función Javascript setAttribute() está dentro de iepeers.dll y se llama CPersistUserData::setAttribute(). Lo importante de esta función es la siguiente llamada:

.text:42085398 push VT_BSTR ; vt
.text:4208539A push 0 ; wFlags
.text:4208539C lea esi, [ebp+pvargDest]

.text:4208539F push 409h ; lcid

.text:420853A4 mov eax, esi

.text:420853A6 push eax ; pvarSrc

.text:420853A7 push eax ; pvargDest

.text:420853A8 call ds:__imp__VariantChangeTypeEx@20

La función VariantChangeTypeEx() se utiliza para convertir el objeto COM de un tipo a otro (typecasting). Una llamada a esta rutina también invoca a la función VariantClear() para limpiar la referencia al objeto.

La rutina VariantClear() finaliza haciendo una llamada a PlainRelease(), que se encarga de tratar el contador de referencia y de destruir los objetos tear-off:

oleaut32.dll:77124940 jz short loc_77124903
oleaut32.dll:77124942 mov ecx, [eax]

oleaut32.dll:77124944 push eax

oleaut32.dll:77124945 call dword ptr [ecx+8] ; PlainRelease()

Dentro de PlainRelease(), el código disminuye el contador de referencia:

.text:3DAD54A5 mov edi, edi
.text:3DAD54A7 push ebp

.text:3DAD54A8 mov ebp, esp

.text:3DAD54AA push esi

.text:3DAD54AB mov esi, [ebp+Value]

.text:3DAD54AE dec dword ptr [esi+4] ; Decrement reference count

.text:3DAD54B1 mov eax, [esi+4]

.text:3DAD54B4 jnz short loc_3DAD ; if the reference count is zero, destroy, otherwise return.


Por tanto y digamos en resumen, llamamos a la rutina Javascript setAttribute() que llamará a VariantChangeTypeEx(), que a su vez llamará a VariantClear(), y finalmente a PlainRelease(). Debido a que el contador nunca se incrementa cuando CPersistUserData::setAttribute() accede al objeto, se harán un montón de llamadas a setAttribute() en todas las referencias al objeto. Durante la explotación de la vulnerabilidad, el contador se fija a 9, sin embargo esta cuenta puede variar dependiendo de si la vulnerabilidad ha tenido o no éxito.


El fallo puede ocurrir si la llamada a setAttribute() ocurre sólo una vez. Este ‘crash’ sería debido a que CPersistUserData::setAttribute() disminuye el contador en uno, de forma que PlainRelease() destruiría el objeto ‘tear-off’ de forma prematura, dejando una referencia ‘huérfana’.

Una vez que el contador ha llegado a cero y el objeto es liberado, la memoria será reutilizada finalizando con un puntero al objeto CWindow::s_apfnIHTMLPrivateWindow3 dentro de la variable que antes tenía un puntero a la tabla de funciones de los objetos originales. Una referencia hecha al objeto original asume el tipo inicial, pero hay que tener en cuenta que este tipo inicial fue reemplazado con un tipo diferente de objeto dado que la memoria ha sido reutilizada. Este cambio significa que cualquier llamada a una función virtual podría devolver un resultado inesperado. En el caso de explotar la vulnerabilidad, se referencia a un puntero en 0xF09F883h y se salta a TearOffThunk7(). Ese área de memoria será liberada y preparada para volver a inicializarse, pero si se explota la vulnerabilidad correctamente es posible rellenar esa memoria con datos controlados por el atacante…:-)

Comentarios

  1. Excelente informacion. Obtuve un CD de XP y al poco tiempo de instalar y conectarlo a internet me descargan virus de todos los colores. Analizando el contenido del CD me encontre con este archivo iepeers.dll, que me resulto extraño desde un principio, pues estaba indicado con un icono distinto a los demas archivos .dll
    Gracias.

    ResponderEliminar

Publicar un comentario