2017-03-27 15:36:47 -05:00
# -*- coding: binary -*-
# 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 )
2017-03-28 11:19:30 -05:00
File . directory? ( path )
2017-03-27 15:36:47 -05:00
end
2025-04-04 20:01:44 +01:00
# @param [String] parent_path Root directory to load modules from
# @param [String] type Such as auxiliary, exploit, etc
# @param [String] module_reference_name The module reference name, without the type prefix
# @param [nil,Msf::Modules::Metadata::Obj] cached_metadata
# @return [Boolean] True this loader can load the module, false otherwise
def loadable_module? ( parent_path , type , module_reference_name , cached_metadata : nil )
full_path = cached_metadata & . path || module_path ( parent_path , type , module_reference_name )
2020-12-10 10:30:53 +00:00
script_path? ( full_path )
end
2017-03-27 15:36:47 -05:00
protected
2025-04-04 20:01:44 +01:00
def read_script_env_runtime ( full_path )
# Extract the runtime from the first line of the script, i.e.
# #!/usr/bin/env python
# //usr/bin/env go run "$0" "$@"; exit "$?"
first_line = File . open ( full_path , 'rb' ) { | f | f . gets }
first_line . to_s [ %r{ \ A(?: # !|/)/usr/bin/env \ s+( \ w+) } , 1 ]
end
# @param [String] full_path The full path to the module file.
# @return [Boolean] True if the script's required runtime is available on the host, false otherwise
def script_runtime_available? ( full_path )
return false unless script_path? ( full_path )
# Modules currently use /usr/bin/env - in the future absolute paths may need to be supported
script_runtime = read_script_env_runtime ( full_path )
return ! ! Rex :: FileUtils . find_full_path ( script_runtime ) if script_runtime
# If the script runtime isn't known, we assume the script is executable
true
end
2017-03-27 15:36:47 -05:00
# 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
2017-03-28 11:19:30 -05:00
unless :: File . directory? ( full_entry_path ) && module_manager . type_enabled? ( type )
2017-03-27 15:36:47 -05:00
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 |
2018-04-10 11:07:21 -05:00
# Assume that all modules are scripts for now, workaround
# filesystems where all files are labeled as executable.
if script_path? ( entry_descendant_path )
2017-03-27 15:36:47 -05:00
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
2021-01-22 11:07:14 -06:00
next if File :: basename ( relative_entry_descendant_path ) . start_with? ( 'example' )
2017-03-27 15:36:47 -05:00
# 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 )
2023-03-24 14:00:05 +00:00
read_module_content_from_path ( full_path )
end
# Loads the module content from the on disk file.
#
# @param (see Msf::Modules::Loader::Base#read_module_content_from_path)
# @return (see Msf::Modules::Loader::Base#read_module_content_from_path)
def read_module_content_from_path ( full_path )
2025-04-04 20:01:44 +01:00
unless script_path? ( full_path )
2017-03-27 15:36:47 -05:00
load_error ( full_path , Errno :: ENOENT . new )
return ''
end
2025-04-04 20:01:44 +01:00
unless script_runtime_available? ( full_path )
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 ) } " ) )
return ''
end
2017-04-09 11:50:03 -05:00
begin
2018-07-24 12:02:54 -05:00
content = Msf :: Modules :: External :: Shim . generate ( full_path , @module_manager . framework )
2018-05-04 15:55:21 -05:00
if content
return content
else
elog " Unable to load module #{ full_path } , unknown module type "
return ''
end
2020-12-10 10:30:53 +00:00
rescue LoadError = > e
load_error ( full_path , e )
return ''
2017-04-09 11:50:03 -05:00
rescue :: Exception = > e
2020-06-11 13:09:25 +01:00
elog ( " Unable to load module #{ full_path } " , error : e )
2017-04-09 15:03:05 -05:00
# XXX migrate this to a full load_error when we can tell the user why the
# module did not load and/or how to resolve it.
# load_error(full_path, e)
2017-04-09 11:50:03 -05:00
''
end
2017-03-27 15:36:47 -05:00
end
end