Exploit-DB #35212
Description: WordPress Plugin / Joomla! Component XCloner - Multiple Vulnerabilities
Type: webapps
Platform: php
Date: 2014-11-10
Author: Larry W. Cashdollar

Exploit:
Title: XCloner Wordpress/Joomla! backup Plugin v3.1.1 (Wordpress) v3.5.1 (Joomla!) Vulnerabilities
Author: Larry W. Cashdollar, @_larry0
Date: 10/17/2014
Download: https://wordpress.org/plugins/xcloner-backup-and-restore/
Download: http://extensions.joomla.org/extensions/access-a-security/site-security/backup/665
Downloads: Wordpress 313,647 Joomla! 515745 StandAlone 69175
Website: http://www.xcloner.com
Advisory: http://www.vapid.dhs.org/advisories/wordpress/plugins/Xcloner-v3.1.1/
Vendor: Notified 10/17/14 Ovidiu Liuta, @thinkovi Acknowledged & no other response.
CVEID: Requested, TDB.
OSVDBID:114176,114177,114178,114179,114180

Description: “XCloner is a Backup and Restore component designed for PHP/Mysql websites, it can work as a native plugin for WordPress and Joomla!.”

Vulnerabilities:  There are multiple vulnerabilities I’ve discovered in this plugin, they are as follows.

1. Arbitrary command execution.
2. Clear text MySQL password exposure through html text box under configuration panel.
3. Database backups exposed to local users due to open file permissions.
4. Unauthenticated remote access to backup files via easily guessable file names.
5. Authenticated remote file access. 
6. MySQL password exposed to process table.

Arbitrary Command Execution

Plugin allows arbitrary commands to be executed by an authenticated user.  The user will require administrative access rights to backup the database. User input when specifying your own file name is not sanitized as well as various other input fields.

PoC
All input fields I believe are vulnerable, I’ve chosen the backup filename and a wget of sh.txt which is simply <?php passthru($_GET)?> into a writeable directory by www-data.

Screenshots available at the advisory URL above.

All user configurable variables are vulnerable, these variables need to be sanitized before being passed to the exec() function for execution.
$_CONFIG[tarpath]
$exclude
$_CONFIG['tarcompress']
$_CONFIG['filename']
$_CONFIG['exfile_tar']
$_CONFIG[sqldump]
$_CONFIG['mysql_host']
$_CONFIG['mysql_pass']
$_CONFIG['mysql_user']
$database_name
$sqlfile
$filename

Vulnerable code  

./cloner.functions.php:

1672       exec($_CONFIG[tarpath] . " $exclude -c" . $_CONFIG['tarcompress'] . "vf $filename ./administrator/backups/index.html");
1673       exec($_CONFIG[tarpath] . " -" . $_CONFIG['tarcompress'] . "vf $filename --update ./administrator/backups/database-sql.sql");
1674       exec($_CONFIG[tarpath] . " -" . $_CONFIG['tarcompress'] . "vf $filename --update ./administrator/backups/htaccess.txt");
1675       exec($_CONFIG[tarpath] . " -" . $_CONFIG['tarcompress'] . "vf $filename --update ./administrator/backups/perm.txt");

1695-          if ($_REQUEST[cron_dbonly] != 1) {
1696:              exec($_CONFIG[tarpath] . " $excl_cmd " . " -X " . $_CONFIG['exfile_tar'] . "  -chv" . $_CONFIG['tarcompress'] . "f $filename ./");
1697-          } else {
1698-
1699-
1700:      exec($_CONFIG[tarpath] . "  -" . $_CONFIG['tarcompress'] . "cvf $filename ./administrator/backups/database-sql.sql");
1701-
1702-              if (is_array($databases_incl)) {
1703-                  foreach ($databases_incl as $database_name)
1704-                      if ($database_name != "") {
1705:                          exec($_CONFIG[tarpath] . "  -" . $_CONFIG['tarcompress'] . "vf $filename --update  ./administrator/backups/" . $database_name . "-sql.sql");
1706-                      }
1707-              }
1708-          }
--
1873-  {
1874-      //$sizeInBytes = filesize($path);
1875-      $sizeInBytes = sprintf("%u", filesize($path));
1876:      if ((!$sizeInBytes) and (function_exists("exec"))){
1877-          $command = "ls -l \"$path\" | cut -d \" \" -f 5";
1878:          $sizeInBytes = @exec($command);
1879-      }

2010-      if ($_CONFIG['sql_mem']) {
2011:          exec($_CONFIG[sqldump] . " -h " . $_CONFIG['mysql_host'] . " -u " . $_CONFIG['mysql_user'] . " -p" . $_CONFIG['mysql_pass'] . " " . $dbname . " > " . $sqlfile . " $drop --allow-k
eywords " . $ex_dump);
2012-
2013-          if (get_filesize($sqlfile) > 0)
2014-              $databaseResult = LM_DATABASE_BACKUP_COMPLETED . ' ( ' . getFileSizeText(get_filesize($sqlfile)) . ' )';
2015-          else
2016-              $databaseResult = LM_MSG_BACK_14;
2017-
2018:          exec("chmod 777 $sqlfile");
2019-
2020-          return $sqlfile;
2021-      }

./classes/fileRecursion.php
339-    public static function getFileSize($file){
340-
341-      $sizeInBytes = sprintf("%u", filesize($file));
342:      if ((!$sizeInBytes) and (function_exists("exec"))){
343-          $command = "ls -l \"$file\" | cut -d \" \" -f 5";
344:          $sizeInBytes = @exec($command);
345-      }
346-
347-      return $sizeInBytes;
./restore/XCloner.php
290-    }else{
291-            if($ext == '.tgz') $compress = 'z';
292-            else $compress = '';
293:            shell_exec("tar -x".$compress."pf $file -C $_CONFIG[output_path]");
294-    }
295-}


1077-   if($_REQUEST['use_mysqldump'] == 1){
1078:           echo shell_exec($_REQUEST['mysqldump_path']." -u ".$_REQUEST[mysql_username]." -p".$_REQUEST[mysql_pass]." -h ".$_REQUEST[mysql_server]." ".$_REQUEST[mysql_db]." < ".$sqlfile);
1079-           return;
1080-    }




Clear Text MySQL Database Password


The plugin also returns the MySQL clear text password via html text box back to the user in the configuration panel.  A password should never be repeated back to you in clear text.  The plugin will happily send this over a clear text connection.

Screenshots available at the advisory URL above.

Remote Database Download & Local File Permissions


The default recommend path for backup storage is /usr/share/wordpress/administrator/backups.
An index.html file is created under this directory to prevent casual browsing however the file names are easily predictable. From the