Les injections CSS - Règle @import
15 Décembre 2022
On continue sur le sujet des injections CSS avec cette fois l'utilisation de la règle
@import
. L'objectif est toujours de récupérer des informations contenues dans les attributs HTML, mais cette fois, sans avoir besoin d'iframer la page vulnérable.
Utilisation de la règle @import
La règle CSS @import
permet d'importer des règles CSS en référençant d'autres feuilles de styles. Pour fonctionner, la règle @import
doit être déclarée avant toutes les autres règles CSS (à l'exception de la règle @charset
). L'import d'une feuille de style s'effectue comme ceci :
Par exemple, le navigateur appliquant le style suivant n'effectuera pas l'import :
Ce qui rend donc impossible son utilisation lors des injections déjà vues dans les exemples précédents :
C'est donc le code suivant qui sera utilisé pour illustrer l'utilisation de la règle @import
dans une injection CSS :
L'injection permettant d'importer une feuille de style contrôlée par l'attaquant étant la suivante :
Lorsque la victime visite la page injectée, son navigateur effectue la requête permettant de charger la feuille de style importée :
En admettant que le contenu de la feuille de style malicieuse n'effectue seulement qu'un changement de couleur du titre :
La victime verra donc ainsi l'application du style CSS :
Pour l'attaquant, l'idée ici sera d'utiliser la feuille de style CSS sous son contrôle afin d'exécuter les attaques vues précédemment, mais sans avoir besoin d'iframer le site vulnérable ou de passer plusieurs fois des liens malicieux à sa victime (un premier lien pour identifier le premier caractère, un second lien pour le second caractère, etc).
@import et sélecteurs d'attributs
Il est possible d'utiliser la règle @import
afin de récupérer la valeur d'un attribut HTML, comme déjà vu dans la première partie. L'attaquant va tout d'abord créer la page d'instructions CSS suivante :
Puis, fournir le lien exploitant l'injection à sa victime :
Une fois le premier caractère connu, il forgera une nouvelle feuille de style puis tentera de tromper à nouveau sa victime. Soit, en admettant que le premier caractère récupéré est c
:
Il n'y a en fait pas réellement de différence avec la première méthode, excepté que l'attaquant n'est plus contraint par la taille de l'URL.
Avec la première méthode, le paramètre GET
contenait toutes les instructions CSS permettant de connaitre le caractère ciblé. L'URL pouvait donc parfois dépasser la taille autorisée et, dans ce cas, nécessitait de découper la payload en plus petites parties.
L'intérêt réel de cette mécanique devient surtout visible lors de son automatisation.
Automatisation de l'exploitation utilisant la règle @import
Le principe de l'automatisation de l'exploitation est le suivant :
L'attaquant va soumettre une URL à sa victime (comme c'est déjà le cas pour l'exploitation manuelle)
L'appel va entrainer la génération d'une feuille CSS à la volée par le serveur de l'attaquant de la façon suivante :
un import récursif
des sélecteurs d'attribut CSS permettant de faire fuiter un des caractères de l'information
un sélecteur d'attribut CSS prenant en compte tous les caractères connus par l'attaquant afin de savoir quand stopper l'attaque
Soit le diagramme suivant (se répétant jusqu'à ce que la totalité du jeton soit récupéré) :
En théorie cela fonctionne, mais en pratique, plusieurs problématiques restent à régler avec que cela fonctionne.
Directive d'import bloquante
L'import de la première feuille de style va permettre de récupérer le caractère n
de l'information à récupérer (grâce aux sélecteurs CSS) mais également d'importer la prochaine feuille de style (qui permettra la récupération le caractère n+1
et ainsi de suite) :
Il est donc nécessaire que le serveur de l'attaquant récupère le caractère avant de générer la nouvelle feuille CSS, bloquant ainsi l'import. Il faut donc que le navigateur, en attendant la réponse de l'import, applique les autres règles CSS présentes dans le fichier (permettant ici de faire fuiter un des caractères).
Seuls les navigateurs basés sur Chromium fonctionne de la sorte. Firefox par exemple, va bloquer à l'import et se mettre en attente de la nouvelle feuille CSS sans jamais appliquer les sélecteurs CSS présents déclarés dans la feuille. Ce qui aura pour conséquence de ne pas faire fuiter le caractère nécessaire à la génération de la feuille de l'import, faisant ainsi tomber le navigateur dans une boucle infinie.
Multiple application d'un style CSS à un élément
Afin de gagner en performance, si plusieurs règles CSS ciblent le même élément, alors une seule de ces règles sera appliquée. Par exemple :
Ici seul le background2.png
sera appliqué. Mais ce n'est pas tout, car seul l'URL https://example.com/images/background2.png
sera appelée, la première règle CSS sera donc complètement ignorée.
Cela va poser problème dans le cadre de l'automatisation. Lors de l'application de la première feuille de style, le sélecteur CSS du premier caractère sera appliqué (en admettant que le premier caractère est un h
) :
Mais la règle (présente dans le prochain import) ciblant le même élément sera alors ignorée ne faisant ainsi pas fuiter le second caractère :
Ici, la solution est d'utiliser la pseudo-class :first-child
autant de fois que nécessaire de la façon suivante :
Cela ajoute une autre problématique. L'utilisation des sélecteurs ~*
ne semble pas compatibles avec l'utilisation d'une pseudo-class comme :first-child
. Etant donné que seuls les navigateurs basés sur Chromium sont de toute façon exploitables, il est possible d'utiliser à la place la pseudo-class has()
:
Si quelqu'un identifie la possibilité d'utiliser les sélecteurs
~*
avec la pseudo-class:first-child
, qu'il n'hésite pas à me contacter par mail ou sur mon Twitter :)
Position de l'injection
La position de l'injection semble avoir une incidence sur le bon déroulement de l'exploitation. Si le point d'injection se situe après l'élément ciblé, alors, l'attaque se déroule rapidement et sans encombre :
Dans le cas contraire, la récupération sera soit très lente voir même bloquée, excepté si la victime effectue des clics sur la page injectée :
Le mieux reste encore de tester soi-même l'automatisation pour bien se rendre compte de ces difficultés.
Un PoC est disponible ici.
Références
Dernière mise à jour