jueves, 27 de febrero de 2014

Bypass de la protección contra Clickjacking en Facebook Studio

Facebook Studio es una aplicación web propiedad de Facebook y, como tal, está dentro del ámbito de su programa de recompensas. Después de unos cuantos tiros a puerta contra el poste, decidí probar suerte revisando su protección frente a ataques de Clickjacking. A priori, esta protección parecía cumplir su función, pero como suele ocurrir, no sabes lo fuerte que es el muro hasta que intentas romperlo. Este fallo de seguridad fue reportado y parcheado en enero, por lo que recibí una recompensa de 500 dólares.

Las protecciones contra ataques de Clickjacking persiguen siempre un mismo objetivo: impedir que una página que realiza acciones sensibles pueda acabar dentro de un iframe en una página maliciosa. Básicamente existen tres formas de hacer esto. Las más comunes, seguras y sencillas, están basadas en el uso de las cabeceras X-FRAME-OPTIONS o Content-Security-Policy en las respuestas HTTP. Sin embargo, hay casos en los que se requiere cierta versatilidad si queremos que la página pueda ser iframada o no dependiendo de condiciones más complejas. En estos casos, a veces se opta por una segunda opción que consiste en asegurarse mediante javascript de que la página se encuentra en el nivel más alto de la jerarquía de iframes (window.top) y que, por lo tanto, no está iframada.

Revisando las cabeceras de respuesta HTTP de Facebook Studio, lo primero que podíamos notar es que no estaba haciendo uso de X-FRAME-OPTIONS. Aún así, si intentábamos incrustar cualquier página de la web dentro de un iframe, también nos resultaba imposible, ya que se resistía a ser "enjaulada" haciendo una redirección de la página contenedora a la propia web de Facebook Studio. Lo vemos de un vistazo revisando el javascript:

<script>
  if (self == top || lc != -1) {
    var theBody = document.getElementsByTagName('body')[0];
    theBody.style.display = "block";
  }
  if (self != top && lc == -1) {
     top.location = self.location;
  }
</script>

Nos interesa el segundo "if", ya que self != top es evaluado a verdadero cuando intentamos iframar la web. Por tanto, si no queremos que esa redirección se produzca, ¿ya sabéis qué toca ahora no?........ ¡Exacto!, ¿qué demonios es esa variable "lc" y cómo podemos hacer que tome un valor distinto de -1?

Rebuscando por el código me encontré con este curioso fragmento codificado:

var _0x1c60=["\x74\x6F\x6B\x65\x6E","\x69\x6E\x64\x65\x78\x4F\x66","\x68\x72\x65\x66","\x6C\x6F\x63\x61\x74\x69\x6F\x6E"];

var lc=window[_0x1c60[3]][_0x1c60[2]][_0x1c60[1]](_0x1c60[0]);

Como vemos, en la primera línea aparece una variable "_0x1c60" que se establece a un array de cadenas codificadas. ¡A estas alturas no nos va a asustar un poco de codificación! Después de pasar cada uno de los valores del array por la función "unescape" de javascript obtenemos lo siguiente:

_0x1c60[3] = "location"
_0x1c60[2] = "href"
_0x1c60[1] = "indexOf"
_0x1c60[0] = "token"

¡¡Uuff!! Esto empieza a tomar forma. Así que la instrucción que establece el valor de la variable "lc" realmente es:

var lc=window["location"]["href"]["indexOf"]("token");

Para los que no estén familiarizados con la notación de corchetes en javascript, esto es equivalente a:

var lc=window.location.href.indexOf("token");

Es decir, si queremos que la variable "lc" tome un valor distinto de -1, ¡simplemente tenemos que usar una URL que contenga la palabra "token"!

¿¿En serio?? Probemos de nuevo a iframar una página de Facebook Studio, pero esta vez añadiendo por ejemplo un parámetro ficticio llamado "token" a la URL:

<iframe width="600" height="500" src='https://www.facebook-studio.com/dashboard/?token'></iframe>

Y el resultado:


Como podemos comprobar en la imagen, la web quedó correctamente incrustada dentro de un iframe al que añadí opacidad para la prueba de concepto.

Facebook corrigió el fallo cambiando la protección al método habitual, añadiendo en las respuestas HTTP la cabecera X-FRAME-OPTIONS con el valor SAMEORIGIN, y todo acabó con un final feliz:



¡Saludos!