SimpleBBS ********* Informations : °°°°°°°°°°°°°° Langage : PHP Versions testées : 1.0.3, 1.0.6 Website : http://www.simplemedia.org Problèmes : - Récuperation d'info - Ecriture de code PHP Developpement : °°°°°°°°°°°°°°° SimpleBB est un forum opensource. Il permet de créer des sujets, d'y répondre, de s'inscrire comme membre, d'avoir et d'editer son profile, une liste de tout les membres et des membres en ligne 1.0.3 ~~~~~ Le problème se trouve dans l'enregistrement des nouveaux utilisateurs. L'url pour s'enregistrer est http://[website]/index.php?v=register. Dans index.php, on peut voir : --------------------------------------------------- [...] dans users/users.php, on pourra l'utiliser de cette façon : http://[target]/users/users.php?value=hop ce qui affichera juste le texte "hop". Mais le code peut être beaucoup plus pertinent et plus nocif, comme par exemple si on insere : ------------------- ------------------- Dans un cas apreil, l'url http://[target]/users/users.php?cmd=[COMMAND] executera la commande UNIX [COMMAND] avec les droits et restrictions du serveur, sur l'ordinateur du serveur. On peut tenter d'écrire du code PHP directement par l'url, de cette manière : http://[target]/includes/register.php?name=[PHP code] ou http://[target]/includes/register.php?realname=[PHP code] http://[target]/includes/register.php?email=[PHP code] http://[target]/includes/register.php?age=[PHP code] http://[target]/includes/register.php?usertext=[PHP code] etc... Mais cela ne donnera rien, car le fichier http://[target]/includes/users/users.php n'existe pas ! (Et aussi pour d'autres raisons qu'on verra plus tard) Il existe en fait à http://[target]/users/users.php. Cet élément doit directement nous dissuader de traiter directement par le fichier includes/register.php. Il faut donc utiliser un autre fichier; index.php. On va donc essayer d'inserer des données, et comme on a une interface graphique (un formulaire en html), on va l'utiliser, c'est toujours plus clair. Dans le formulaire, se trouvant donc à http://[website]/index.php?v=register, on doit rentrer les données suivantes (que je fait suivre par leur variables correspondantes) : - Nom ($name) - Password ($password) - Password une deuxième fois ($password2 qui n'est pas inscrite dans users/users.php) - Nom réel ($realname) - E-mail ($email) - Age ($age) - Texte utilisateur ($usertext) - Signature ($signature) Si dans chaque champs du formulaire on met le même code, par exemple , et qu'on va voir la ligne qui correspond à l'enregistrement dans users/users.php, on devrait voir quelque chose du style : -------------------------------------------------------------------------------------------- [...] [ID]|||<? echo $value; ?>|||34965c5a6fb2c45714df0e0f7631b4af|||<? echo $value; ?>|||<? echo $value; ?>|||", $signature); [...] $fd = fopen ("users/users.php", "r"); while (!feof ($fd)) { $buffer = fgets($fd, 1024); if($buffer != "") $nameCheck = explode("|||", $buffer); if(isset($nameCheck[1])) { if($nameCheck[0] > $id) $id = $nameCheck[0]; if(strtolower($nameCheck[1]) == "$loweredname") die("This username already exists!"); } } fclose ($fd); $id = $id + 1; $userFile = fopen ("users/users.php", "a"); fwrite($userFile, "$id|||$name|||$password|||$realname|||$email|||$age|||$usertext|||$signature|||0|||"); fclose($userFile); print("Thank you for registering!"); include("includes/login_form.php"); } else { include("includes/registration_form.php"); } ?> --------------------------------------------------------------------------------- On voit d'abord que seul les variables $name et $password sont indispensables à l'execution du script : --------------------------------------------- if(!isset($name) || $name == "") die("Please insert your name"); if(!isset($password) || $password == "") die("Please insert your password"); --------------------------------------------- Et aussi que la variable $password2, la vérification du password, doit être identique à $password : ---------------------------------------- if($password != $password2) die("Passwords didn't match!"); ---------------------------------------- Puis vient la ligne : --------------------------- $password = md5($password); --------------------------- Qui explique la donnée en md5 dans users/users.php, et qui nous empêche d'utiliser la variable $password pour inserer du code php. On voit ensuite le code : ------------------------------------------ $name = htmlspecialchars($name); $realname = htmlspecialchars($realname); $email = htmlspecialchars($email); $usertext = htmlspecialchars($usertext); $signature = htmlspecialchars($signature); ------------------------------------------ La fonction htmlspecialchars() transforme : - & en & - " en " (si ENT_NOQUOTES n'est pas actif) - ' en ' (si ENT_QUOTES est actif) - < en < - > en > Ce code explique donc pourquoi certains '' se sont transformés en <? echo $value; ?>, et que le code php n'était donc pas exécuté. Ces 5 variables ne sont donc pas utilisables pour l'insertion du code php dans users/users.php. Voyons maintenant pourquoi un des '' devient ' [...] [...] -------------------------------------------------- L'argument de l'input maxlength="3" dit que l'on pourra mettre maximum 3 caractères dans ce champ de formulaire texte. Mais cette restriction est très facilement contournable, en passant par l'url, car le script PHP lui ne vérifie rien. Résumons les restrictions : - $name n'est pas utilisable -> htmlspecialchars() - $password n'est pas utilisable -> md5() - $password2 n'est pas inscrit dans le fichier users/users.php - $realname n'est pas utilisable -> htmlspecialchars() - $email n'est pas utilisable -> htmlspecialchars() - $usertext n'est pas utilisable -> htmlspecialchars() - $signature n'est pas utilisable -> htmlspecialchars() Il ne reste plus que la variable $age qui pourrait nous servir. La restriction html maxlenght="3" est bien la seule restriction appliquée à cette variable. On va donc pouvoir l'utiliser pour inscrire du code par une url. Mais pour cela il faut s'assurer d'une url qui execute bien le script jusqu'à l'écriture dans le fichier users/users.php . Tout d'abord, la base de l'url permettant d'inscrire du code doit être : http://[target]/index.php?v=register Comme nous le montre la première ligne : ------------------------- Cette dernière url inscrira la ligne : [ID]|||Haxor|||c4ca4238a0b923820dcc509a6f75849b||| ||| |||||| ||| |||0||| et on pourra utiliser le code de cette façon, comme vu plus haut : http://[target]/users/users.php?value=Hello%20World ce qui nous montrera la ligne : [ID]|||Haxor|||c4ca4238a0b923820dcc509a6f75849b||| ||| |||Hello World||| ||| |||0||| Une autre façon d'utiliser ce système à des fins néfastes serait de cracker le password crypté en md5 avec John the ripper mais c'est directement plus long :) 1.0.6 ~~~~~ La même faille se trouve dans la version 1.0.6, mais une restriction de plus a été installée, nous empechant d'utiliser une url. Les lignes ------------------------ if(isset($sendRegister)) { ------------------------ on été remplacées par : ---------------------------------- if(isset($_POST["sendRegister"])) { $name = $_POST["name"]; $password = $_POST["password"]; $password2 = $_POST["password2"]; $realname = $_POST["realname"]; $email = $_POST["email"]; $age = $_POST["age"]; $usertext = $_POST["usertext"]; $signature = $_POST["signature"]; ---------------------------------- Ces lignes nous obligent à utiliser un formulaire de methode POST en précisant que les variables $sendRegister, $name, $password,... doivent venir d'un formulaire. En venant de l'url, ce sont des variables GET et pas POST. Hors on a vu qu'à cause du maxlenght="3" de l'input de la variable $age, il nous est impossible d'inserer du code en utilisant le formulaire proposé par le produit. La solution à ce problème est de créer nous-même un formulaire de methode POST renvoyant vers le site [target]. Il suffit donc de créer une page html, dont voici un exemple 'operationnel' : --------------------------------------------------------------------------- SimpleBBS Vulnerability
Name :
PHP code :
--------------------------------------------------------------------------- Patch : °°°°°°° Très simple. Il suffit, dans includes/register.php d'ajouter avant (ou après ) : $name = htmlspecialchars($name); la ligne $age = htmlspecialchars($age); Cette solution est valable pour les 2 versions. Les patchs sont disponibles sur http://www.phpsecure.info. Credits : °°°°°°°°° Auteur : frog-m@n E-mail : leseulfrog@hotmail.com Website : http://www.frog-man.org