Encontrando y parchando vulnerabilidades XSS

Mientras desarrollaba un Plugin para wordpress me encontré con una vulnerabilidad que permitía el uso de XSS para atacar el sitio web. Documento aquí mi encuentro con la esperanza de que a alguno le parezca útil. También me tomo la libertad de hacer un paréntesis para mencionar que aun estoy necesitado de sus donaciones. Si el blog les ha sido de ayuda, y tiene la posibilidad de ayudarme a mantener vivo el proyecto p-freeweb se los agradecería mucho.

El plugin, entre otras cosas, da la posibilidad de especificar una imagen como default para el listado de categorías. La url de dicha imagen tiene que ser pasada por la url ya que de otra forma se complica el asunto. La url entonces queda algo asi:

http://site.com/?s=search&d=defaultImage.png

Y luego con php hacemos lo siguiente:

$defaultImageURL = urldecode($_GET['d']);
$imgUrl = file_exists("/images/{$cat->slug}.jpg") ? "images/{$cat->slug}.jpg" : $defaultImageURL;
echo  "<img src=\"$imgUrl\" width=\"225\" />";

El código real es un poquito diferente, pero la idea es la misma:

1) Tomar el valor de la imagen default.

2) Chequear si hay un archivo con el nombre del slug de la categoría. Si lo hay, usar ese como imagen para la categoría. Si no lo hay, usar la imagen default.

3) Imprimir el código HTML para mostrar la imagen.

Espero que para este momento ya hayas detectado la vulnerabilidad, de no ser así, vuelve a revisar el código antes de seguir con la lectura. Si no logras detectar la vulnerabilidad, probablemente tengas que pulir tu conocimiento sobre seguridad en el desarrollo web.

El problema está en la forma en la que insertamos la imagen default. Para entender mejor, hay que ver que es lo que finalmente se escribe en nuestro HTML:

<img src="defaultImage.png" width="225" />

Eso suponiendo que la url es la mostrada inicialmente.

Ahora, que pasa si modificamos la url? En este punto sabemos que la variable $imgUrl se imprime como el valor de src de la imagen. Este mismo conocimiento puede ser obtenido sin leer el código fuente. Basta con hacer unas pruebas y ver el resultado para adivinar como es que el código está siendo procesado.

Cual es nuestro objetivo?

Nuestro objetivo es inyectar algo como:

<img src="image.png" onload="alert('b')" width="225" />

Conociendo que la variable $imgUrl se imprime en el HTML, podemos decir que el resultado es equivalente a:

<img src="valor de $imgUrl" width="225" />

En este punto seguramente ya sabes lo que hay que hacer. Para producir nuestro ataque, es necesario que la variable $imgUrl tenga el siguiente valor:

image.png" onload="alert('b')

de modo que una al imprimirse en el HTML quede de esta manera:

<img src="image.png" onload="alert('b')" width="225" />

Hasta ahí todo bien. Tenemos un ataque XSS.

Cuales son las implicaciones?

Con este tipo de ataque podemos hacer muchas cosas, pero una de las que mas nos interesaría es atacar un sitio ecomerce. Estos sitios generalmente requieres un logeo por parte del comprador. El comprador puede escoger ser recordado por el sistema, lo cual guardaría unas cookies en su computadora por medio de las cuales el sistema lo puede identificar nuevamente cuando este de regreso en el sitio. Nuestro objetivo es robar esas cookies de modo que podamos “secuestrar” la sesión del usuario. Nada mal eh?

Pero como atacaríamos?

Bueno, pues sabemos que la url de la imagen se toma de una variable en la url, de modo que si logramos modificar la url para inyectar el ataque hemos tenido éxito.

En este caso bastaría con agregar lo siguiente justo después de png:

%2522%2Bonload%253D%2522alert%2528%2527b%2527%2529%2522
nuestra url quedaría así:
http://site.com/?s=search&d=defaultImage.png%2522%2Bonload%253D%2522alert%2528%2527b%2527%2529%2522
Y solo necesitamos lograr que alguien visite esa url.

Nuestro ataque en este caso es inofensivo; si acaso llega a ser irritante, pero nada más. Un ataque más elaborado, sin embargo, podría causar graves daños.

Como evitarlo?

Una forma sencilla, es reemplazar algunos caracteres como ,, ( y ) de nuestra url, ya que dichos caracteres son prácticamente necesarios para realizar el ataque. Para ello podemos hacer lo siguiente:

$imgUrl = file_exists("/images/{$cat->slug}.jpg") ? "images/{$cat->slug}.jpg" : $defaultImageURL;
 $imgUrl = str_replace(array('(', ')', '"', "'"), '', $imgUrl);

Esto puede causar problemas si algún archivo legitimo tiene alguno de eso caracteres como parte de su url. Hasta donde se los paréntesis son permitidos en los nombres de archivos. En mi caso lo he resuelto de esa manera, por lo menos temporalmente, ya que la imagen default se especifica desde el backend de wordpress y se puede especificar que dichos caracteres no deben formar parte de la url del archivo.

En otros casos quizá quieras utilizar otros métodos. Uno que se me ocurre es combinar php y javascript. Pero dado que no lo he probado, prefiero no describirlo ya que pudiera no ser 100% efectivo.

Aun me falta hacer algunas pruebas respecto a mi solución y considerar algunos otros escenarios y soluciones, pero por ahora creo que identificar el problema es lo más importante.

Que piensas de la vulnerabilidad y la solución?