home > tutoriel-injection-sql

Voici un tutoriel sur l'injection SQL sous MySQL et MS SQL server. Dans ce tuto vous verez toutes la puissance des injections; ma platforme de test : LAMP avec magic_quote à OFF dans une première partie et ensuite magic_quote à ON ;-)
Une injection SQL est un type d'exploitation d'une faille de sécurité d'une application web. On va injecter une requête SQL non prévue par le système et pouvant compromettre sa sécurité, cette requête va permettre dans certain cas d'afficher carrement les mot de passe et les identifiants associées.

Grâce a un mauvais filtrage des variables (injection via des variables php dans notre cas mais cela fonctionne tous aussi bien pour de l'asp ou tout autres languages web dynamique), on va pourvoir modifier la requête et afficher ce que l'on veux dans la base de données.
Voici une fonction php qui permet de filter mes variables
addslashes(), qui ajoute des slashes ;-) alors la chaîne ' -- devient \'
--.
mysql_real_escape_string(), ajoute un slash aux caractères suivants : NULL , \x00, \n, \r, \, ', " et \x1a.
Une des principales faiblesse des script php codé par des
mauvais développeur et d'utiliser addslash() comme unique filtre
de variable.
La fonction addslashes() ne suffit pas pour stopper les injections via
les variables numériques, qui ne sont pas encadrées
d'apostrophes ou de guillemets dans les requètes SQL.
Notez que si la directive PHP magic_quotes_gpc est à on par défaut, et elle appelle addslashes() sur toutes les données GET, POST et COOKIE.
Grâce à la fonction mysql_error() nous essayons de provoquer des erreurs pour que mysql_error() nous indique des infos non souhaité et nous permette de connaitre le nom des tables. Pour que mysql_error() puissent s'afficher il faut que dans le php.ini error_reporting et display_error() soit activé ou que le dev n'est pas mis de @ devant mysql_connect() mysql_select() mysql_query() ...
Passons à la pratique :
Dans un premier temps nous allons tester si les champs a renseigner
sont sensible a l'injection et donc si magic_quote est à ON dans
php.ini.
Pour provoquer une erreur il suffit de mettre une simple quote ' dans le champ ou un double quotes " . Si un message d'erreur s'affiche
 alors les quotes ( ' ou " ) passent sans être bloquées.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'mon_texte' and pass='"mon_pass'' at line 1
La fonction magic_quotes() dans
mon php.ini est a OFF; et donc ce
qui suit de ma requète sera exécuté ;).
A ce stade il faut bien connaitre la synthaxe sql pour jongler avec les quotes et les expressions pour détourner la requête initial. Voila la requête type que l'on devrait avoir:
Pour que la condition soit True
il suffit de la comparer a une valeur similaire comme 1=1 ou 'X'='X' qui renvoi obligatoirement
true...
Ici on selectionne SELECT dans ma_table
tous les champs * ou le login est NULL OU 1=1 (condition toujours vrai) ET
le password est NULL OU 1=1
Il suffit donc pour cette injection d'insérer pour le login: ' OR 'X'='X et pour le password. La
modification de notre requete va ressembler a ça.
Exemples d'expressions qui retourne toujours true:
SELECT * FROM table WHERE 1=1
SELECT * FROM table WHERE 'toto'='toto'
SELECT * FROM table WHERE 1<>2
SELECT * FROM table WHERE 3>2
SELECT * FROM table WHERE 2<3
SELECT * FROM table WHERE 1
SELECT * FROM table WHERE 1+1
SELECT * FROM table WHERE 1--1
SELECT * FROM table WHERE ISNULL(NULL)
SELECT * FROM table WHERE ISNULL(COT(0))
SELECT * FROM table WHERE 1 IS NOT NULL
SELECT * FROM table WHERE NULL IS NULL
SELECT * FROM table WHERE 2 BETWEEN 1 AND 3
SELECT * FROM table WHERE 'b' BETWEEN 'a'
AND 'c'
SELECT * FROM table WHERE 2 IN (0,1,2)
SELECT * FROM table WHERE CASE WHEN 1>0
THEN 1 END
Ici on voit bien que la variable id et passé en GET dans mon url par l'intermédaire de id=1 . Si ma variable était passé en POST cela ne serait pas de problème non plus pour cela un plugin firefox (par exemple UrlParams) permet d'éditer mes variables POST.
Dans un premier temps je test si ma variable est bien filtrée en ajoutant un simple quote comme ceciNotez que id est un entier !!! (voir plus haut)
Si j'ai une erreur du style :
You have an error in your SQL Syntax
Warning: mysql_fetch_array():
Warning: mysql_fetch_assoc():
Warning: mysql_numrows():
Warning: mysql_num_rows():
Warning: mysql_result():
Warning: mysql_preg_match():
Je peux en conclure que mon site est potentiellement vulnérable noter que si j'ai une parti de la page qui disparaît cela signifie d'autant plus la vulnérabilité importante du site.
Maintenant nous allons faire afficher le nombre de colonnes contenu dans la table: pour cela on peu utiliser la syntaxe sql GROUP BY en fin de requête; de cette manière et en fonction du résultat nous modifierons la requête (blind injection feras l'objet d'un tuto prochainement):
Code:On peu en conclure ici que le nombre de colonnes est 10, erreur à la 11ème.
Grâce a cette info on va pouvoir afficher le nom des colonnes, le nom des bases présentent sur le serveur, la version de la base, les utilisateurs ...
Les commentaires ne sont pas toujours nécessaire dans une injection sql, pour php /* mon_code_php */
Nous pourrions injecter
/* en fin de requête pour la tronquée, ainsi le reste ne serait pas excécuter.
Ici on mets le champ PASS en commentaire avec /* */, notons qu'il faut connaître le champ LOGIN ainsi on peu faire afficher les pass correspondant au login de l'admin.
On peu aussi utiliser les caractères échappatoire sql # ou -- pour microso$ SQL Server:
UNION est implémentée en MySQL 4.0.0.
L'objectif de la commande UNION de SQL est de combiner ensemble les résultats de deux requêtes. Nous pouvons donc insérer n'importe quel requête à la suite.
Les colonnes listées dans la partie select_expression du SELECT doivent être du même type (int, varchar...). Les noms de colonnes
utilisées dans le premier SELECT seront utilisées comme nom de champs pour les résultats retournés.
Comme vue précédemment dans l'exemple d'utilisation nous avons réussi a connaître le nombre de champs contenu dans la table. Ici 10 champs on construit la requête en conséquence.
Le résultat devra être un page sans erreur auquel cas la base ne supporte pas le statement UNION.
A ce stade on va ajouter un signe - devant le 1
Comme l'id -1 n'existe pas dans la base, les champs sélectionnés avec l'UNION seront retournés (1,2,3,4,5,6,7,8,9,10), à la place de ces derniers, l'attaquant peut sélectionner des données de n'importe quelle table.
Pour obtenir la version:
Pour la liste des bases:
La base qui est utilisé actuellement :
Pour l'utilisateur:
Pour la liste des tables de la bases :
Pour la liste des champs de ma table je dois convertir le nom de ma table en hexadécimal (pour ce faire utilisez le plugin firefox :hackbar):
pour moi le nom de ma table est testaa ce qui donne en hexa 746573746161 sans oublier le 0x pour informer la base qu'il s 'agit bien de l'hexadecimal.
Et pour finir maintenant que je connais le nom des champs je peux deviner leur valeur :)
Pour finir le /etc/password
Comme son nom l'indique on peu rediriger la sortie standard avec output file + le path.
On peu exporter la liste de résultat du select.
La fonction INTO OUTFILE écrira (par défaut) dans le dossier lib\mysql\nomdelatable
le fichier contenant la sortie du select.
(Attention à bien replacer le fichier dans le répertoire www, sans quoi vous ne pourrez le consulter)
Il faut également les droits des fichiers. Exemple:
Imaginons un login contenant
Ce qui donne comme requète:
On a pas le bon pass, mais nous pouvons tout de même aller à la racine du serveur rechercher le notre fichier file.txt
et on obtient les pass et logs de connexion. On pourrait aussi pouvoir lire le contenu du code php dans notre ../../www/file.txt imaginez ce que l'on peu faire afficher.
Le code php sera enregistré dans file.text, il nous suffira de le consulter et de modifier $ma_variable par ce que vous désirez ;-).
Les magic quotes permettent de faire automatiquement ce que fait la fonction addslashes(), sur les données transmises par l'utilisateur (magic_quotes_gpc) et/ou provenant d'une source externe (magic_quotes_runtime). Lorsque les magic_quotes sont activées, tous les caractères ' (apostrophes), " (guillemets), \ (antislash) et NULL sont donc échappés par un antislash. D'après nos connaissances personnelles en SQL, on sait que $le_debut est la première variable de la clause LIMIT, qui est sous la forme :
Grace à la clause LIMIT qui ne nécessite aucunes quotes pour délimiter les limites ;)). L'exploitation est exactemenet la mème. $la_fin est souvent calculé suivant le nombre de messages afficher par page ($la_fin=$le_debut+$messagesparpages). Donc
pour peu que la variable $le_debut ne soit pas filtrée, on peut faire afficher ce qu'on veut.
Imaginons une requête pour qu'on puisse récupérer le login et le pass.
On ne connait pas le nombre de champs. Donc nous ferons les test habituelles pour découvrir les champs.
Voici la requête avec UNION:
Pour avoir le login
A vous de trouver pour le pass ( c'est la même chose ...)
Voila c'est tout pour le moment sur les injections sql mais ce type de faille n'a de limite que votre imagination a vous de faire évoluer le domaine de la
sécurité informatique.
La Base de Données Open Source la plus Populaire au Monde
|
|