metasploit-framework/lib/msf/core/exploit.rb

543 lines
12 KiB
Ruby
Raw Normal View History

require 'msf/core'
module Msf
###
#
# ExploitEvents
# -------------
#
# Event notifications that affect exploits.
#
###
module ExploitEvents
#
# Notified when an exploit is successful
#
def on_exploit_success(exploit)
end
#
# Notified when an exploit is unsuccessful
#
def on_exploit_failure(exploit, reason)
end
end
###
#
# Exploit
# -------
#
# The exploit class acts as the base class for all exploit modules. It
# provides a common interface for interacting with exploits at the most basic
# level.
#
###
class Exploit < Msf::Module
#
# The various check codes that can be returned from the ``check'' routine.
#
module CheckCode
Safe = [ 0, "The target is not exploitable." ]
Detected = [ 1, "The target service is running, but could not be validated." ]
Appears = [ 2, "The target appears to be vulnerable." ]
Vulnerable = [ 3, "The target is vulnerable." ]
Unsupported = [ 4, "This exploit does not support check." ]
end
#
# The various basic types of exploits
#
module Type
Remote = "remote"
Local = "local"
Omni = "omnipresent"
end
#
# The types of stances an exploit can take, such as passive or aggressive.
# Stances indicate whether or not the exploit triggers the exploit without
# waiting for one or more conditions to be met (aggressive) or whether it
# must wait for certain conditions to be satisfied before the exploit can
# be initiated (passive)
#
module Stance
Aggressive = "aggresive"
Passive = "passive"
end
###
#
# Local
# -----
#
# The local exploit class is a specialization of the exploit module class that
# is geared toward exploits that are performed locally. Locally, in this
# case, is defined as an exploit that is realized by means other than network
# communication.
#
###
class Local < Exploit
def exploit_type
Exploit::Type::Local
end
end
###
#
# Remote
# ------
#
# The remote exploit class is a specialization of the exploit module class
# that is geared toward exploits that are performed against targets other than
# the local machine. This typically implies exploiting other machines via a
# network connection, though it is not limited to this scope.
#
###
class Remote < Exploit
def exploit_type
Exploit::Type::Remote
end
end
require 'msf/core/exploit/brute'
require 'msf/core/exploit/tcp'
require 'msf/core/exploit/dcerpc'
#
# Constructo the clown! Watch as he kills small children with balloons.
#
def initialize(info = {})
super(info)
self.targets = Rex::Transformer.transform(info['Targets'], Array,
[ Target ], 'Targets')
self.default_target = info['DefaultTarget']
self.payload_info = info['Payload'] || {}
end
##
#
# Core exploit interface
# ----------------------
#
# These are the methods that exploits will override to perform various
# tasks, such as checking a target to see if it's vulnerable, automatically
# selecting a target, or performing an exploit.
#
##
#
# Checks to see if the target is vulnerable, returning unsupported if it's
# not supported.
#
def check
CheckCode::Unsupported
end
#
# Kicks off the actual exploit. Prior to this call, the framework will
# have validated the data store using the options associated with this
# exploit module. It will also pre-generate the desired payload, though
# exploits can re-generate the payload if necessary.
#
def exploit
end
#
# Prepares the module for exploitation, initializes any state, and starts
# the payload handler.
#
def setup
if (payload_instance)
# Set up the payload handlers
payload_instance.setup_handler
# Start the payload handler
payload_instance.start_handler
end
end
#
# Performs any cleanup that may be necessary, such as disconnecting
# connections and any other such fun things. If a payload is active then
# its handler cleanup routines are called as well.
#
def cleanup
if (payload_instance)
payload_instance.cleanup_handler
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)
# Set the encoded payload to the result of the encoding process
self.payload = generate_single_payload(pinst)
# Save the payload instance
self.payload_instance = (pinst) ? pinst : self.payload_instance
return self.payload
end
#
# This method generates a non-cached payload which is typically useful for
# passive exploits that will have more than one client.
#
def generate_single_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.payload_instance
if (real_payload == nil)
raise MissingPayloadError, "No payload has been selected.",
caller
end
# Duplicate the exploit payload requirements
reqs = self.payload_info
# Pass save register requirements to the NOP generator
reqs['SaveRegisters'] = nop_save_registers
return EncodedPayload.create(real_payload, reqs)
end
#
# Re-generates an encoded payload, typically called after something in the
# datastore has changed.
#
def regenerate_payload
generate_single_payload
end
##
#
# Feature detection
# -----------------
#
# These methods check to see if there is a derived implementation of
# various methods as a way of inferring whether or not a given exploit
# supports the feature.
#
##
def supports_check?
derived_implementor?(Msf::Exploit, 'check')
end
def supports_exploit?
derived_implementor?(Msf::Exploit, 'exploit')
end
#
# Returns a hash of the capabilities this exploit module has
#
def capabilities
{
'check' => supports_check?,
'exploit' => supports_exploit?
}
end
##
#
# Getters/Setters
# ---------------
#
# Querying and set interfaces for some of the exploit's attributes.
#
##
#
# The module's type
#
def self.type
MODULE_EXPLOIT
end
def type
MODULE_EXPLOIT
end
#
# If we don't know the exploit type, then I guess it's omnipresent!
#
def exploit_type
Type::Omni
end
#
# Generally, all exploits take an aggressive stance.
#
def stance
module_info['Stance'] || Stance::Aggressive
end
#
# Returns if the exploit has a passive stance
#
def passive?
(stance == Stance::Passive)
end
#
# Returns the active target for this exploit
#
def target
target_idx = datastore['TARGET']
return (target_idx) ? targets[target_idx.to_i] : nil
end
#
# Returns a list of compatible payloads based on platform, architecture,
# and size requirements.
#
def compatible_payloads
payloads = []
framework.payloads.each_module(
'Platform' => target ? target.platform : nil,
'Arch' => target ? target.arch : nil) { |name, mod|
# Skip over payloads that are too big
if ((payload_space) and
(framework.payloads.sizes[name] > payload_space))
dlog("#{refname}: Skipping payload #{name} for being too large", 'core',
LEV_1)
next
end
# This one be compatible!
payloads << [ name, mod ]
}
return payloads;
end
#
# Return any text that should be prepended to the payload. The payload
# module is passed so that the exploit can take a guess at architecture
# and platform if it's a multi exploit.
#
def payload_prepend(payload_module)
if (target and target['PayloadPrepend'])
target['PayloadPrepend']
else
payload_info['Prepend'] || ''
end
end
#
# Return any text that should be appended to the payload. The payload
# module is passed so that the exploit can take a guess at architecture
# and platform if it's a multi exploit.
#
def payload_append(payload_module)
if (target and target['PayloadAppend'])
target['PayloadAppend']
else
payload_info['Append'] || ''
end
end
#
# Return any text that should be prepended to the encoder of the payload.
# The payload module is passed so that the exploit can take a guess
# at architecture and platform if it's a multi exploit.
#
def payload_prepend_encoder(payload_module)
if (target and target['PayloadEncoder'])
target['PayloadEncoder']
else
payload_info['PrependEncoder'] || ''
end
end
#
# Maximum number of nops to use as a hint to the framework.
# Nil signifies that the framework should decide.
#
def payload_max_nops
if (target and target['PayloadMaxNops'])
target['PayloadMaxNops']
else
payload_info['MaxNops'] || nil
end
end
#
# Minimum number of nops to use as a hint to the framework.
# Nil snigifies that the framework should decide.
#
def payload_min_nops
if (target and target['PayloadMinNops'])
target['PayloadMinNops']
else
payload_info['MinNops'] || nil
end
end
#
# Returns the maximum amount of room the exploit has for a payload.
#
def payload_space
if (target and target['PayloadSpace'])
target['PayloadSpace']
elsif (payload_info['Space'])
payload_info['Space'].to_i
else
nil
end
end
#
# Returns the bad characters that cannot be in any payload used by this
# exploit.
#
def payload_badchars
payload_info['BadChars']
end
##
#
# NOP requirements
# ----------------
#
# Hints to the nop generator on how it should perform if it's used.
#
##
#
# Returns the list of registers that the NOP generator should save,
# if any. It will use the current target's save registers in precedence
# over those defined globally for the exploit module.
#
# If there are no save registers, nil is returned.
#
def nop_save_registers
if (target and target.save_registers)
return target.save_registers
else
return module_info['SaveRegisters']
end
end
#
# Returns the first compatible NOP generator for this exploit's payload
# instance.
#
def nop_generator
return nil if (!payload_instance)
payload_instance.compatible_nops.each { |nopname, nopmod|
return nopmod.new
}
end
#
# Generates a nop sled of a supplied length and returns it to the caller.
#
def make_nops(count)
nop_sled = nil
# If there is no payload instance then we can't succeed.
return nil if (!payload_instance)
payload_instance.compatible_nops.each { |nopname, nopmod|
# Create an instance of the nop module
nop = nopmod.new
# The list of save registers
save_regs = nop_save_registers || []
if (save_regs.empty? == true)
save_regs = nil
end
begin
nop_sled = nop.generate_sled(count,
'BadChars' => payload_badchars || '',
'SaveRegisters' => save_regs)
rescue
dlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit: #{$!}",
'core', LEV_1)
end
break
}
nop_sled
end
##
#
# Handler interaction
#
##
#
# Passes the connection to the associated payload handler to see if the
# exploit succeeded and a connection has been established. The return
# value can be one of the Handler::constants.
#
def handler(*args)
return self.payload_instance.handler(*args) if (self.payload_instance)
end
##
#
# Attributes
#
##
#
# The list of targets.
#
attr_reader :targets
#
# The default target.
#
attr_reader :default_target
#
# The payload requirement hash.
#
attr_reader :payload_info
#
# The active payload instance.
#
attr_accessor :payload_instance
#
# The encoded payload instance. An instance of an
# EncodedPayload object.
#
attr_accessor :payload
protected
attr_writer :targets
attr_writer :default_target
attr_writer :payload_info
end
end