Skip to content

Commit e6ab820

Browse files
authored
Merge pull request rapid7#20015 from adfoster-r7/skip-loading-external-modules-with-unsupported-runtimes
Skip loading external modules with unsupported runtimes
2 parents 2116cea + 07b731b commit e6ab820

File tree

8 files changed

+341
-132
lines changed

8 files changed

+341
-132
lines changed

lib/msf/core/module_manager/cache.rb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,15 @@ def cache_in_memory(class_or_module, options={})
7474
# @return [false] if a module with the given type and reference name does not exist in the cache.
7575
# @return (see Msf::Modules::Loader::Base#load_module)
7676
def load_cached_module(type, reference_name, cache_type: Msf::ModuleManager::Cache::MEMORY)
77+
cached_metadata = nil
78+
7779
case cache_type
7880
when Msf::ModuleManager::Cache::FILESYSTEM
7981
cached_metadata = Msf::Modules::Metadata::Cache.instance.get_module_reference(type: type, reference_name: reference_name)
8082
return false unless cached_metadata
8183

8284
parent_path = get_parent_path(cached_metadata.path, type)
8385
when Msf::ModuleManager::Cache::MEMORY
84-
cached_metadata = nil
8586
module_info = self.module_info_by_path.values.find { |inner_info|
8687
inner_info[:type] == type and inner_info[:reference_name] == reference_name
8788
}
@@ -92,16 +93,21 @@ def load_cached_module(type, reference_name, cache_type: Msf::ModuleManager::Cac
9293
raise ArgumentError, "#{cache_type} is not a valid cache type."
9394
end
9495

95-
try_load_module(parent_path, reference_name, type, cached_metadata: cached_metadata)
96+
try_load_module(parent_path, type, reference_name, cached_metadata: cached_metadata)
9697
end
9798

98-
def try_load_module(parent_path, reference_name, type, cached_metadata: nil)
99+
# @param [String] parent_path Root directory to load modules from
100+
# @param [String] reference_name THe module reference name, without the type prefix
101+
# @param [String] type Such as auxiliary, exploit, etc
102+
# @param [nil,Msf::Modules::Metadata::Obj] cached_metadata
103+
# @return [Boolean] True if loaded, false otherwise
104+
def try_load_module(parent_path, type, reference_name, cached_metadata: nil)
99105
loaded = false
100106
# XXX borked
101107
loaders.each do |loader|
102-
next unless cached_metadata || loader.loadable_module?(parent_path, type, reference_name)
103-
104-
loaded = loader.load_module(parent_path, type, reference_name, force: true, cached_metadata: cached_metadata)
108+
if loader.loadable_module?(parent_path, type, reference_name, cached_metadata: cached_metadata)
109+
loaded = loader.load_module(parent_path, type, reference_name, force: true, cached_metadata: cached_metadata)
110+
end
105111

106112
break if loaded
107113
end

lib/msf/core/modules/loader/base.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,9 @@ def module_path(parent_path, type, module_reference_name)
472472
# the path is not hidden (starts with '.')
473473
# @return [false] otherwise
474474
def module_path?(path)
475-
path.ends_with?(MODULE_EXTENSION) &&
475+
path.end_with?(MODULE_EXTENSION) &&
476476
File.file?(path) &&
477-
!path.starts_with?(".") &&
477+
!path.start_with?(".") &&
478478
!path.match?(UNIT_TEST_REGEX) &&
479479
!script_path?(path)
480480
end

lib/msf/core/modules/loader/directory.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ def loadable?(path)
1212
File.directory?(path)
1313
end
1414

15-
def loadable_module?(parent_path, type, module_reference_name)
16-
full_path = module_path(parent_path, type, module_reference_name)
15+
# @param [String] parent_path Root directory to load modules from
16+
# @param [String] type Such as auxiliary, exploit, etc
17+
# @param [String] module_reference_name The module reference name, without the type prefix
18+
# @param [nil,Msf::Modules::Metadata::Obj] cached_metadata
19+
# @return [Boolean] True this loader can load the module, false otherwise
20+
def loadable_module?(parent_path, type, module_reference_name, cached_metadata: nil)
21+
full_path = cached_metadata&.path || module_path(parent_path, type, module_reference_name)
1722
module_path?(full_path)
1823
end
1924

lib/msf/core/modules/loader/executable.rb

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,39 @@ def loadable?(path)
1111
File.directory?(path)
1212
end
1313

14-
def loadable_module?(parent_path, type, module_reference_name)
15-
full_path = module_path(parent_path, type, module_reference_name)
14+
# @param [String] parent_path Root directory to load modules from
15+
# @param [String] type Such as auxiliary, exploit, etc
16+
# @param [String] module_reference_name The module reference name, without the type prefix
17+
# @param [nil,Msf::Modules::Metadata::Obj] cached_metadata
18+
# @return [Boolean] True this loader can load the module, false otherwise
19+
def loadable_module?(parent_path, type, module_reference_name, cached_metadata: nil)
20+
full_path = cached_metadata&.path || module_path(parent_path, type, module_reference_name)
1621
script_path?(full_path)
1722
end
1823

1924
protected
2025

26+
def read_script_env_runtime(full_path)
27+
# Extract the runtime from the first line of the script, i.e.
28+
# #!/usr/bin/env python
29+
# //usr/bin/env go run "$0" "$@"; exit "$?"
30+
first_line = File.open(full_path, 'rb') { |f| f.gets }
31+
first_line.to_s[%r{\A(?:#!|/)/usr/bin/env\s+(\w+)}, 1]
32+
end
33+
34+
# @param [String] full_path The full path to the module file.
35+
# @return [Boolean] True if the script's required runtime is available on the host, false otherwise
36+
def script_runtime_available?(full_path)
37+
return false unless script_path?(full_path)
38+
39+
# Modules currently use /usr/bin/env - in the future absolute paths may need to be supported
40+
script_runtime = read_script_env_runtime(full_path)
41+
return !!Rex::FileUtils.find_full_path(script_runtime) if script_runtime
42+
43+
# If the script runtime isn't known, we assume the script is executable
44+
true
45+
end
46+
2147
# Yields the module_reference_name for each module file found under the directory path.
2248
#
2349
# @param [String] path The path to the directory.
@@ -90,10 +116,15 @@ def read_module_content(parent_path, type, module_reference_name)
90116
# @param (see Msf::Modules::Loader::Base#read_module_content_from_path)
91117
# @return (see Msf::Modules::Loader::Base#read_module_content_from_path)
92118
def read_module_content_from_path(full_path)
93-
unless File.executable?(full_path)
119+
unless script_path?(full_path)
94120
load_error(full_path, Errno::ENOENT.new)
95121
return ''
96122
end
123+
unless script_runtime_available?(full_path)
124+
load_error(full_path, RuntimeError.new("Unable to load module as the following runtime was not found on the path: #{read_script_env_runtime(full_path)}"))
125+
return ''
126+
end
127+
97128
begin
98129
content = Msf::Modules::External::Shim.generate(full_path, @module_manager.framework)
99130
if content

0 commit comments

Comments
 (0)