Vulnerabilidades fantásticas y dónde encontrarlas (Parte 1): secuencias de comandos entre sitios con errores de formulario de Django

Vulnerabilidades de scripting XSS

¿Por qué Cross-Site Scripting (XSS) sigue siendo la vulnerabilidad web más común? La teoría de identificar XSS es bastante sencilla, hay muchas herramientas de análisis estático creadas para detectarlo y, sin embargo, hay muchas vulnerabilidades sin descubrir. Entonces, ¿qué da?

Bueno, una de las razones es que los métodos tradicionales de análisis de programas a menudo no pueden identificar la intención de un determinado código. Por ejemplo, una herramienta podría tener dificultades para descubrir qué objetos en el programa podrían contener información del usuario.

En mi publicación anterior describí cómo abordamos este problema mediante la construcción de un sistema que aprende las especificaciones de seguridad de miles de proyectos de código abierto y los utiliza para encontrar vulnerabilidades reales. También prometí compartir algunos ejemplos interesantes de lo que aprendió.

Decidí comenzar con una fuente interesante y bastante inesperada de posibles problemas en proyectos que usan Django. Esta publicación es una guía para identificar y explotar vulnerabilidades XSS utilizando errores de validación en formularios Django. Aquí hay un ejemplo real: https://github.com/mozilla/pontoon/pull/1175.

Saltemos directamente y comencemos con un pequeño cuestionario. ¿Cuántas veces has escrito / visto código similar al siguiente fragmento?

¿Qué hay de este?

¿O este?

Todos ellos son ejemplos generalizados de cómo generalmente le haces saber al usuario que la entrada proporcionada no es válida, ¿verdad? La entrada se toma de los parámetros de solicitud HTTP y se desarma perfectamente en un objeto MyForm. Si alguno de los campos contiene entradas no válidas (por ejemplo, alguien ingresó la cadena "foobar" en un campo numérico), entonces se devuelve una página 400 Solicitud incorrecta con una descripción del error. La diferencia entre los fragmentos es el formato del error devuelto: una lista HTML, texto sin formato o JSON.

Ahora una pregunta de un millón de dólares: ¿cuál de estos fragmentos hará que su aplicación web sea XSSable?

Para responderlo, investiguemos la API de formularios de Django desde dos puntos de vista:

  1. ¿El atacante puede inyectar información maliciosa en la página web que se muestra en el navegador del usuario?
  2. ¿Esta entrada maliciosa siempre se escapará correctamente antes de que se devuelva al usuario?

Según la documentación de Django, la forma de generar mensajes de error dinámicos para errores de validación de campo es generar una excepción django.core.exceptions.ValidationError con el mensaje correspondiente. Tal excepción lanzada desde cualquiera de las funciones de validación del formulario (por ejemplo, los métodos clean () y clean_ () de la clase django.forms.BaseForm) hará que el mensaje se almacene en el diccionario de errores del formulario (django .forms.utils.ErrorDict) y luego posiblemente se muestren al usuario.

Una forma de explotar tal excepción es usar algunos de los campos de formulario integrados que reflejan convenientemente la entrada defectuosa en el mensaje de excepción. Probé todos los tipos de campos de formulario de Django enumerados aquí y obtuve la siguiente lista: ChoiceField, TypedChoiceField, MultipleChoiceField, FilePathField. Cada uno de estos genera un mensaje de error como "Seleccione una opción válida.% (Valor) s no es una de las opciones disponibles", donde el valor es la entrada defectuosa. para la victoria .

La segunda opción es explotar campos personalizados y / o procedimientos de validación. Por ejemplo, considere el siguiente fragmento (tomado de un proyecto real y modificado por brevedad):

Aquí una buena carga útil sería algo así como foo. .

Sí, tienes razón, las excepciones ValidationError por sí solas no nos otorgarán un XSS. Para una vulnerabilidad adecuada, necesitamos un ingrediente más: la capacidad de inyectar mensajes de error en la página HTML final que se devuelve al usuario.

La clase ErrorDict mencionada anteriormente tiene los siguientes métodos para extraer los mensajes de error:

  1. as_data () - sin desinfección
  2. get_json_data (escape_html = False): sin desinfección si escape_html == False (valor predeterminado)
  3. as_json (escape_html = False) sin desinfección si escape_html == False (el valor predeterminado)
  4. as_ul () - seguro
  5. as_text () - sin desinfección
  6. __str __ () (invoca as_ul ()) - seguro

Ahora volvamos a nuestro pequeño cuestionario. Es fácil ver que el fragmento 1 es seguro, porque usa el método __str __ (), que escapa a la entrada. Los fragmentos 2 y 3, sin embargo, son peligrosos y pueden provocar XSS.

Hay dos mensajes principales para llevar aquí. El de los desarrolladores es el mantra "siempre desinfecta la entrada no confiable". El de los investigadores de seguridad es: un simple grep -R "ValidationError" podría ampliar la superficie de ataque para usted.

Por cierto, me respetas si pasaste la prueba correctamente sin leer la publicación completa.

¿Está creando una aplicación web o móvil?

Crowdbotics es la forma más rápida de construir, lanzar y escalar una aplicación.

¿Desarrollador? Pruebe Crowdbotics App Builder para andamiar e implementar rápidamente aplicaciones con una variedad de marcos populares.

¿Ocupado o no técnico? Únase a cientos de equipos felices que crean software con los PM de Crowdbotics y desarrolladores expertos. Alcance el cronograma y el costo con Crowdbotics Managed App Development de forma gratuita.