Vulnérabilités fantastiques et où les trouver (première partie) - Scripts intersites avec des erreurs de formulaire Django

Vulnérabilités de script XSS

Pourquoi le Cross-Site Scripting (XSS) est-il toujours la vulnérabilité Web la plus courante? La théorie de l'identification de XSS est assez simple, il existe de nombreux outils d'analyse statiques créés pour le détecter et pourtant, il existe de nombreuses vulnérabilités non découvertes. Alors, qu'est-ce qui donne?

L'une des raisons est que les méthodes d'analyse de programme traditionnelles échouent souvent à identifier l'intention d'un code donné. Par exemple, un outil peut avoir du mal à déterminer quels objets du programme peuvent contenir une entrée utilisateur.

Dans mon précédent article, je décrivais comment nous avions résolu ce problème en construisant un système qui apprend les spécifications de sécurité de milliers de projets Open Source et les utilisait pour trouver de véritables vulnérabilités. J'ai également promis de partager quelques exemples intéressants de ce qu'il a appris.

J'ai décidé de commencer par une source intéressante et plutôt inattendue de problèmes éventuels dans les projets utilisant Django. Cet article est un guide pour identifier et exploiter les vulnérabilités XSS en utilisant des erreurs de validation dans les formulaires Django. Voici un exemple réel: https://github.com/mozilla/pontoon/pull/1175.

Lançons-nous directement et commençons par un petit quiz. Combien de fois avez-vous écrit / vu un code similaire à l'extrait suivant?

Qu'en est-il de celui-ci?

Ou celui-ci?

Ce sont tous des exemples répandus de la façon dont vous informez généralement l'utilisateur que l'entrée fournie est invalide, non? L'entrée est extraite des paramètres de la requête HTTP et parfaitement décompressée dans un objet MyForm. Si l'un des champs contient une entrée non valide (par exemple, quelqu'un a saisi la chaîne "foobar" dans un champ numérique), une page 400 Bad Request est renvoyée avec une description de l'erreur. La différence entre les extraits est le format de l'erreur renvoyée - une liste HTML, du texte brut ou JSON.

Maintenant, une question à un million de dollars - lequel de ces extraits rendra votre application Web XSSable?

Pour y répondre, examinons l’API de formulaires Django de deux points de vue:

  1. L’attaquant peut-il injecter des entrées malveillantes dans la page Web affichée dans le navigateur de l’utilisateur?
  2. Cette entrée malveillante sera-t-elle toujours correctement échappée avant d'être renvoyée à l'utilisateur?

Selon la documentation de Django, la création de messages d’erreur dynamiques pour les erreurs de validation de champ consiste à générer une exception django.core.exceptions.ValidationError avec le message correspondant. Une telle exception levée par l’une des fonctions de validation du formulaire (par exemple, les méthodes clean () et clean_ () de la classe django.forms.BaseForm) entraînera l’enregistrement du message dans le dictionnaire d’erreur du formulaire (django). .forms.utils.ErrorDict) et plus tard éventuellement montré à l'utilisateur.

Une façon d'exploiter une telle exception consiste à utiliser certains des champs de formulaire intégrés qui reflètent commodément l'entrée erronée dans le message d'exception. J'ai essayé tous les types de champs de formulaire Django énumérés ici et j'ai obtenu la liste suivante: ChoiceField, TypedChoiceField, MultipleChoiceField, FilePathField. Chacun de ceux-ci génère un message d'erreur du type "Sélectionnez un choix valide.% (Valeur) s n'est pas l'un des choix disponibles.", Où valeur est l'entrée erronée.