Définition
Le nom parle de lui-même : cette faille apparaît quand il est possible d'injecter du code SQL dans les requêtes SQL qui sont faites dans une page web. C'est actuellement la "meilleure" vulnérabilité Web en rapport fréquence/surface d'exploitation. Les conséquences d'une faille SQL peuvent être multiples, du contournement de formulaires d'authentification au dump complet de la base de données en passant par l'exécution arbitraire de code. Dans ce premier article, nous allons essayer de nous familiariser avec des injections simples (appellées aussi injections du premier ordre).
Une requête SQL classique pour la vérification de mots de passe est SELECT * from admins WHERE login='$login' AND password='$password', en français "Sélectionner les lignes de la base de données qui appartiennent à la table "admins" et dont les champs pseudo et password sont respectivement égaux aux variables $pseudo et $password". Ainsi, il y a deux manières de passer l'identification sans avoir le mot de passe, voir même le pseudo :
- Imaginons que le programmeur vérifie qu'il existe des lignes qui répondent correctement à la requête SQL, autrement dit il vérifie que le nombre de lignes renvoyées n'est pas égal à 0. Imaginons que nous envoyons comme pseudo ' OR 1=1#, le # étant le caractère de commentaire supprime tout ce qui le suit dans la requête (selon les serveurs, on trouve aussi -- ou les commentaires style C /* */). Ainsi, la requête devient SELECT * from admins WHERE login='' OR 1=1, ce qui se lit "Sélectionner les lignes de la base de données qui appartiennent à la table "admins" et qui on soit un pseudo nul, soit telles que 1=1". Vous l'avez compris, 1=1 est une expression toujours vraie, par conséquent cette requête renverra toutes les lignes de la base de données. Ainsi, le nombre de lignes est différent de 0 et l'utilisateur aura bypass cette requête.
- La deuxième manière est beaucoup plus sûre, elle permet de s'identifier en tant qu'un pseudo connu à l'avance. Ainsi, vous remplissez le champ pseudo convenablement (il est souvent assez aisé de connaître certains pseudos réels) et injectez du code SQL comme précédemment dans le champ mot de passe. Ainsi, la requête devient vraie uniquement pour la ligne contenant le pseudo de la victime et vous devenez identifié en tant que la personne ciblée. Ainsi, même si le programmeur a vérifié qu'il n'existait qu'une ligne correspondant au couple (pseudo,mot de passe), vous passerez l'identification.
Ce type de faille est en général facile à reconnaître, puisque si l'on injecte un simple guillemet dans le formulaire, une erreur du type suivant interviendra : Warning: mysql_numrows(): supplied argument is not a valid MySQL result resource (pour peu que les rapports d'erreurs soient activés, ce qui est le cas par défaut ; sinon il est tou de même possible en général d'observer des différences de comportement comme l'apparition d'une page vide).
Voici une liste non-exhaustive d'instructions toujours vraies qui peuvent être utilisées (certains sites se défendent en dressant une liste de ce type d'instructions, ce qui est stupide puisque il y en a une infinité). Attention, tous ne marchent pas dans tous les cas, réfléchissez à chacune d'entre elles pour être bien sûr d'avoir compris !
'='
'OR 1=1
'OR a=a
'OR'
'OR''='
'OR"="
'OR'="
'OR '="
'OR "='
'OR ''='
'OR '=''
'OR "=''
'OR ''="
On peut agrémenter ces requêtes comme bon nous semble en utilisant d'autres fonctionnalités du langage SQL :
UNION SELECT pseudo,password FROM admins
UNION SELECT pseudo,password FROM admins WHERE pseudo='OR 1=1# AND password='OR ''="
UNION SELECT pseudo,password FROM admins WHERE pseudo='OR "=' AND password='OR "='
Nous l'avons dit, ce type d'injections reste basique. Le langage SQL est très riche, bien qu'il n'ait pas la puissance de Turing (capacité à stocker des variables notamment). En complexifiant les requêtes, il est déjà possible de causer des dégâts significatifs, comme la divulgation de la base de données. Ceci dit, les injections SQL deviennent plus graves lorsqu'il est possible de doubler les requêtes, c'est-à-dire rajouter un symbole de fin de requête ";) puis une instruction comme INSERT INTO ou UPDATE SET qui permettraient de porter atteinte à l'intégrité des données. Des exemples d'injections avancées (Blind injection, Timing Attack, UNION) vous seront exposées dans la section sur les injections SQL avancées.
Un petit exemple
Comme d'habitude, nous allons prouver nos dires par un petit exemple. Notre exemple est composé de 3 pages : index.php, la page de login du site, auth.php, la page de vérification de l'authentification, et enfin la page qui est protégée, admin.php (qui n'est pas réellement protégée, mais volontairement bloquée par referrer, ce qui nous permettra d'illuster le HTTP headers spoofing). Nous nous sommes placés dans le cas où l'utilisateur connait le pseudo d'un des admins (ce qui est pratiquement toujours le cas), SeriousHack. Il est à remarquer que dans énormément de sites, root, admin, administrator ou webmaster sont aussi des logins très courants.
<!-- index.php - Bases Hacking Administration login Page -->
<html>
<head>
- <div align="center"><h1>Bases Hacking Administration Zone</h1></div>
- <title>Faille de type SQL Injection</title>
</head>
<body>
- <img src="../images/penguinroot.gif">
- <br><br>
- <div align="center">
- <form action="./auth.php" method="POST">
- <table>
- <tr>
- <td>Login</td>
- <td><input type="text" name="pseudo" maxlength="30"></td>
</tr>
- <tr>
- <td>Pass</td>
- <td><input type="password" name="mdp" maxlength="30"></td>
</tr>
- <tr><td colspan=2 align="center"><input type="submit" name="login" value="Login"></td></tr>
</table>
</form>
- </div>
</body>
</html>
<?
- // auth.php - Authentification des admins Bases Hacking
- $login = $_POST["pseudo"];
- $mdp = $_POST["mdp"];
- if ($login != "" && $mdp != "") {
- @mysql_connect("localhost", "serioushack", "mdpmysql") or die("Impossible de se connecter à la base de données");
- @mysql_select_db("users") or die("Table inexistante");
- $resultat = mysql_numrows(mysql_query("SELECT * from admin WHERE pseudo='$login' AND mdp='$mdp';"));
- mysql_close();
- if ($resultat == 1) echo "Authentification réussie, vous allez être redirigés immédiatement. <script>window.location='./admin.php'</script>";
- else header("Location: ./");
- } else header("Location: ./");
?>
<?
- //admin.php - Bases Hacking Administration Panel
- $headers = http_get_request_headers(); //On récupère les headers et on vérifie que l'user est passé par auth.php
- if (!isset($headers["Referer"]) || $headers["Referer"] != "http://".$headers["Host"]."/hacking/admin/auth.php")
- header("Location: ./");
?>
<html>
<head>
- <div align="center"><h1>Bases Hacking Administration Zone</h1></div>
- <title>Faille de type SQL Injection et Referrer Spoofing</title>
</head>
<body>
- <img src="../images/penguinroot.gif">
- <br><br>
- [Message d'accueil]
</body>
</html>
Voici donc les screenshots de ce qui se passe quand on injecte dans le mot de passe le classique ' OR 1=1#
Comme prévu, la requête est modifiée par notre injection et nous réussissons à afficher admin.php (qui nous aurait redirigés si nous n'étions pas au préalable passés par auth.php), et ce sans le moindre mot de passe !
Les injections SQL sont communes, sous des formes plus ou moins faciles à exploiter et à démasquer. Ceci dit, unr programmation rigoureuse permet de les éradiquer aisément. Ces injections ne sont pas isolées : les injections LDAP ou XML (XXE) fonctionnent selon le même principe.
un commentaire
Pingback: Injections SQL avancées - Netpratic