sábado, 25 de octubre de 2014

Atrapando bugs CSRF con peticiones JSON en Atrapalo.com

Las razones

En el momento de publicar esta entrada, el fallo de seguridad que voy a exponer a continuación sigue siendo explotable. Fue reportado a través y por la motivación que recibí de un intermediario que disponía de contactos con la empresa. Después de más de tres meses, y a pesar de volver a preguntar por el estado del reporte tanto al intermediario como directamente a la empresa (info@atrapalo.com), no he vuelto a obtener respuesta de ninguna de las dos partes. Desde mi punto de vista, revelar un fallo de seguridad que aún no está solucionado es totalmente ético si se dan circunstancias de este tipo. Por un lado, los usuarios son conscientes de los ataques y pueden intentar tomar medidas de precaución y, por otro, se presiona de algún modo a la empresa para que lo corrija. Aparte, y para qué os voy a engañar, otra de las razones de publicarlo es que odio que me hagan emplear mi tiempo sin ningún tipo de consideración o agradecimiento, ya que este análisis no surgió por iniciativa propia. Así, que si las horas que dediqué a encontrar este y otros curiosos fallos de seguridad (que quizá den para más post) sirven al menos para entretener a unos lectores, también me daré por satisfecho.

El fallo

Si tienes experiencia explotando fallos de tipo CSRF, es posible que te hayas topado con el inconveniente de que las peticiones web que intentas explotar son de tipo JSON. Esto puede hacer que un atacante olvide la posibilidad de llevar a cabo esta clase de ataques, ya que los navegadores, haciendo uso de la Same Origin Policy (SOP), bloquearán las peticiones cuyo "Content-Type" se establece a "application/json" cuando se lanzan desde una web controlada por el atacante hacia un dominio distinto. Es decir, los navegadores no permiten llamadas cross-domain de tipo JSON. Para permitir a los programadores lidiar con esta restricción, nació JSONP, pero desgraciadamente estamos en el rol del atacante y no tenemos esta opción.

Echemos un vistazo a las peticiones de modificación de datos de atrapalo.com, por ejemplo a las de la sección "Mi Perfil":



Como vemos en la imagen, las peticiones usan el método PUT para lanzar peticiones JSON. Aparte, no usan ningún tipo de token anti-CSRF, ni se realiza validación del parámetro "id". Tal como comentamos anteriormente, nuestro ataque no va a tener éxito si intenta replicar este tipo de peticiones desde un dominio externo, ya que serán bloqueadas por el navegador. El problema que suelen tener muchas webs en este punto, es que no comprueban en el servidor que la petición que les llega del cliente sea del tipo correcto, por lo que si tenemos suerte, nuestra petición seguirá funcionando igualmente si cambiamos el método PUT por POST y el Content-Type a otro formato distinto de "application/json". Ahora bien, ¿qué Content-Type escogemos y cómo hacemos esta petición desde nuestra web maliciosa? Lo primero, es que el Content-Type debe contener un valor que nuestro navegador permita usar en peticiones cross-domain, y lo segundo es que, a priori, no nos vale crear el típico formulario HTML para enviar la petición, ya que los valores se enviarían con el formato "id=17769328&nombre=MyName&apellido=MyApel..." que no es un formato JSON válido.

Una solución es establecer el Content-Type a "text/plain" y realizar la petición de la siguiente forma:


Estando logueados en atrapalo.com, cuando lanzamos este script desde nuestra web maliciosa, podemos notar que el navegador muestra el siguiente error en consola:


Sin embargo, la petición se ha enviado correctamente al servidor, y si nos logueamos de nuevo en la web, comprobaremos que nuestros datos de perfil han sido modificados:


Si queremos llevar a cabo el ataque mediante un formulario HTML con enctype="text/plain", podemos hacerlo siempre que nos aseguremos que los datos a enviar queden en formato JSON (recordemos que un formulario normal con parámetros input envía los datos en formato "id=17769328&nombre=MyName&apellido=MyApel..."). Para ello, podemos "trucarlo" enviando en un solo parámetro del formulario todos los parámetros JSON incluyéndolos en el atributo "name" y añadiendo un nuevo parámetro ficticio (clave) que consuma el signo "=" sin romper el formato de JSON. Quedaría de la siguiente forma:


Para evitar ser víctimas de estos ataques y hasta que este fallo se arregle, recomendaría a los usuarios de atrapalo.com no visitar ninguna otra web externa mientras estén autenticados.

¡Saludos!