jueves, 11 de agosto de 2016

Un XSS olímpico

Llegaron los Juegos Olímpicos. Millones de personas en todo el mundo siguen con expectación el progreso de los participantes en la competición deportiva más global y multitudinaria del planeta. Y, cómo no, un evento de tales características cuenta con una web oficial dedicada a ofrecer toda la información detallada. Todo esto, y el tiempo libre extra que siempre ofrecen las vacaciones, me animaron a darme una vuelta por www.olympic.org en busca de algún fallo de seguridad. Como resultado encontré una interesante vulnerabilidad de tipo XSS que intenté reportar a la organización. Desgraciadamente, no localicé ningún medio de contacto para hacerlo a excepción de la cuenta oficial de Twitter @Olympics.

¿Dónde reporto esto?
Como era de esperar, no obtuve ninguna respuesta.

El punto de inyección vulnerable a XSS se encuentra en el buscador de la sección de FAQ situado en http://registration.olympic.org/en/faq/search. En este dominio (registration.olympic.org) los usuarios pueden registrarse y autenticarse para configurar la recepción de noticias sobre novedades y eventos.


Punto de inyección
El valor del parámetro "search" (que puede ser enviado por método POST o GET) es reflejado sin escapar en el atributo "placeholder", por lo que después de insertar una comilla " podríamos inyectar código HTML malicioso.  Sin embargo, la web cuenta con un WAF (Web Application Firewall) que nos hace la vida un poco más difícil. Vectores del tipo " onfocus=alert(1) autofocus ", "><script>alert(1)</script>, <img src=1 onerror=alert(1)>, etc. son fácilmente detectados y anulados por el WAF. De hecho, el sistema parece que cuenta con dos niveles de WAF. El primero y más determinístico, muestra un mensaje de error cuando detecta cualquier tag o atributo HTML sospechoso de poder ejecutar código javascript como <script>, <iframe>, href=, onfocus=, onerror=, src=, etc.

Mensaje "Access Denied" del WAF

El segundo nivel de WAF parece seguir un patrón más heurístico, ya que el simple hecho de usar un número determinado de caracteres concretos, o usar caracteres numéricos o albéticos en cierto punto de la inyección, puede despertarlo y cortarnos el paso con otro mensaje de error distinto del anterior:

Mensaje "Security error" del WAF
Llegados a este punto, toca practicar uno de nuestros deportes olímpicos favoritos: El salto de WAF. El objetivo será conseguir la típica ejecución de una alerta javascript como prueba de concepto. Como siempre en estos casos, seguro que existirán múltiples soluciones igualmente válidas a la descrita en este post.

Lo primero, las comprobaciones de rigor: no es sensible a mayúsculas, caracteres de separación, inserción de nulos, fallos de parseo y demás bypasses básicos. Como norma general, para estos test iniciales suelo seguir el patrón descrito por Rafay Baloch en el paper que puedes encontrar pulsando en  este link.

Como hemos comentado anteriormente, todos los atributos HTML de eventos interesantes como on*, href, etc. son detectados por el WAF. Sin embargo, el atributo formaction como parte de una etiqueta <input type=submit> es considerado válido. Este atributo permite especificar la URL a la que se enviarán los datos del formulario, sobrescribiendo la ya proporcionada en el atributo action del form principal. Con suerte, podremos usar este atributo para especificar una URL de tipo javascript: o data: que nos permita ejecutar código.

Ya tenemos nuestro punto de partida:

"><input type=submit formaction="

Tras intentar proporcionar una URL de tipo javascript: o data: en el atributo formaction, el WAF detecta nuestras intenciones y muestra el mensaje de error correspondiente. Intentamos ofuscar la cadena javascript: usando codificaciones HTML tales como j&#x61;vascript:javascr&#105;pt: pero el WAF sigue parándonos los pies. Tras jugar un poco a prueba y error, me doy cuenta de que el WAF detecta codificaciones que incluyen específicamente el carácter almohadilla "#". El carácter dos puntos ":" tiene su propia entidad HTML especial &colon; y no necesita hacer uso de su representación numérica con almohadilla. Por tanto, salvamos este obstáculo codificando el principio de nuestra URL como javascript&colon;.

"><input type=submit formaction="javascript&colon;

Ok, estamos en la mitad de la carrera. ¡Recordad! ¡Estamos en los Juegos Olímpicos! Todavía nos quedan obstáculos para llegar a la meta. Las funciones como alert, prompt o confirm también son detectadas y denegadas por el WAF heurístico. Necesitamos una manera de ejecutar la función alert sin llamarla explícitamente. Las llamadas a window.alert y top.alert son igualmente denegadas, incluso cualquier llamada del tipo window.*, top.* o document.*. Como vimos en el post de este blog correspondiente al fallo reportado a Facebook, la notación de corchetes de javascript puede ser muy útil para ofuscar llamadas a métodos. Esto es así porque nos permite realizarlas a través de cadenas javascript. Por ejemplo, window.alert puede ser reescrito igualmente como window['alert']. A pesar de que el WAF detectaba también las cadenas window[*, top[* o document[*, existía una alternativa que no era detectada por el WAF: this[*. Esto nos permite llamar a la función alert de la forma this['alert']. Sin embargo, la cadena alert sigue siendo detectada como maliciosa en este contexto. ¡No hay problema! Ahora es una cadena javascript y podemos ofuscarla, por ejemplo, separándola en varias cadenas: this['a'+'lert'].

¿Algo más? Sí, el WAF tampoco dejaba pasar los corchetes ni el símbolo "+" como parte del vector XSS. Por suerte, las codificaciones a entidades HTML de los corchetes (&lsqb; y &rsqb;) y del símbolo "+" (&plus;) acudieron al rescate. Con todo esto, llegados a este punto nuestro vector tiene esta pinta:

"><input type=submit formaction="javascript&colon;this&lsqb;'a'&plus;'lert'&rsqb;

Si al WAF no le gustaban los corchetes, no es ninguna sorpresa que tampoco le gustasen los paréntesis necesarios para ejecutar nuestro alert(1). Al igual que con los corchetes, podríamos aplicar la solución de usar las entidades HTML correspondientes de &lpar; y &rpar;. Sin embargo, existe otra posibilidad más sencilla que consiste en usar el carácter de acento grave ` en lugar de los paréntesis. Finalmente, nuestro vector quedaría de la siguiente manera:

"><input type="submit" formaction="javascript&colon;this&lsqb;'a'&plus;'lert'&rsqb;`1`"

Ya solo nos queda codificar correctamente todos los caracteres especiales para incluir el vector de ataque en el valor del parámetro search de la URL:

http://registration.olympic.org/en/faq/search?search=Type%20here%22%3E%3Cinput%20type=submit%20formaction=%22javascript%26colon%3Bthis%26lsqb%3B%27a%27%26plus%3B%27lert%27%26rsqb%3B%601%60%22


Y, por fin, después de visitar la URL anterior en un navegador sin filtro de XSS reflejado e introducir cualquier texto en el buscador, obtenemos nuestra medalla olímpica en forma de alerta javascript:

Ejecución exitosa del XSS en Firefox
¡Fin de la carrera! ¡Un saludo!

2 comentarios: