Land #2922, multithreaded check command

bug/bundler_fix
William Vu 2014-02-04 11:21:05 -06:00
commit a58698c177
No known key found for this signature in database
GPG Key ID: E761DCB4C1629024
5 changed files with 141 additions and 34 deletions

View File

@ -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

View File

@ -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
#

View File

@ -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

View File

@ -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,

View File

@ -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) +