Heureusement, on les rencontre de moins en moins. Mais c’est très important de savoir les détecter et les exploiter !
Récupérez le contenu des bases de données avec les injections SQL
Les injections SQL sont généralement considérées comme critiques car elles permettent :
de récupérer (voire de modifier) le contenu de la base de données, notamment les identifiants, mots de passe, ou leurs hashs ;
et pire encore, en fonction du moteur de base de données utilisé (MySQL, Oracle, MS SQL), de prendre le contrôle du serveur (via une Remote Code Execution, ou “RCE”).
À noter qu’une fois que nous avons le contenu de la base de données, nous avons normalement l’ensemble de la donnée, ce qui est le plus important in fine pour une application.
Comment ça fonctionne une injection SQL ?
Dans la plupart des applications, les données sont stockées dans une base. Cette base de données peut être sur le même serveur que l’application ou sur un autre serveur dédié (on va alors parler d’architecture n-tiers).
Pour que l’application puisse stocker ses données dans la base de données et les lire ensuite, elle va effectuer des requêtes SQL avec un compte qu’on lui aura configuré.
Le serveur web envoie une requête SQL au serveur de base de données pour récupérer les données dont il a besoin. Parfois, ces requêtes sont contextualisées avec des données fournies par les utilisateurs de l’application.
Comment on modifie une requête SQL ?
En injectant des caractères spéciaux SQL dans le ou les champs qui seront pris en paramètre pour construire la requête SQL :
![Le pentester injecte une SQL dans les requêtes SQL envoyées de l'application et du serveur web à la base de données. L'objectif est de récupérer des informations comme des mots de passe.](https://user.oc-static.com/upload/2022/07/22/16584844436493_FR_7727176_Intrusion-Web_Static-Assets_p4c2-1.png)
Voyons ce qui les différencie.
Les injections SQL de type error-based
Les SQLi error-based sont les plus faciles à identifier et à exploiter. On les trouve généralement moins souvent dans la nature : comme elles sont facilement détectables, elles sont généralement rapidement identifiées et corrigées.
La détection est assez simple : si le code est vulnérable, lorsque vous rentrez un caractère spécial (comme une apostrophe ‘
ou un guillemet “
) dans un champ qui va être utilisé comme paramètre dans une requête SQL, la requête va buguer.
Si les erreurs sont affichées, vous aurez un message d’erreur qui vous indique parfois même ce qui ne va pas, à l’image de la réponse de l’application de démo :
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'test'' at line 1
On dit que l’injection est error-based, car sa détection et son exploitation reposent sur l’apparition d’une erreur dans la page.
Les injections SQL de type boolean-based
Les SQLi boolean-based sont déjà plus compliquées à expliquer et à détecter.
À la place, une page ou un résultat par défaut va être renvoyé.
Comment savoir alors si la requête est vulnérable ?
La détection de ces injections est basée sur une différence de réponse du serveur en fonction du résultat de l’injection. Si la requête SQL est vulnérable et que l’injection fonctionne, nous obtenons une réponse A. Si elle ne fonctionne pas, nous obtenons une réponse B, comme par exemple une page défaut.
Un exemple concret : vous avez une mire d’authentification avec un identifiant et un mot de passe. Si la requête est construite de la forme suivante :
SELECT username FROM users WHERE username like ‘$_GET[username]’ AND password like ‘$_GET[password]’
L’utilisation de caractères “wildcard” (c'est-à-dire qui remplacent 0 ou plusieurs caractères) comme %
devrait permettre d’avoir une requête valide.
Il est alors possible d’itérer sur le paramètre vulnérable, jusqu’à extraire les informations que l’on vise de la base de données. Dans notre cas, si nous souhaitions trouver le mot de passe, il suffirait de tester tous les caractères possibles suivis de %
jusqu’à trouver la bonne lettre, puis recommencer en ajoutant un caractère jusqu'à trouver le mot de passe complet !
Les injections SQL de type time-based
Le dernier type d’injection SQL est lui aussi de type blind.
Supposons que vous ayez la même requête qu’avant, mais que cette fois l'application gère bien les retours, et que même l’utilisation de wildcards ne permette pas de savoir si la requête est vulnérable ou non.
Comment faire alors ?
En jouant sur le temps ! Eh oui, dans les requêtes SQL il est possible d’ajouter des instructions comme sleep()
et de mettre ainsi en pause la requête. Si la requête est vulnérable à une injection SQL et que nous arrivons à faire en sorte que le sleep()
soit interprété, alors il sera possible de savoir qu’il y a une injection SQL, en mesurant le temps que prennent les requêtes à s’exécuter ! Ces injections sont appelées les injections SQL time-based.
Il existe un dernier type d’injection encore plus difficile à détecter, et qui dépend de beaucoup de facteurs : les injections SQL blind avec exfiltration de données via des canaux auxiliaires, ou "Out-of-band Application Security Testing SQL Injection" (OAST SQLi). Ces injections consistent à exfiltrer les données issues du résultat de la requête dans des canaux autres que la réponse HTTP, par exemple une requête DNS.
Exploitez des injections SQL
Prenons un exemple d’injection SQL potentielle avec une fonctionnalité de notre application example.com
.
Notre application permet aux médecins de se créer un profil, avec leurs nom, prénom, adresse, etc. Il faut bien que ces données soient stockées en base de données si l’application veut se souvenir d’eux et des autres utilisateurs !
Admettons maintenant que l’application ait une fonctionnalité qui permette de chercher les autres utilisateurs de l’application, et que ce champ soit vulnérable aux injections SQL. La requête SQL sera de la forme :
SELECT prenom, nom FROM utilisateurs WHERE nom like ‘$param’
$param
est notre variable qui contient ce que l’utilisateur recherche.
Si l’utilisateur écrit Bob
en tant que nom, tout va bien se passer. Mais si l’utilisateur écrit ‘bob
, que va devenir la requête ? Quelque chose comme ça :
SELECT prenom, nom FROM utilisateurs WHERE nom like ‘’bob’
Là, vous aurez vraisemblablement une erreur indiquant que les apostrophes ne sont pas fermées correctement, ou que bob n’est pas un mot clé valide. Vous avez votre injection SQL ! Vous n’avez plus qu’à l’exploiter correctement ensuite pour exfiltrer les données qui vous intéressent.
Concrètement, comment on l’exploite ?
La manière la plus simple d’exploiter des injections SQL avec des conditions WHERE
(comme dans l’exemple), c'est de faire en sorte que la condition retourne toujours "vrai", en ajoutant une assertion toujours vraie comme “ 1=1
” ou “ ‘a’=‘a’
”.
À la place de bob
, nous allons envoyer bob’ or 1=1 --
dans le champ. Ce qui donne une requête modifiée comme ceci, si on reprend notre exemple :
SELECT prenom, nom FROM utilisateurs WHERE nom like ‘bob’ or 1=1 --
Le moteur de base de données va donc comprendre
"Sélectionne le nom et le prénom des utilisateurs où ceci est vrai”
En fait, la condition nom like ‘bob’ or 1=1 --
va renvoyer toujours vrai, puisque c’est un OR
booléen. La base de données va donc renvoyer tous les noms et prénoms des utilisateurs de la base de données.
Pour aller chercher des données plus intéressantes, comme les mots de passe des utilisateurs, il faudrait compléter cette requête. Nous ne verrons pas cela dans ce cours car c’est de la "post-exploitation", c'est-à-dire que ça fait partie d'opérations qui se déroulent généralement après une exploitation.
L’outil SQLMap présenté dans la vidéo de démonstration est très utile pour automatiser l’exploitation des injections SQL. Cet outil permet de détecter et d’automatiser l’exploitation de vulnérabilités de type injection SQL. Je vous conseille néanmoins de ne l’utiliser que pour exploiter les injections et vous aider à les trouver, pas pour faire tout le travail à votre place, car il est loin d’être infaillible !
On ne va pas rentrer dans le détail des contre-mesures, mais ces vulnérabilités sont relativement “faciles” à éviter ou à corriger : il suffit d’utiliser ce qu’on appelle des requêtes SQL paramétrées (prepared statements, en anglais) ! Il y en a dans la plupart des langages de programmation, pour ne pas dire tous.
À vous de jouer !
Challenge
Vous allez à votre tour exploiter une injection SQL sur un challenge officiel de Root Me.
Bon courage et à vos claviers !
Solution
Rappelez-vous de la philosophie du hacker : on vous invite à apprendre à chercher, tester, essayer, bidouiller. Nous ne pouvons pas vous donner de réponse toute faite, sinon c'est contre-productif. Cela dit, toutes les notions dont vous avez besoin sont dans ce chapitre !
En résumé
Les injections SQL sont des injections dites “serveur” puisqu’elles sont exécutées côté serveur (et précisément en base de données, dans le cas de cette vulnérabilité).
Ces injections peuvent survenir dès lors que la requête SQL est construite à partir de paramètres contrôlables par un utilisateur, comme un champ de recherche ou un pseudo.
Il existe plusieurs types d’injections SQL, listés ici par ordre de difficulté de détection :
les error-based ;
les blind boolean-based ;
les blind time-based ;
les blind oast-based.
Les injections SQL permettent généralement de récupérer du contenu dans la base de données.
Elles peuvent également permettre de modifier la base de données en ajoutant ou supprimant des données.
Dans le pire des cas et en fonction du moteur de base de données, notamment MS-SQL, il peut être possible d'exécuter du code sur le serveur.
Dans le prochain chapitre, nous allons aborder les vulnérabilités liées à la manipulation du système de fichiers ainsi que les injections de commandes arbitraires. C’est parti !