Module: Msf::ModuleManager::Cache

Extended by:
ActiveSupport::Concern
Included in:
Msf::ModuleManager
Defined in:
lib/msf/core/module_manager/cache.rb

Overview

Concerns the module cache maintained by the Msf::ModuleManager.

Constant Summary collapse

MEMORY =
:memory
FILESYSTEM =
:filesystem

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#module_info_by_pathObject (protected)



159
160
161
# File 'lib/msf/core/module_manager/cache.rb', line 159

def module_info_by_path
  @module_info_by_path
end

Instance Method Details

#cache_empty?true, false

Returns whether the cache is empty

Returns:

  • (true)

    if the cache has no entries.

  • (false)

    if the cache has any entries.



17
18
19
# File 'lib/msf/core/module_manager/cache.rb', line 17

def cache_empty?
  module_info_by_path.empty?
end

#cache_in_memory(class_or_module, options = {}) ⇒ void

Note:

path, reference_name, and type must be passed as options because when class_or_module is a payload Module, those attributes will either not be set or not exist on the module.

This method returns an undefined value.

Updates the in-memory cache so that Loading#file_changed? will report false if the module is loaded again.

Parameters:

  • class_or_module (Class<Msf::Module>, ::Module)

    either a module Class or a payload Module.

  • options (Hash{Symbol => String}) (defaults to: {})

Options Hash (options):

  • :path (String)

    the path to the file from which class_or_module was loaded.

  • :reference_name (String)

    the reference name for class_or_module.

  • :type (String)

    the module type

Raises:

  • (KeyError)

    unless :path is given.

  • (KeyError)

    unless :reference_name is given.

  • (KeyError)

    unless :type is given.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/msf/core/module_manager/cache.rb', line 39

def cache_in_memory(class_or_module, options={})
  options.assert_valid_keys(:path, :reference_name, :type)

  path = options.fetch(:path)

  begin
    modification_time = File.mtime(path)
  rescue Errno::ENOENT => error
    log_lines = []
    log_lines << "Could not find the modification of time of #{path}:"
    log_lines << error.class.to_s
    log_lines << error.to_s
    log_lines << "Call stack:"
    log_lines += error.backtrace

    log_message = log_lines.join("\n")
    elog(log_message)
  else
    parent_path = class_or_module.module_parent.parent_path
    reference_name = options.fetch(:reference_name)
    type = options.fetch(:type)

    module_info_by_path[path] = {
        :modification_time => modification_time,
        :parent_path => parent_path,
        :reference_name => reference_name,
        :type => type
    }
  end
end

#get_parent_path(module_path, type) ⇒ Object (protected)



209
210
211
212
213
# File 'lib/msf/core/module_manager/cache.rb', line 209

def get_parent_path(module_path, type)
  # The load path is assumed to be the next level above the type directory
  type_dir = File.join('', Mdm::Module::Detail::DIRECTORY_BY_TYPE[type], '')
  module_path.split(type_dir)[0..-2].join(type_dir) # TODO: rewrite
end

#load_cached_module(type, reference_name, cache_type: Msf::ModuleManager::Cache::MEMORY) ⇒ false, true

Forces loading of the module with the given type and module reference name from the cache.

Parameters:

  • type (String)

    the type of the module.

  • reference_name (String)

    the module reference name.

Returns:

  • (false)

    if a module with the given type and reference name does not exist in the cache.

  • (false)

    if :force is false and parent_path has not changed.

  • (false)

    if exception encountered while parsing module content

  • (false)

    if the module is incompatible with the Core or API version.

  • (false)

    if the module does not implement a Metasploit class.

  • (false)

    if the module's is_usable method returns false.

  • (true)

    if all those condition pass and the module is successfully loaded.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/msf/core/module_manager/cache.rb', line 76

def load_cached_module(type, reference_name, cache_type: Msf::ModuleManager::Cache::MEMORY)
   = nil

  case cache_type
  when Msf::ModuleManager::Cache::FILESYSTEM
     = Msf::Modules::Metadata::Cache.instance.get_module_reference(type: type, reference_name: reference_name)
    return false unless 

    parent_path = get_parent_path(.path, type)
  when Msf::ModuleManager::Cache::MEMORY
    module_info = self.module_info_by_path.values.find { |inner_info|
      inner_info[:type] == type and inner_info[:reference_name] == reference_name
    }
    return false unless module_info

    parent_path = module_info[:parent_path]
  else
    raise ArgumentError, "#{cache_type} is not a valid cache type."
  end

  try_load_module(parent_path, type, reference_name, cached_metadata: )
end

#module_info_by_path_from_database!(allowed_paths = [""]) ⇒ Hash{String => Hash{Symbol => Object}} (protected)

Note:

Also sets module_set(module_type) to nil if it is not already set.

Return a module info from Msf::Modules::Metadata::Obj.

Returns:

  • (Hash{String => Hash{Symbol => Object}})

    Maps path (Mdm::Module::Detail#file) to module information. Module information is a Hash derived from Mdm::Module::Detail. It includes :modification_time, :parent_path, :type, :reference_name.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/msf/core/module_manager/cache.rb', line 168

def module_info_by_path_from_database!(allowed_paths=[""])
  self.module_info_by_path = {}

  allowed_paths = allowed_paths.map{|x| x + "/"}

   = Msf::Modules::Metadata::Cache.instance.
  .each do ||
    path = .path
    type = .type
    reference_name = .ref_name

    # Skip cached modules that are not in our allowed load paths
    next if allowed_paths.select{|x| path.index(x) == 0}.empty?

    parent_path = get_parent_path(path, type)

    module_info_by_path[path] = {
        :reference_name => reference_name,
        :type => type,
        :parent_path => parent_path,
        :modification_time => .mod_time
    }
    .aliases.each do |a|
      self.aliases[a] = .fullname
    end
    self.inv_aliases[.fullname] = .aliases unless .aliases.empty?

    typed_module_set = module_set(type)

    # Don't want to trigger as {Msf::ModuleSet#create} so check for
    # key instead of using ||= which would call {Msf::ModuleSet#[]}
    # which would potentially call {Msf::ModuleSet#create}.
    next unless typed_module_set
    unless typed_module_set.has_key?(reference_name)
      typed_module_set[reference_name] = nil
    end
  end

  self.module_info_by_path
end

#refresh_cache_from_database(allowed_paths = [""]) ⇒ void

This method returns an undefined value.

Refreshes the in-memory cache from the database cache.



151
152
153
# File 'lib/msf/core/module_manager/cache.rb', line 151

def refresh_cache_from_database(allowed_paths=[""])
  self.module_info_by_path_from_database!(allowed_paths)
end

#refresh_cache_from_module_filesvoid #refresh_cache_from_module_files(module_class_or_instance) ⇒ void

Overloads:

  • #refresh_cache_from_module_filesvoid

    This method returns an undefined value.

    Rebuilds module metadata store and in-memory cache for all modules.

  • #refresh_cache_from_module_files(module_class_or_instance) ⇒ void

    This method returns an undefined value.

    Rebuilds database and in-memory cache for given module_class_or_instance.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/msf/core/module_manager/cache.rb', line 128

def refresh_cache_from_module_files(module_class_or_instance = nil)
  if module_class_or_instance
    Msf::Modules::Metadata::Cache.instance.(module_class_or_instance)
  else
    module_sets =
        [
            ['exploit', @framework.exploits],
            ['auxiliary', @framework.auxiliary],
            ['post', @framework.post],
            ['payload', @framework.payloads],
            ['encoder', @framework.encoders],
            ['nop', @framework.nops],
            ['evasion', @framework.evasion]
        ]
    @framework.payloads.recalculate # Ensure all payloads are calculated before refreshing metadata
    Msf::Modules::Metadata::Cache.instance.(module_sets)
  end
  refresh_cache_from_database(self.module_paths)
end

#try_load_module(parent_path, type, reference_name, cached_metadata: nil) ⇒ Boolean

Returns True if loaded, false otherwise.

Parameters:

  • parent_path (String)

    Root directory to load modules from

  • reference_name (String)

    THe module reference name, without the type prefix

  • type (String)

    Such as auxiliary, exploit, etc

  • cached_metadata (nil, Msf::Modules::Metadata::Obj) (defaults to: nil)

Returns:

  • (Boolean)

    True if loaded, false otherwise



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/msf/core/module_manager/cache.rb', line 104

def try_load_module(parent_path, type, reference_name, cached_metadata: nil)
  loaded = false
  # XXX borked
  loaders.each do |loader|
    if loader.loadable_module?(parent_path, type, reference_name, cached_metadata: )
      loaded = loader.load_module(parent_path, type, reference_name, force: true, cached_metadata: )
    end

    break if loaded
  end

  loaded
end