Land #2922, multithreaded check command
commit
a58698c177
|
@ -32,6 +32,16 @@ def initialize(info = {})
|
|||
end
|
||||
|
||||
|
||||
def check
|
||||
nmod = replicant
|
||||
begin
|
||||
nmod.check_host(datastore['RHOST'])
|
||||
rescue NoMethodError
|
||||
Exploit::CheckCode::Unsupported
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# The command handler when launched from the console
|
||||
#
|
||||
|
@ -79,7 +89,7 @@ def run
|
|||
|
||||
@tl = []
|
||||
|
||||
while (true)
|
||||
loop do
|
||||
# Spawn threads for each host
|
||||
while (@tl.length < threads_max)
|
||||
ip = ar.next_ip
|
||||
|
|
|
@ -104,9 +104,9 @@ class Exploit < Msf::Module
|
|||
Vulnerable = [ 'vulnerable', "The target is vulnerable." ]
|
||||
|
||||
#
|
||||
# The exploit does not support the check method.
|
||||
# The module does not support the check method.
|
||||
#
|
||||
Unsupported = [ 'unsupported', "This exploit does not support check." ]
|
||||
Unsupported = [ 'unsupported', "This module does not support check." ]
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -36,39 +36,138 @@ module ModuleCommandDispatcher
|
|||
self.driver.active_module = m
|
||||
end
|
||||
|
||||
def check_progress
|
||||
return 0 unless @range_done and @range_count
|
||||
pct = (@range_done / @range_count.to_f) * 100
|
||||
end
|
||||
|
||||
def check_show_progress
|
||||
pct = check_progress
|
||||
if(pct >= (@range_percent + @show_percent))
|
||||
@range_percent = @range_percent + @show_percent
|
||||
tdlen = @range_count.to_s.length
|
||||
print_status("Checked #{"%.#{tdlen}d" % @range_done} of #{@range_count} hosts (#{"%.3d" % pct.to_i}% complete)")
|
||||
end
|
||||
end
|
||||
|
||||
def check_multiple(hosts)
|
||||
# This part of the code is mostly from scanner.rb
|
||||
@show_progress = framework.datastore['ShowProgress'] || mod.datastore['ShowProgress'] || false
|
||||
@show_percent = ( framework.datastore['ShowProgressPercent'] || mod.datastore['ShowProgressPercent'] ).to_i
|
||||
|
||||
@range_count = hosts.length || 0
|
||||
@range_done = 0
|
||||
@range_percent = 0
|
||||
|
||||
# Set the default thread to 1. The same behavior as before.
|
||||
threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || 1).to_i
|
||||
@tl = []
|
||||
|
||||
|
||||
if Rex::Compat.is_windows
|
||||
if threads_max > 16
|
||||
print_warning("Thread count has been adjusted to 16")
|
||||
threads_max = 16
|
||||
end
|
||||
end
|
||||
|
||||
if Rex::Compat.is_cygwin
|
||||
if threads_max > 200
|
||||
print_warning("Thread count has been adjusted to 200")
|
||||
threads_max = 200
|
||||
end
|
||||
end
|
||||
|
||||
loop do
|
||||
while (@tl.length < threads_max)
|
||||
ip = hosts.next_ip
|
||||
break unless ip
|
||||
|
||||
@tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) { |tip|
|
||||
# Make sure this is thread-safe when assigning an IP to the RHOST
|
||||
# datastore option
|
||||
instance = mod.replicant
|
||||
instance.datastore['RHOST'] = tip.dup
|
||||
framework.events.on_module_created(instance)
|
||||
check_simple(instance)
|
||||
}
|
||||
end
|
||||
|
||||
break if @tl.length == 0
|
||||
|
||||
tla = @tl.length
|
||||
|
||||
# This exception handling is necessary, the first thread with errors can kill the
|
||||
# whole check_multiple and leave the rest of the threads running in background and
|
||||
# only accessible with the threads command (Thread.list)
|
||||
begin
|
||||
@tl.first.join
|
||||
rescue ::Exception => exception
|
||||
if exception.kind_of?(::Interrupt)
|
||||
raise exception
|
||||
else
|
||||
elog("#{exception} #{exception.class}:\n#{exception.backtrace.join("\n")}")
|
||||
end
|
||||
end
|
||||
|
||||
@tl.delete_if { |t| not t.alive? }
|
||||
tlb = @tl.length
|
||||
|
||||
@range_done += (tla - tlb)
|
||||
check_show_progress if @show_progress
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Checks to see if a target is vulnerable.
|
||||
#
|
||||
def cmd_check(*args)
|
||||
defanged?
|
||||
|
||||
ip_range_arg = args.shift || ''
|
||||
ip_range_arg = args.shift || framework.datastore['RHOSTS'] || mod.datastore['RHOSTS'] || ''
|
||||
hosts = Rex::Socket::RangeWalker.new(ip_range_arg)
|
||||
|
||||
if hosts.ranges.blank?
|
||||
# Check a single rhost
|
||||
check_simple
|
||||
else
|
||||
# Check a range
|
||||
last_rhost_opt = mod.rhost
|
||||
begin
|
||||
hosts.each do |ip|
|
||||
mod.datastore['RHOST'] = ip
|
||||
check_simple
|
||||
begin
|
||||
if hosts.ranges.blank?
|
||||
# Check a single rhost
|
||||
check_simple
|
||||
else
|
||||
# Check multiple hosts
|
||||
last_rhost_opt = mod.rhost
|
||||
last_rhosts_opt = mod.datastore['RHOSTS']
|
||||
mod.datastore['RHOSTS'] = ip_range_arg
|
||||
begin
|
||||
check_multiple(hosts)
|
||||
ensure
|
||||
# Restore the original rhost if set
|
||||
mod.datastore['RHOST'] = last_rhost_opt
|
||||
mod.datastore['RHOSTS'] = last_rhosts_opt
|
||||
mod.cleanup
|
||||
end
|
||||
ensure
|
||||
# Restore the original rhost if set
|
||||
mod.datastore['RHOST'] = last_rhost_opt
|
||||
end
|
||||
rescue ::Interrupt
|
||||
# When the user sends interrupt trying to quit the task, some threads will still be active.
|
||||
# This means even though the console tells the user the task has aborted (or at least they
|
||||
# assume so), the checks are still running. Because of this, as soon as we detect interrupt,
|
||||
# we force the threads to die.
|
||||
if @tl
|
||||
@tl.each { |t| t.kill }
|
||||
end
|
||||
print_status("Caught interrupt from the console...")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def check_simple
|
||||
rhost = mod.rhost
|
||||
rport = mod.rport
|
||||
def check_simple(instance=nil)
|
||||
unless instance
|
||||
instance = mod
|
||||
end
|
||||
|
||||
rhost = instance.rhost
|
||||
rport = instance.rport
|
||||
|
||||
begin
|
||||
code = mod.check_simple(
|
||||
code = instance.check_simple(
|
||||
'LocalInput' => driver.input,
|
||||
'LocalOutput' => driver.output)
|
||||
if (code and code.kind_of?(Array) and code.length > 1)
|
||||
|
@ -80,19 +179,14 @@ module ModuleCommandDispatcher
|
|||
else
|
||||
print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.")
|
||||
end
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error
|
||||
# Connection issues while running check should be handled by the module
|
||||
rescue ::RuntimeError
|
||||
# Some modules raise RuntimeError but we don't necessarily care about those when we run check()
|
||||
rescue Msf::OptionValidateError => e
|
||||
print_error("Check failed: #{e.message}")
|
||||
rescue ::Exception => e
|
||||
if(e.class.to_s != 'Msf::OptionValidateError')
|
||||
print_error("Exploit check failed: #{e.class} #{e}")
|
||||
print_error("Call stack:")
|
||||
e.backtrace.each do |line|
|
||||
break if line =~ /lib.msf.base.simple/
|
||||
print_error(" #{line}")
|
||||
end
|
||||
else
|
||||
print_error("#{rhost}:#{rport} - Exploit check failed: #{e.class} #{e}")
|
||||
end
|
||||
print_error("#{rhost}:#{rport} - Check failed: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
##
|
||||
|
||||
require "msf/core"
|
||||
require 'msf/core/module/deprecated'
|
||||
|
||||
class Metasploit4 < Msf::Auxiliary
|
||||
|
||||
|
@ -11,6 +12,8 @@ class Metasploit4 < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::SMB
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Module::Deprecated
|
||||
deprecated Date.new(2014, 2, 26), "exploit/windows/smb/ms08_067_netapi"
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
|
|
@ -1091,7 +1091,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
print_status("Verifying vulnerable status... (path: 0x%08x)" % path.length)
|
||||
vprint_status("Verifying vulnerable status... (path: 0x%08x)" % path.length)
|
||||
|
||||
stub =
|
||||
NDR.uwstring(server) +
|
||||
|
|
Loading…
Reference in New Issue