Attaques DOM-based XSS : principes, impacts, exploitations et bonnes pratiques sécurité

Les DOM-based XSS sont des vulnérabilités particulièrement méconnues car plutôt rares. En effet, il s’agit d’une variante de XSS (Cross-Site Scripting) – certainement l’une des failles les plus répandues dans les applications web.

Principes, impacts, exploitations possibles, nous vous présentons dans cet article un aperçu complet des vulnérabilités DOM XSS ainsi que les bonnes pratiques pour prévenir les risques d’attaques et de compromission de vos applications web.

Qu’est-ce qu’une attaque DOM-based XSS ?

Le Document Object Model, abrégé DOM, est une interface de programmation d’application (API) pour les fichiers HTML et XML.

Document modélisé sous forme d’arbre, il est composé de nœuds subdivisés en objets, ce qui facilite la modification du contenu d’un navigateur web à l’aide de scripts.

De plus, le World Wide Web Consortium (W3C) a normalisé ce standard, qui peut par ailleurs être utilisé avec tous les navigateurs et n’importe quel langage de programmation. 

Ainsi, cette API permet à JavaScript d’accéder aux éléments de la page HTML dans le contexte d’un navigateur web et de récupérer les événements utilisateurs comme : des événements liés à la page (par exemple, « onload » après le chargement de la page), à la souris, au clavier, au formulaire ou à des objets tels que le corps de la page (avec document.body).

Structure DOM (Document Object Model)

Maintenant, revenons sur la vulnérabilité DOM-based XSS. Un attaquant va injecter du code malveillant dans un élément du DOM, et ainsi pouvoir exploiter cette faille si le JavaScript de l’application ciblée prend des données à partir d’une entrée contrôlable par l’attaquant, appelée la source. Il les envoie vers une sortie qui supporte l’exécution dynamique de code, appelée sink. Il n’y a aucune interaction avec le serveur pour déclencher l’exécution du JavaScript voulu.

La source la plus répandue est l’URL : du phishing est donc possible pour provoquer une faille XSS DOM-based. Cependant, la criticité est réduite si une interaction utilisateur est nécessaire.

Comment identifier et exploiter une DOM-based XSS ?

Les DOM-based XSS se produisent uniquement côté client. En effet, aucune interception avec un proxy n’est nécessaire.

Les outils intégrés au navigateur seront donc très importants pour détecter cette vulnérabilité. Dans l’onglet « Sources » de ces outils, nous avons accès au code de la page dont le JavaScript.

L’analyse des scripts présents dans la page web est prioritaire. Il s’agit de regarder de plus près le code et voir si une variable d’un script est sous le contrôle de l’utilisateur. Vous retrouverez toutes les sources et les sinks qui peuvent mener à des DOM XSS.

Après inspection du code, si nous avons détecté une source potentielle, nous cherchons ensuite à comprendre où se situe cette source. Une solution possible est d’utiliser la console du navigateur qui permet d’obtenir la valeur de cette source.

//un exemple venant de la console du navigateur//
location.hash
'#test'

Une fois que nous avons bien saisi le paramètre que l’on contrôle, il faut cerner où ce paramètre va se retrouver dans le DOM. Pour cela, nous faisons une recherche du terme mis en entrée sous l’onglet « Sources ». Cette analyse va nous permettre de comprendre échapper l’objet courant du DOM pour exécuter du code.

Cette première méthodologie est envisageable si le code n’est pas minifié (la taille du code est réduite ce qui diminue fortement sa compréhension) mais cela n’est pas tout le temps le cas lors des pentests d’application web.

Détecter les failles DOM-based XSS avec DOM Invader

Si vous êtes un utilisateur de Burp, vous pouvez utiliser DOM Invader. C’est une extension préinstallée dans le navigateur intégré de Burp. Elle rend la détection des DOM XSS plus facile et plus rapide. Il y a deux fonctionnalités majeures :

  • La vision du DOM où l’on peut identifier toutes les sources contrôlables et les sinks de façon instantanée. Il est aussi très simple de retrouver où la charge utile est injectée dans le code côté client.
  • La vision des Web Message (ou postMessage) qui permet de capturer, éditer et rejouer tous les web message qui passent par la page.

La première de ces deux fonctionnalités est la plus importante pour détecter une DOM-based XSS classique. L’extension est capable d’injecter une chaine de caractère défini par l’utilisateur dans toutes les sources possibles ou seulement dans l’URL. Elle va ensuite signaler les sources et les sinks où cette chaine de caractère se retrouve. Le plus de l’extension est que les caractères « ‘ « <> » sont ajoutés à la chaine injectée pour savoir s’ils sont échappés ou encodés. Le second grand avantage de l’extension est que l’on a accès à la stack trace dans la console du navigateur.

DOM invader

Nous voyons directement où notre chaine de caractère se retrouve dans le code en cliquant simplement sur le lien dans la console (comme le montre l’image ci-dessus). Cette extension est d’une grande aide pour réussir à exploiter les DOM-based XSS.

Identifier les DOM XSS via de l’analyse statique de code

Une bonne pratique est de faire de l’analyse statique de code (le fait d’inspecter le code source de façon automatique). Des librairies peuvent être utilisées à ce sujet, ce sont des linters. Eslint est un bon exemple de ce genre de librairie. Mozilla a développé un plug-in Eslint pour identifier les DOM-based XSS.

Son utilisation est très efficace pour corriger les vulnérabilités, car les endroits dangereux dans le code seront détectés plus précisément qu’une recherche de chaine de caractères. Une revue manuelle est ensuite nécessaire pour confirmer si le code est vulnérable ou non.

Comment se protéger d’une vulnérabilité DOM-based XSS ?

La particularité des DOM XSS fait qu’il n’est pas toujours possible de les détecter au niveau du serveur, car tout se passe côté client. De fait, les protections envisageables peuvent être les mêmes que pour les failles XSS standards, même s’il faut porter une attention particulière à assainir le code côté client et non côté serveur.

Dans cette optique, la première mesure à prendre est d’éviter que des sources contrôlées par un utilisateur changent dynamiquement la valeur d’un sink. C’est en effet la méthode la plus efficace. Cependant, si une fonctionnalité l’oblige, une validation côté client doit être mise en place sur la base d’une liste blanche (whitelist), afin d’autoriser uniquement le contenu de confiance.

Une seconde solution est d’assainir les données. Pour ce faire, Mozilla a par exemple conçu une API : Sanitizer API. Cette API est directement intégrée au navigateur, ce qui permet d’éviter des problèmes liés au changement de parser (qui pourrait survenir dans une nouvelle version d’un navigateur). En effet, sa conception a été pensée pour empêcher spécifiquement les DOM XSS. Cependant, cette fonctionnalité intégrée au navigateur reste expérimentale et doit être activée par l’utilisateur.

Aussi, les Trusted Types sont un autre moyen de se protéger contre des DOM XSS. Et même si ce mécanisme est plutôt récent, il a déjà fait ses preuves contrairement à la Sanitizer API mentionnée précédemment. En ce qui concerne les DOM XSS, l’utilisation de sinks représente le danger. En implémentant ce mécanisme, les sinks ne recevront plus des chaines de caractère, mais des Trusted Types. Par ailleurs, la mise en œuvre est relativement simple, car on peut spécifier un comportement par défaut qui va agir comme filtre pour toutes les chaines de caractères envoyées aux sinks. Néanmoins, une configuration plus poussée est possible lorsque pour des cas d’usage précis il faut autoriser certains comportements spécifiques.  

Enfin, il existe deux autres solutions assez simples à mettre en place : utiliser textContent quand l’utilisation de HTML n’est pas nécessaire et encoder les entrées utilisateurs avec une fonction de ce type :

function htmlEncode(str){
  return String(str).replace(/[^\w. ]/gi, function(c){
     return '&#'+c.charCodeAt(0)+';';
  });

Conclusion

En résumé, Les DOM XSS sont des vulnérabilités qui ne sont pas évidentes à identifier, d’autant que la compréhension est plus complexe que pour une XSS stockée ou reflétée.

Cependant, l’impact de ce type de faille est rarement critique. En effet, une DOM XSS tout comme une XSS standard peut permettre à un attaquant de voler un cookie, rediriger un utilisateur vers un site malveillant, etc.

Par ailleurs,  les DOM-XSS peuvent être exploitées par l’intermédiaire des postMessage, lors de la communication entre deux fenêtres. DOM-Invader peut aussi être utilisé pour détecter l’utilisation des postMessage et faire des tests pour savoir si l’implémentation est correcte.

Auteur : Julien BRACON – Pentester @Vaadata