SQL Injection : démonstration, explications.
Réponse AJAX

Introduction

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 :

  1. le développeur du site a oublié d'appliquer real_escape_string (ou équivalent) dans les valeurs envoyées aux requêtes SQL,
    ou alors il utilise des valeurs numériques dans les WHERE sans avoir pris les précautions suffisantes,
  2. les résultats du SELECT sont affichés dans le navigateur.

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 ;

Mise en pratique

Découverte du site de test

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.

Est-ce que le développeur a utilisé real_escape_string ?

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 ?

Votre réponse n°1

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).

Votre réponse n°2

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...

Votre réponse n°3

Il nous donne donc une indication précieuse : le nom de la table. Quel est-il ?

Votre réponse n°4

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.

Déterminer le nombre de champs sélectionnés dans le SELECT

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 :

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
...

Combien de champs dans le union select pour ne plus obtenir d'erreur ?

Votre réponse n°5

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).

Votre réponse n°6

Trouver le nom de la base de données

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 :

Votre réponse n°7

Trouver toutes les tables de cette base de données

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

Notez ici le nom de la deuxième table contenue dans cette BDD :

Votre réponse n°8

Trouvez les champs de la table user

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

Notez ici la liste des champs de la table user :

Votre réponse n°9

Trouvez les login et les mot de passe des utilisateurs

À 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é :

Votre réponse n°10

Notez les login et mots de passe des utilisateurs que vous avez trouvé :

Votre réponse n°11

Quel est le cryptage utilisé ? Un pirate doit arriver à reconnaitre un cryptage selon sa taille et les types de caractères qu'il contient. Éventuellement chercher un peu sur Internet.

Votre réponse n°12

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 :

Votre réponse n°13

Protégez-vous... de l'injection SQL

Et de peur qu'un pirate arrive quand même à trouver une faille :