Land #18704, Leverage the module metadata cache in the module_sets
This commit is contained in:
commit
48221e594d
|
@ -76,7 +76,10 @@ jobs:
|
|||
include:
|
||||
- os: ubuntu-latest
|
||||
ruby: '3.1'
|
||||
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" DATASTORE_FALLBACKS=1'
|
||||
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DATASTORE_FALLBACKS=1'
|
||||
- os: ubuntu-latest
|
||||
ruby: '3.1'
|
||||
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DEFER_MODULE_LOADS=1'
|
||||
test_cmd:
|
||||
- bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content"
|
||||
- bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag ~content"
|
||||
|
|
|
@ -140,7 +140,7 @@ class EncodedPayload
|
|||
# as the framework's list of encoder names so we can compare them later.
|
||||
# This is important for when we get input from RPC.
|
||||
if reqs['Encoder']
|
||||
reqs['Encoder'] = reqs['Encoder'].encode(framework.encoders.keys[0].encoding)
|
||||
reqs['Encoder'] = reqs['Encoder'].encode(framework.encoders.module_refnames[0].encoding)
|
||||
end
|
||||
|
||||
# If the caller had a preferred encoder, use this encoder only
|
||||
|
@ -237,9 +237,9 @@ class EncodedPayload
|
|||
|
||||
begin
|
||||
eout = self.encoder.encode(eout, reqs['BadChars'], nil, pinst.platform)
|
||||
rescue EncodingError
|
||||
wlog("#{err_start}: Encoder #{encoder.refname} failed: #{$!}", 'core', LEV_1)
|
||||
dlog("#{err_start}: Call stack\n#{$@.join("\n")}", 'core', LEV_3)
|
||||
rescue EncodingError => e
|
||||
wlog("#{err_start}: Encoder #{encoder.refname} failed: #{e}", 'core', LEV_1)
|
||||
dlog("#{err_start}: Call stack\n#{e.backtrace}", 'core', LEV_3)
|
||||
next_encoder = true
|
||||
break
|
||||
|
||||
|
@ -342,7 +342,7 @@ class EncodedPayload
|
|||
wlog("#{pinst.refname}: Failed to find preferred nop #{reqs['Nop']}")
|
||||
end
|
||||
|
||||
nops.each { |nopname, nopmod|
|
||||
nops.each_module { |nopname, nopmod|
|
||||
# Create an instance of the nop module
|
||||
self.nop = nopmod.new
|
||||
|
||||
|
|
|
@ -48,9 +48,8 @@ module Msf
|
|||
# @return [void]
|
||||
def init_exploits
|
||||
# First we're going to avoid using #find_all because that gets very slow.
|
||||
framework.exploits.each_pair do |fullname, place_holder|
|
||||
# If the place holder isn't __SYMBOLIC__, then that means the module is initialized,
|
||||
# and that's gotta be the active browser autopwn.
|
||||
framework.exploits.module_refnames.each do |fullname|
|
||||
|
||||
next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}"
|
||||
|
||||
# The user gets to specify which modules to include/exclude
|
||||
|
@ -269,7 +268,7 @@ module Msf
|
|||
|
||||
# The payload is legit, we can use it.
|
||||
# Avoid #create seems faster
|
||||
return payload_name if framework.payloads.keys.include?(payload_name)
|
||||
return payload_name if framework.payloads.module_refnames.include?(payload_name)
|
||||
|
||||
default = DEFAULT_PAYLOADS[platform][:payload]
|
||||
|
||||
|
|
|
@ -131,7 +131,6 @@ class Msf::Module::Platform
|
|||
# the string).
|
||||
#
|
||||
def self.find_portion(mod, str)
|
||||
|
||||
# Check to see if we've built the abbreviated cache
|
||||
if (not (
|
||||
mod.const_defined?('Abbrev') and
|
||||
|
|
|
@ -25,11 +25,7 @@ class Msf::Module::PlatformList
|
|||
# convenient.
|
||||
#
|
||||
def self.transform(src)
|
||||
if (src.kind_of?(Array))
|
||||
from_a(src)
|
||||
else
|
||||
from_a([src])
|
||||
end
|
||||
from_a(Array.wrap(src))
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -20,11 +20,11 @@ class Msf::ModuleSet < Hash
|
|||
# and then returns the now-loaded class afterwards.
|
||||
#
|
||||
# @param [String] name the module reference name
|
||||
# @return [Msf::Module] instance of the of the Msf::Module subclass with the given reference name
|
||||
# @return [Msf::Module] Class of the of the Msf::Module with the given reference name
|
||||
def [](name)
|
||||
module_instance = super
|
||||
if module_instance == Msf::SymbolicModule || module_instance.nil?
|
||||
create(name)
|
||||
module_class = super
|
||||
if module_class == Msf::SymbolicModule || module_class.nil?
|
||||
load_module_class(name)
|
||||
end
|
||||
|
||||
super
|
||||
|
@ -36,14 +36,8 @@ class Msf::ModuleSet < Hash
|
|||
# @return [Msf::Module,nil] Instance of the named module or nil if it
|
||||
# could not be created.
|
||||
def create(reference_name, cache_type: Msf::ModuleManager::Cache::FILESYSTEM)
|
||||
klass = fetch(reference_name, nil)
|
||||
klass = load_module_class(reference_name, cache_type: cache_type)
|
||||
instance = nil
|
||||
# If there is no module associated with this class, then try to demand load it.
|
||||
if klass.nil? or klass == Msf::SymbolicModule
|
||||
framework.modules.load_cached_module(module_type, reference_name, cache_type: cache_type)
|
||||
klass = fetch(reference_name, nil)
|
||||
end
|
||||
|
||||
# If the klass is valid for this reference_name, try to create it
|
||||
unless klass.nil? or klass == Msf::SymbolicModule
|
||||
instance = klass.new
|
||||
|
@ -56,7 +50,7 @@ class Msf::ModuleSet < Hash
|
|||
self.delete(reference_name)
|
||||
end
|
||||
|
||||
return instance
|
||||
instance
|
||||
end
|
||||
|
||||
# Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+
|
||||
|
@ -68,7 +62,7 @@ class Msf::ModuleSet < Hash
|
|||
# @return [void]
|
||||
def each(&block)
|
||||
list = []
|
||||
self.keys.sort.each do |sidx|
|
||||
module_metadata.keys.sort.each do |sidx|
|
||||
list << [sidx, self[sidx]]
|
||||
end
|
||||
list.each(&block)
|
||||
|
@ -81,9 +75,7 @@ class Msf::ModuleSet < Hash
|
|||
# @yieldparam (see #each_module_list)
|
||||
# @return (see #each_module_list)
|
||||
def each_module(opts = {}, &block)
|
||||
demand_load_modules
|
||||
|
||||
self.mod_sorted = self.sort
|
||||
self.mod_sorted = module_metadata.sort
|
||||
|
||||
each_module_list(mod_sorted, opts, &block)
|
||||
end
|
||||
|
@ -107,8 +99,6 @@ class Msf::ModuleSet < Hash
|
|||
# @yieldparam (see #each_module_list)
|
||||
# @return (see #each_module_list)
|
||||
def each_module_ranked(opts = {}, &block)
|
||||
demand_load_modules
|
||||
|
||||
each_module_list(rank_modules, opts, &block)
|
||||
end
|
||||
|
||||
|
@ -166,7 +156,6 @@ class Msf::ModuleSet < Hash
|
|||
# @return [true] if the module can be {#create created} and cached.
|
||||
# @return [false] otherwise
|
||||
def valid?(reference_name)
|
||||
create(reference_name)
|
||||
(self[reference_name]) ? true : false
|
||||
end
|
||||
|
||||
|
@ -203,28 +192,12 @@ class Msf::ModuleSet < Hash
|
|||
klass
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Load all modules that are marked as being symbolic.
|
||||
#
|
||||
# @return [void]
|
||||
def demand_load_modules
|
||||
found_symbolics = false
|
||||
# Pre-scan the module list for any symbolic modules
|
||||
self.each_pair { |name, mod|
|
||||
if (mod == Msf::SymbolicModule)
|
||||
found_symbolics = true
|
||||
mod = create(name)
|
||||
next if (mod.nil?)
|
||||
end
|
||||
}
|
||||
|
||||
# If we found any symbolic modules, then recalculate.
|
||||
if (found_symbolics)
|
||||
recalculate
|
||||
end
|
||||
def module_refnames
|
||||
module_metadata.keys
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Enumerates the modules in the supplied array with possible limiting factors.
|
||||
#
|
||||
# @param [Array<Array<String, Class>>] ary Array of module reference name and module class pairs
|
||||
|
@ -238,35 +211,32 @@ class Msf::ModuleSet < Hash
|
|||
# @yieldparam [Class] module The module class: a subclass of {Msf::Module}.
|
||||
# @return [void]
|
||||
def each_module_list(ary, opts, &block)
|
||||
ary.each { |entry|
|
||||
name, mod = entry
|
||||
|
||||
# Skip any lingering symbolic modules.
|
||||
next if (mod == Msf::SymbolicModule)
|
||||
ary.each do |entry|
|
||||
name, module_metadata = entry
|
||||
|
||||
# Filter out incompatible architectures
|
||||
if (opts['Arch'])
|
||||
if (!architectures_by_module[mod])
|
||||
architectures_by_module[mod] = mod.new.arch
|
||||
if (!architectures_by_module[name])
|
||||
architectures_by_module[name] = Array.wrap(module_metadata.arch)
|
||||
end
|
||||
|
||||
next if ((architectures_by_module[mod] & opts['Arch']).empty? == true)
|
||||
next if ((architectures_by_module[name] & opts['Arch']).empty? == true)
|
||||
end
|
||||
|
||||
# Filter out incompatible platforms
|
||||
if (opts['Platform'])
|
||||
if (!platforms_by_module[mod])
|
||||
platforms_by_module[mod] = mod.new.platform
|
||||
if (!platforms_by_module[name])
|
||||
platforms_by_module[name] = module_metadata.platform_list
|
||||
end
|
||||
|
||||
next if ((platforms_by_module[mod] & opts['Platform']).empty? == true)
|
||||
next if ((platforms_by_module[name] & opts['Platform']).empty? == true)
|
||||
end
|
||||
|
||||
# Custom filtering
|
||||
next if (each_module_filter(opts, name, entry) == true)
|
||||
|
||||
block.call(name, mod)
|
||||
}
|
||||
block.call(name, self[name])
|
||||
end
|
||||
end
|
||||
|
||||
# @!attribute [rw] ambiguous_module_reference_name_set
|
||||
|
@ -304,33 +274,23 @@ class Msf::ModuleSet < Hash
|
|||
# @return [Array<Array<String, Class>>] Array of arrays where the inner array is a pair of the module reference name
|
||||
# and the module class.
|
||||
def rank_modules
|
||||
self.sort_by { |pair| module_rank(*pair) }.reverse!
|
||||
module_metadata.sort_by do |refname, metadata|
|
||||
[metadata.rank || Msf::NormalRanking, refname]
|
||||
end.reverse!
|
||||
end
|
||||
|
||||
# Retrieves the rank from a loaded, not-yet-loaded, or unloadable Metasploit Module.
|
||||
#
|
||||
# @param reference_name [String] The reference name of the Metasploit Module
|
||||
# @param metasploit_module_class [Class<Msf::Module>, Msf::SymbolicModule] The loaded `Class` for the Metasploit
|
||||
# Module, or {Msf::SymbolicModule} if the Metasploit Module is not loaded yet.
|
||||
# @return [Integer] an `Msf::*Ranking`. `Msf::ManualRanking` if `metasploit_module_class` is `nil` or
|
||||
# {Msf::SymbolicModule} and it could not be loaded by {#create}. Otherwise, the `Rank` constant of the
|
||||
# `metasploit_module_class` or {Msf::NormalRanking} if `metasploit_module_class` does not define `Rank`.
|
||||
def module_rank(reference_name, metasploit_module_class)
|
||||
if metasploit_module_class.nil?
|
||||
Msf::ManualRanking
|
||||
elsif metasploit_module_class == Msf::SymbolicModule
|
||||
# TODO don't create an instance just to get the Class.
|
||||
created_metasploit_module_instance = create(reference_name)
|
||||
def module_metadata
|
||||
Msf::Modules::Metadata::Cache.instance.module_metadata(module_type)
|
||||
end
|
||||
|
||||
if created_metasploit_module_instance.nil?
|
||||
module_rank(reference_name, nil)
|
||||
else
|
||||
module_rank(reference_name, created_metasploit_module_instance.class)
|
||||
end
|
||||
elsif metasploit_module_class.const_defined? :Rank
|
||||
metasploit_module_class.const_get :Rank
|
||||
else
|
||||
Msf::NormalRanking
|
||||
def load_module_class(reference_name, cache_type: Msf::ModuleManager::Cache::FILESYSTEM)
|
||||
klass = fetch(reference_name, nil)
|
||||
|
||||
# If there is no module associated with this class, then try to demand load it.
|
||||
if klass.nil? || klass == Msf::SymbolicModule
|
||||
framework.modules.load_cached_module(module_type, reference_name, cache_type: cache_type)
|
||||
klass = fetch(reference_name, nil)
|
||||
end
|
||||
klass
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,6 +86,14 @@ class Cache
|
|||
end
|
||||
end
|
||||
|
||||
def module_metadata(type)
|
||||
@mutex.synchronize do
|
||||
wait_for_load
|
||||
# TODO: Should probably figure out a way to cache this
|
||||
@module_metadata_cache.filter_map { |_, metadata| [metadata.ref_name, metadata] if metadata.type == type }.to_h
|
||||
end
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
@ -154,7 +162,7 @@ class Cache
|
|||
@module_metadata_cache = {}
|
||||
@store_loaded = false
|
||||
@console = Rex::Ui::Text::Output::Stdio.new
|
||||
@load_thread = Thread.new {
|
||||
@load_thread = Thread.new {
|
||||
init_store
|
||||
@store_loaded = true
|
||||
}
|
||||
|
|
|
@ -28,8 +28,10 @@ class Obj
|
|||
attr_reader :description
|
||||
# @return [Array<String>]
|
||||
attr_reader :references
|
||||
# @return [Boolean]
|
||||
# @return [String]
|
||||
attr_reader :platform
|
||||
# @return [Msf::Module::PlatformList]
|
||||
attr_reader :platform_list
|
||||
# @return [String]
|
||||
attr_reader :arch
|
||||
# @return [Integer]
|
||||
|
@ -90,6 +92,7 @@ class Obj
|
|||
@default_credential = module_instance.default_cred?
|
||||
|
||||
@platform = module_instance.platform_to_s
|
||||
@platform_list = module_instance.platform
|
||||
# Done to ensure that differences do not show up for the same array grouping
|
||||
sort_platform_string
|
||||
|
||||
|
@ -235,6 +238,7 @@ class Obj
|
|||
@author = obj_hash['author'].nil? ? [] : obj_hash['author']
|
||||
@references = obj_hash['references']
|
||||
@platform = obj_hash['platform']
|
||||
@platform_list = parse_platform_list(@platform)
|
||||
@arch = obj_hash['arch']
|
||||
@rport = obj_hash['rport']
|
||||
@mod_time = Time.parse(obj_hash['mod_time'])
|
||||
|
@ -288,6 +292,18 @@ class Obj
|
|||
@references = @references.map {|r| r.dup.force_encoding(encoding)}
|
||||
end
|
||||
|
||||
def parse_platform_list(platform_string)
|
||||
return nil if platform_string.nil?
|
||||
|
||||
if platform_string.casecmp('All')
|
||||
# empty string represents all platforms in Msf::Module::PlatformList
|
||||
platforms = ['']
|
||||
else
|
||||
platforms = platform_string.split(',')
|
||||
end
|
||||
Msf::Module::PlatformList.transform(platforms)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -598,7 +598,7 @@ module Msf
|
|||
# @return [True] if the payload is a valid Metasploit Payload
|
||||
# @return [False] if the payload is not a valid Metasploit Payload
|
||||
def payload_is_valid?
|
||||
(framework.payloads.keys + ['stdin']).include? payload
|
||||
(framework.payloads.module_refnames + ['stdin']).include? payload
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ class RPC_Module < RPC_Base
|
|||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.exploits')
|
||||
def rpc_exploits
|
||||
{ "modules" => self.framework.exploits.keys }
|
||||
{ "modules" => self.framework.exploits.module_refnames }
|
||||
end
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@ class RPC_Module < RPC_Base
|
|||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.evasion')
|
||||
def rpc_evasion
|
||||
{ "modules" => self.framework.evasion.keys }
|
||||
{ "modules" => self.framework.evasion.module_refnames }
|
||||
end
|
||||
|
||||
|
||||
|
@ -35,7 +35,7 @@ class RPC_Module < RPC_Base
|
|||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.auxiliary')
|
||||
def rpc_auxiliary
|
||||
{ "modules" => self.framework.auxiliary.keys }
|
||||
{ "modules" => self.framework.auxiliary.module_refnames }
|
||||
end
|
||||
|
||||
|
||||
|
@ -185,7 +185,7 @@ class RPC_Module < RPC_Base
|
|||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.post')
|
||||
def rpc_post
|
||||
{ "modules" => self.framework.post.keys }
|
||||
{ "modules" => self.framework.post.module_refnames }
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -468,7 +468,7 @@ class RPC_Session < RPC_Base
|
|||
ret = []
|
||||
|
||||
mtype = "post"
|
||||
names = self.framework.post.keys.map{ |x| "post/#{x}" }
|
||||
names = self.framework.post.module_refnames.map{ |x| "post/#{x}" }
|
||||
names.each do |mname|
|
||||
m = _find_module(mtype, mname)
|
||||
next if not m.session_compatible?(sid)
|
||||
|
|
|
@ -66,14 +66,14 @@ class Evasion
|
|||
#
|
||||
def cmd_run_tabs(str, words)
|
||||
fmt = {
|
||||
'-e' => [ framework.encoders.map { |refname, mod| refname } ],
|
||||
'-e' => [ framework.encoders.module_refnames ],
|
||||
'-f' => [ nil ],
|
||||
'-h' => [ nil ],
|
||||
'-j' => [ nil ],
|
||||
'-J' => [ nil ],
|
||||
'-n' => [ framework.nops.map { |refname, mod| refname } ],
|
||||
'-n' => [ framework.nops.module_refnames ],
|
||||
'-o' => [ true ],
|
||||
'-p' => [ framework.payloads.map { |refname, mod| refname } ],
|
||||
'-p' => [ framework.payloads.module_refnames ],
|
||||
'-r' => [ nil ],
|
||||
'-t' => [ true ],
|
||||
'-z' => [ nil ]
|
||||
|
|
|
@ -66,14 +66,14 @@ class Exploit
|
|||
#
|
||||
def cmd_run_tabs(str, words)
|
||||
fmt = {
|
||||
'-e' => [ framework.encoders.map { |refname, mod| refname } ],
|
||||
'-e' => [ framework.encoders.module_refnames ],
|
||||
'-f' => [ nil ],
|
||||
'-h' => [ nil ],
|
||||
'-j' => [ nil ],
|
||||
'-J' => [ nil ],
|
||||
'-n' => [ framework.nops.map { |refname, mod| refname } ],
|
||||
'-n' => [ framework.nops.module_refnames ],
|
||||
'-o' => [ true ],
|
||||
'-p' => [ framework.payloads.map { |refname, mod| refname } ],
|
||||
'-p' => [ framework.payloads.module_refnames ],
|
||||
'-r' => [ nil ],
|
||||
'-t' => [ true ],
|
||||
'-z' => [ nil ]
|
||||
|
|
|
@ -438,10 +438,10 @@ module Msf
|
|||
fmt = {
|
||||
'-h' => [ nil ],
|
||||
'-x' => [ nil ],
|
||||
'-p' => [ framework.payloads.map { |refname, mod| refname } ],
|
||||
'-p' => [ framework.payloads.module_refnames ],
|
||||
'-P' => [ true ],
|
||||
'-H' => [ :address ],
|
||||
'-e' => [ framework.encoders.map { |refname, mod| refname } ],
|
||||
'-e' => [ framework.encoders.module_refnames ],
|
||||
'-n' => [ true ]
|
||||
}
|
||||
tab_complete_generic(fmt, str, words)
|
||||
|
|
|
@ -256,7 +256,7 @@ module Msf
|
|||
fmt = {
|
||||
'-b' => [ true ],
|
||||
'-E' => [ nil ],
|
||||
'-e' => [ framework.encoders.map { |refname, _mod| refname } ],
|
||||
'-e' => [ framework.encoders.module_refnames ],
|
||||
'-h' => [ nil ],
|
||||
'-o' => [ :file ],
|
||||
'-P' => [ true ],
|
||||
|
|
|
@ -305,14 +305,14 @@ module Msf
|
|||
# Provide valid nops options for the current exploit
|
||||
#
|
||||
def option_values_nops
|
||||
framework.nops.map { |refname, _mod| refname }
|
||||
framework.nops.module_refnames
|
||||
end
|
||||
|
||||
#
|
||||
# Provide valid encoders options for the current exploit or payload
|
||||
#
|
||||
def option_values_encoders
|
||||
framework.encoders.map { |refname, _mod| refname }
|
||||
framework.encoders.module_refnames
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -572,7 +572,7 @@ protected
|
|||
end
|
||||
|
||||
def tab_complete_postmods
|
||||
tabs = client.framework.modules.post.map { |name, klass|
|
||||
tabs = client.framework.modules.post.module_refnames.each { | name |
|
||||
mod = client.framework.modules.post.create(name)
|
||||
if mod && mod.session_compatible?(client)
|
||||
mod.fullname.dup
|
||||
|
|
|
@ -1697,7 +1697,7 @@ protected
|
|||
end
|
||||
end
|
||||
|
||||
client.framework.modules.post.map do |name,klass|
|
||||
client.framework.modules.post.module_refnames.each do | name |
|
||||
tabs << 'post/' + name
|
||||
end
|
||||
client.framework.modules.module_names('exploit').
|
||||
|
|
|
@ -140,8 +140,9 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
# Collects exploits into an array
|
||||
@local_exploits = []
|
||||
framework.exploits.each_with_index do |(name, _obj), index|
|
||||
print "%bld%blu[*]%clr Collecting exploit #{index + 1} / #{framework.exploits.count}\r"
|
||||
exploit_refnames = framework.exploits.module_refnames
|
||||
exploit_refnames.each_with_index do |name, index|
|
||||
print "%bld%blu[*]%clr Collecting exploit #{index + 1} / #{exploit_refnames.count}\r"
|
||||
mod = framework.exploits.create name
|
||||
next unless mod
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class MetasploitModule < Msf::Post
|
|||
print_status("Running module against #{sysinfo['Computer']}") if !sysinfo.nil?
|
||||
|
||||
# Check that the payload is a Windows one and on the list
|
||||
if !session.framework.payloads.keys.grep(/windows/).include?(datastore['PAYLOAD'])
|
||||
if !session.framework.payloads.module_refnames.grep(/windows/).include?(datastore['PAYLOAD'])
|
||||
print_error("The Payload specified #{datastore['PAYLOAD']} is not a valid for this system")
|
||||
return
|
||||
end
|
||||
|
|
|
@ -104,7 +104,7 @@ module Msf
|
|||
File.open(xindex.to_s, 'w+') do |f|
|
||||
# need to add version line.
|
||||
f.puts(Msf::Framework::Version)
|
||||
framework.exploits.sort.each do |refname, mod|
|
||||
framework.exploits.each_module do |refname, mod|
|
||||
stuff = ''
|
||||
o = nil
|
||||
begin
|
||||
|
|
|
@ -5,18 +5,28 @@ RSpec.describe Msf::EncodedPayload do
|
|||
include_context 'Msf::Simple::Framework#modules loading'
|
||||
|
||||
before do
|
||||
ancestor_reference_names = [
|
||||
# Excellent rank
|
||||
'x86/shikata_ga_nai',
|
||||
# Great rank
|
||||
'x86/call4_dword_xor',
|
||||
'x86/xor_dynamic',
|
||||
'generic/none',
|
||||
]
|
||||
expect_to_load_module_ancestors(
|
||||
ancestor_reference_names: [
|
||||
# Excellent rank
|
||||
'x86/shikata_ga_nai',
|
||||
# Great rank
|
||||
'x86/call4_dword_xor',
|
||||
'x86/xor_dynamic',
|
||||
'generic/none',
|
||||
],
|
||||
ancestor_reference_names: ancestor_reference_names,
|
||||
module_type: 'encoder',
|
||||
modules_path: modules_path,
|
||||
)
|
||||
|
||||
# Improve test performance - return only the test modules that we're interested in
|
||||
allow(framework.encoders).to receive(:rank_modules).and_wrap_original do |original, *args|
|
||||
ranked_modules = original.call(*args)
|
||||
|
||||
ranked_modules.select do |ref_name, _metadata|
|
||||
ancestor_reference_names.include?(ref_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:ancestor_reference_names) {
|
||||
|
|
|
@ -361,7 +361,7 @@ RSpec.describe Msf::Exploit::Remote::BrowserAutopwn2 do
|
|||
# Prepare framework.exploits
|
||||
exploits = double('exploits')
|
||||
allow(exploits).to receive(:create) { |arg| mock_exploit_create(arg) }
|
||||
allow(exploits).to receive(:each_pair).and_yield(available_exploits[0].fullname, '__SYMBOLIC__').and_yield(available_exploits[1].fullname, '__SYMBOLIC__').and_yield(available_exploits[2].fullname, '__SYMBOLIC__')
|
||||
allow(exploits).to receive(:module_refnames).and_return(available_exploits.map(&:fullname))
|
||||
allow(framework).to receive(:exploits).and_return(exploits)
|
||||
|
||||
# Prepare jobs
|
||||
|
@ -401,7 +401,7 @@ RSpec.describe Msf::Exploit::Remote::BrowserAutopwn2 do
|
|||
payload_class_by_reference_name[reference_name] = klass
|
||||
end
|
||||
|
||||
allow(payloads).to receive(:keys) {
|
||||
allow(payloads).to receive(:module_refnames) {
|
||||
payload_class_by_reference_name.keys
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,38 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Msf::ModuleSet do
|
||||
subject(:module_set) {
|
||||
subject(:module_set) do
|
||||
described_class.new(module_type)
|
||||
}
|
||||
end
|
||||
|
||||
let(:module_type) {
|
||||
let(:module_type) do
|
||||
FactoryBot.generate :mdm_module_detail_mtype
|
||||
}
|
||||
end
|
||||
|
||||
context '#rank_modules' do
|
||||
subject(:rank_modules) {
|
||||
describe '#rank_modules' do
|
||||
subject(:rank_modules) do
|
||||
module_set.send(:rank_modules)
|
||||
}
|
||||
end
|
||||
|
||||
let(:module_metadata_a) do
|
||||
instance_double(Msf::Modules::Metadata::Obj)
|
||||
end
|
||||
|
||||
let(:module_metadata_b) do
|
||||
instance_double(Msf::Modules::Metadata::Obj)
|
||||
end
|
||||
|
||||
let(:module_metadata_c) do
|
||||
instance_double(Msf::Modules::Metadata::Obj)
|
||||
end
|
||||
|
||||
let(:module_metadata) do
|
||||
{
|
||||
'a' => module_metadata_a,
|
||||
'b' => module_metadata_b,
|
||||
'c' => module_metadata_c
|
||||
}
|
||||
end
|
||||
|
||||
context 'with Msf::SymbolicModule' do
|
||||
before(:example) do
|
||||
|
@ -21,47 +41,41 @@ RSpec.describe Msf::ModuleSet do
|
|||
module_set['c'] = Msf::SymbolicModule
|
||||
end
|
||||
|
||||
context 'create' do
|
||||
describe '#create' do
|
||||
#
|
||||
# lets
|
||||
#
|
||||
|
||||
let(:b_class) {
|
||||
let(:b_class) do
|
||||
Class.new
|
||||
}
|
||||
end
|
||||
|
||||
let(:c_class) {
|
||||
let(:c_class) do
|
||||
Class.new
|
||||
}
|
||||
end
|
||||
|
||||
context 'returns nil' do
|
||||
before(:example) do
|
||||
hide_const('A::Rank')
|
||||
allow(module_set).to receive(:create).with('a').and_return(nil)
|
||||
|
||||
stub_const('B', b_class)
|
||||
stub_const('B::Rank', Msf::LowRanking)
|
||||
allow(module_set).to receive(:create).with('b').and_return(b_class.new)
|
||||
|
||||
stub_const('C', c_class)
|
||||
stub_const('C::Rank', Msf::AverageRanking)
|
||||
allow(module_set).to receive(:create).with('c').and_return(c_class.new)
|
||||
allow(module_metadata_a).to receive(:rank).and_return(nil)
|
||||
allow(module_metadata_b).to receive(:rank).and_return(Msf::AverageRanking)
|
||||
allow(module_metadata_c).to receive(:rank).and_return(Msf::GoodRanking)
|
||||
allow(Msf::Modules::Metadata::Cache.instance).to receive(:module_metadata).with(anything).and_return(module_metadata)
|
||||
end
|
||||
|
||||
specify {
|
||||
expect {
|
||||
specify do
|
||||
expect do
|
||||
rank_modules
|
||||
}.not_to raise_error
|
||||
}
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'is ranked as Manual' do
|
||||
it 'is ranked as Normal' do
|
||||
expect(rank_modules).to eq(
|
||||
[
|
||||
['c', Msf::SymbolicModule],
|
||||
['b', Msf::SymbolicModule],
|
||||
['a', Msf::SymbolicModule]
|
||||
]
|
||||
)
|
||||
[
|
||||
['c', module_metadata_c],
|
||||
['a', module_metadata_a],
|
||||
['b', module_metadata_b]
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -70,9 +84,9 @@ RSpec.describe Msf::ModuleSet do
|
|||
# lets
|
||||
#
|
||||
|
||||
let(:a_class) {
|
||||
let(:a_class) do
|
||||
Class.new
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Callbacks
|
||||
|
@ -86,47 +100,20 @@ RSpec.describe Msf::ModuleSet do
|
|||
|
||||
context 'with Rank' do
|
||||
before(:example) do
|
||||
stub_const('A', a_class)
|
||||
stub_const('A::Rank', Msf::LowRanking)
|
||||
|
||||
stub_const('B', b_class)
|
||||
stub_const('B::Rank', Msf::AverageRanking)
|
||||
|
||||
stub_const('C', c_class)
|
||||
stub_const('C::Rank', Msf::GoodRanking)
|
||||
allow(module_metadata_a).to receive(:rank).and_return(Msf::LowRanking)
|
||||
allow(module_metadata_b).to receive(:rank).and_return(Msf::AverageRanking)
|
||||
allow(module_metadata_c).to receive(:rank).and_return(Msf::GoodRanking)
|
||||
allow(Msf::Modules::Metadata::Cache.instance).to receive(:module_metadata).with(anything).and_return(module_metadata)
|
||||
end
|
||||
|
||||
it 'is ranked using Rank' do
|
||||
expect(rank_modules).to eq(
|
||||
[
|
||||
['c', Msf::SymbolicModule],
|
||||
['b', Msf::SymbolicModule],
|
||||
['a', Msf::SymbolicModule]
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without Rank' do
|
||||
before(:example) do
|
||||
stub_const('A', a_class)
|
||||
hide_const('A::Rank')
|
||||
|
||||
stub_const('B', b_class)
|
||||
stub_const('B::Rank', Msf::AverageRanking)
|
||||
|
||||
stub_const('C', c_class)
|
||||
stub_const('C::Rank', Msf::GoodRanking)
|
||||
end
|
||||
|
||||
it 'is ranked as Normal' do
|
||||
expect(rank_modules).to eq(
|
||||
[
|
||||
['c', Msf::SymbolicModule],
|
||||
['a', Msf::SymbolicModule],
|
||||
['b', Msf::SymbolicModule]
|
||||
]
|
||||
)
|
||||
[
|
||||
['c', module_metadata_c],
|
||||
['b', module_metadata_b],
|
||||
['a', module_metadata_a]
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -138,17 +125,17 @@ RSpec.describe Msf::ModuleSet do
|
|||
# lets
|
||||
#
|
||||
|
||||
let(:a_class) {
|
||||
let(:a_class) do
|
||||
Class.new
|
||||
}
|
||||
end
|
||||
|
||||
let(:b_class) {
|
||||
let(:b_class) do
|
||||
Class.new
|
||||
}
|
||||
end
|
||||
|
||||
let(:c_class) {
|
||||
let(:c_class) do
|
||||
Class.new
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Callbacks
|
||||
|
@ -162,47 +149,174 @@ RSpec.describe Msf::ModuleSet do
|
|||
|
||||
context 'with Rank' do
|
||||
before(:example) do
|
||||
stub_const('A', a_class)
|
||||
stub_const('A::Rank', Msf::LowRanking)
|
||||
allow(module_metadata_a).to receive(:rank).and_return(Msf::LowRanking)
|
||||
allow(module_metadata_b).to receive(:rank).and_return(Msf::AverageRanking)
|
||||
allow(module_metadata_c).to receive(:rank).and_return(Msf::GoodRanking)
|
||||
allow(Msf::Modules::Metadata::Cache.instance).to receive(:module_metadata).with(anything).and_return(module_metadata)
|
||||
end
|
||||
|
||||
stub_const('B', b_class)
|
||||
stub_const('B::Rank', Msf::AverageRanking)
|
||||
|
||||
stub_const('C', c_class)
|
||||
stub_const('C::Rank', Msf::GoodRanking)
|
||||
end
|
||||
|
||||
it 'is ranked using Rank' do
|
||||
expect(rank_modules).to eq(
|
||||
[
|
||||
['c', c_class],
|
||||
['b', b_class],
|
||||
['a', a_class]
|
||||
]
|
||||
)
|
||||
end
|
||||
it 'is ranked using Rank' do
|
||||
expect(rank_modules).to eq(
|
||||
[
|
||||
['c', module_metadata_c],
|
||||
['b', module_metadata_b],
|
||||
['a', module_metadata_a]
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without Rank' do
|
||||
before(:example) do
|
||||
stub_const('A', a_class)
|
||||
hide_const('A::Rank')
|
||||
|
||||
stub_const('B', b_class)
|
||||
stub_const('B::Rank', Msf::AverageRanking)
|
||||
|
||||
stub_const('C', c_class)
|
||||
stub_const('C::Rank', Msf::GoodRanking)
|
||||
allow(module_metadata_a).to receive(:rank).and_return(nil)
|
||||
allow(module_metadata_b).to receive(:rank).and_return(Msf::AverageRanking)
|
||||
allow(module_metadata_c).to receive(:rank).and_return(Msf::GoodRanking)
|
||||
allow(Msf::Modules::Metadata::Cache.instance).to receive(:module_metadata).with(anything).and_return(module_metadata)
|
||||
end
|
||||
|
||||
it 'is ranked as Normal' do
|
||||
expect(rank_modules).to eq(
|
||||
[
|
||||
['c', c_class],
|
||||
['a', a_class],
|
||||
['b', b_class]
|
||||
]
|
||||
)
|
||||
[
|
||||
['c', module_metadata_c],
|
||||
['a', module_metadata_a],
|
||||
['b', module_metadata_b]
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the same rank' do
|
||||
before(:example) do
|
||||
allow(module_metadata_a).to receive(:rank).and_return(Msf::AverageRanking)
|
||||
allow(module_metadata_b).to receive(:rank).and_return(Msf::AverageRanking)
|
||||
allow(module_metadata_c).to receive(:rank).and_return(Msf::AverageRanking)
|
||||
allow(Msf::Modules::Metadata::Cache.instance).to receive(:module_metadata).with(anything).and_return(module_metadata)
|
||||
end
|
||||
|
||||
it 'ranks the modules consistently' do
|
||||
expect(rank_modules).to eq(
|
||||
[
|
||||
['c', module_metadata_c],
|
||||
['b', module_metadata_b],
|
||||
['a', module_metadata_a]
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#[]' do
|
||||
let(:module_refname) { 'module_refname' }
|
||||
let(:framework) { instance_double(Msf::Framework) }
|
||||
let(:module_manager) { instance_double(Msf::ModuleManager) }
|
||||
let(:cache_type) { Msf::ModuleManager::Cache::FILESYSTEM }
|
||||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:create).with(module_refname)
|
||||
allow(subject).to receive(:framework).and_return(framework)
|
||||
allow(framework).to receive(:modules).and_return(module_manager)
|
||||
allow(module_manager).to receive(:load_cached_module)
|
||||
end
|
||||
|
||||
context 'when the module set is empty' do
|
||||
it 'loads the module class from the cache' do
|
||||
subject[module_refname]
|
||||
is_expected.not_to have_received(:create).with(module_refname)
|
||||
expect(module_manager).to have_received(:load_cached_module).with(module_type, module_refname, cache_type: cache_type)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the module set has symbolic modules' do
|
||||
before(:each) do
|
||||
subject[module_refname] = Msf::SymbolicModule
|
||||
end
|
||||
it 'attempts to create the module' do
|
||||
subject[module_refname]
|
||||
is_expected.not_to have_received(:create).with(module_refname)
|
||||
expect(module_manager).to have_received(:load_cached_module).with(module_type, module_refname, cache_type: cache_type)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a module is contained within the set' do
|
||||
let(:stored_module) { double('module') }
|
||||
before(:each) do
|
||||
subject[module_refname] = stored_module
|
||||
end
|
||||
it 'does not attempt to create the module' do
|
||||
expect(subject[module_refname]).to be(stored_module)
|
||||
is_expected.not_to have_received(:create).with(module_refname)
|
||||
expect(module_manager).not_to have_received(:load_cached_module)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#fetch' do
|
||||
let(:module_refname) { 'module_refname' }
|
||||
|
||||
context 'when the module set is empty' do
|
||||
before(:each) do
|
||||
allow(subject).to receive(:create).with(module_refname)
|
||||
end
|
||||
|
||||
# TODO: it's unexpected that `fetch` and `[]` would act this differently
|
||||
# investigate implementing `to_hash` to tell ruby we act like a hash over extending Hash
|
||||
# seems like this is potentially a feature not a bug, we use `fetch` to intentionally not create modules sometimes
|
||||
xit 'attempts to create the module' do
|
||||
subject.fetch(module_refname)
|
||||
is_expected.to have_received(:create).with(module_refname)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
let(:module_refname) { 'module_refname' }
|
||||
let(:framework) { instance_double(Msf::Framework) }
|
||||
let(:module_manager) { instance_double(Msf::ModuleManager) }
|
||||
let(:events) { double('events') }
|
||||
let(:cache_type) { Msf::ModuleManager::Cache::FILESYSTEM }
|
||||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:framework).and_return(framework)
|
||||
allow(framework).to receive(:modules).and_return(module_manager)
|
||||
allow(framework).to receive(:events).and_return(events)
|
||||
allow(events).to receive(:on_module_created)
|
||||
end
|
||||
|
||||
context 'when module set is empty' do
|
||||
context 'when the module cannot be loaded' do
|
||||
before(:each) do
|
||||
allow(subject).to receive(:fetch).and_return(nil)
|
||||
allow(subject).to receive(:delete)
|
||||
allow(module_manager).to receive(:load_cached_module)
|
||||
end
|
||||
|
||||
it 'fails to create the module' do
|
||||
subject.create(module_refname, cache_type: cache_type)
|
||||
expect(subject).to have_received(:fetch).with(module_refname, nil).twice
|
||||
expect(subject).to have_received(:delete).with(module_refname)
|
||||
expect(module_manager).to have_received(:load_cached_module).with(module_type, module_refname, cache_type: cache_type)
|
||||
expect(events).not_to have_received(:on_module_created)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the module can be loaded' do
|
||||
let(:loaded_module) { instance_double(Class) }
|
||||
let(:module_instance) { Class.new }
|
||||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:fetch).and_return(nil, loaded_module)
|
||||
allow(subject).to receive(:delete)
|
||||
allow(module_manager).to receive(:load_cached_module)
|
||||
allow(loaded_module).to receive(:new).and_return(module_instance)
|
||||
end
|
||||
|
||||
it 'creates the module' do
|
||||
expect(subject.create(module_refname, cache_type: cache_type)).to be(module_instance)
|
||||
expect(subject).to have_received(:fetch).with(module_refname, nil).twice
|
||||
expect(subject).not_to have_received(:delete).with(module_refname)
|
||||
expect(module_manager).to have_received(:load_cached_module).with(module_type, module_refname, cache_type: cache_type)
|
||||
expect(events).to have_received(:on_module_created).with(module_instance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1128,7 +1128,7 @@ RSpec.describe Msf::PayloadGenerator do
|
|||
}
|
||||
|
||||
it 'calls the generate_war on the payload' do
|
||||
allow(framework).to receive_message_chain(:payloads, :keys).and_return ['java/meterpreter/reverse_tcp']
|
||||
allow(framework).to receive_message_chain(:payloads, :module_refnames).and_return ['java/meterpreter/reverse_tcp']
|
||||
allow(framework).to receive_message_chain(:payloads, :create).and_return(payload_module)
|
||||
expect(payload_module).to receive(:generate_war).and_call_original
|
||||
payload_generator.generate_java_payload
|
||||
|
@ -1194,7 +1194,7 @@ RSpec.describe Msf::PayloadGenerator do
|
|||
}
|
||||
|
||||
it 'calls the generate_jar on the payload' do
|
||||
allow(framework).to receive_message_chain(:payloads, :keys).and_return ['java/meterpreter/reverse_tcp']
|
||||
allow(framework).to receive_message_chain(:payloads, :module_refnames).and_return ['java/meterpreter/reverse_tcp']
|
||||
allow(framework).to receive_message_chain(:payloads, :create).and_return(payload_module)
|
||||
expect(payload_module).to receive(:generate_jar).and_call_original
|
||||
payload_generator.generate_java_payload
|
||||
|
@ -1232,7 +1232,7 @@ RSpec.describe Msf::PayloadGenerator do
|
|||
}
|
||||
|
||||
it 'calls #generate' do
|
||||
allow(framework).to receive_message_chain(:payloads, :keys).and_return ['java/jsp_shell_reverse_tcp']
|
||||
allow(framework).to receive_message_chain(:payloads, :module_refnames).and_return ['java/jsp_shell_reverse_tcp']
|
||||
allow(framework).to receive_message_chain(:payloads, :create).and_return(payload_module)
|
||||
expect(payload_module).to receive(:generate).and_call_original
|
||||
payload_generator.generate_java_payload
|
||||
|
|
|
@ -159,12 +159,18 @@ RSpec.configure do |config|
|
|||
end
|
||||
end
|
||||
|
||||
if ENV['DATASTORE_FALLBACKS']
|
||||
if ENV['MSF_FEATURE_DATASTORE_FALLBACKS']
|
||||
config.before(:suite) do
|
||||
Msf::FeatureManager.instance.set(Msf::FeatureManager::DATASTORE_FALLBACKS, true)
|
||||
end
|
||||
end
|
||||
|
||||
if ENV['MSF_FEATURE_DEFER_MODULE_LOADS']
|
||||
config.before(:suite) do
|
||||
Msf::FeatureManager.instance.set(Msf::FeatureManager::DEFER_MODULE_LOADS, true)
|
||||
end
|
||||
end
|
||||
|
||||
# rex-text table performs word wrapping on msfconsole tables:
|
||||
# https://github.com/rapid7/rex-text/blob/11e59416f7d8cce18b8b8b9893b3277e6ad0bea1/lib/rex/text/wrapped_table.rb#L74
|
||||
# This can cause some integration tests to fail if the tests are run from smaller consoles
|
||||
|
|
|
@ -40,7 +40,7 @@ class MetasploitModule < Msf::Post
|
|||
session_type = session.type
|
||||
|
||||
module_results = []
|
||||
framework.modules.post.each do |refname, _clazz|
|
||||
framework.modules.post.module_refnames.each do |refname|
|
||||
next unless refname.start_with?('test/') && refname != self.refname
|
||||
mod = framework.modules.create(refname)
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ options_set_by_ancestor_reference_name = Hash.new { |hash, ancestor_reference_na
|
|||
hash[ancestor_reference_name] = Set.new
|
||||
}
|
||||
|
||||
framework.payloads.each { |reference_name, payload_class|
|
||||
framework.payloads.each_module { |reference_name, payload_class|
|
||||
next unless payload_class
|
||||
module_ancestors = payload_class.ancestors.select { |ancestor|
|
||||
# need to use try because name may be nil for anonymous Modules
|
||||
|
|
|
@ -23,7 +23,7 @@ require 'rex'
|
|||
|
||||
# Initialize the simplified framework instance.
|
||||
$framework = Msf::Simple::Framework.create('DisableDatabase' => true)
|
||||
|
||||
# XXX: this is weird, merging module sets together for different module types could lead to unforseen issues
|
||||
all_modules = $framework.exploits.merge($framework.auxiliary)
|
||||
all_ports = {}
|
||||
|
||||
|
|
Loading…
Reference in New Issue