HACKING
WEB + MySQL/SQL server/PostgreSQL

1. Intro

Cet article a pour but de vous présenter un type de faille présent sur de nombreux sites web utilisant une base de données de type (SQL par exemple) et qui permette aux visiteur de faire des requètes grâce à des applications en PHP/ASP/CFML/JSP. Ces requêtes sont effectuées par des applications comme une inscription à une newsletter, un post sur un forum en passant par les moteurs de recherche. Ces failles sont dues à un abus de confiance du programmeur envers le publique, soit les visiteurs dans ntore cas.

J'étudierais dans cet article que des cas de scripts PHP avec une connection à un serveur MySQL mais de toute manière, la faille est identique pour les autres language de scripting pour le web associé à des serveurs de bases de données SQL/PostGreSQL/Oracle/...
Et puis je suis pas un AS en ASP/CFML/JSP ;)
Tout d'abord je vais aborder quelques notions de SQL car sans ça, vous ne comprendrez rien à mon article, cependant je vais seulement vous donnez que des exemples précis et donc ce ne sera pas très complet, c'est pourquoi je vous conseillerais d'aller sur ces sites web pour apprendre un peu :

http://www.mysql.com/
http://www.nexen.net/
http://www.linuxguru.com/

2. Quelques bases en SQL

Le SQL est un language qui vous permet de communiquer avec les bases de données ;)
Pour communiquer, il faut envoyer des requêtes au serveur de base de données. Voici quelques exemples de requêtes SQL qui pourront nous servir dans cet article :

Donc notre table newsletter avec nos 2 champs (nom,email).

Pour sélectionner la ligne avec le nom "billy" :
"select * from newsletter where nom='billy'"

Pour sélectionner l'email et le nom de la personne qui à comme nom "billy" :
"select nom,email from newsletter where nom='billy'"

Pour remplacer l'email de la personne nommée billy par billy@billy.fr :
"update newsletter set email='billy@billy.fr' where nom='billy'"

Pour effacer l'inscrit nommé billy avec ses informations (email+nom) :
"delete from newsletter where nom='billy'"

Pour executer plusieurs requêtes SQL sur 1 seule ligne il faut les finir par un ";".
"delete from newsletter where nom='billy';delete from newsletter where nom='billy2'"

Pour insérer des commentaires dans une requêtes SQL,
avec MySQL, on met un # devant les commentaires
avec Microsoft SQL Server on met -- devant les commentaires

Bon avec ces quelques requêtes SQL balancées en vrac, vous devriez un peu mieux cogiter.

3. Explication de la faille

Au lieu de vous écrire un blabla imcompréhensif, je vais vous donner un exemple :
Vous arrivez sur un site web qui vous propose de vous inscrire à une Newsletter via un formulaire qui nous permet de remplir 2 champs : "nom" et "email". En temps normal, le visiteur arrive sur la page, et tappe son nom et son email dans les champs, valide le formulaire et il est inscrit. En réalité, que c'est il passé ?

Si on analyse la source de la page web , on devrait trouver quelque chose comme :
<input type=text name="nom"><input type=text name="email">

On va supposer que notre formulaire va envoyer les données à un script qui ajoutera une ligne dans notre table SQL qui est composée de 2 champs : nom et email.

Donc imaginons que note visiteur Billy a tappé son email et son nom sur le formulaire et qu'il l'a validé. Le formulaire va passer les variables à un script (PHP/ASP/CFML/JSP), ce script va interpréter ces variables et va faire une requête SQL sur le serveur de base de données.
Dans notre cas, le script va rajouter une ligne dans la table "newsletter". Notre script (PHP dans notre cas) se trouve de la forme :

<?
connection au server SQL
mysql_query("insert into newsletter ('$nom','$email')"); //execution de la requêtes
echo "Vous venez d\'être inscrit à la newsletter\n";
// ce script est basique mais c'est plus simple pour débuter
?>

Si vous analyser mon code vous verrez que le nom des variables ($nom & $email) correspondent aux noms que j'avais attribué aux tag <input> de mon formulaire HTML.
Donc notre Billy remplit le formulaire et le script php va executer la requête :
"insert into newsletter ('billy','billy@billy.fr')"

Dans ce cas, tout c'est bien passé, Billy est inscrit est heureux car il va recevoir la newsletter, le codeur est content parce que son script marche bien, bref tout va bien.

Et maintenant étant donné que nous sommes curieux, nous allons faire ce que nous ne sommes pas censé faire ;)
C'est à dire qu'on va essayer d'inscrire 2 personnes en validant 1 seule fois le formulaire, intéressant nan ?
Pour inscrire 2 personnes en même temps, il faut qu'on execute 2 requêtes SQL en une fois, Donc mettre 2 requêtes SQL sur la même ligne séparée par un ";".
Un truc comme :
"insert into newsletter ('billy','billy@billy.fr');insert into newsletter ('billy2','billy2@billy.fr')"

Donc si nous voulons executer une telle requête sur le serveur, il faudrait que notre script PHP execute ca sur le serveur, donc si on tappe ça dans les champs du formulaire :

dans le champ nom : "billy"
dans le champ email : "billy@billy.fr');insert into newsletter ('billy2','billy2@billy.fr')"

En fait le but est d'imbriquer une requête SQL dans notre variable $email.
J'éspère que vous em suivez , c'est dur à expliquer plus clairement.
Donc cet exemple est instructif mais pas très utile, ca c'est clair mais bon, maintenant que vous avez tout compris sur la théorie, je vais vous montrer des exemples nettement plus intéressant :)

4. La pratique

Dans notre cas précédent, l'attaque était un peu facile car on connaissait le nom de la table et il n'y avait pas d'information subsidiaire à insérer dans la base de données et nous savions comment été structuré le script PHP.

En réalité, il se pose plusieurs problèmes :

- si nous ne connaissions pas la requête SQL que le script envoie au serveur MySQL, nous aurions eu une erreur lors de la validation du formulaire.
il pourrait très bien y avoir un champ ID avant le nom et l'email, ou tout autre chose dont nous ne connaissions pas l'existence, ce qui rend difficile à écrire notre requête car nous ne connaissons pas suffisament d'information.

- Il peut très bien y avoir une clause WHERE dans la requête SQL, ce qui fausserait notre attaque car nous aurions encore une fois, une requête imcompréhensible. C'est à cause des WHERE ,que je vous conseille , à chaque fois que vous essayerez de réaliser une telle attaque, de rajouter un charactère de commentaires à la fin de la dernière variables envoyées. Dans notre cas, c'était email, donc il est bien de rajouter un # ou -- à la fin pour empécher les clauses WHERE d'être prise en compte.
Ceci nous conduit à avoir des requêtes comme ca :
1 ère requête SQL ; 2 eme requête offensie # fin de la 1 ere requête qui ne sera pas pris en compte.

- nous ne connaissons pas les noms de la table et des champs de la base de données. Nous aurions très bien pu avoir à la place du champs nom, quelque chose comme username. Pour passer ce problème, soit vous faites du Brute Force, soit vous êtes logique et vous vous mettez à la place du qebdéveloppeur qui à codé le script pour penser à utiliser des noms "logiques".

- le problème de l'affichage (pas très explicite :) ).
Le problème de cette attaque est qu'elle est souvent limitée, car imaginons que je veuille connaître le mot de passes d'un modérateur de news, et supposons que je connais la requêtes SQL et que tout marche bien, donc on va dire que la requête est :
"select login,pass from utilisateurs"
or en remplissant ce formulaire j'étais censé accéder à une page qui elle n'execute aucune requêtes SQL ou qui ne m'affiche aucune information relative à l'administrateur.
Ma requête aura bien été exécutée mais l'information ne sera pas affichée car le script n'a pas dans son code les instructions qui lui permettent d'afficher à l'écran le résultat d'une telle requête car cette requête n'est pas censée exister.

En fait la base de l'attaque est simple (enfin ...) mais il y a beaucoup de facteurs qui compromettent l'attaque car on ne connaît pas le type de serveur de base de données, le nom de la table, le nom des champs et même la structure des requêtes SQL.
C'est pour ça, qu'on dit que dans ce cas, le serveur est une Black Box (aucune alusion avec le phreacking) mais comme on ne sait rien du serveur, on dit que c'est une boîte noir enfin Black Box ca fait mieux =)


5. Trouver ce genre de faille

Bon en réalité c'est ça le plus important, aussi si bien pour les hacker que pour ceux qui sécurisent ou tout du moins qui essaye de sécuriser leurs applications.

- commencez par analyer le code de la page qui envoie le formulaire. Sauvez la page sur votre disque dur et analyser la, regarder les noms des variables qui vont être envoyées, si il y a des des <input type=hidden> qui pourrait nous aider.

- il faut se mettre à la place du programmeur pour les hackers et vis-versa pour les programmeurs. Donc normallement , un champ qui stockera des noms ne s'appellera pas "IUBFE". Donc il faut un peu d'intuition pour trouver les bons noms de table/champs.

- essayez de provoquer une erreur lors de l'execution du script car si le script est mal programmé, votre browser vous affichera peut-être la ligne de code qui bug, ce qui vous permetterait de voir le code source. Pour provoquer des erreurs, essaye des techniques de Buffer Overflow, c'est à dire, entrez un maximum de données dans les champs, entrez des caractères non attendus : '"%x°@8\|{ (enfin des trucs dans le genre)
Si vous tapper un ' ou " dans un champs, que le serveur ne vous retourne aucune erreur et que vous êtes sur que le script execute une requête SQL, le script à 99% de chance d'être sécurisé sur ce point.
Car en fait, je pense que vous l'avez compris, en PHP,les variables de type texte se finissent par un " donc si le script escape (je sais pas comment ca se dit en français) les charactères ' et ", c'est à dire met des \ devant, php inerprétera vos guillements du texte normal, alors que si le script ne traite pas les ' et ", il croiera que le guillemet que vous aurez tappé sera un délimitateur de variable. J'éspère que je suis clair, mais j'ai des petits soucis d'expressions ;)

Le mieux pour trouver une faille est d'avoir accés au code source du script, ce qui pourrait nous permettre de ne plus faire du Black-Boxing.

Donc le mieux est d'utiliser un exploit sur le serveur web pour récupérer la source :

- Utilisez showcode.asp sur IIS installé par défaut.

- Si vous voyez un script sur le serveur qui permet de télécharger des fichiers en passant le nom du fichier en variable. (download.cgi?file=blabla.zip), il suffit d'un peu de créativité pour penser à tapper download.cgi?file=le fichier qu'on veut, sans oublier qu'il est peut-être possible de remonter dans les répertoire : download.cgi?file=../../../etc/passwd ;))

- Si le programmeur est un peu étourdit et qu'il a laissé les scripts de debbugage sur le serveur, tappez lescript.phps et si le serveur est configuré pour, vous verrezle code source à l'écran.

6. Sécuriser la faille

Donc comme d'habitude à tout problème, il existe une solution. Dans notre cas, il existe même plusieurs solutions plus ou moins adaptée.
Le principe est de filtrer les données qui vont être envoyées sur le serveur.
Par exemple si j'ai un formulaire qui me demande un numéro de téléphone et qu'on entre des lettres à la place de chiffres,il y a quelque chose de louche :)
Donc pour résoudre ça, il y a plusieurs méthodes :

- on sécurise au niveau du client : un javascript peut faire l'affaire. Donc le javascript vérifie si le champs "numéro de téléphone" contient autre chose que des nombres, et si c'est le cas, il n'envoie pas le formulaire au serveur. Cependant cette méthode n'est pas très efficace, car il y a un moyen de passer cette sécurité. En effet, supposons que le formulaire envoie les données en POST, si nous remplissont le formulaire avec des charactères non-conformes, la sécurité fonctionne. Maintenant, si au lieu de remplir le formulaire, on envoie directement la variable avec quelque chose comme : formulaire.php?tel=cequonveut . La variable sera interprétée par le serveur et non par le client, donc la sécurité aura été inefficace. C'est pour ça que je vous recommande de sécuriser au niveau du serveur.

- on sécurise au niveau du serveur. C'est notre script php qui va analyser les données entrée, et si elles sont non-conformes, ils les refusent et renvoie un message d'erreur. Pour contrôler le contenu de variable on peut utiliser plusieurs procédés comme les Regex (ex: [^0-9] ou encore des fonctions intégrées au support du language sur le serveur comme (je ne les commenterais pas car si vous êtes webdéveloppeur, je suppose que vous connaissez déjà tout ça) :

Pour PHP

- addslashes($votrevariable);
- MAGIC_QUOTES = on (à modifier dans php.ini)
- SAFE_MODE = on

Pour ASP

Ba je ne connais pas grand chose à l'ASP, mais je sais juste que vous pouvez empêcher des ../ d'être insérer dans une variable et que vous pouvez désactiver le système de fichier accéssible par un script ASP en tappant ca en commande ms-dos :
regsvr32 scrrun.dll /u (Merci Rain Forest Puppy)

Vous pouvez aussi directement limiter les droits sur le serveur MySQL en créant un user qui ne pourra accéder à une certaine partie de la base de données ou qui ne pourra qu'executer que certains requêtes SQL.

Le mieux est encore de sécuriser au 2 niveaux, c-a-d, au niveau client et au niveau serveur. Je conseille aussi au webdéveloppeur de ne pas sous-estimer l'étendue de cette faille, car bien nombreux sont les webdéveloppeur qui traittent (sécurisent) les variables où du texte est attendu et laisse les variables où des nombres sont attendus.
Normallement tous les scripts sont sécurisables à 100%, mais bien sur, la faille peut venir d'autre part.

Bon j'éspère vous en avoir appris.

Greetings : superluck , ticthack , HWA , synnergy et jalilos pour m'avoir rappelé le pti showcode.asp :)

CrazyLord@Hackerstreet.com

Crazy Lord
25.11.2000