TP3 : injection SQL
groupe : Arthur Gaillard, Elian Loraux et Lillian Steimer
Sujet
Set up
Nous avons crée un site web comportant des postes d'utilisateur qu'il est possible de consulter ainsi qu'une page d'administration. Le projet s'organise comme suit :
Index.php
index.php comporte un formulaire permettant d'affichier les postes des utilisateurs
<?php
echo "<p>Bienvenue sur ce (superbe) réseau social !</p>\n";
echo "<p>De quel utilisateur voulez-vous voir les posts ?</p>\n";
$conn = new mysqli("db", "tdi", "tdi", "tdi");
$query = mysqli_query($conn, "SELECT login FROM users;");
echo "<form action=\"posts.php\" method=\"get\">\n";
echo " <select name=\"poster\" id=\"user-select\">\n";
while($row = mysqli_fetch_assoc($query)) {
$user = $row['login'];
echo " <option value=\"${user}\">${user}</option>\n";
}
echo " </select>\n";
echo " <input type=\"submit\" value=\"Voir les posts\">\n";
echo "</form>\n";
?>
poste.php
poste.php affiche les poste des utilisateur selectionné dans index.php
<?php
$poster = $_GET["poster"];
$conn = new mysqli("db", "tdi", "tdi", "tdi");
$recent_query = 'SELECT * FROM posts WHERE date > (NOW() - INTERVAL 1 WEEK)';
$from_login_query = "SELECT * FROM posts where login='${poster}'";
$query = "${recent_query};${from_login_query};";
echo($query);
mysqli_multi_query($conn, $query);
echo "<h3>Posts récents</h3>";
do {
if ($result = mysqli_store_result($conn)) {
while ($row = mysqli_fetch_row($result)) {
echo '<pre>';
print_r ($row);
echo '</pre>';
}
}
if (mysqli_more_results($conn)) {
echo "<h3>Posts de ${poster}</h3>";
}
} while (mysqli_next_result($conn));
$conn = null;
admin.php
admin.php est une page qui nécessite un couple identifiant/mot de passe. Il permet d'accéder à une page d'administration
<!-- admin.php -->
<html>
<head>
<title>Sans titre</title>
</head>
<body>
<p align="center">
<span style="font-size:14pt;"><b>Page d'administration</b></span>
</p>
<?php
if (isset($_POST["login"]) && isset($_POST["pass"])) {
$connect = new mysqli("db", "tdi", "tdi", "tdi");
# mysql_select_db(BASE, $connect);
$sql = "SELECT login, password FROM users WHERE login = '" . $_POST["login"] . "' AND password = '" . $_POST["pass"] . "';";
echo ($sql);
echo ("<br>");
$result = @mysqli_query($connect, $sql) or die("Erreur lors du traitement de la requête " . $sql);
if (mysqli_num_rows($result) < 1) {
die("Login/Pass incorrect !<br><a href='admin.php'>Retour</a>");
}
?>
<p align="center">Bienvenue ! Vous avez désormais accès à toutes les fonctions d'administration du site !</p>
<p align="center">> Consulter la BDD</p>
<p align="center">> Gérer les articles</p>
<p align="center">> ...</p>
<?php } else { ?>
<p align="left">Cette page est réservée aux administrateurs ! Si vous n'en êtes pas un, cliquez <a href="index.php">ici</a> !</p>
<p align="left"> </p>
<form name="formlogin" method="post" action="admin.php">
<p align="center">Entrez votre login / pass :</p>
<p align="center"> <input type="text" name="login" value="login"></p>
<p align="center"><input type="text" name="pass" value="pass"></p>
<p align="center"><input type="submit" name="ok" value="OK"></p>
</form>
<?php } ?>
</body>
</html>
Injection
Dans la page poste.php
, il est possible d'injecter du SQL dans le champs poster
. Par exemple, avec la commande '*''; SELECT * FROM users WHERE login=''*'
Il est possible de récuper l'entiértié de la table utilisateur de la base de donnée. Cette table comporte identifiant, adresse mail et mot de passe.
Dans le page admin.php
, le champs "mot de passe" est également injectable. Il est possible ainsi d'accéder à la page d'administration sans mot de passe grace à la commande : pass' OR 1 = 1 ; --
Correction du code
Nettoyage
L'idée est de crée une fonction qui prend en entrée l'entée utilisateur et donne en sortie l'entrée utilisateur nettoyé de tout caractère possiblement interprétable (comme les balise html, les guillemet, les apostrophe)
Hashage
Dans certain cas, hasher les entrée utilisateurs peut marcher. La methode n'est pas recommandé et il ne faut pas avoir besoin des information dans le système, mais pour une simple page de connexion, cela fonctionne.
Préparation de la requete SQL
Une manière de faire est de préparer la requete SQL, par exemple :
$sql = "INSERT INTO Utilisateur (login, password, mail) values (:login,:password,:mail)";
$stmt = $db->prepare($sql);
$login = "lagaffeg"
$password = "andreFranquin"
$mail = "gaston.lagaffe@dupuis.fr"
$stmt->BindParam(':login',$login);
$stmt->BindParam(':password',$password);
$stmt->BindParam(':mail',$mail);
$stmt->execute();
Conclusion
Ayant fait du CTF, du pentest et l'ayant vu l'année derrnière en Licence pro ASSR. Les injections SQL de base font partie de la boite à outils de base de la sécurité type "red type" (côté attaquant) et peut être très pratique côté "blue type" (partie défense) pour tester des champs. De faite, ce TP n'a été que des révision.