moved shit around again

git-svn-id: file:///home/svn/incoming/trunk@2498 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
Matt Miller
2005-05-21 17:57:00 +00:00
parent cd01083c14
commit 8a192dae88
26 changed files with 2058 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
###
#
# framework-shared
# ----------------
#
# The shared library in the framework contains classes that are
# used by various framework subsystems.
#
###
# Shared single purpose classes
require 'Shared/ReadWriteLock'
require 'Shared/Transformer'
# Logging facilities
require 'Shared/Logging/LogSink'
require 'Shared/Logging/LogDispatcher'
+35
View File
@@ -0,0 +1,35 @@
###
#
# framework-core
# --------------
#
# The core library provides all of the means by which to interact
# with the framework insofar as maniuplating encoders, nops,
# payloads, exploits, recon, and sessions.
#
###
# Unit testing
require 'test/unit'
require 'Core/UnitTestSuite'
# framework-core depends on framework-shared
require 'Shared'
# General
require 'Core/Constants'
require 'Core/Exceptions'
require 'Core/DataTypes'
require 'Core/EventDispatcher'
require 'Core/DataStore'
# Framework context and core classes
require 'Core/Framework'
require 'Core/Session'
# Modules
require 'Core/Module'
require 'Core/Encoder'
require 'Core/Exploit'
require 'Core/Nop'
require 'Core/Recon'
+177
View File
@@ -0,0 +1,177 @@
module Msf
###
#
# Author
# ------
#
# This data type represents an author of a piece of code in either
# the framework, a module, a script, or something entirely unrelated.
#
###
class 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
def Author.from_s(str)
instance = Author.new
# If the serialization fails...
if (instance.from_s(str) == false)
return nil
end
return instance
end
def initialize(name = nil, email = nil)
self.name = name
self.email = email
end
# Serialize the author object to a string in form:
#
# name <email>
def to_s
str = "#{name}"
if (email != nil)
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)
# List of known framework authors that can be referred by just name
known_authors =
{
'H D Moore' => 'hdm@metasploit.com',
'spoonm' => 'spoonm@gmail.com',
'skape' => 'mmiller@hick.org',
'vlad902' => 'vlad902@gmail.com'
}
# Make fix up this regex to be a bit better...I suck at regex
m = /^([A-Za-z0-9 _]*?) <(.*?)>/.match(str)
if (m != nil)
self.name = m[1]
self.email = m[2]
else
self.email = known_authors[str]
if (self.email != nil)
self.name = str
else
return false
end
end
return true
end
attr_accessor :name, :email
end
###
#
# Reference
# ---------
#
# A reference to some sort of information.
#
###
class Reference
def Reference.from_s(str)
return Reference.new(str)
end
def initialize(in_str)
self.str = in_str
end
def to_s
return self.str
end
def from_s(in_str)
self.str = in_str
end
attr_reader :str
protected
attr_writer :str
end
###
#
# SiteReference
# -------------
#
# A reference to a website.
#
###
class SiteReference < Reference
# Class method that translates a URL into a site reference instance
def SiteReference.from_s(str)
instance = SiteReference.new
if (instance.from_s(str) == false)
return nil
end
return instance
end
# Initialize the site reference
def initialize(in_site = nil, in_ctx_id = nil)
self.ctx_id = in_ctx_id
if (in_site == 'OSVDB')
self.site = 'http://www.osvdb.org/' + in_ctx_id.to_s
elsif (in_site == 'CVE')
self.site = 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=' + in_ctx_id.to_s
elsif (in_site == 'BID')
self.site = 'http://www.securityfocus.com/bid/' + in_ctx_id.to_s
elsif (in_site == 'MSB')
self.site = 'http://www.microsoft.com/technet/security/bulletin/' + in_ctx_id.to_s + '.mspx'
else
self.site = in_site
end
end
# Returns the absolute site URL
def to_s
return site || ''
end
# Serializes a site URL string
def from_s(str)
if (/(http:\/\/|https:\/\/|ftp:\/\/)/.match(str))
self.site = str
else
return false
end
return true
end
attr_reader :site, :ctx_id
protected
attr_writer :site, :ctx_id
end
end
+88
View File
@@ -0,0 +1,88 @@
module Msf
class ModuleLoader
attr_accessor :ext, :base, :namespace, :recursive, :history
def initialize(namespace, base, opts = { })
# merge in the defaults
opts = {
'ext' => '.rb',
'recursive' => true
}.update(opts)
self.ext = opts['ext']
self.base = base
self.namespace = namespace
self.recursive = opts['recursive']
self.history = { }
end
def mod_from_name(name)
obj = Object
name.split('::').each { |m|
obj = obj.const_get(m)
}
return obj
end
def error(msg)
puts '[!] ' + msg
end
def clear_history
self.history = { }
end
def modload()
loaded = { }
mod = mod_from_name(namespace)
# build the glob to search on
glob = base
glob += '/**' if(recursive)
glob += '/*' + ext
Dir[glob].each { |file|
modold = mod.constants
begin
if !load(file)
error('Load failed for ' + file)
next
end
rescue LoadError
error('LoadError: ' + $!)
next
end
added = mod.constants - modold
if added.length > 1
error('More than one class added in ' + file)
next
end
if added.empty?
if history[file]
added = history[file]
else
error('Loaded ' + file + ' but no class added')
next
end
else
added = mod.const_get(added[0])
end
loaded[file] = added
}
self.history.update(loaded)
return loaded.values
end
end end # ModuleLoader/Msf
+26
View File
@@ -0,0 +1,26 @@
require 'Core'
module Msf
module Test
###
#
# FrameworkCoreTestSuite
# ----------------------
#
# This test suite is used to test the various core components of
# framework-core.
#
###
class FrameworkCoreTestSuite
def self.suite
suite = ::Test::Unit::TestSuite.new
suite << Msf::Test::DataStoreTestCase.suite
return suite;
end
end
end
end
+387
View File
@@ -0,0 +1,387 @@
require 'resolv'
require 'Core'
module Msf
###
#
# DataStoreOption
# ---------------
#
# A data store option is an option that is stored in a data store! It contains
# meta information about the type of option being stored, such as type, as
# well as the option's actual value
#
###
class DataStoreOption
def initialize(in_name, attrs = [])
self.name = in_name
self.advanced = false
self.required = attrs[0] || false
self.desc = attrs[1] || nil
self.default_value = attrs[2] || nil
self.value = self.default_value
end
def empty?
return (value == nil)
end
def required?
return required
end
def advanced?
return advanced
end
def valid?
return (empty? and required?) ? false : true
end
def type?(in_type)
return (type == in_type)
end
def reset
value = default_value
end
attr_reader :name, :required, :desc, :default_value, :value
attr_writer :name, :value
attr_accessor :advanced
protected
attr_writer :required, :desc, :default_value
end
###
#
# Core data store option types. The core supported option types are:
#
# OptString - Multi-byte character string
# OptRaw - Multi-byte raw string
# OptBool - Boolean true or false indication
# OptPort - TCP/UDP service port
# OptAddress - IP address or hostname
# OptPath - Path name on disk
#
###
class OptString < DataStoreOption
def type
return 'string'
end
end
class OptRaw < DataStoreOption
def type
return 'raw'
end
end
class OptBool < DataStoreOption
def type
return 'bool'
end
def valid?
if ((empty? == false) and
(value.match(/^(y|n|t|f|0|1)$/i) == nil))
return false
end
end
def is_true?
return (value.match(/^(y|t|1)$/i) != nil) ? true : false
end
def is_false?
return !is_true?
end
end
class OptPort < DataStoreOption
def type
return 'port'
end
def valid?
if ((empty? == false) and
((value.to_i < 0 or value.to_i > 65535)))
return false
end
return super
end
end
class OptAddress < DataStoreOption
def type
return 'address'
end
def valid?
if (empty? == false)
begin
Resolv.getaddress(value)
rescue
return false
end
end
return super
end
end
class OptPath < DataStoreOption
def type
return 'path'
end
def valid?
if ((empty? == false) and
(File.exists?(value) == false))
return false
end
return super
end
end
###
#
# DataStore
# ---------
#
# The data store's purpose in life is to associate named options
# with arbitrary values at the most simplistic level. Each
# module contains a DataStore that is used to hold the
# various options that the module depends on. Example of options
# that are stored in the DataStore are RHOST and RPORT for
# payloads or exploits that need to connect to a host and
# port, for instance.
#
###
class DataStore < Hash
# Merges in the supplied options and converts them to a DataStoreOption
# as necessary.
def initialize(opts = {})
add_options(opts)
end
# Return the value associated with the supplied name
def [](name)
return get(name)
end
# Set the value associated with the supplied name
def set(name, value)
option = fetch(name)
if (option == nil)
return false
end
option.value = value
return true
end
# Return the option associated with the supplied name
def get(name)
return fetch(name)
end
# Return the value associated with the supplied name
def get_value(name)
return fetch(name).value
end
# Adds one or more options
def add_options(opts)
return false if (opts == nil)
opts.each_key { |name|
option = opts[name]
# Skip flags
next if (name.match(/^_Flag/))
if (option.kind_of?(Array))
option = option.shift.new(name, option)
elsif (!option.kind_of?(DataStoreOption))
raise ArgumentError,
"The option named #{name} did not come in a compatible format.",
caller
end
option.name = name
# If the advanced flag was supplied, flag the new option as being
# an advanced option
if (opts['_FlagAdvanced'] == true)
option.advanced = true
end
self.store(name, option)
}
end
# Alias to add advanced options that sets the proper state flag
def add_advanced_options(opts = {})
opts['_FlagAdvanced'] = true if (opts)
add_options(opts)
end
# Make sures that each of the options has a value of a compatible
# format and that all the required options are set
def validate
errors = []
each_pair { |name, option|
if (!option.valid?)
errors << name
end
}
if (errors.empty? == false)
raise OptionValidateError.new(errors),
"One or more options failed to validate", caller
end
return true
end
# Enumerates each option name
def each_option(&block)
each_pair(&block)
end
end
module Test
###
#
# DataStoreTestCase
# -----------------
#
# This class implements some testing routines for ensuring that the data
# store is operating correctly.
#
###
class DataStoreTestCase < ::Test::Unit::TestCase
# Tests the initialization of the DataStore object
def test_initialize
# Make sure initialization works
ds = nil
assert_block("initialize failed") {
ds = DataStore.new(
'RHOST' => [ OptAddress, true, 'host.com' ],
'RPORT' => [ OptPort, true, 1234 ])
if (ds == nil)
false
end
true
}
# Make sure there are 2 options
assert_equal(2, ds.length, "invalid number of options #{ds.length}")
# Make sure that the constructor raises an argument error when
# an invalid option is supplied
assert_raise(ArgumentError, "initialize invalid failed") {
DataStore.new(
'RHOST' => 'invalid');
}
end
# Tests getting the value of an option
def test_get
ds = DataStore.new(
'RPORT' => [ OptPort, true, nil, 1234 ])
assert_equal(1234, ds.get_value('RPORT'),
"RPORT does not match")
ds.set('RPORT', 1235)
assert_equal(1235, ds.get_value('RPORT'),
"RPORT does not match (2)")
assert_equal('RPORT', ds['RPORT'].name,
"option name does not match")
end
# Tests setting the value of an option
def test_set
assert_block("set failed") {
ds = DataStore.new(
'RHOST' => [ OptAddress ])
ds.set('RHOST', 'host.com')
if (ds.get_value('RHOST') != 'host.com')
false
else
true
end
}
end
# Tests validation
def test_validate
# Test validating required options
ds = DataStore.new(
'RHOST' => [ OptAddress, true ],
'RPORT' => [ OptPort, true ],
'LIB' => [ OptString ])
assert_raise(OptionValidateError, "required validation failed") {
ds.validate
}
# Test validating the form of individual options
ds.set('RHOST', 'www.invalid.host.tldinvalid')
ds.set('RPORT', 1234)
assert_raise(OptionValidateError, "host validation failed") {
ds.validate
}
# Make sure address validation does work
ds.set('RHOST', 'www.google.com')
assert_equal(true, ds.validate, "overall validation failed")
# Make sure port validation does work
ds.set('RPORT', 123452)
assert_raise(OptionValidateError, "port validation failed") {
ds.validate
}
end
# Make sure advanced additions work
def test_advanced
ds = DataStore.new
ds.add_advanced_options(
'DONKEY' => [ OptString, false ])
assert_equal(true, ds.get('DONKEY').advanced?,
"advanced option failed")
end
end
end
end
+26
View File
@@ -0,0 +1,26 @@
###
#
# This file contains constants that are referenced by the core
# framework and by framework modules.
#
###
module Msf
#
# Architecture constants
#
ARCH_IA32 = 'ia32'
ARCH_MIPS = 'mips'
ARCH_PPC = 'ppc'
ARCH_SPARC = 'sparc'
#
# Module types
#
MODULE_ENCODER = 'encoder'
MODULE_EXPLOIT = 'exploit'
MODULE_NOP = 'nop'
MODULE_RECON = 'recon'
end
+14
View File
@@ -0,0 +1,14 @@
module Msf
###
#
# DataStore
# ---------
#
# The data store is just a bitbucket that holds keyed values.
#
###
class DataStore < Hash
end
end
+264
View File
@@ -0,0 +1,264 @@
require 'Core'
module Msf
###
#
# EncoderState
# ------------
#
# This class is used to track the state of a single encoding operation
# from start to finish.
#
###
class EncoderState
def initialize(key = nil)
reset(key)
end
# Reset the encoder state
def reset(key = nil)
init_key(key)
self.encoded = ''
end
# Set the initial encoding key
def init_key(key)
self.key = key
self.orig_key = key
end
attr_accessor :key
attr_accessor :orig_key
attr_accessor :encoded
attr_accessor :context
end
###
#
# Encoder
# -------
#
# This class is the base class that all encoders inherit from.
#
###
class Encoder < Module
def initialize(info)
super(info)
end
#
# Encoder information accessors that can be overriden
# by derived classes
#
def type
return MODULE_ENCODER
end
def decoder_stub
return module_info['DecoderStub']
end
def decoder_key_offset
return module_info['DecoderKeyOffset']
end
def decoder_key_size
return module_info['DecoderKeySize']
end
def decoder_block_size
return module_info['DecoderBlockSize']
end
def decoder_key_pack
return module_info['DecoderKeyPack'] || 'V'
end
#
# Encoding
#
def encode(buf, badchars, state = nil)
# Initialize the encoding state and key as necessary
if (state == nil)
state = EncoderState.new
end
# Prepend data to the buffer as necessary
buf = prepend_buf + buf
# If this encoder is key-based and we don't already have a key, find one
if ((decoder_key_size) and
(state.key == nil))
# Find a key that doesn't contain and wont generate any bad
# characters
state.init_key(find_key(buf, badchars))
if (state.key == nil)
raise NoKeyException, "A key could not be found for the #{self.name} encoder.", caller
end
end
# Call encode_begin to do any encoder specific pre-processing
encode_begin(state)
# Perform the actual encoding operation with the determined state
do_encode(buf, badchars, state)
# Call encoded_end to do any encoder specific post-processing
encode_end(state)
# Return the encoded buffer to the caller
return state.encoded
end
def do_encode(buf, badchars, state)
# Copy the decoder stub since we may need to modify it
stub = decoder_stub.dup
if (state.key != nil)
# Substitute the decoder key in the copy of the decoder stub with the
# one that we found
stub[decoder_key_offset,decoder_key_size] = [ state.key.to_i ].pack(decoder_key_pack)
end
# Walk the buffer encoding each block along the way
offset = 0
while (offset < buf.length)
block = buf[offset, decoder_block_size]
state.encoded += encode_block(state,
block + ("\x00" * (decoder_block_size - block.length)))
offset += decoder_block_size
end
# Prefix the decoder stub to the encoded buffer
state.encoded = stub + state.encoded
# Last but not least, do one last badchar pass to see if the stub +
# encoded payload leads to any bad char issues...
if ((badchar_idx = has_badchars?(state.encoded, badchars)) != nil)
raise BadcharException.new(state.encoded, badchar_idx, stub.length, badchars[badchar_idx]),
"The #{self.name} encoder failed to encode without bad characters.",
caller
end
return true
end
#
# Buffer management
#
def prepend_buf
return ''
end
#
# Pre-processing, post-processing, and block encoding stubs
#
def encode_begin(state)
return nil
end
def encode_end(state)
return nil
end
def encode_block(state, block)
return block
end
protected
def find_key(buf, badchars)
key_bytes = [ ]
cur_key = [ ]
bad_keys = find_bad_keys(buf, badchars)
found = false
# Keep chugging until we find something...right
while (!found)
# Scan each byte position
0.upto(decoder_key_size - 1) { |index|
cur_key[index] = rand(255)
# Scan all 255 bytes (wrapping around as necessary)
for cur_char in (cur_key[index] .. (cur_key[index] + 255))
cur_char = (cur_char % 255) + 1
# If this is a known bad character at this location in the
# key or it doesn't pass the bad character check...
if (((bad_keys != nil) and
(bad_keys[index][cur_char] == true)) or
(badchars.index(cur_char) != nil))
next
end
key_bytes[index] = cur_char
end
}
# Assume that we're going to rock this shit...
found = true
# Scan each byte and see what we've got going on to make sure
# no funny business is happening
key_bytes.each { |byte|
if (badchars.index(byte) != nil)
found = false
end
}
end
# Do we have all the key bytes accounted for?
if (key_bytes.length != decoder_key_size)
return nil
end
return key_bytes_to_integer(key_bytes)
end
def find_bad_keys
return [ {}, {}, {}, {} ]
end
def has_badchars?(buf, badchars)
badchars.each_byte { |badchar|
idx = buf.index(badchar)
if (idx != nil)
return idx
end
}
return nil
end
# Convert individual key bytes into a single integer based on the
# decoder's key size and packing requirements
def key_bytes_to_integer(key_bytes)
return key_bytes.pack('C' + decoder_key_size.to_s).unpack(decoder_key_pack)[0]
end
# Convert an integer into the individual key bytes based on the
# decoder's key size and packing requirements
def integer_to_key_bytes(integer)
return [ integer.to_i ].pack(decoder_key_pack).unpack('C' + decoder_key_size.to_s)
end
end
end
require 'Core/Encoder/Xor'
require 'Core/Encoder/XorAdditiveFeedback'
+36
View File
@@ -0,0 +1,36 @@
require 'Core'
###
#
# Xor
# ---
#
# This class provides basic XOR encoding of buffers.
#
###
class Msf::Encoder::Xor < Msf::Encoder
def encode_block(state, block)
return Msf::Encoding::Xor.encode_block(state.key, block, decoder_block_size, decoder_key_pack)
end
def find_bad_keys(buf, badchars)
bad_keys = [ {}, {}, {}, {} ]
byte_idx = 0
# Scan through all the badchars and build out the bad_keys array
# based on the XOR'd combinations that can occur at certain bytes
# to produce bad characters
badchars.each_byte { |badchar|
buf.each_byte { |byte|
bad_keys[byte_idx % decoder_key_size][byte ^ badchar] = true
byte_idx += 1
}
}
return bad_keys
end
end
@@ -0,0 +1,82 @@
require 'Core'
###
#
# XorAdditiveFeedback
# -------------------
#
# This class performs per-block XOR additive feedback encoding.
#
###
class Msf::Encoder::XorAdditiveFeedback < Msf::Encoder::Xor
def initialize(info)
super(info)
end
def encode_block(state, block)
# XOR the key with the current block
orig = block.unpack(decoder_key_pack)[0]
oblock = orig ^ state.key
# Add the original block contents to the key
state.key = (state.key + orig) % (1 << (decoder_key_size * 8))
# Return the XOR'd block
return [ oblock ].pack(decoder_key_pack)
end
def find_key(buf, badchars)
key_bytes = integer_to_key_bytes(super(buf, badchars))
state = Msf::EncoderState.new
valid = false
# Save the original key_bytes so we can tell if we loop around
orig_key_bytes = key_bytes.dup
# While we haven't found a valid key, keep trying the encode operation
while (!valid)
begin
# Reset the encoder state's key to the current set of key bytes
state.reset(key_bytes_to_integer(key_bytes))
# If the key itself contains a bad character, throw the bad
# character exception with the index of the bad character in the
# key. Use a stub_size of zero to bypass the check to in the
# rescue block.
if ((idx = has_badchars?([state.key.to_i].pack(decoder_key_pack), badchars)) != nil)
raise Msf::BadcharException.new(nil, idx, 0, nil)
end
# Perform the encode operation...if it encounters a bad character
# an exception will be thrown
valid = do_encode(buf, badchars, state)
rescue Msf::BadcharException => info
# If the decoder stub contains a bad character, then there's not
# much we can do about it
if (info.index < info.stub_size)
raise info, "The #{self.name} decoder stub contains a bad character.", caller
end
# Determine the actual index to the bad character inside the
# encoded payload by removing the decoder stub from the index and
# modulus off the decoder's key size
idx = (info.index - info.stub_size) % (decoder_key_size)
# Increment the key byte at the index that the bad character was
# detected
key_bytes[idx] = ((key_bytes[idx] + 1) % 255)
# If we looped around, then give up.
if (key_bytes[idx] == orig_key_bytes[idx])
raise info, "The #{self.name} encoder failed to encode without bad characters.",
caller
end
end
end
# Return the original key
return state.orig_key
end
end
+30
View File
@@ -0,0 +1,30 @@
module Msf
module Encoding
###
#
# Xor
# ---
#
# This class provides basic XOR encoding facilities and is used
# by XOR encoders.
#
###
class Xor
def Xor.encode_block(key, block, block_size = 4, block_pack = 'V')
offset = 0
oblock = ''
while (offset < block.length)
cblock = block[offset, block_size].unpack(block_pack)[0]
cblock ^= key
oblock += [ cblock ].pack(block_pack)
end
return oblock
end
end
end end
+120
View File
@@ -0,0 +1,120 @@
require 'Core'
module Msf
###
#
# EventDispatcher
# ---------------
#
# This class manages subscriber registration and is the entry point
# for dispatching various events that occur for modules, such as
# recon discovery and exploit success or failure. The framework
# and external modules can register themselves as subscribers to
# various events such that they can perform custom actions when
# a specific event or events occur.
#
###
class EventDispatcher
def initialize
self.exploit_event_subscribers = []
self.session_event_subscribers = []
self.recon_event_subscribers = []
self.subscribers_rwlock = ReadWriteLock.new
end
#
# Subscriber registration
#
def add_recon_subscriber(subscriber)
add_event_subscriber(recon_event_subscribers, subscriber)
end
def remove_recon_subscriber(subscriber)
remove_event_subscriber(recon_event_subscribers, subscriber)
end
def add_exploit_subscriber(subscriber)
add_event_subscriber(exploit_event_subscribers, subscriber)
end
def remove_exploit_subscriber(subscriber)
remove_event_subscriber(exploit_event_subscribers, subscriber)
end
def add_session_subscriber(subscriber)
add_event_subscriber(session_event_subscribers, subscriber)
end
def remove_session_subscriber(subscriber)
remove_event_subscriber(session_event_subscribers, subscriber)
end
#
# Event dispatching entry point
#
def on_recon_discovery(group, info)
subscribers_rwlock.synchronize_read {
recon_event_subscribers.each { |subscriber|
subscriber.on_recon_discovery(group, info)
}
}
end
def on_exploit_success(exploit, target)
subscribers_rwlock.synchronize_read {
exploit_event_subscribers.each { |subscriber|
subscriber.on_exploit_success(exploit, target)
}
}
end
def on_exploit_failure(exploit, target)
subscribers_rwlock.synchronize_read {
exploit_event_subscribers.each { |subscriber|
subscriber.on_exploit_failure(exploit, target)
}
}
end
def on_session_open(session)
subscribers_rwlock.synchronize_read {
session_event_subscribers.each { |subscriber|
subscriber.on_session_open(session)
}
}
end
def on_session_close(session)
subscribers_rwlock.synchronize_read {
session_event_subscribers.each { |subscriber|
subscriber.on_session_close(session)
}
}
end
protected
def add_event_subscriber(array, subscriber)
subscribers_rwlock.synchronize_write {
array << subscriber
}
end
def remove_event_subscriber(array, subscriber)
subscribers_rwlock.synchronize_write {
array.delete(subscriber)
}
end
attr_accessor :exploit_event_subscribers
attr_accessor :session_event_subscribers
attr_accessor :recon_event_subscribers
attr_accessor :subscribers_rwlock
end
end
+54
View File
@@ -0,0 +1,54 @@
module Msf
class EncodingException < RuntimeError
end
###
#
# NoKeyException
# --------------
#
# Thrown when an encoder fails to find a viable encoding key.
#
###
class NoKeyException < EncodingException
end
###
#
# BadcharException
# ----------------
#
# Thrown when an encoder fails to encode a buffer due to a bad character.
#
###
class BadcharException < EncodingException
def initialize(buf, index, stub_size, char)
@buf = buf
@index = index
@stub_size = stub_size
@char = char
end
attr_reader :buf, :index, :stub_size, :char
end
###
#
# MissingOptionError
# ------------------
#
# This exception is thrown when one or more options failed
# to pass data store validation. The list of option names
# can be obtained through the options attribute.
#
###
class OptionValidateError < ArgumentError
def initialize(options)
@options = options
end
attr_reader :options
end
end
+15
View File
@@ -0,0 +1,15 @@
require 'Core'
module Msf
module ExploitEvents
def on_exploit_success(exploit, target)
end
def on_exploit_failure(exploit, target)
end
end
end
+49
View File
@@ -0,0 +1,49 @@
require 'Core'
module Msf
###
#
# Framework
# ---------
#
# This class is the primary context that modules, scripts, and user
# interfaces interact with. It ties everything together.
#
###
class Framework
include Msf::Logging::LogDispatcher
def initialize()
self.events = EventDispatcher.new
# self.encoders = EncoderManager.new
# self.exploits = ExploitManager.new
# self.nops = NopManager.new
# self.payloads = PayloadManager.new
# self.recon = ReconManager.new
super
end
attr_reader :events
attr_reader :ui
attr_reader :encoders
attr_reader :exploits
attr_reader :nops
attr_reader :payloads
attr_reader :recon
protected
attr_writer :events
attr_writer :ui
attr_writer :encoders
attr_writer :exploits
attr_writer :nops
attr_writer :payloads
attr_writer :recon
end
end
+115
View File
@@ -0,0 +1,115 @@
require 'Core'
module Msf
###
#
# Module
# ------
#
# The module base class is responsible for providing the common interface
# that is used to interact with modules at the most basic levels, such as
# by inspecting a given module's attributes (name, dsecription, version,
# authors, etc) and by managing the module's data store.
#
###
class Module
def initialize(info)
self.module_info = info || {}
set_defaults
# Transform some of the fields to arrays as necessary
self.author = Transformer.transform(module_info['Author'], Array,
[ Author ], 'Author')
self.arch = Transformer.transform(module_info['Arch'], Array,
[ String ], 'Arch')
self.platform = Transformer.transform(module_info['Platform'], Array,
[ String ], 'Platform')
self.refs = Transformer.transform(module_info['Ref'], Array,
[ SiteReference, Reference ], 'Ref')
# Create and initialize the datastore for this module
self.datastore = DataStore.new
self.datastore.add_options(info['Options'])
self.datastore.add_advanced_options(info['AdvancedOptions'])
end
# Return the module's name
def name
return module_info['Name']
end
# Return the module's description
def description
return module_info['Description']
end
# Return the module's version information
def version
return module_info['Version']
end
# Return the module's abstract type
def type
raise NotImplementedError
end
# Return a comma separated list of author for this module
def author_to_s
return author.collect { |author| author.to_s }.join(", ")
end
# Enumerate each author
def each_author(&block)
author.each(&block)
end
# Return a comma separated list of supported architectures, if any
def arch_to_s
return arch.join(", ")
end
# Enumerate each architecture
def each_arch(&block)
arch.each(&block)
end
# Return whether or not the module supports the supplied architecture
def arch?(what)
return arch.index(what) != nil
end
# Return a comma separated list of supported platforms, if any
def platform_to_s
return platform.join(", ")
end
attr_reader :author, :arch, :platform, :refs, :datastore
protected
# Sets the modules unsupplied info fields to their default values
def set_defaults
{
'Name' => 'No module name',
'Description' => 'No module description',
'Version' => '0',
'Author' => nil,
'Arch' => nil,
'Platform' => nil,
'Ref' => nil
}.each_pair { |field, value|
if (module_info[field] == nil)
module_info[field] = value
end
}
end
attr_accessor :module_info
attr_writer :author, :arch, :platform, :refs, :datastore
end
end
+40
View File
@@ -0,0 +1,40 @@
require 'Core'
module Msf
###
#
# Nop
# ---
#
# This class acts as the base class for all nop generators.
#
###
class Nop < Msf::Module
#
# Stub method for generating a sled with the provided arguments. Derived
# Nop implementations must supply a length and can supply one or more of
# the following options:
#
# - Random (true/false)
# Indicates that the caller desires random NOPs (if supported).
# - SaveRegisters (array)
# The list of registers that should not be clobbered by the NOP
# generator.
# - Badchars (string)
# The list of characters that should be avoided by the NOP
# generator.
#
def generate_sled(length, opts)
return nil
end
# Default repetition threshold when find nop characters
def nop_repeat_threshold
return 10000
end
end
end
+21
View File
@@ -0,0 +1,21 @@
module Msf
###
#
# ReconEvents
# -----------
#
# This interface is called by recon modules to notify the framework when
# network elements, services, or other types of things recon modules
# might discovery.
#
###
module ReconEvents
def on_recon_discovery(group, info)
return nil
end
end
end
+25
View File
@@ -0,0 +1,25 @@
require 'Core'
module Msf
module SessionEvents
def on_session_open(session)
end
def on_session_close(session)
end
end
###
#
# Session
# -------
#
#
###
class Session
end
end
+20
View File
@@ -0,0 +1,20 @@
module Msf
#
# Log severities
#
LOG_ERROR = 'error'
LOG_DEBUG = 'debug'
LOG_INFO = 'info'
LOG_WARN = 'warn'
LOG_RAW = 'raw'
#
# Log levels
#
LEV_0 = 0
LEV_1 = 1
LEV_2 = 2
LEV_3 = 3
end
+163
View File
@@ -0,0 +1,163 @@
require 'thread'
module Msf
###
#
# ReadWriteLock
# -------------
#
# This class implements a read/write lock synchronization
# primitive. It is meant to allow for more efficient access to
# resources that are more often read from than written to and many
# times can have concurrent reader threads. By allowing the reader
# threads to lock the resource concurrently rather than serially,
# a large performance boost can be seen. Acquiring a write lock
# results in exclusive access to the resource and thereby prevents
# any read operations during the time that a write lock is acquired.
# Only one write lock may be acquired at a time.
#
###
class ReadWriteLock
def initialize
@read_sync_mutex = Mutex.new
@write_sync_mutex = Mutex.new
@exclusive_mutex = Mutex.new
@readers = 0
@writer = false
end
# Acquires the read lock for the calling thread
def lock_read
read_sync_mutex.lock
begin
# If there are a non-zero number of readers and a
# writer is waiting to acquire the exclusive lock,
# free up the sync mutex temporarily and lock/unlock
# the exclusive lock. This is to give the writer
# thread a chance to acquire the lock and prevents
# it from being constantly starved.
if ((@readers > 0) and
(@writer))
read_sync_mutex.unlock
exclusive_mutex.lock
exclusive_mutex.unlock
read_sync_mutex.lock
end
# Increment the active reader count
@readers += 1
# If we now have just one reader, acquire the exclusive
# lock. Track the thread owner so that we release the
# lock from within the same thread context later on.
if (@readers == 1)
exclusive_mutex.lock
@owner = Thread.current
end
ensure
read_sync_mutex.unlock
end
end
# Releases the read lock for the calling thread
def unlock_read
read_sync_mutex.lock
begin
unlocked = false
# Keep looping until we've lost this thread's reader
# lock
while (!unlocked)
# If there are no more readers left after this one
if (@readers - 1 == 0)
# If the calling thread is the owner of the exclusive
# reader lock, then let's release that shit!
if (Thread.current == @owner)
@owner = nil
exclusive_mutex.unlock
end
# If there is more than one reader left and this thread is
# the owner of the exclusive lock, then keep looping so that
# we can eventually unlock the exclusive mutex in this thread's
# context
elsif (Thread.current == @owner)
read_sync_mutex.unlock
next
end
# Unlocked!
unlocked = true
# Decrement the active reader count
@readers -= 1
end
ensure
read_sync_mutex.unlock
end
end
# Acquire the exclusive write lock
def lock_write
write_sync_mutex.lock
begin
@writer = true
exclusive_mutex.lock
@owner = Thread.current
ensure
write_sync_mutex.unlock
end
end
# Release the exclusive write lock
def unlock_write
# If the caller is not the owner of the write lock, then someone is
# doing something broken, let's let them know.
if (Thread.current != @owner)
raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
end
# Otherwise, release the exclusive write lock
@writer = false
exclusive_mutex.unlock
end
# Synchronize a block for read access
def synchronize_read
lock_read
begin
yield
ensure
unlock_read
end
end
# Synchronize a block for write access
def synchronize_write
lock_write
begin
yield
ensure
unlock_write
end
end
protected
attr_accessor :read_sync_mutex
attr_accessor :write_sync_mutex
attr_accessor :exclusive_mutex
end
end
+87
View File
@@ -0,0 +1,87 @@
module Msf
###
#
# Transformer - more than meets the eye!
# -----------
#
# This class, aside from having a kickass name, is responsible for translating
# object instances of one or more types into a single list instance of one or
# more types. This is useful for translating object instances that be can
# either strings or an array of strings into an array of strings, for
# instance. It lets you make things take a uniform structure in an abstract
# manner.
#
###
class Transformer
# Translates the object instance supplied in src_instance to an instance of
# dst_class. The dst_class parameter's instance must support the <<
# operator. An example call to this method looks something like:
#
# Transformer.transform(string, Array, [ String ], target)
def Transformer.transform(src_instance, dst_class, supported_classes,
target = nil)
dst_instance = dst_class.new
if (src_instance.kind_of?(Array))
src_instance.each { |src_inst|
Transformer.transform_single(src_inst, dst_instance,
supported_classes, target)
}
elsif (!src_instance.kind_of?(NilClass))
Transformer.transform_single(src_instance, dst_instance,
supported_classes, target)
end
return dst_instance
end
protected
# Transform a single source instance.
def Transformer.transform_single(src_instance, dst_instance,
supported_classes, target)
# If the src instance's class is supported, just add it to the dst
# instance
if (supported_classes.include?(src_instance.class))
dst_instance << src_instance
# If the source instance is a string, query each of the supported
# classes to see if they can serialize it to their particular data
# type.
elsif (src_instance.kind_of?(String))
new_src_instance = nil
# Walk each supported class calling from_s if exported
supported_classes.each { |sup_class|
new_src_instance = sup_class.from_s(src_instance)
if (new_src_instance != nil)
dst_instance << new_src_instance
break
end
}
# If we don't have a valid new src instance, then we suck
if (new_src_instance == nil)
bomb_translation(src_instance, target)
end
# Otherwise, bomb translation
else
bomb_translation(src_instance, target)
end
end
def Transformer.bomb_translation(src_instance, target)
error = "Invalid source class (#{src_instance.class})"
if (target != nil)
error += " for #{target}"
end
raise ArgumentError, error, caller
end
end
end
+68
View File
@@ -0,0 +1,68 @@
require 'Shared'
module Msf
module Logging
###
#
# LogDispatcher
# -------------
#
# This interface is included in the Framework class and is used to provide a
# common interface for dispatching logs to zero or more registered log sinks.
# Log sinks are typically backed against arbitrary storage mediums, such as a
# flatfile, a database, or the console. The log dispatcher itself is really
# just a log sink that backs itself against zero or more log sinks rather than
# a file, database, or other persistent storage.
#
###
module LogDispatcher
include Msf::Logging::LogSink
def initialize()
initialize_log_dispatcher
end
#
# Log sink registration
#
def add_log_sink(sink)
log_sinks_rwlock.synchronize_write {
log_sinks << sink
}
end
def remove_log_sink(sink)
log_sinks_rwlock.synchronize_write {
sink.cleanup
log_sinks.delete(sink)
}
end
#
# Log dispatching
#
protected
def initialize_log_dispatcher
self.log_sinks = []
self.log_sinks_rwlock = ReadWriteLock.new
end
def log(sev, level, msg, from)
log_sinks_rwlock.synchronize_read {
log_sinks.each { |sink|
sink.dispatch_log(sev, level, msg, from)
}
}
end
attr_accessor :log_sinks
attr_accessor :log_sinks_rwlock
end
end; end
+55
View File
@@ -0,0 +1,55 @@
require 'Shared/Constants'
module Msf
module Logging
###
#
# LogSink
# -------
#
# This abstract interface is what must be implemented by any class
# that would like to register as a log sink on a given LogDispatcher
# instance, such as the Framework object.
#
###
module LogSink
def cleanup
end
def dlog(level, msg, from = caller)
log(LOG_DEBUG, level, msg, from)
end
def elog(level, msg, from = caller)
log(LOG_ERROR, level, msg, from)
end
def wlog(level, msg, from = caller)
log(LOG_WARN, level, msg, from)
end
def ilog(level, msg, from = caller)
log(LOG_INFO, level, msg, from)
end
def rlog(msg)
log(LOG_RAW, 0, msg, nil)
end
protected
def log(sev, level, msg, from)
raise NotImplementedError
end
def get_current_timestamp
return Time.now.strftime("%m/%d/%Y %H:%M:%S")
end
end
end; end
require 'Shared/Logging/Sinks/Flatfile'
+44
View File
@@ -0,0 +1,44 @@
module Msf
module Logging
module Sinks
###
#
# Flatfile
# --------
#
# This class implements the LogSink interface and backs it against a
# file on disk.
#
###
class Flatfile
include Msf::Logging::LogSink
def initialize(file)
self.fd = File.new(file, "a")
ilog(0, "Logging initialized.")
end
def cleanup
ilog(0, "Logging finished.")
fd.close
end
protected
def log(sev, level, msg, from)
if (sev == LOG_RAW)
fd.write(msg)
else
fd.write("[#{get_current_timestamp}] (sev=#{sev},level=#{level}): #{msg}\n")
end
end
attr_accessor :fd
end
end; end; end