Files
metasploit-gs/lib/msf/core/module.rb
T

812 lines
18 KiB
Ruby
Raw Normal View History

2005-07-09 21:18:49 +00:00
require 'msf/core'
2005-05-21 17:57:00 +00:00
module Msf
###
#
# The module base class is responsible for providing the common interface
# that is used to interact with modules at the most basic levels, such as
# by inspecting a given module's attributes (name, dsecription, version,
# authors, etc) and by managing the module's data store.
#
###
class Module
2005-07-17 07:06:05 +00:00
# Modules can subscribe to a user-interface, and as such they include the
# UI subscriber module. This provides methods like print, print_line, etc.
# User interfaces are designed to be medium independent, and as such the
# user interface subscribes are designed to provide a flexible way of
# interacting with the user, n stuff.
include Rex::Ui::Subscriber
2005-07-11 20:49:33 +00:00
# Make include public so we can runtime extend
public_class_method :include
2005-07-10 19:21:40 +00:00
class <<self
include Framework::Offspring
2005-07-14 20:05:41 +00:00
#
# Class method to figure out what type of module this is
#
def type
raise NotImplementedError
end
def fullname
return type + '/' + refname
end
2006-07-29 22:37:39 +00:00
def shortname
return refname.split('/')[-1]
end
2005-10-01 21:26:17 +00:00
#
# Returns this module's ranking.
#
def rank
(const_defined?('Rank')) ? const_get('Rank') : NormalRanking
end
#
# Returns this module's ranking as a string representation.
#
def rank_to_s
RankingName[rank]
end
2005-07-10 19:21:40 +00:00
#
# The module's name that is assigned it it by the framework
# or derived from the path that the module is loaded from.
#
attr_accessor :refname
2005-10-10 00:30:14 +00:00
#
# This attribute holds the non-duplicated copy of the module
# implementation. This attribute is used for reloading purposes so that
# it can be re-duplicated.
#
attr_accessor :orig_cls
#
# The path from which the module was loaded.
#
attr_accessor :file_path
2005-07-10 19:21:40 +00:00
end
#
# Returns the class reference to the framework
#
def framework
return self.class.framework
end
2005-11-02 14:18:50 +00:00
#
# This method allows modules to tell the framework if they are usable
# on the system that they are being loaded on in a generic fashion.
# By default, all modules are indicated as being usable. An example of
# where this is useful is if the module depends on something external to
# ruby, such as a binary.
#
def self.is_usable
true
end
2005-07-09 21:18:49 +00:00
require 'msf/core/module/author'
require 'msf/core/module/platform_list'
require 'msf/core/module/reference'
require 'msf/core/module/target'
require 'msf/core/module/auxiliary_action'
2005-06-05 00:03:23 +00:00
2005-11-15 15:11:43 +00:00
#
# Creates an instance of an abstract module using the supplied information
# hash.
#
2005-06-04 22:26:42 +00:00
def initialize(info = {})
self.module_info = info
2005-05-21 17:57:00 +00:00
set_defaults
# Initialize module compatibility hashes
init_compat
# Fixup module fields as needed
info_fixups
2005-05-21 17:57:00 +00:00
# Transform some of the fields to arrays as necessary
2005-06-05 00:03:23 +00:00
self.author = Author.transform(module_info['Author'])
self.arch = Rex::Transformer.transform(module_info['Arch'], Array, [ String ], 'Arch')
2005-06-05 00:33:38 +00:00
self.platform = PlatformList.transform(module_info['Platform'])
self.references = Rex::Transformer.transform(module_info['References'], Array, [ SiteReference, Reference ], 'Ref')
2005-05-21 17:57:00 +00:00
2005-05-21 18:27:24 +00:00
# Create and initialize the option container for this module
self.options = OptionContainer.new
2005-06-05 23:45:58 +00:00
self.options.add_options(info['Options'], self.class)
self.options.add_advanced_options(info['AdvancedOptions'], self.class)
2006-01-05 03:57:12 +00:00
self.options.add_evasion_options(info['EvasionOptions'], self.class)
2005-05-21 18:27:24 +00:00
# Create and initialize the data store for this module
self.datastore = ModuleDataStore.new(self)
2005-06-05 05:42:14 +00:00
# Import default options into the datastore
import_defaults
2005-06-05 05:42:14 +00:00
self.privileged = module_info['Privileged'] || false
2006-01-22 19:25:55 +00:00
self.license = module_info['License'] || MSF_LICENSE
2005-05-21 17:57:00 +00:00
end
#
# Creates a fresh copy of an instantiated module
#
def replicant
obj = self.class.new
obj.datastore = self.datastore.dup
obj.user_input = self.user_input
obj.user_output = self.user_output
obj
end
#
# Overwrite the Subscriber print_line to do time stamps
#
def print_prefix
if(
datastore['TimestampOutput'] =~ /^(t|y|1)/i or
framework.datastore['TimestampOutput'] =~ /^(t|y|1)/i
)
return "[#{Time.now.strftime("%Y.%m.%d-%H:%M:%S")}] "
end
""
end
def print_status(msg='')
print_line(print_prefix + "[*] " + msg)
end
def print_error(msg='')
print_line(print_prefix + "[-] " + msg)
end
#
# Returns the module's framework full reference name. This is the
# short name that end-users work with (refname) plus the type
# of module prepended. Ex:
#
# payloads/windows/shell/reverse_tcp
#
def fullname
return self.class.fullname
end
2005-07-10 19:21:40 +00:00
#
# Returns the module's framework reference name. This is the
2005-07-10 19:21:40 +00:00
# short name that end-users work with. Ex:
#
# windows/shell/reverse_tcp
2005-07-10 19:21:40 +00:00
#
def refname
return self.class.refname
end
2005-05-21 17:57:00 +00:00
2006-09-12 05:34:58 +00:00
#
# Returns the module's rank.
#
def rank
return self.class.rank
end
2006-07-29 22:37:39 +00:00
#
# Returns the module's framework short name. This is a
# possibly conflicting name used for things like console
# prompts.
#
# reverse_tcp
#
def shortname
return self.class.shortname
end
2005-10-10 00:30:14 +00:00
#
# Returns the unduplicated class associated with this module.
#
def orig_cls
return self.class.orig_cls
end
#
# The path to the file in which the module can be loaded from.
#
def file_path
self.class.file_path
end
2005-06-05 05:42:14 +00:00
#
2005-11-02 14:18:50 +00:00
# Return the module's name from the module information hash.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def name
module_info['Name']
2005-05-21 17:57:00 +00:00
end
2005-07-07 23:11:03 +00:00
#
# Returns the module's alias, if it has one. Otherwise, the module's
# name is returned.
#
def alias
module_info['Alias']
2005-07-07 23:11:03 +00:00
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Return the module's description.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def description
module_info['Description']
2005-05-21 17:57:00 +00:00
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Return the module's version information.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def version
module_info['Version'].split(/,/).map { |ver|
ver.gsub(/\$Rev.s.on:\s+|\s+\$$/, '')
}.join(',')
end
#
2005-11-15 15:11:43 +00:00
# Returns the hash that describes this module's compatibilities.
#
def compat
module_info['Compat'] || {}
end
#
# Returns whether or not this module is compatible with the supplied
# module.
#
def compatible?(mod)
ch = nil
# Invalid module? Shoot, we can't compare that.
return true if (mod == nil)
# Determine which hash to used based on the supplied module type
if (mod.type == MODULE_ENCODER)
ch = self.compat['Encoder']
elsif (mod.type == MODULE_NOP)
ch = self.compat['Nop']
elsif (mod.type == MODULE_PAYLOAD)
ch = self.compat['Payload']
else
return true
end
2005-12-27 04:16:23 +00:00
# Enumerate each compatibility item in our hash to find out
# if we're compatible with this sucker.
2005-12-27 04:16:23 +00:00
ch.each_pair do |k,v|
# Get the value of the current key from the module, such as
# the ConnectionType for a stager (ws2ord, for instance).
mval = mod.module_info[k]
2005-12-27 04:16:23 +00:00
# Reject a filled compat item on one side, but not the other
return false if (v and not mval)
2005-12-27 04:16:23 +00:00
# Track how many of our values matched the module
mcnt = 0
# Values are whitespace separated
sv = v.split(/\s+/)
mv = mval.split(/\s+/)
sv.each do |x|
dlog("Checking compat [#{mod.refname} with #{self.refname}]: #{x} to #{mv.join(", ")}", 'core', LEV_3)
2005-12-27 04:16:23 +00:00
# Verify that any negate values are not matched
if (x[0,1] == '-' and mv.include?(x[1, x.length-1]))
2005-12-27 04:16:23 +00:00
dlog("Module #{mod.refname} is incompatible with #{self.refname} for #{k}: limiter was #{x}, value was #{mval}", 'core', LEV_1)
return false
end
2005-12-27 04:16:23 +00:00
mcnt += 1 if mv.include?(x)
end
# No values matched, reject this module
if (mcnt == 0)
dlog("Module #{mod.refname} is incompatible with #{self.refname} for #{k}: limiter was #{v}, value was #{mval}", 'core', LEV_1)
return false
end
end
# If we get here, we're compatible.
return true
2005-05-21 17:57:00 +00:00
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Return the module's abstract type.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def type
raise NotImplementedError
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Return a comma separated list of author for this module.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def author_to_s
return author.collect { |author| author.to_s }.join(", ")
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Enumerate each author.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def each_author(&block)
author.each(&block)
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Return a comma separated list of supported architectures, if any.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def arch_to_s
return arch.join(", ")
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Enumerate each architecture.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def each_arch(&block)
arch.each(&block)
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Return whether or not the module supports the supplied architecture.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def arch?(what)
return true if (what == ARCH_ANY)
2005-05-21 17:57:00 +00:00
return arch.index(what) != nil
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Return a comma separated list of supported platforms, if any.
2005-06-05 05:42:14 +00:00
#
2005-05-21 17:57:00 +00:00
def platform_to_s
return ((platform.all?) ? [ "All" ] : platform.names).join(", ")
2005-05-21 17:57:00 +00:00
end
2005-06-05 05:42:14 +00:00
#
# Checks to see if this module is compatible with the supplied platform
#
def platform?(what)
(platform & what).empty? == false
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Returns whether or not the module requires or grants high privileges.
2005-06-05 05:42:14 +00:00
#
def privileged?
return (privileged == true)
end
2005-07-09 19:35:29 +00:00
2005-07-11 02:03:48 +00:00
#
# The default communication subsystem for this module. We may need to move
# this somewhere else.
#
def comm
return Rex::Socket::Comm::Local
end
#
# Overrides the class' own datastore with the one supplied. This is used
# to allow modules to share datastores, such as a payload sharing an
# exploit module's datastore.
#
def share_datastore(ds)
self.datastore = ds
self.datastore.import_options(self.options)
end
#
# Imports default options into the module's datastore, optionally clearing
# all of the values currently set in the datastore.
#
def import_defaults(clear_datastore = true)
# Clear the datastore if the caller asked us to
self.datastore.clear if clear_datastore
self.datastore.import_options(self.options, 'self', true)
# If there are default options, import their values into the datastore
if (module_info['DefaultOptions'])
self.datastore.import_options_from_hash(module_info['DefaultOptions'], true, 'self')
end
end
#
# This method ensures that the options associated with this module all
# have valid values according to each required option in the option
# container.
#
def validate
self.options.validate(self.datastore)
end
2007-01-05 06:38:24 +00:00
#
# Returns true if this module is being debugged. The debug flag is set
# by setting datastore['DEBUG'] to 1|true|yes
#
def debugging?
(datastore['DEBUG'] || '') =~ /^(1|t|y)/i
end
##
2005-07-14 14:46:18 +00:00
#
# Just some handy quick checks
#
##
2005-11-15 15:11:43 +00:00
#
# Returns true if this module is an exploit module.
#
2005-07-14 14:46:18 +00:00
def exploit?
return (type == MODULE_EXPLOIT)
end
2005-11-15 15:11:43 +00:00
#
# Returns true if this module is a payload module.
#
2005-07-14 14:46:18 +00:00
def payload?
return (type == MODULE_PAYLOAD)
end
2005-11-15 15:11:43 +00:00
#
# Returns true if this module is an encoder module.
#
2005-07-14 14:46:18 +00:00
def encoder?
return (type == MODULE_ENCODER)
end
2005-11-15 15:11:43 +00:00
#
# Returns true if this module is a nop module.
#
2005-07-14 14:46:18 +00:00
def nop?
return (type == MODULE_NOP)
end
2005-11-15 15:11:43 +00:00
#
# Returns true if this module is an auxiliary module.
2005-11-15 15:11:43 +00:00
#
def auxiliary?
return (type == MODULE_AUX)
2005-07-14 14:46:18 +00:00
end
#
# Returns false since this is the real module
#
def self.cached?
false
end
2005-11-15 15:11:43 +00:00
#
# The array of zero or more authors.
#
attr_reader :author
#
# The array of zero or more architectures.
#
attr_reader :arch
#
# The array of zero or more platforms.
#
attr_reader :platform
#
# The reference count for the module.
#
attr_reader :references
#
# The module-specific datastore instance.
#
attr_reader :datastore
#
# The module-specific options.
#
attr_reader :options
#
# Whether or not this module requires privileged access.
#
2005-06-05 05:42:14 +00:00
attr_reader :privileged
2006-01-22 19:25:55 +00:00
#
# The license under which this module is provided.
#
attr_reader :license
#
# The job identifier that this module is running as, if any.
#
attr_accessor :job_id
2005-05-21 17:57:00 +00:00
protected
2005-11-15 15:11:43 +00:00
#
# The list of options that support merging in an information hash.
#
UpdateableOptions = [ "Name", "Description", "Alias" ]
2005-11-15 15:11:43 +00:00
#
# Sets the modules unsupplied info fields to their default values.
#
2005-05-21 17:57:00 +00:00
def set_defaults
2005-05-22 07:46:41 +00:00
self.module_info = {
2005-05-21 17:57:00 +00:00
'Name' => 'No module name',
'Description' => 'No module description',
'Version' => '0',
'Author' => nil,
2005-11-24 04:23:43 +00:00
'Arch' => nil, # No architectures by default.
'Platform' => [], # No platforms by default.
2005-06-05 05:42:14 +00:00
'Ref' => nil,
'Privileged' => false,
2006-01-22 19:28:05 +00:00
'License' => MSF_LICENSE,
2005-05-22 07:46:41 +00:00
}.update(self.module_info)
2005-05-21 17:57:00 +00:00
end
#
# This method initializes the module's compatibility hashes by normalizing
# them into one single hash. As it stands, modules can define
# compatibility in their supplied info hash through:
#
# Compat - direct compat definitions
# PayloadCompat - payload compatibilities
# EncoderCompat - encoder compatibilities
# NopCompat - nop compatibilities
#
# In the end, the module specific compatibilities are merged as sub-hashes
# of the primary Compat hash key to make checks more uniform.
#
def init_compat
c = module_info['Compat']
if (c == nil)
c = module_info['Compat'] = Hash.new
end
# Initialize the module sub compatibilities
c['Payload'] = Hash.new if (c['Payload'] == nil)
c['Encoder'] = Hash.new if (c['Encoder'] == nil)
c['Nop'] = Hash.new if (c['Nop'] == nil)
# Update the compat-derived module specific compatibilities from
# the specific ones to make a uniform view of compatibilities
c['Payload'].update(module_info['PayloadCompat'] || {})
c['Encoder'].update(module_info['EncoderCompat'] || {})
c['Nop'].update(module_info['NopCompat'] || {})
end
#
# Register options with a specific owning class.
#
def info_fixups
# Each reference should be an array consisting of two elements
refs = module_info['References']
if(refs and not refs.empty?)
refs.each_index do |i|
if(not (refs[i].respond_to?('[]') and refs[i].length == 2))
refs[i] = nil
end
end
# Purge invalid references
refs.delete(nil)
end
end
2005-06-05 23:45:58 +00:00
#
2005-11-15 15:11:43 +00:00
# Register options with a specific owning class.
2005-06-05 23:45:58 +00:00
#
def register_options(options, owner = self.class)
self.options.add_options(options, owner)
self.datastore.import_options(self.options, 'self', true)
2005-06-05 23:45:58 +00:00
end
#
2005-11-15 15:11:43 +00:00
# Register advanced options with a specific owning class.
2005-06-05 23:45:58 +00:00
#
def register_advanced_options(options, owner = self.class)
self.options.add_advanced_options(options, owner)
self.datastore.import_options(self.options, 'self', true)
2005-06-05 23:45:58 +00:00
end
2006-01-05 03:57:12 +00:00
#
# Register evasion options with a specific owning class.
#
def register_evasion_options(options, owner = self.class)
self.options.add_evasion_options(options, owner)
self.datastore.import_options(self.options, 'self', true)
2006-01-05 03:57:12 +00:00
end
#
# Removes the supplied options from the module's option container
2005-11-15 15:11:43 +00:00
# and data store.
#
def deregister_options(*names)
names.each { |name|
self.options.remove_option(name)
self.datastore.delete(name)
}
end
2005-06-04 22:26:42 +00:00
#
# Checks to see if a derived instance of a given module implements a method
# beyond the one that is provided by a base class. This is a pretty lame
# way of doing it, but I couldn't find a better one, so meh.
#
def derived_implementor?(parent, method_name)
(self.method(method_name).to_s.match(/#{parent}[^:]/)) ? false : true
2005-06-04 22:26:42 +00:00
end
2005-06-05 05:42:14 +00:00
#
# Merges options in the info hash in a sane fashion, as some options
# require special attention.
#
def merge_info(info, opts)
opts.each_pair { |name, val|
2005-07-11 05:15:30 +00:00
merge_check_key(info, name, val)
}
return info
end
#
# Updates information in the supplied info hash and merges other
# information. This method is used to override things like Name, Version,
# and Description without losing the ability to merge architectures,
# platforms, and options.
#
def update_info(info, opts)
opts.each_pair { |name, val|
# If the supplied option name is one of the ones that we should
# override by default
if (UpdateableOptions.include?(name) == true)
# Only if the entry is currently nil do we use our value
if (info[name] == nil)
2005-07-09 00:24:02 +00:00
info[name] = val
end
2005-07-11 05:15:30 +00:00
# Otherwise, perform the merge operation like normal
else
merge_check_key(info, name, val)
2005-06-05 05:42:14 +00:00
end
}
return info
end
2005-07-11 05:15:30 +00:00
#
2005-11-15 15:11:43 +00:00
# Checks and merges the supplied key/value pair in the supplied hash.
2005-07-11 05:15:30 +00:00
#
def merge_check_key(info, name, val)
if (self.respond_to?("merge_info_#{name.downcase}"))
eval("merge_info_#{name.downcase}(info, val)")
else
# If the info hash already has an entry for this name
if (info[name])
# If it's not an array, convert it to an array and merge the
# two
if (info[name].kind_of?(Array) == false)
curr = info[name]
2005-07-18 04:07:56 +00:00
info[name] = [ curr ]
end
# If the value being merged is an array, add each one
if (val.kind_of?(Array) == true)
val.each { |v|
if (info[name].include?(v) == false)
info[name] << v
end
}
# Otherwise just add the value
elsif (info[name].include?(val) == false)
info[name] << val
2005-07-11 05:15:30 +00:00
end
# Otherwise, just set the value equal if no current value
# exists
else
info[name] = val
end
end
end
2005-07-09 00:24:02 +00:00
#
2005-11-15 15:11:43 +00:00
# Merge aliases with an underscore delimiter.
2005-07-09 00:24:02 +00:00
#
def merge_info_alias(info, val)
merge_info_string(info, 'Alias', val, '_')
end
#
2005-11-15 15:11:43 +00:00
# Merges the module name.
2005-07-09 00:24:02 +00:00
#
def merge_info_name(info, val)
2005-07-11 23:10:48 +00:00
merge_info_string(info, 'Name', val, ', ', true)
2005-07-09 00:24:02 +00:00
end
#
2005-11-15 15:11:43 +00:00
# Merges the module description.
2005-07-09 00:24:02 +00:00
#
def merge_info_description(info, val)
merge_info_string(info, 'Description', val)
end
2005-07-10 00:16:48 +00:00
#
2005-11-15 15:11:43 +00:00
# Merge the module version.
2005-07-10 00:16:48 +00:00
#
def merge_info_version(info, val)
merge_info_string(info, 'Version', val)
end
2005-07-09 00:24:02 +00:00
#
2005-11-15 15:11:43 +00:00
# Merges a given key in the info hash with a delimiter.
2005-07-09 00:24:02 +00:00
#
2005-07-11 23:10:48 +00:00
def merge_info_string(info, key, val, delim = ', ', inverse = false)
2005-07-09 00:24:02 +00:00
if (info[key])
2005-07-11 23:10:48 +00:00
if (inverse == true)
info[key] = info[key] + delim + val
else
info[key] = val + delim + info[key]
end
2005-07-09 00:24:02 +00:00
else
info[key] = val
end
end
2005-06-05 05:42:14 +00:00
#
2005-11-15 15:11:43 +00:00
# Merges options.
2005-06-05 05:42:14 +00:00
#
2006-01-05 03:57:12 +00:00
def merge_info_options(info, val, advanced = false, evasion = false)
key_name = ((advanced) ? 'Advanced' : (evasion) ? 'Evasion' : '') + 'Options'
2005-06-05 05:42:14 +00:00
new_cont = OptionContainer.new
2006-01-05 03:57:12 +00:00
new_cont.add_options(val, advanced, evasion)
2005-06-05 05:42:14 +00:00
cur_cont = OptionContainer.new
2006-01-05 03:57:12 +00:00
cur_cont.add_options(info[key_name] || [], advanced, evasion)
2005-06-05 05:42:14 +00:00
new_cont.each_option { |name, option|
next if (cur_cont.get(name))
info[key_name] = [] if (!info[key_name])
info[key_name] << option
}
end
#
2005-11-15 15:11:43 +00:00
# Merges advanced options.
2005-06-05 05:42:14 +00:00
#
2006-01-05 03:57:12 +00:00
def merge_info_advanced_options(info, val)
merge_info_options(info, val, true, false)
2005-06-05 05:42:14 +00:00
end
2006-01-05 03:57:12 +00:00
#
# Merges advanced options.
#
def merge_info_evasion_options(info, val)
merge_info_options(info, val, false, true)
end
2006-01-05 03:57:12 +00:00
2005-11-15 15:11:43 +00:00
attr_accessor :module_info # :nodoc:
attr_writer :author, :arch, :platform, :references, :datastore, :options # :nodoc:
attr_writer :privileged # :nodoc:
2006-01-22 19:25:55 +00:00
attr_writer :license # :nodoc:
2005-05-21 17:57:00 +00:00
end
2005-06-05 00:03:23 +00:00
#
# Alias the data types so people can reference them just by Msf:: and not
# Msf::Module::
#
Author = Msf::Module::Author
Reference = Msf::Module::Reference
SiteReference = Msf::Module::SiteReference
Platform = Msf::Module::Platform
Target = Msf::Module::Target
end