# -*- coding: binary -*- require 'rex/proto/sunrpc' module Msf ### # # This mixin provides utility methods for interacting with a SunRPC service on # a remote machine. These methods may generally be useful in the context of # exploitation. This mixin extends the Tcp exploit mixin. Only one SunRPC # service can be accessed at a time using this class. # # http://www.ietf.org/rfc/rfc1057.txt # ### module Exploit::Remote::SunRPC include Exploit::Remote::Tcp XDR = Rex::Encoder::XDR MSG_ACCEPTED = 0 SUCCESS = 0 # RPC executed successfully PROG_UMAVAIL = 1 # Remote hasn't exported program PROG_MISMATCH = 2 # Remote can't support version # PROC_UNAVAIL = 3 # Program can't support procedure GARBAGE_ARGS = 4 # Procedure can't decode params' SYSTEM_ERR = 5 # System encountered some error def initialize(info = {}) super register_evasion_options( [ OptBool.new('ONCRPC::tcp_request_fragmentation', [false, 'Enable fragmentation of TCP ONC/RPC requests', 'false']), ], Msf::Exploit::Remote::SunRPC ) register_advanced_options( [ # XXX: Use portmapper to do call - Direct portmap to make the request to the program portmap_req ], Msf::Exploit::Remote::SunRPC) register_options( [ # XXX: XPORT Opt::RHOST, Opt::RPORT(111), ], Msf::Exploit::Remote::SunRPC ) end def sunrpc_create(protocol, program, version) self.rpcobj = Rex::Proto::SunRPC::Client.new( :rhost => rhost, :rport => rport.to_i, :proto => protocol, :program => program, :version => version, :context => { 'Msf' => framework, 'MsfExploit' => self, } ) if datastore['ONCRPC::tcp_request_fragmentation'] == true self.rpcobj.should_fragment = 1 end ret = rpcobj.create return print_error("#{rhost} - No response to SunRPC PortMap request") unless ret arr = XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer) if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0 err = "#{rhost} - SunRPC PortMap request failed: " err << 'Message not accepted' if arr[1] != MSG_ACCEPTED err << 'RPC did not execute' if arr[4] != SUCCESS err << 'Program not available' if arr[5] == 0 print_error(err) return nil end rpcobj.pport = arr[5] #progname = progresolv(rpcobj.program) #print_status("#{rhost} - SunRPC Found #{progname} on #{protocol} port #{rpcobj.pport}") end def sunrpc_call(proc, buf, timeout=20) ret = rpcobj.call(proc, buf, timeout) return print_error("#{rhost} - No response to SunRPC call for procedure: #{proc}") unless ret arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer) if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS progname = progresolv(rpcobj.program) err = "SunRPC call for program #{rpcobj.program} [#{progname}], procedure #{proc}, failed: " if (arr[1] != MSG_ACCEPTED) err << 'Message not accepted' elsif (arr[4] and arr[4] != SUCCESS) case arr[4] when PROG_UMAVAIL then err << "Program Unavailable" when PROG_MISMATCH then err << "Program Version Mismatch" when PROC_UNAVAIL then err << "Procedure Unavailable" when GARBAGE_ARGS then err << "Garbage Arguments" when SYSTEM_ERR then err << "System Error" else err << "Unknown Error" end end print_error("#{rhost} - #{err}") return nil end return ret end def sunrpc_callsock self.rpcobj.call_sock end def sunrpc_destroy rpcobj.destroy rpcobj = nil end def sunrpc_authnull(*args) rpcobj.authnull_create(*args) end def sunrpc_authunix(*args) rpcobj.authunix_create(*args) end # XXX: Incomplete. Just moved from Rex::Proto::SunRPC::Client def portmap_qry() ret = portmap_req() arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer) if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0 progname = progresolv(rpcobj.program) err = "SunRPC query for program #{rpcobj.program} [#{progname}] failed: " case arr[4] when PROG_UMAVAIL then err << "Program Unavailable" when PROG_MISMATCH then err << "Program Version Mismatch" when PROC_UNAVAIL then err << "Procedure Unavailable" when GARBAGE_ARGS then err << "Garbage Arguments" else err << "Unknown Error" end print_error("#{rhost} - #{err}") return nil end return ret end def progresolv(number) names = File.join(Msf::Config.install_root, "data", "wordlists", "rpc_names.txt") File.open(names, "rb").each_line do |line| next if line.empty? || line =~ /^\s*#/ if line =~ /^(\S+?)\s+(\d+)/ && number == $2.to_i return $1 end end return "UNKNOWN-#{number}" end # Used to track the last SunRPC context attr_accessor :rpcobj end end