In this article, I will be sharing several critical vulnerabilities of Seagate Central Storage NAS product.
Advisory Informations
Remotely Exploitable: Yes
Authentication Required: NO
Vendor URL: https://www.seagate.com/as/en/support/external-hard-drives/network-storage/seagate-central/
Date of found: 19 Dec 2019
Technical Details
Upon obtaining the latest device firmware from the Seagate download page i have started analyzing the firmware ZIP file. Inside the ZIP file there was another compressed binary with the name Seagate-HS-update-201509160008F.img
by simply changing the file extension to “tar.gz” i was able to extract the SquashFS file system that contained the management application source code, start-up scripts and busy-box binaries for the ARM based NAS device.
With using the sudo unsquashfs -f -d /media/seagate /tmp/file.squashfs
command i have mounted the file system and started analyzing the contents. After a brief reconnaissance i found the PHP source code for the device management interface and jumped right into the source code analysis phase. During the analysis i noticed that the application was developed using the CodeIgniter framework. Considering the size of the management application i directly started tracing the most vulnerable PHP functions. I have used find . -name "*.php" | xargs grep "<function-name>"
command for the following list of functions and got several interesting input vectors.
- exec
- shell_exec
- system
- passthru
- pcntl_exec
- popen
- proc_open
- eval
- preg_replace (with /e modifier)
- create_function
- file_get_contents
- file_put_contents
- readfile
- include
- require
- require_once
- include_once
One of the grep result for “proc_open” showed a call with several dynamic variables used as parameter inside the ./cirrus/application/helpers/mv_backend_helper.php
file.
Subject function is called inside the mv_backend_launch function witch is also located inside the ./cirrus/application/helpers/mv_backend_helper.php
file.
function mv_backend_launch($cmd, $noLog = false) { $desc = array( 0 => array("pipe","r"), 1 => array("pipe","w"), 2 => array("pipe","w") ); $cwd = './'; $process = proc_open($cmd,$desc,$pipes,$cwd); if(is_resource($process)) { fclose($pipes[0]); $data =stream_get_contents($pipes[1]); fclose($pipes[1]); $errors=stream_get_contents($pipes[2]); if(strlen(trim($errors))>0) mv_log_errors($cmd,$errors); fclose($pipes[2]); proc_close($process); if ( ! $noLog ) { syslog(LOG_INFO, "CMD: '$cmd', RESPONSE: '$data'"); } return $data; } }
After tracing back the function references i was able to detect check_device_name function witch was passing a unsanitized user input into the mv_backend_launch function with the $name
parameter.
public function check_device_name() { $info = $this->get_start_info(); $isStart = $info && array_key_exists('state', $info) && $info['state'] == 'start'; if ( ! $isStart ) { mv_is_admin(); } $name = $this->input->post("name"); $result = mv_backend_launch("check_netbios_name.sh $name"); echo header('Content-type: text/xml'); echo $result; }
Here at this point we have ourselves a function with remote code execution vulnerability. But the problem was this function only works if the device state is set to “start” or else it requires admin level access to the application. So we either need to find a way to change the device state without authentication or bypass authentication and escalate privileges. Because of this issue i went back to analyzing the source code and found a even better attack vector. When analyzing the the device state mechanics i noticed that when the device is in “start” state it allows the registration of new users in order to perform initial setup of the device. When i look for the how the state change operation is performed i found the set_start_info
function inside the application/core/MV_BaseController.php
file. This functions sets the device state with a JSON post request and gues what? there is no any kind of control 🙂
public function reset_start_info() { self::save_object_to_file(null, self::START_FILE); $uri = $_SERVER['REQUEST_URI']; $idx = strpos($uri, 'index.php'); if ( $idx !== false ) { $uri = substr($uri, 0, $idx); } $uri .= 'index.php/SCSS'; header('Content-type: text/plain'); header("Location: ".$uri, TRUE, 302); exit(); }
So by simply changing the device state to “start” we are able to add a new admin user to the device. Users inside the device are created as a Linux system user thus they all have SSH access to the device.
Metasploit Module
Up until this point we have identified multiple vulnerabilities. For the exploit code i prefer to use the second way witch we add a new admin user for establishing SSH connection. Because triggering reverse/bind shell connections with busybox binaries is hard in terms of Metasploit payload compatibility. Also by default device ports are closed except common services such as HTTP,HTTPS,SSH,FTP… Since the SSH is enabled by default and it is not possible to disable it using the administrator interface writing the exploit with the second way is the obvious choice. And here is the end result…
After reporting this vulnerability to Seagate we were very frustrated with the response. They first claimed that “this product was designed and targeted for personal home use within a personal LAN” thus has no real attack surface. But then we proved othervise by providing the number of exploitable devices open to internet using services such as shodan.io and censys.io. But it seems they just don’t care ¯\_(ツ)_/¯ We had no expectation of any kind of bounty or points we just wanted to write a cool blog post, the only reason for using Bugcrowd platform was, Seagate is only accepting bug reports by a external Bugcrowd submission form.