Merge staging/great-backport to master

Conflicts:
	spec/lib/msf/core/module_spec.rb
bug/bundler_fix
Luke Imhoff 2014-11-12 11:08:18 -06:00
commit 1fd8fe57df
No known key found for this signature in database
GPG Key ID: 5B1FB01FB33356F8
59 changed files with 1664 additions and 1363 deletions

View File

@ -20,17 +20,17 @@ class ReadableText
# @return [String] formatted text output of the dump.
def self.dump_module(mod, indent = " ")
case mod.type
when MODULE_PAYLOAD
when Msf::MODULE_PAYLOAD
return dump_payload_module(mod, indent)
when MODULE_NOP
when Msf::MODULE_NOP
return dump_basic_module(mod, indent)
when MODULE_ENCODER
when Msf::MODULE_ENCODER
return dump_basic_module(mod, indent)
when MODULE_EXPLOIT
when Msf::MODULE_EXPLOIT
return dump_exploit_module(mod, indent)
when MODULE_AUX
when Msf::MODULE_AUX
return dump_auxiliary_module(mod, indent)
when MODULE_POST
when Msf::MODULE_POST
return dump_post_module(mod, indent)
else
return dump_generic_module(mod, indent)

View File

@ -54,12 +54,12 @@ module Framework
ModuleSimplifiers =
{
MODULE_ENCODER => Msf::Simple::Encoder,
MODULE_EXPLOIT => Msf::Simple::Exploit,
MODULE_NOP => Msf::Simple::Nop,
MODULE_PAYLOAD => Msf::Simple::Payload,
MODULE_AUX => Msf::Simple::Auxiliary,
MODULE_POST => Msf::Simple::Post,
Msf::MODULE_ENCODER => Msf::Simple::Encoder,
Msf::MODULE_EXPLOIT => Msf::Simple::Exploit,
Msf::MODULE_NOP => Msf::Simple::Nop,
Msf::MODULE_PAYLOAD => Msf::Simple::Payload,
Msf::MODULE_AUX => Msf::Simple::Auxiliary,
Msf::MODULE_POST => Msf::Simple::Post,
}
#

View File

@ -18,6 +18,16 @@ require 'rex'
require 'rex/ui'
module Msf
autoload :Author, 'msf/core/author'
autoload :Platform, 'msf/core/platform'
autoload :Reference, 'msf/core/reference'
autoload :SiteReference, 'msf/core/site_reference'
autoload :Target, 'msf/core/target'
#
# Constants
#
LogSource = "core"
end

149
lib/msf/core/author.rb Normal file
View File

@ -0,0 +1,149 @@
# -*- coding: binary -*-
require 'msf/core'
###
#
# This data type represents an author of a piece of code in either
# the framework, a module, a script, or something entirely unrelated.
#
###
class Msf::Author
# A hash of known author names
Known =
{
'amaloteaux' => 'alex_maloteaux' + 0x40.chr + 'metasploit.com',
'anonymous' => 'Unknown',
'bannedit' => 'bannedit' + 0x40.chr + 'metasploit.com',
'Carlos Perez' => 'carlos_perez' + 0x40.chr + 'darkoperator.com',
'cazz' => 'bmc' + 0x40.chr + 'shmoo.com',
'CG' => 'cg' + 0x40.chr + 'carnal0wnage.com',
'ddz' => 'ddz' + 0x40.chr + 'theta44.org',
'egypt' => 'egypt' + 0x40.chr + 'metasploit.com',
'et' => 'et' + 0x40.chr + 'metasploit.com',
'Christian Mehlmauer' => 'FireFart' + 0x40.chr + 'gmail.com',
'hdm' => 'hdm' + 0x40.chr + 'metasploit.com',
'I)ruid' => 'druid' + 0x40.chr + 'caughq.org',
'jcran' => 'jcran' + 0x40.chr + 'metasploit.com',
'jduck' => 'jduck' + 0x40.chr + 'metasploit.com',
'joev' => 'joev' + 0x40.chr + 'metasploit.com',
'juan vazquez' => 'juan.vazquez' + 0x40.chr + 'metasploit.com',
'kf' => 'kf_list' + 0x40.chr + 'digitalmunition.com',
'kris katterjohn' => 'katterjohn' + 0x40.chr + 'gmail.com',
'MC' => 'mc' + 0x40.chr + 'metasploit.com',
'Ben Campbell' => 'eat_meatballs' + 0x40.chr + 'hotmail.co.uk',
'msmith' => 'msmith' + 0x40.chr + 'metasploit.com',
'mubix' => 'mubix' + 0x40.chr + 'hak5.org',
'natron' => 'natron' + 0x40.chr + 'metasploit.com',
'optyx' => 'optyx' + 0x40.chr + 'no$email.com',
'patrick' => 'patrick' + 0x40.chr + 'osisecurity.com.au',
'pusscat' => 'pusscat' + 0x40.chr + 'metasploit.com',
'Ramon de C Valle' => 'rcvalle' + 0x40.chr + 'metasploit.com',
'sf' => 'stephen_fewer' + 0x40.chr + 'harmonysecurity.com',
'sinn3r' => 'sinn3r' + 0x40.chr + 'metasploit.com',
'skape' => 'mmiller' + 0x40.chr + 'hick.org',
'skylined' => 'skylined' + 0x40.chr + 'edup.tudelft.nl',
'spoonm' => 'spoonm' + 0x40.chr + 'no$email.com',
'stinko' => 'vinnie' + 0x40.chr + 'metasploit.com',
'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com',
'todb' => 'todb' + 0x40.chr + 'metasploit.com',
'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com',
'wvu' => 'wvu' + 0x40.chr + 'metasploit.com'
}
#
# Class method that translates a string to an instance of the Author class,
# if it's of the right format, and returns the Author class instance
#
def self.from_s(str)
instance = self.new
# If the serialization fails...
if (instance.from_s(str) == false)
return nil
end
return instance
end
#
# Transforms the supplied source into an array of authors
#
def self.transform(src)
Rex::Transformer.transform(src, Array, [ self ], 'Author')
end
def initialize(name = nil, email = nil)
self.name = name
self.email = email || Known[name]
end
#
# Compares authors
#
def ==(tgt)
return (tgt.to_s == to_s)
end
#
# Serialize the author object to a string in form:
#
# name <email>
#
def to_s
str = "#{name}"
if (email and not email.empty?)
str += " <#{email}>"
end
return str
end
#
# Translate the author from the supplied string which may
# have either just a name or also an email address
#
def from_s(str)
# Supported formats:
# known_name
# user [at/@] host [dot/.] tld
# Name <user [at/@] host [dot/.] tld>
if ((m = str.match(/^\s*([^<]+)<([^>]+)>\s*$/)))
self.name = m[1].sub(/<.*/, '')
self.email = m[2].sub(/\s*\[at\]\s*/, '@').sub(/\s*\[dot\]\s*/, '.')
else
if (Known[str])
self.email = Known[str]
self.name = str
else
self.email = str.sub(/\s*\[at\]\s*/, '@').sub(/\s*\[dot\]\s*/, '.').gsub(/^<|>$/, '')
m = self.email.match(/([^@]+)@/)
self.name = m ? m[1] : nil
if !(self.email and self.email.index('@'))
self.name = self.email
self.email = ''
end
end
end
self.name.strip! if self.name
return true
end
#
# Sets the name of the author and updates the email if it's a known author.
#
def name=(name)
self.email = Known[name] if (Known[name])
@name = name
end
attr_accessor :email
attr_reader :name
end

View File

@ -21,14 +21,14 @@ class Auxiliary < Msf::Module
# Returns MODULE_AUX to indicate that this is an auxiliary module.
#
def self.type
MODULE_AUX
Msf::MODULE_AUX
end
#
# Returns MODULE_AUX to indicate that this is an auxiliary module.
#
def type
MODULE_AUX
Msf::MODULE_AUX
end
#

View File

@ -162,14 +162,14 @@ class Encoder < Module
# Returns MODULE_ENCODER to indicate that this is an encoder module.
#
def self.type
return MODULE_ENCODER
return Msf::MODULE_ENCODER
end
#
# Returns MODULE_ENCODER to indicate that this is an encoder module.
#
def type
return MODULE_ENCODER
return Msf::MODULE_ENCODER
end
#

View File

@ -621,14 +621,14 @@ class Exploit < Msf::Module
# Returns MODULE_EXPLOIT to indicate that this is an exploit module.
#
def self.type
MODULE_EXPLOIT
Msf::MODULE_EXPLOIT
end
#
# Returns MODULE_EXPLOIT to indicate that this is an exploit module.
#
def type
MODULE_EXPLOIT
Msf::MODULE_EXPLOIT
end
#

View File

@ -69,7 +69,7 @@ class Framework
def initialize(opts={})
# Allow specific module types to be loaded
types = opts[:module_types] || MODULE_TYPES
types = opts[:module_types] || Msf::MODULE_TYPES
self.threads = ThreadManager.new(self)
self.events = EventDispatcher.new(self)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
module Msf::Module::Arch
#
# Attributes
#
# @!attribute arch
# The array of zero or more architectures.
attr_reader :arch
#
# Instance Methods
#
#
# Return whether or not the module supports the supplied architecture.
#
def arch?(what)
if (what == ARCH_ANY)
true
else
arch.index(what) != nil
end
end
#
# Return a comma separated list of supported architectures, if any.
#
def arch_to_s
arch.join(", ")
end
#
# Enumerate each architecture.
#
def each_arch(&block)
arch.each(&block)
end
protected
#
# Attributes
#
attr_writer :arch
end

View File

@ -1,149 +1,36 @@
# -*- coding: binary -*-
require 'msf/core'
module Msf::Module::Author
#
# Attributes
#
###
#
# This data type represents an author of a piece of code in either
# the framework, a module, a script, or something entirely unrelated.
#
###
class Msf::Module::Author
# A hash of known author names
Known =
{
'amaloteaux' => 'alex_maloteaux' + 0x40.chr + 'metasploit.com',
'anonymous' => 'Unknown',
'bannedit' => 'bannedit' + 0x40.chr + 'metasploit.com',
'Carlos Perez' => 'carlos_perez' + 0x40.chr + 'darkoperator.com',
'cazz' => 'bmc' + 0x40.chr + 'shmoo.com',
'CG' => 'cg' + 0x40.chr + 'carnal0wnage.com',
'ddz' => 'ddz' + 0x40.chr + 'theta44.org',
'egypt' => 'egypt' + 0x40.chr + 'metasploit.com',
'et' => 'et' + 0x40.chr + 'metasploit.com',
'Christian Mehlmauer' => 'FireFart' + 0x40.chr + 'gmail.com',
'hdm' => 'hdm' + 0x40.chr + 'metasploit.com',
'I)ruid' => 'druid' + 0x40.chr + 'caughq.org',
'jcran' => 'jcran' + 0x40.chr + 'metasploit.com',
'jduck' => 'jduck' + 0x40.chr + 'metasploit.com',
'joev' => 'joev' + 0x40.chr + 'metasploit.com',
'juan vazquez' => 'juan.vazquez' + 0x40.chr + 'metasploit.com',
'kf' => 'kf_list' + 0x40.chr + 'digitalmunition.com',
'kris katterjohn' => 'katterjohn' + 0x40.chr + 'gmail.com',
'MC' => 'mc' + 0x40.chr + 'metasploit.com',
'Ben Campbell' => 'eat_meatballs' + 0x40.chr + 'hotmail.co.uk',
'msmith' => 'msmith' + 0x40.chr + 'metasploit.com',
'mubix' => 'mubix' + 0x40.chr + 'hak5.org',
'natron' => 'natron' + 0x40.chr + 'metasploit.com',
'optyx' => 'optyx' + 0x40.chr + 'no$email.com',
'patrick' => 'patrick' + 0x40.chr + 'osisecurity.com.au',
'pusscat' => 'pusscat' + 0x40.chr + 'metasploit.com',
'Ramon de C Valle' => 'rcvalle' + 0x40.chr + 'metasploit.com',
'sf' => 'stephen_fewer' + 0x40.chr + 'harmonysecurity.com',
'sinn3r' => 'sinn3r' + 0x40.chr + 'metasploit.com',
'skape' => 'mmiller' + 0x40.chr + 'hick.org',
'skylined' => 'skylined' + 0x40.chr + 'edup.tudelft.nl',
'spoonm' => 'spoonm' + 0x40.chr + 'no$email.com',
'stinko' => 'vinnie' + 0x40.chr + 'metasploit.com',
'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com',
'todb' => 'todb' + 0x40.chr + 'metasploit.com',
'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com',
'wvu' => 'wvu' + 0x40.chr + 'metasploit.com'
}
# @!attribute author
# The array of zero or more authors.
attr_reader :author
#
# Class method that translates a string to an instance of the Author class,
# if it's of the right format, and returns the Author class instance
# Instance Methods
#
def self.from_s(str)
instance = self.new
# If the serialization fails...
if (instance.from_s(str) == false)
return nil
end
return instance
#
# Return a comma separated list of author for this module.
#
def author_to_s
author.collect { |author| author.to_s }.join(", ")
end
#
# Transforms the supplied source into an array of authors
# Enumerate each author.
#
def self.transform(src)
Rex::Transformer.transform(src, Array, [ self ], 'Author')
def each_author(&block)
author.each(&block)
end
def initialize(name = nil, email = nil)
self.name = name
self.email = email || Known[name]
end
protected
#
# Compares authors
# Attributes
#
def ==(tgt)
return (tgt.to_s == to_s)
end
#
# Serialize the author object to a string in form:
#
# name <email>
#
def to_s
str = "#{name}"
if (email and not email.empty?)
str += " <#{email}>"
end
return str
end
#
# Translate the author from the supplied string which may
# have either just a name or also an email address
#
def from_s(str)
# Supported formats:
# known_name
# user [at/@] host [dot/.] tld
# Name <user [at/@] host [dot/.] tld>
if ((m = str.match(/^\s*([^<]+)<([^>]+)>\s*$/)))
self.name = m[1].sub(/<.*/, '')
self.email = m[2].sub(/\s*\[at\]\s*/, '@').sub(/\s*\[dot\]\s*/, '.')
else
if (Known[str])
self.email = Known[str]
self.name = str
else
self.email = str.sub(/\s*\[at\]\s*/, '@').sub(/\s*\[dot\]\s*/, '.').gsub(/^<|>$/, '')
m = self.email.match(/([^@]+)@/)
self.name = m ? m[1] : nil
if !(self.email and self.email.index('@'))
self.name = self.email
self.email = ''
end
end
end
self.name.strip! if self.name
return true
end
#
# Sets the name of the author and updates the email if it's a known author.
#
def name=(name)
self.email = Known[name] if (Known[name])
@name = name
end
attr_accessor :email
attr_reader :name
# @!attribute [w] author
attr_writer :author
end

View File

@ -0,0 +1,115 @@
module Msf::Module::Compatibility
#
# 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 == Msf::MODULE_ENCODER)
ch = self.compat['Encoder']
elsif (mod.type == Msf::MODULE_NOP)
ch = self.compat['Nop']
elsif (mod.type == Msf::MODULE_PAYLOAD)
ch = self.compat['Payload']
if self.respond_to?("target") and self.target and self.target['Payload'] and self.target['Payload']['Compat']
ch = ch.merge(self.target['Payload']['Compat'])
end
else
return true
end
# Enumerate each compatibility item in our hash to find out
# if we're compatible with this sucker.
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]
# Reject a filled compat item on one side, but not the other
if (v and not mval)
dlog("Module #{mod.refname} is incompatible with #{self.refname} for #{k}: limiter was #{v}")
return false
end
# 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)
# Verify that any negate values are not matched
if (x[0,1] == '-' and mv.include?(x[1, x.length-1]))
dlog("Module #{mod.refname} is incompatible with #{self.refname} for #{k}: limiter was #{x}, value was #{mval}", 'core', LEV_1)
return false
end
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
dlog("Module #{mod.refname} is compatible with #{self.refname}", "core", LEV_1)
# If we get here, we're compatible.
return true
end
protected
#
# 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
end

View File

@ -0,0 +1,41 @@
module Msf::Module::DataStore
#
# Attributes
#
# @attribute [r] datastore
# The module-specific datastore instance.
#
# @return [Hash{String => String}]
attr_reader :datastore
#
# 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
#
# 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
protected
attr_writer :datastore
end

View File

@ -0,0 +1,41 @@
# Constants indicating the reason for an unsuccessful module attempt
module Msf::Module::Failure
# The exploit settings were incorrect
BadConfig = 'bad-config'
# The network service disconnected us mid-attempt
Disconnected = 'disconnected'
# The application replied indication we do not have access
NoAccess = 'no-access'
# No confidence in success or failure
None = 'none'
# The target is not compatible with this exploit or settings
NoTarget = 'no-target'
# The application endpoint or specific service was not found
NotFound = 'not-found'
# The application response indicated it was not vulnerable
NotVulnerable = 'not-vulnerable'
# The payload was delivered but no session was opened (AV, network, etc)
PayloadFailed = 'payload-failed'
# The exploit triggered some form of timeout
TimeoutExpired = 'timeout-expired'
# The application replied in an unexpected fashion
UnexpectedReply = 'unexpected-reply'
# No confidence in success or failure
Unknown = 'unknown'
# The network service was unreachable (connection refused, etc)
Unreachable = 'unreachable'
# The exploit was interrupted by the user
UserInterrupt = 'user-interrupt'
end

View File

@ -0,0 +1,67 @@
# @note {Msf::Module::ModuleInfo#name} is unrelated to {#fullname} and should instead be thought of as the title or
# summary of the module.
#
# Names related to {#fullname}, such as {#fullname}, {#refname}, and {#shortname}.
module Msf::Module::FullName
extend ActiveSupport::Concern
module ClassMethods
#
# Attributes
#
# @attribute refname
# 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
#
# Class Methods
#
def fullname
type + '/' + refname
end
def shortname
refname.split('/').last
end
end
#
# Instance Methods
#
#
# 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
self.class.fullname
end
#
# Returns the module's framework reference name. This is the
# short name that end-users work with. Ex:
#
# windows/shell/reverse_tcp
#
def refname
self.class.refname
end
#
# Returns the module's framework short name. This is a
# possibly conflicting name used for things like console
# prompts.
#
# reverse_tcp
#
def shortname
self.class.shortname
end
end

View File

@ -0,0 +1,220 @@
module Msf::Module::ModuleInfo
#
# CONSTANTS
#
# The list of options that support merging in an information hash.
UpdateableOptions = [ "Name", "Description", "Alias", "PayloadCompat" ]
#
# Instance Methods
#
#
# Returns the module's alias, if it has one. Otherwise, the module's
# name is returned.
#
def alias
module_info['Alias']
end
#
# Return the module's description.
#
def description
module_info['Description']
end
#
# Returns the disclosure date, if known.
#
def disclosure_date
date_str = Date.parse(module_info['DisclosureDate'].to_s) rescue nil
end
#
# Return the module's name from the module information hash.
#
def name
module_info['Name']
end
protected
#
# Attributes
#
# @!attribute module_info
attr_accessor :module_info
#
# Instance Methods
#
#
# 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 !(refs[i].respond_to?('[]') and refs[i].length == 2)
refs[i] = nil
end
end
# Purge invalid references
refs.delete(nil)
end
end
#
# Checks and merges the supplied key/value pair in the supplied hash.
#
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]
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
end
# Otherwise, just set the value equal if no current value
# exists
else
info[name] = val
end
end
end
#
# 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|
merge_check_key(info, name, val)
}
info
end
#
# Merges advanced options.
#
def merge_info_advanced_options(info, val)
merge_info_options(info, val, true, false)
end
#
# Merge aliases with an underscore delimiter.
#
def merge_info_alias(info, val)
merge_info_string(info, 'Alias', val, '_')
end
#
# Merges the module description.
#
def merge_info_description(info, val)
merge_info_string(info, 'Description', val, ". ", true)
end
#
# Merges advanced options.
#
def merge_info_evasion_options(info, val)
merge_info_options(info, val, false, true)
end
#
# Merges the module name.
#
def merge_info_name(info, val)
merge_info_string(info, 'Name', val, ', ', true)
end
#
# Merges options.
#
def merge_info_options(info, val, advanced = false, evasion = false)
key_name = ((advanced) ? 'Advanced' : (evasion) ? 'Evasion' : '') + 'Options'
new_cont = Msf::OptionContainer.new
new_cont.add_options(val, advanced, evasion)
cur_cont = Msf::OptionContainer.new
cur_cont.add_options(info[key_name] || [], advanced, evasion)
new_cont.each_option { |name, option|
next if (cur_cont.get(name))
info[key_name] = [] if (!info[key_name])
info[key_name] << option
}
end
#
# Merges a given key in the info hash with a delimiter.
#
def merge_info_string(info, key, val, delim = ', ', inverse = false)
if (info[key])
if (inverse == true)
info[key] = info[key] + delim + val
else
info[key] = val + delim + info[key]
end
else
info[key] = val
end
end
#
# Merge the module version.
#
def merge_info_version(info, val)
merge_info_string(info, 'Version', val)
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)
info[name] = val
end
# Otherwise, perform the merge operation like normal
else
merge_check_key(info, name, val)
end
}
return info
end
end

View File

@ -0,0 +1,28 @@
module Msf::Module::ModuleStore
#
# Attributes
#
#
# A generic hash used for passing additional information to modules
#
attr_accessor :module_store
#
# Instance Methods
#
#
# Read a value from the module store
#
def [](k)
self.module_store[k]
end
#
# Store a value into the module
#
def []=(k,v)
self.module_store[k] = v
end
end

View File

@ -0,0 +1,32 @@
module Msf::Module::Network
#
# The default communication subsystem for this module. We may need to move
# this somewhere else.
#
def comm
Rex::Socket::Comm::Local
end
#
# Indicates whether the module supports IPv6. This is true by default,
# but certain modules require additional work to be compatible or are
# hardcoded in terms of application support and should be skipped.
#
def support_ipv6?
true
end
#
# Returns the address of the last target host (rough estimate)
#
def target_host
self.respond_to?('rhost') ? rhost : self.datastore['RHOST']
end
#
# Returns the address of the last target port (rough estimate)
#
def target_port
self.respond_to?('rport') ? rport : self.datastore['RPORT']
end
end

View File

@ -0,0 +1,65 @@
# Register, deregister, and validate {#options}.
module Msf::Module::Options
#
# Attributes
#
# @attribute [r] options
# The module-specific options.
attr_reader :options
#
# Instance Methods
#
#
# 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
protected
#
# Removes the supplied options from the module's option container
# and data store.
#
def deregister_options(*names)
names.each { |name|
self.options.remove_option(name)
self.datastore.delete(name)
}
end
attr_writer :options
#
# Register advanced options with a specific owning class.
#
def register_advanced_options(options, owner = self.class)
self.options.add_advanced_options(options, owner)
self.datastore.import_options(self.options, 'self', true)
import_defaults(false)
end
#
# 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)
import_defaults(false)
end
#
# Register options with a specific owning class.
#
def register_options(options, owner = self.class)
self.options.add_options(options, owner)
self.datastore.import_options(self.options, 'self', true)
import_defaults(false)
end
end

View File

@ -0,0 +1,29 @@
module Msf::Module::Privileged
#
# Attributes
#
# @!attribute [r] privileged
# Whether or not this module requires privileged access.
attr_reader :privileged
#
# Instance Methods
#
#
# Returns whether or not the module requires or grants high privileges.
#
def privileged?
privileged == true
end
protected
#
# Attributes
#
# @!attribute [w] privileged
attr_writer :priveli
end

View File

@ -0,0 +1,51 @@
module Msf::Module::Ranking
extend ActiveSupport::Concern
module ClassMethods
#
# Returns this module's ranking.
#
def rank
(const_defined?('Rank')) ? const_get('Rank') : Msf::NormalRanking
end
#
# Returns this module's ranking as a string for display.
#
def rank_to_h
rank_to_s.gsub('Rank', '').downcase
end
#
# Returns this module's ranking as a string representation.
#
def rank_to_s
Msf::RankingName[rank]
end
end
#
# Instance Methods
#
#
# Returns the module's rank.
#
def rank
self.class.rank
end
#
# Returns the module's rank in display format.
#
def rank_to_h
self.class.rank_to_h
end
#
# Returns the module's rank in string format.
#
def rank_to_s
self.class.rank_to_s
end
end

View File

@ -0,0 +1,109 @@
module Msf::Module::Search
#
# This provides a standard set of search filters for every module.
# The search terms are in the form of:
# {
# "text" => [ [ "include_term1", "include_term2", ...], [ "exclude_term1", "exclude_term2"], ... ],
# "cve" => [ [ "include_term1", "include_term2", ...], [ "exclude_term1", "exclude_term2"], ... ]
# }
#
# Returns true on no match, false on match
#
def search_filter(search_string)
return false if not search_string
search_string += " "
# Split search terms by space, but allow quoted strings
terms = search_string.split(/\"/).collect{|t| t.strip==t ? t : t.split(' ')}.flatten
terms.delete('')
# All terms are either included or excluded
res = {}
terms.each do |t|
f,v = t.split(":", 2)
if not v
v = f
f = 'text'
end
next if v.length == 0
f.downcase!
v.downcase!
res[f] ||=[ [], [] ]
if v[0,1] == "-"
next if v.length == 1
res[f][1] << v[1,v.length-1]
else
res[f][0] << v
end
end
k = res
refs = self.references.map{|x| [x.ctx_id, x.ctx_val].join("-") }
is_server = (self.respond_to?(:stance) and self.stance == "aggressive")
is_client = (self.respond_to?(:stance) and self.stance == "passive")
[0,1].each do |mode|
match = false
k.keys.each do |t|
next if k[t][mode].length == 0
k[t][mode].each do |w|
# Reset the match flag for each keyword for inclusive search
match = false if mode == 0
# Convert into a case-insensitive regex
r = Regexp.new(Regexp.escape(w), true)
case t
when 'text'
terms = [self.name, self.fullname, self.description] + refs + self.author.map{|x| x.to_s}
if self.respond_to?(:targets) and self.targets
terms = terms + self.targets.map{|x| x.name}
end
match = [t,w] if terms.any? { |x| x =~ r }
when 'name'
match = [t,w] if self.name =~ r
when 'path'
match = [t,w] if self.fullname =~ r
when 'author'
match = [t,w] if self.author.map{|x| x.to_s}.any? { |a| a =~ r }
when 'os', 'platform'
match = [t,w] if self.platform_to_s =~ r or self.arch_to_s =~ r
if not match and self.respond_to?(:targets) and self.targets
match = [t,w] if self.targets.map{|x| x.name}.any? { |t| t =~ r }
end
when 'port'
match = [t,w] if self.datastore['RPORT'].to_s =~ r
when 'type'
match = [t,w] if Msf::MODULE_TYPES.any? { |modt| w == modt and self.type == modt }
when 'app'
match = [t,w] if (w == "server" and is_server)
match = [t,w] if (w == "client" and is_client)
when 'cve'
match = [t,w] if refs.any? { |ref| ref =~ /^cve\-/i and ref =~ r }
when 'bid'
match = [t,w] if refs.any? { |ref| ref =~ /^bid\-/i and ref =~ r }
when 'osvdb'
match = [t,w] if refs.any? { |ref| ref =~ /^osvdb\-/i and ref =~ r }
when 'edb'
match = [t,w] if refs.any? { |ref| ref =~ /^edb\-/i and ref =~ r }
end
break if match
end
# Filter this module if no matches for a given keyword type
if mode == 0 and not match
return true
end
end
# Filter this module if we matched an exclusion keyword (-value)
if mode == 1 and match
return true
end
end
false
end
end

View File

@ -0,0 +1,65 @@
module Msf::Module::Type
extend ActiveSupport::Concern
module ClassMethods
#
# Class method to figure out what type of module this is
#
def type
raise NotImplementedError
end
end
#
# Instance Methods
#
#
# Returns true if this module is an auxiliary module.
#
def auxiliary?
(type == Msf::MODULE_AUX)
end
#
# Returns true if this module is an encoder module.
#
def encoder?
(type == Msf::MODULE_ENCODER)
end
#
# Returns true if this module is an exploit module.
#
def exploit?
(type == Msf::MODULE_EXPLOIT)
end
#
# Returns true if this module is a nop module.
#
def nop?
(type == Msf::MODULE_NOP)
end
#
# Returns true if this module is a payload module.
#
def payload?
(type == Msf::MODULE_PAYLOAD)
end
#
# Returns true if this module is an post-exploitation module.
#
def post?
(type == Msf::MODULE_POST)
end
#
# Return the module's abstract type.
#
def type
raise NotImplementedError
end
end

16
lib/msf/core/module/ui.rb Normal file
View File

@ -0,0 +1,16 @@
module Msf::Module::UI
autoload :Line, 'msf/core/module/ui/line'
autoload :Message, 'msf/core/module/ui/message'
# 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
# Overwrite the {Rex::UI::Subscriber#print_line} to do custom prefixes
include Msf::Module::UI::Line
# Overwrite the {Rex::Ui::Subscriber} print_(status|error|good) to do time stamps
include Msf::Module::UI::Message
end

View File

@ -0,0 +1,13 @@
module Msf::Module::UI::Line
autoload :Verbose, 'msf/core/module/ui/line/verbose'
include Msf::Module::UI::Line::Verbose
def print_line(msg='')
super(print_line_prefix + msg)
end
def print_line_prefix
datastore['CustomPrintPrefix'] || framework.datastore['CustomPrintPrefix'] || ''
end
end

View File

@ -0,0 +1,6 @@
module Msf::Module::UI::Line::Verbose
# Verbose version of #print_line
def vprint_line(msg)
print_line(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
end
end

View File

@ -0,0 +1,40 @@
# Methods for print messages with status indicators
module Msf::Module::UI::Message
autoload :Verbose, 'msf/core/module/ui/message/verbose'
include Msf::Module::UI::Message::Verbose
def print_error(msg='')
super(print_prefix + msg)
end
def print_good(msg='')
super(print_prefix + msg)
end
def print_prefix
ret = ''
if (datastore['TimestampOutput'] =~ /^(t|y|1)/i) || (
framework && framework.datastore['TimestampOutput'] =~ /^(t|y|1)/i
)
prefix = "[#{Time.now.strftime("%Y.%m.%d-%H:%M:%S")}] "
xn ||= datastore['ExploitNumber']
xn ||= framework.datastore['ExploitNumber']
if xn.is_a?(Fixnum)
prefix << "[%04d] " % xn
end
ret = prefix
end
ret
end
def print_status(msg='')
super(print_prefix + msg)
end
def print_warning(msg='')
super(print_prefix + msg)
end
end

View File

@ -0,0 +1,26 @@
module Msf::Module::UI::Message::Verbose
# Verbose version of #print_debug
def vprint_debug(msg)
print_debug(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
end
# Verbose version of #print_error
def vprint_error(msg)
print_error(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
end
# Verbose version of #print_good
def vprint_good(msg)
print_good(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
end
# Verbose version of #print_status
def vprint_status(msg)
print_status(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
end
# Verbose version of #print_warning
def vprint_warning(msg)
print_warning(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
end
end

View File

@ -0,0 +1,27 @@
module Msf::Module::UUID
#
# Attributes
#
# @!attribute [r] uuid
# A unique identifier for this module instance
attr_reader :uuid
protected
#
# Attributes
#
# @!attribute [w] uuid
attr_writer :uuid
#
# Instance Methods
#
def generate_uuid
self.uuid = Rex::Text.rand_text_alphanumeric(8).downcase
end
end

View File

@ -14,14 +14,14 @@ class Nop < Msf::Module
# Returns MODULE_NOP to indicate that this is a NOP module.
#
def self.type
return MODULE_NOP
return Msf::MODULE_NOP
end
#
# Returns MODULE_NOP to indicate that this is a NOP module.
#
def type
return MODULE_NOP
return Msf::MODULE_NOP
end
#

View File

@ -102,14 +102,14 @@ class Payload < Msf::Module
# Returns MODULE_PAYLOAD to indicate that this is a payload module.
#
def self.type
return MODULE_PAYLOAD
return Msf::MODULE_PAYLOAD
end
#
# Returns MODULE_PAYLOAD to indicate that this is a payload module.
#
def type
return MODULE_PAYLOAD
return Msf::MODULE_PAYLOAD
end
#

View File

@ -21,7 +21,7 @@ class PayloadSet < ModuleSet
# set class that has custom handling for payloads.
#
def initialize
super(MODULE_PAYLOAD)
super(Msf::MODULE_PAYLOAD)
# A hash of each of the payload types that holds an array
# for all of the associated modules

1
lib/msf/core/platform.rb Normal file
View File

@ -0,0 +1 @@
Msf::Platform = Msf::Module::Platform

View File

@ -0,0 +1 @@
Msf::Reference = Msf::Module::Reference

View File

@ -0,0 +1 @@
Msf::SiteReference = Msf::Module::SiteReference

1
lib/msf/core/target.rb Normal file
View File

@ -0,0 +1 @@
Msf::Target = Msf::Module::Target

View File

@ -2355,17 +2355,17 @@ class Core
dispatcher = nil
case mod.type
when MODULE_ENCODER
when Msf::MODULE_ENCODER
dispatcher = Msf::Ui::Console::CommandDispatcher::Encoder
when MODULE_EXPLOIT
when Msf::MODULE_EXPLOIT
dispatcher = Msf::Ui::Console::CommandDispatcher::Exploit
when MODULE_NOP
when Msf::MODULE_NOP
dispatcher = Msf::Ui::Console::CommandDispatcher::Nop
when MODULE_PAYLOAD
when Msf::MODULE_PAYLOAD
dispatcher = Msf::Ui::Console::CommandDispatcher::Payload
when MODULE_AUX
when Msf::MODULE_AUX
dispatcher = Msf::Ui::Console::CommandDispatcher::Auxiliary
when MODULE_POST
when Msf::MODULE_POST
dispatcher = Msf::Ui::Console::CommandDispatcher::Post
else
print_error("Unsupported module type: #{mod.type}")

View File

@ -1,7 +1,28 @@
require 'spec_helper'
describe Msf::Author do
it 'is an alias for Msf::Module::Author' do
expect(described_class.name).to eq('Msf::Module::Author')
context 'Known' do
subject(:known) {
described_class::Known
}
it { is_expected.to be_a Hash }
end
it { is_expected.to respond_to :== }
it { is_expected.to respond_to :email }
it { is_expected.to respond_to :email= }
it { is_expected.to respond_to :from_s }
it { is_expected.to respond_to :name }
it { is_expected.to respond_to :name= }
it { is_expected.to respond_to :to_s }
context 'class' do
subject {
described_class
}
it { is_expected.to respond_to :from_s }
it { is_expected.to respond_to :transform }
end
end

View File

@ -1,141 +1,41 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'msf/core/module'
require 'msf/core/module/platform_list'
shared_examples "search_filter" do |opts|
accept = opts[:accept] || []
reject = opts[:reject] || []
accept.each do |query|
it "should accept a query containing '#{query}'" do
# if the subject matches, search_filter returns false ("don't filter me out!")
subject.search_filter(query).should be_falsey
end
unless opts.has_key?(:test_inverse) and not opts[:test_inverse]
it "should reject a query containing '-#{query}'" do
subject.search_filter("-#{query}").should be_truthy
end
end
end
reject.each do |query|
it "should reject a query containing '#{query}'" do
# if the subject doesn't matches, search_filter returns true ("filter me out!")
subject.search_filter(query).should be_truthy
end
unless opts.has_key?(:test_inverse) and not opts[:test_inverse]
it "should accept a query containing '-#{query}'" do
subject.search_filter("-#{query}").should be_truthy # what? why?
end
end
end
end
REF_TYPES = %w(CVE BID OSVDB EDB)
describe Msf::Module do
subject(:msf_module) {
described_class.new
}
it { is_expected.to respond_to :[] }
it { is_expected.to respond_to :[]= }
it { is_expected.to respond_to :alias }
it { is_expected.to respond_to :arch? }
it { is_expected.to respond_to :arch_to_s }
it { is_expected.to respond_to :author_to_s }
it { is_expected.to respond_to :auxiliary? }
it { is_expected.to respond_to :check }
it { is_expected.to respond_to :comm }
it { is_expected.to respond_to :compat }
it { is_expected.to respond_to :compatible? }
it { is_expected.to respond_to :debugging? }
it { is_expected.to respond_to :description }
it { is_expected.to respond_to :disclosure_date }
it { is_expected.to respond_to :each_arch }
it { is_expected.to respond_to :each_author }
it { is_expected.to respond_to :encoder? }
it { is_expected.to respond_to :exploit? }
it { is_expected.to respond_to_protected :derived_implementor? }
it { is_expected.to respond_to :fail_with }
it { is_expected.to respond_to :file_path }
it { is_expected.to respond_to :framework }
it { is_expected.to respond_to :fullname }
it { is_expected.to respond_to :import_defaults }
it { is_expected.to respond_to :name }
it { is_expected.to respond_to :nop? }
it { is_expected.to respond_to :orig_cls }
it { is_expected.to respond_to :owner }
it { is_expected.to respond_to :payload? }
it { is_expected.to respond_to :platform? }
it { is_expected.to respond_to :platform_to_s }
it { is_expected.to respond_to :post? }
it { is_expected.to respond_to :print_error }
it { is_expected.to respond_to :print_good }
it { is_expected.to respond_to :print_line }
it { is_expected.to respond_to :print_line_prefix }
it { is_expected.to respond_to :print_prefix }
it { is_expected.to respond_to :print_status }
it { is_expected.to respond_to :print_warning }
it { is_expected.to respond_to :privileged? }
it { is_expected.to respond_to :rank }
it { is_expected.to respond_to :rank_to_h }
it { is_expected.to respond_to :rank_to_s }
it { is_expected.to respond_to :refname }
it { is_expected.to respond_to :register_parent }
it { is_expected.to respond_to :replicant }
it { is_expected.to respond_to :share_datastore }
it { is_expected.to respond_to :shortname }
it { is_expected.to respond_to :support_ipv6? }
it { is_expected.to respond_to :target_host }
it { is_expected.to respond_to :target_port }
it { is_expected.to respond_to :type }
it { is_expected.to respond_to :validate }
it { is_expected.to respond_to :vprint_debug }
it { is_expected.to respond_to :vprint_error }
it { is_expected.to respond_to :vprint_good }
it { is_expected.to respond_to :vprint_line }
it { is_expected.to respond_to :vprint_status }
it { is_expected.to respond_to :vprint_warning }
it { is_expected.to respond_to_protected :set_defaults }
it { is_expected.to respond_to :workspace }
[
:deregister_options,
:derived_implementor?,
:generate_uuid,
:info_fixups,
:init_compat,
:merge_check_key,
:merge_info,
:merge_info_advanced_options,
:merge_info_alias,
:merge_info_description,
:merge_info_evasion_options,
:merge_info_name,
:merge_info_options,
:merge_info_string,
:merge_info_version,
:register_advanced_options,
:register_evasion_options,
:register_options,
:set_defaults,
:update_info,
].each do |sym|
it "should respond to protected method #{sym}" do
expect { subject.respond_to?(sym, true) }.to be_truthy
end
end
context 'CONSTANTS' do
context 'UpdateableOptions' do
subject(:updateable_options) {
described_class::UpdateableOptions
}
it { is_expected.to match_array(%w{Name Description Alias PayloadCompat})}
end
end
it_should_behave_like 'Msf::Module::Arch'
it_should_behave_like 'Msf::Module::Compatibility'
it_should_behave_like 'Msf::Module::DataStore'
it_should_behave_like 'Msf::Module::FullName'
it_should_behave_like 'Msf::Module::ModuleInfo'
it_should_behave_like 'Msf::Module::ModuleStore'
it_should_behave_like 'Msf::Module::Network'
it_should_behave_like 'Msf::Module::Options'
it_should_behave_like 'Msf::Module::Privileged'
it_should_behave_like 'Msf::Module::Ranking'
it_should_behave_like 'Msf::Module::Search'
it_should_behave_like 'Msf::Module::Type'
it_should_behave_like 'Msf::Module::UI'
it_should_behave_like 'Msf::Module::UUID'
context 'class' do
subject {
@ -143,146 +43,6 @@ describe Msf::Module do
}
it { is_expected.to respond_to :cached? }
it { is_expected.to respond_to :fullname }
it { is_expected.to respond_to :is_usable }
it { is_expected.to respond_to :rank }
it { is_expected.to respond_to :rank_to_h }
it { is_expected.to respond_to :rank_to_s }
it { is_expected.to respond_to :shortname }
it { is_expected.to respond_to :type }
end
describe '#search_filter' do
let(:opts) { Hash.new }
before { subject.stub(:fullname => '/module') }
subject { Msf::Module.new(opts) }
accept = []
reject = []
context 'on a blank query' do
it_should_behave_like 'search_filter', :accept => [''], :test_inverse => false
end
context 'on a client module' do
before { subject.stub(:stance => 'passive') }
accept = %w(app:client)
reject = %w(app:server)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a server module' do
before { subject.stub(:stance => 'aggressive') }
accept = %w(app:server)
reject = %w(app:client)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with the author "joev"' do
let(:opts) { ({ 'Author' => ['joev'] }) }
accept = %w(author:joev author:joe)
reject = %w(author:unrelated)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with the authors "joev" and "blarg"' do
let(:opts) { ({ 'Author' => ['joev', 'blarg'] }) }
accept = %w(author:joev author:joe)
reject = %w(author:sinn3r)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the osx platform' do
let(:opts) { ({ 'Platform' => %w(osx) }) }
accept = %w(platform:osx os:osx)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the linux platform' do
let(:opts) { ({ 'Platform' => %w(linux) }) }
accept = %w(platform:linux os:linux)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the windows platform' do
let(:opts) { ({ 'Platform' => %w(windows) }) }
accept = %w(platform:windows os:windows)
reject = %w(platform:bsd platform:osx platform:unix os:bsd os:osx os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the osx and linux platforms' do
let(:opts) { ({ 'Platform' => %w(osx linux) }) }
accept = %w(platform:osx platform:linux os:osx os:linux)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the windows and irix platforms' do
let(:opts) { ({ 'Platform' => %w(windows irix) }) }
accept = %w(platform:windows platform:irix os:windows os:irix)
reject = %w(platform:bsd platform:osx platform:linux os:bsd os:osx os:linux)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with a default RPORT of 5555' do
before { subject.stub(:datastore => { 'RPORT' => 5555 }) }
accept = %w(port:5555)
reject = %w(port:5556)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with a #name of "blah"' do
let(:opts) { ({ 'Name' => 'blah' }) }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
it_should_behave_like 'search_filter', :accept => %w(name:blah), :reject => %w(name:foo)
end
context 'on a module with a #fullname of "blah"' do
before { subject.stub(:fullname => '/c/d/e/blah') }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
it_should_behave_like 'search_filter', :accept => %w(path:blah), :reject => %w(path:foo)
end
context 'on a module with a #description of "blah"' do
let(:opts) { ({ 'Description' => 'blah' }) }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
end
context 'when filtering by module #type' do
all_module_types = Msf::MODULE_TYPES
all_module_types.each do |mtype|
context "on a #{mtype} module" do
before(:each) { subject.stub(:type => mtype) }
accept = ["type:#{mtype}"]
reject = all_module_types.reject { |t| t == mtype }.map { |t| "type:#{t}" }
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
end
end
REF_TYPES.each do |ref_type|
ref_num = '1234-1111'
context 'on a module with reference #{ref_type}-#{ref_num}' do
let(:opts) { ({ 'References' => [[ref_type, ref_num]] }) }
accept = ["#{ref_type.downcase}:#{ref_num}"]
reject = %w(1235-1111 1234-1112 bad).map { |n| "#{ref_type.downcase}:#{n}" }
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
end
end
end

View File

@ -0,0 +1,7 @@
RSpec::Matchers.define :respond_to_protected do |method_name|
protected_and_private = true
match do |receiver|
receiver.respond_to?(method_name, protected_and_private)
end
end

View File

@ -0,0 +1,6 @@
shared_examples_for 'Msf::Module::Arch' do
it { is_expected.to respond_to :arch }
it { is_expected.to respond_to :arch? }
it { is_expected.to respond_to :arch_to_s }
it { is_expected.to respond_to :each_arch }
end

View File

@ -0,0 +1,5 @@
shared_examples_for 'Msf::Module::Author' do
it { is_expected.to respond_to :author }
it { is_expected.to respond_to :author_to_s }
it { is_expected.to respond_to :each_author }
end

View File

@ -0,0 +1,5 @@
shared_examples_for 'Msf::Module::Compatibility' do
it { is_expected.to respond_to :compat }
it { is_expected.to respond_to :compatible? }
it { is_expected.to respond_to_protected :init_compat }
end

View File

@ -0,0 +1,5 @@
shared_examples_for 'Msf::Module::DataStore' do
it { is_expected.to respond_to :datastore }
it { is_expected.to respond_to :import_defaults }
it { is_expected.to respond_to :share_datastore }
end

View File

@ -0,0 +1,16 @@
shared_examples_for 'Msf::Module::FullName' do
it { is_expected.to respond_to :fullname }
it { is_expected.to respond_to :refname }
it { is_expected.to respond_to :shortname }
context 'class' do
subject {
described_class
}
it { is_expected.to respond_to :fullname }
it { is_expected.to respond_to :refname }
it { is_expected.to respond_to :refname= }
it { is_expected.to respond_to :shortname }
end
end

View File

@ -0,0 +1,28 @@
shared_examples_for 'Msf::Module::ModuleInfo' do
context 'CONSTANTS' do
context 'UpdateableOptions' do
subject(:updateable_options) {
described_class::UpdateableOptions
}
it { is_expected.to match_array(%w{Name Description Alias PayloadCompat})}
end
end
it { is_expected.to respond_to :alias }
it { is_expected.to respond_to :description }
it { is_expected.to respond_to :disclosure_date }
it { is_expected.to respond_to_protected :info_fixups }
it { is_expected.to respond_to_protected :merge_check_key }
it { is_expected.to respond_to_protected :merge_info }
it { is_expected.to respond_to_protected :merge_info_advanced_options }
it { is_expected.to respond_to_protected :merge_info_alias }
it { is_expected.to respond_to_protected :merge_info_description }
it { is_expected.to respond_to_protected :merge_info_evasion_options }
it { is_expected.to respond_to_protected :merge_info_name }
it { is_expected.to respond_to_protected :merge_info_options }
it { is_expected.to respond_to_protected :merge_info_string }
it { is_expected.to respond_to_protected :merge_info_version }
it { is_expected.to respond_to :name }
it { is_expected.to respond_to_protected :update_info }
end

View File

@ -0,0 +1,6 @@
shared_examples_for 'Msf::Module::ModuleStore' do
it { is_expected.to respond_to :[] }
it { is_expected.to respond_to :[]= }
it { is_expected.to respond_to :module_store }
it { is_expected.to respond_to :module_store= }
end

View File

@ -0,0 +1,6 @@
shared_examples_for 'Msf::Module::Network' do
it { is_expected.to respond_to :comm }
it { is_expected.to respond_to :support_ipv6? }
it { is_expected.to respond_to :target_host }
it { is_expected.to respond_to :target_port }
end

View File

@ -0,0 +1,8 @@
shared_examples_for 'Msf::Module::Options' do
it { is_expected.to respond_to_protected :deregister_options }
it { is_expected.to respond_to :options }
it { is_expected.to respond_to :validate }
it { is_expected.to respond_to_protected :register_advanced_options }
it { is_expected.to respond_to_protected :register_evasion_options }
it { is_expected.to respond_to_protected :register_options }
end

View File

@ -0,0 +1,4 @@
shared_examples_for 'Msf::Module::Privileged' do
it { is_expected.to respond_to :privileged }
it { is_expected.to respond_to :privileged? }
end

View File

@ -0,0 +1,15 @@
shared_examples_for 'Msf::Module::Ranking' do
it { is_expected.to respond_to :rank }
it { is_expected.to respond_to :rank_to_h }
it { is_expected.to respond_to :rank_to_s }
context 'class' do
subject {
described_class
}
it { is_expected.to respond_to :rank }
it { is_expected.to respond_to :rank_to_h }
it { is_expected.to respond_to :rank_to_s }
end
end

View File

@ -0,0 +1,168 @@
shared_examples_for 'Msf::Module::Search' do
describe '#search_filter' do
REF_TYPES = %w(CVE BID OSVDB EDB)
shared_examples "search_filter" do |opts|
accept = opts[:accept] || []
reject = opts[:reject] || []
accept.each do |query|
it "should accept a query containing '#{query}'" do
# if the subject matches, search_filter returns false ("don't filter me out!")
subject.search_filter(query).should be_falsey
end
unless opts.has_key?(:test_inverse) and not opts[:test_inverse]
it "should reject a query containing '-#{query}'" do
subject.search_filter("-#{query}").should be_truthy
end
end
end
reject.each do |query|
it "should reject a query containing '#{query}'" do
# if the subject doesn't matches, search_filter returns true ("filter me out!")
subject.search_filter(query).should be_truthy
end
unless opts.has_key?(:test_inverse) and not opts[:test_inverse]
it "should accept a query containing '-#{query}'" do
subject.search_filter("-#{query}").should be_truthy # what? why?
end
end
end
end
let(:opts) { Hash.new }
before { subject.stub(:fullname => '/module') }
subject { Msf::Module.new(opts) }
accept = []
reject = []
context 'on a blank query' do
it_should_behave_like 'search_filter', :accept => [''], :test_inverse => false
end
context 'on a client module' do
before { subject.stub(:stance => 'passive') }
accept = %w(app:client)
reject = %w(app:server)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a server module' do
before { subject.stub(:stance => 'aggressive') }
accept = %w(app:server)
reject = %w(app:client)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with the author "joev"' do
let(:opts) { ({ 'Author' => ['joev'] }) }
accept = %w(author:joev author:joe)
reject = %w(author:unrelated)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with the authors "joev" and "blarg"' do
let(:opts) { ({ 'Author' => ['joev', 'blarg'] }) }
accept = %w(author:joev author:joe)
reject = %w(author:sinn3r)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the osx platform' do
let(:opts) { ({ 'Platform' => %w(osx) }) }
accept = %w(platform:osx os:osx)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the linux platform' do
let(:opts) { ({ 'Platform' => %w(linux) }) }
accept = %w(platform:linux os:linux)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the windows platform' do
let(:opts) { ({ 'Platform' => %w(windows) }) }
accept = %w(platform:windows os:windows)
reject = %w(platform:bsd platform:osx platform:unix os:bsd os:osx os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the osx and linux platforms' do
let(:opts) { ({ 'Platform' => %w(osx linux) }) }
accept = %w(platform:osx platform:linux os:osx os:linux)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the windows and irix platforms' do
let(:opts) { ({ 'Platform' => %w(windows irix) }) }
accept = %w(platform:windows platform:irix os:windows os:irix)
reject = %w(platform:bsd platform:osx platform:linux os:bsd os:osx os:linux)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with a default RPORT of 5555' do
before { subject.stub(:datastore => { 'RPORT' => 5555 }) }
accept = %w(port:5555)
reject = %w(port:5556)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with a #name of "blah"' do
let(:opts) { ({ 'Name' => 'blah' }) }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
it_should_behave_like 'search_filter', :accept => %w(name:blah), :reject => %w(name:foo)
end
context 'on a module with a #fullname of "blah"' do
before { subject.stub(:fullname => '/c/d/e/blah') }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
it_should_behave_like 'search_filter', :accept => %w(path:blah), :reject => %w(path:foo)
end
context 'on a module with a #description of "blah"' do
let(:opts) { ({ 'Description' => 'blah' }) }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
end
context 'when filtering by module #type' do
all_module_types = Msf::MODULE_TYPES
all_module_types.each do |mtype|
context "on a #{mtype} module" do
before(:each) { subject.stub(:type => mtype) }
accept = ["type:#{mtype}"]
reject = all_module_types.reject { |t| t == mtype }.map { |t| "type:#{t}" }
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
end
end
REF_TYPES.each do |ref_type|
ref_num = '1234-1111'
context 'on a module with reference #{ref_type}-#{ref_num}' do
let(:opts) { ({ 'References' => [[ref_type, ref_num]] }) }
accept = ["#{ref_type.downcase}:#{ref_num}"]
reject = %w(1235-1111 1234-1112 bad).map { |n| "#{ref_type.downcase}:#{n}" }
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
end
end
end

View File

@ -0,0 +1,17 @@
shared_examples_for 'Msf::Module::Type' do
it { is_expected.to respond_to :auxiliary? }
it { is_expected.to respond_to :encoder? }
it { is_expected.to respond_to :exploit? }
it { is_expected.to respond_to :nop? }
it { is_expected.to respond_to :payload? }
it { is_expected.to respond_to :post? }
it { is_expected.to respond_to :type }
context 'class' do
subject {
described_class
}
it { is_expected.to respond_to :type }
end
end

View File

@ -0,0 +1,4 @@
shared_examples_for 'Msf::Module::UI' do
it_should_behave_like 'Msf::Module::UI::Line'
it_should_behave_like 'Msf::Module::UI::Message'
end

View File

@ -0,0 +1,6 @@
shared_examples_for 'Msf::Module::UI::Line' do
it_should_behave_like 'Msf::Module::UI::Line::Verbose'
it { is_expected.to respond_to :print_line }
it { is_expected.to respond_to :print_line_prefix }
end

View File

@ -0,0 +1,3 @@
shared_examples_for 'Msf::Module::UI::Line::Verbose' do
it { is_expected.to respond_to :vprint_line }
end

View File

@ -0,0 +1,9 @@
shared_examples_for 'Msf::Module::UI::Message' do
it_should_behave_like 'Msf::Module::UI::Message::Verbose'
it { is_expected.to respond_to :print_error }
it { is_expected.to respond_to :print_good }
it { is_expected.to respond_to :print_prefix }
it { is_expected.to respond_to :print_status }
it { is_expected.to respond_to :print_warning }
end

View File

@ -0,0 +1,7 @@
shared_examples_for 'Msf::Module::UI::Message::Verbose' do
it { is_expected.to respond_to :vprint_debug }
it { is_expected.to respond_to :vprint_error }
it { is_expected.to respond_to :vprint_good }
it { is_expected.to respond_to :vprint_status }
it { is_expected.to respond_to :vprint_warning }
end

View File

@ -0,0 +1,3 @@
shared_examples_for 'Msf::Module::UUID' do
it { is_expected.to respond_to :uuid }
end