L'injection SQL from scratch

Une des failles les plus exploitées se nomme l’injection SQL, elle est première du classement des OWSAPTop 10 vulnerabilities.

Je vais partir du principe que vous connaissez PHP et SQL, si ce n’est pas le cas vous pouvez vous référer aux deux cours présents sur Openclassrooms. Ils sont particulièrement complets et amplement suffisant pour suivre cet article.

Etant plutôt familiarisé avec MySQL j’utiliserai uniquement ce SGBD.

0x01 Qu'est ce qu'une injection SQL

Une injection SQL est ce que l’on appel une faille de sécurité. Elle est le résultat d’erreurs de programmation lors du développement d’une application web par exemple.

Les résultats peuvent être catastrophiques : ajout de comptes administrateurs, défacage du site, suppression de données, dump de la base, RCE (plus complexe) …

Grosso merdo une prise de contrôle du serveur et les boules pour l’admin.

Passons à l’aspect technique d’une injection SQL. Pour cela prenons un exemple on ne peut plus basique avec une table et une page PHP qui vient lister les utilisateurs

CREATE TABLE `users` ( `id` int(11) NOT NULL, `username` text NOT NULL, `password` text NOT NULL, `rank` text NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; INSERT INTO `users` (`id`, `username`, `password`, `rank`) VALUES (1, 'switch', '12345', 'none'), (2, 'admin', 'adminp', 'admin'); ALTER TABLE `users` ADD PRIMARY KEY (`id`);
$bdd = new PDO('mysql:host=localhost;dbname=tut_injec', 'root', '');
$bdd->exec("SET NAMES UTF8");
$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = "SELECT id, username, password, rank FROM users WHERE id=$_GET[id] ORDER BY id";

$query = $bdd->query($stmt);
$user = $query->fetch();

echo $stmt."";
echo "Welcome $user[0] (id=$user[1]) (password=$user[2]) (rank=$user[3])";

?>

Le résultat est le suivant

Cependant quelque chose ne va pas. Ici le paramètre id contient l’id du membre dont on veut récupérer le nom, c’est un integer. Ce paramètre est ensuite tout bonnement insérer dans la query SQL. Qu’est ce qui nous empêche de mettre quelque chose d’autre qu’un id ? Rien, le problème est là. Avec MySQL il est possible de faire plusieurs query en une seule avec le keyword UNION.

Quelques petites précisions s’imposent. On va décortiquer l’url

  • and 0 permet de rendre la première requête fausse (1 et 0 retourne 0). Il est nécessaire qu’elle soit annulée car c’est toujours le résultat de la première requête dans le cas d’un union qui sera récupérée dans des cas non prévu (la deuxième requête n’étant pas prévue dans le php

  • UNION SELECT 1,2,3,4 notre requête de base récupère 4 champs (id, username, password, rank) il faut donc obligatoire récupérer dans les autres union le mème nombre de colonne

  •  permet de mettre le reste de la query (ORDER BY id) dans un commentaire et donc d’ignorer le reste de la requête

Voilà le principe d’une injection SQL : on va modifier la requête SQL pour se l’accaparer afin d’exécuter NOTRE code !

0x02 Récupérer des informations dans la base de donnée

Afficher 1 c’est sympa mais ça sert complètement à rien. Maintenant que l’on sait qu’il y a une injection SQL exploitable on va pouvoir essayer différentes choses :

  • Récupérer des informations sur la base de données

  • Récupérer les autres tables et autres base de données afin de trouver des informations intéréssantes

  • Lire des fichiers

  • Récupérer les utilisateurs de la base de données

0x02.1 Récupérer des informations

Il peut être utile de récupérer d’autres informations sur MySQL pour faciliter l’escalade de privilège

.

Pour cela il suffit juste de faire appel aux fonctions de MySQL comme :

  • @@version pour avoir la version de Mysql (5.7.14)

  • database() pour avoir la db utilisée (tut_injec)

  • current_user() pour voir l’utilisateur courant (root@localhost) pas très sécu ça ..

  • @@secure_file_priv qui récupère le répertoir dans lequel on peut notamment utiliser load_file (pour plus tard)

Tout les variables systèmes sont disponibles dans la documentation. Sinon ici j’utilise la Mozilla Hackbartrès pratique pour exploiter des injections SQL ou faille XSS, elle fournie quelques fonctions sympathiques.

0x02.2 Récupérer les informations de la base de données

Etant donné que j’ai voulu tout faire en one shot et que c’est pas forcément très compréhensible je vais tout détailler en couleur plus bas

Si l’image est trop petite n’hésitez pas à récupérer l’url complète, elle sera affichée à taille réelle

  • GROUP_CONTACT qui permet de concatener plusieurs row en un seul

  • CONCACT_WS qui permet de contacter plusieurs colonnes à l’aide d’un séparateur

En rouge :Ici je récupère la liste des base de données déclarer dans MySQL il y en a 10. Je les récupères via la colonne schema_name de la table information_schema.schemata.

En bleu :Je récupère les tablesde la base de donnée tut_inject. Je les récupères via la colonne table_name de la tableinformation_schema.tables. J’utilise la close WHERE table_schema pour bien spécifier la base de donnée voulue

En vert :Je récupère les colonnes de la table users de la base de donnée tut_injec. Je les récupères via la colonne column_name de latable information_schema.columns. J’utilise la close WHERE table_name and table_schema pour bien spécifier la base de donnée et table voulues

En orange :Je récupère l’intégralité de la table users Pour cet étape il est important de bien se situer dans la base de donnée et d’avoir un schèma de celle-ci en tête. Pourquoi ne pas la modéliser en UML ou autre.

0x02.3 Lire un fichier

Maintenant pourquoi ne pas récupérer un fichier présent sur le serveur. Pour cela on va utiliser la méthode LOAD_FILE(). Question de sécurité s’imposant on ne peut pas utiliser load_file sur n’importe quel fichier, uniquement ceux qui sont dans le répertoire spécifié par la variable @@secure_file_priv (vu plus haut). Ici /var/www/local/. Coup de chance notre fichier index.php s’y trouve

Je sais pas si vous avez remarqué mais j’ai utilisé la fonction HEX() qui permet de convertir une variable en héxadécimal. Si je ne l’avais pas utilisée le PHP aurait été chargé comme tel dans le fichier et on n’aurait pas vu le voir. Ici un petit coup dehex to textet on récupère le fichier .php On peut aussi utiliser l’écriture héxadécimal pour spécifier le nom du fichier ainsi :

load_file(‘/var/www/local/injec/index.php’) <=>load_file(0x2f7661722f7777772f6c6f63616c2f696e6a65632f696e6465782e706870). Plutôt pratique pour éviter les quotes

0x02.4 Récupérer les utilisateurs présents

Il est aussi possible de récupérer les utilisateurs MySQL et leurs mot de passe (aussi leurs droits)

En rouge : Je récupère tout les utilisateurs qui sont dans la table mysq.user

En bleu : Je récupère uniquement les administrateurs grâce à WHERE Super_priv=char(0x59) (“Y”)

0x02.5 Pour aller plus loin

Ici je n’ai parlé que des injections SQL très basique avec affichage des erreurs etc. Cependant on peut être confronté à des cas plus complexe avec : * Filtrée(pas de union, pas d’espaces, pas de quote …) injections sql hard filtered

  • Binaireje veux dire par la qu’elle retourne uniquement vrai ou faux

  • Insertou on n’affiche rien mais que l’on insère des données

  • Blindon ne voit pas ce que retourne l’injection

  • Time basedoù l’on doit utiliser le temps de réponse du serveur (cf : mon article)

 

 

0x03 SQLmap

Un superbe outil écrit en python se nommant SQLmap est une référence en matière d’injection SQL. Il permet de tester des SGBD variés et avec différentes techniques (blind, time based etc). Disponible pré installer sous Kali et sur Githubil est ainsi disponible sous windows et linux/

sqlmap -hh

Et hop pleins d’options cool pour une injection bien profonde et de toute beauté. Maitenant il existe deux écoles

sqlmap -u http://votreurlpastresecu/?id=1 -a

ou

sqlmap -u http://votreurlpastresecu/?id=1 --dbs

La première option va faire un full scanc’est à dire usersversiondatabasestables .. (long très long) et la deuxième uniquement les databases (rapide). J’ai choisi ici d’utiliser la deuxième étant donné que c’est pour un but éducatif et non de pwn

Et voilà un beau dump de chacune de mes databases présentes, maintenant passons aux tables présentes dans test_injec. Pour cela :

sqlmap -u http://votreurlpastresecu/?id=1 -D tut_injec --tables

On voit qu’il y a une table users, récupérons les colonnes :

sqlmap -u http://votreurlpastresecu/?id=1 -D tut_injec -T users --columns

Sympa. Allez, dumpons tout ça

sqlmap -u http://votreurlpastresecu/?id=1 -D tut_injec -T users --dump

Comme vous le voyez SQLmap est assez … pratique et puissant. Ilutilise vraiment une panoplie de techniques très diverses, ainsi une faille peux être pas repérée manuellement il la verra surement.

A contrario pour des injections SQL plus poussé avec des filtres il faudra revenir à la main même si SQLmap embarque pas mal d’options de contournement.

Pour obtenir un web shell

sqlmap -u http://votreurlpastresecu/?id=1 --os-shell

Cependant il faut que l’utilisateur mysql et suffisament de droits pour cela. Ainsi dans la plus parts des cas cette commande foire même chez les mauvais sysadmins. Pour obtenir diverses informations :

sqlmap -u http://votreurlpastresecu/?id=1 --banner // banni&egrave;re g&eacute;n&eacute;rale sqlmap -u http://votreurlpastresecu/?id=1 --current-user // utilisateur actuel sqlmap -u http://votreurlpastresecu/?id=1 --current-db // db utilis&eacute;e

Et enfin pour finir SQLmap embarque avec lui un bruteforcer de mot de passe. Ainsi si vous trouvez des passwords dans mysql.users il vous proposera des les crackers pour vous. L’attaque restant du simple brute force elle a peut de chance d’aboutir si le password est assez complexe.

0x04 Bonus utile (vraiment)

Avez vous déjà perdu votre password root ou encore supprimer les droits root et vous vous êtes retrouvé sans possibilité de vous connecter à votre serveur ? Moi oui et plus d’une fois, forcément tu n’avais pas de backup donc tu ne peux pas vraiment réinstaller le serveur mysql.

Grâce àmysqld_safe –skip-grant-tableson va redémarrer le serveur mysql sans les droits ainsi on pourra recréer un compte root ou simplement changer le mot de passe

service mysql stop mysqld_safe --skip-grant-tables mysql use mysql; UPDATE user SET password=PASSWORD('tonpass') WHERE User="root"; flush privileges // NE PAS OUBLIER SRX quit service mysql stop service mysql restart

Et hop un nouveau password pour root

 

0x05 Diverses cheat sheet

Je vous proposes diverses cheat sheet pour aller plus loins, surtout pour contourner des filtres ou découvrir d’autres méthodes d’injections

Sinon pour s’entraîner il y a bien entendu la catégorie web-serveur sur root-me.org

0x1312 Conclusion

Pour évite ce type de vulnérabilité,Never Trust User Input. Filtrezau maximum les interactions grâce aux regex et utilisez les fonctions d’échappement de caractères telque addslashes()

Bref si vous avez des fautes d’orthographes, suggestions, questions, remarques je m’en bat l.. non sérieusement j’ai implémenté Disqus pour rendre le site plus vivant donc n’hésitez pas à vous en servir