.
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
, 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],"
");
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
.
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 :
Pratiquement, il suffit d'envoyer un cookie avec comme nom GuppYUser et comme valeur :
--------------------------------------------------------------------------------
fr||[NICK]||[MAIL]||LR||||on||
--------------------------------------------------------------------------------
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++) {
?>
echo $web6; ?> echo $dbmsg[$i][0]; ?> echo $web7." ".FormatDate($dbmsg[$i][1]); ?>
echo $dbmsg[$i][2]; ?>
if ($dbmsg[$i][0] != $web214) {
?>
[ echo $web140; ?> ]
}
?>
[...]
-----------------------------------------------------------------------------------------------------------------------------
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],"
");
---------------------------------------------------
par :
----------------------------------------------------------------------------------------------
$userprefs[6] = str_replace("\n","
",strip_tags(str_replace("
","\n",$userprefs[6])));
----------------------------------------------------------------------------------------------
Dans postguest.php, remplacer les lignes :
--------------------------------------------------------------------------------------------------------------------
$ptxt = eregi_replace("\\[l\\]www.([^\\[]*)\\[/l\\]", "