1525 lines
40 KiB
Ruby
1525 lines
40 KiB
Ruby
# -*- coding: binary -*-
|
|
require 'msf/core'
|
|
require 'msf/core/module'
|
|
|
|
module Msf
|
|
|
|
###
|
|
#
|
|
# This module exposes an interface that is used when wanting to receive
|
|
# notifications about events pertaining to exploitation.
|
|
#
|
|
###
|
|
module ExploitEvent
|
|
|
|
#
|
|
# This method is called when an exploit succeeds.
|
|
#
|
|
def on_exploit_success(exploit, session)
|
|
end
|
|
|
|
end
|
|
|
|
###
|
|
#
|
|
# 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
|
|
|
|
require 'msf/core/post'
|
|
require 'msf/core/exploit/local'
|
|
|
|
##
|
|
# Exceptions
|
|
##
|
|
|
|
# Indicate that the exploit should abort because it has completed
|
|
class Complete < RuntimeError
|
|
end
|
|
|
|
# Indicate that the exploit should abort because it has failed
|
|
class Failed < RuntimeError
|
|
end
|
|
|
|
|
|
##
|
|
#
|
|
# Default compatibility settings for exploit modules.
|
|
#
|
|
##
|
|
module CompatDefaults
|
|
#
|
|
# Default compatibility specifications for payloads
|
|
#
|
|
Payload =
|
|
{
|
|
# Support reverse, bind, and noconn connection types
|
|
# for all exploits unless expressly disabled.
|
|
'ConnectionType' => 'reverse bind noconn none tunnel',
|
|
}
|
|
end
|
|
|
|
##
|
|
#
|
|
# The various check codes that can be returned from the ``check'' routine.
|
|
# Please read the following wiki to learn how these codes are used:
|
|
# https://github.com/rapid7/metasploit-framework/wiki/How-to-write-a-check()-method
|
|
#
|
|
##
|
|
module CheckCode
|
|
|
|
#
|
|
# Can't tell if the target is exploitable or not. This is recommended if the module fails to
|
|
# retrieve enough information from the target machine, such as due to a timeout.
|
|
#
|
|
Unknown = [ 'unknown', "Cannot reliably check exploitability."]
|
|
|
|
#
|
|
# The target is safe and is therefore not exploitable. This is recommended after the check
|
|
# fails to trigger the vulnerability, or even detect the service.
|
|
#
|
|
Safe = [ 'safe', "The target is not exploitable." ]
|
|
|
|
#
|
|
# The target is running the service in question, but the check fails to determine whether
|
|
# the target is vulnerable or not.
|
|
#
|
|
Detected = [ 'detected', "The target service is running, but could not be validated." ]
|
|
|
|
#
|
|
# The target appears to be vulnerable. This is recommended if the vulnerability is determined
|
|
# based on passive reconnaissance. For example: version, banner grabbing, or having the resource
|
|
# that's known to be vulnerable.
|
|
#
|
|
Appears = [ 'appears', "The target appears to be vulnerable." ]
|
|
|
|
#
|
|
# The target is vulnerable. Only used if the check is able to actually take advantage of the
|
|
# bug, and obtain hard evidence. For example: executing a command on the target machine, and
|
|
# retrieve the output.
|
|
#
|
|
Vulnerable = [ 'vulnerable', "The target is vulnerable." ]
|
|
|
|
#
|
|
# The module does not support the check method.
|
|
#
|
|
Unsupported = [ 'unsupported', "This module does not support check." ]
|
|
end
|
|
|
|
#
|
|
# The various basic types of exploits
|
|
#
|
|
module Type
|
|
|
|
#
|
|
# Indicates that the exploit is a remote exploit.
|
|
#
|
|
Remote = "remote"
|
|
|
|
#
|
|
# Indicates that the exploit is a local exploit.
|
|
#
|
|
Local = "local"
|
|
|
|
#
|
|
# Indicates that the exploit can work anywhere it damn pleases.
|
|
#
|
|
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
|
|
|
|
#
|
|
# Used to indicate that an exploit takes an aggressive stance. This
|
|
# means that the exploit proactively triggers a vulnerability.
|
|
#
|
|
Aggressive = "aggressive"
|
|
|
|
#
|
|
# Used to indicate that an exploit takes a passive stance. This means
|
|
# that the exploit waits for interaction from a client or other entity
|
|
# before being able to trigger the vulnerability.
|
|
#
|
|
Passive = "passive"
|
|
end
|
|
|
|
###
|
|
#
|
|
# 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
|
|
|
|
#
|
|
# Initializes the socket array.
|
|
#
|
|
def initialize(info)
|
|
super
|
|
|
|
self.sockets = Array.new
|
|
end
|
|
|
|
#
|
|
# Returns the fact that this exploit is a remote exploit.
|
|
#
|
|
def exploit_type
|
|
Exploit::Type::Remote
|
|
end
|
|
|
|
#
|
|
# Adds a socket to the list of sockets opened by this exploit.
|
|
#
|
|
def add_socket(sock)
|
|
self.sockets << sock
|
|
end
|
|
|
|
#
|
|
# Removes a socket from the list of sockets.
|
|
#
|
|
def remove_socket(sock)
|
|
self.sockets.delete(sock)
|
|
end
|
|
|
|
#
|
|
# This method is called once a new session has been created on behalf of
|
|
# this exploit instance and all socket connections created by this
|
|
# exploit should be closed.
|
|
#
|
|
def abort_sockets
|
|
sockets.delete_if { |sock|
|
|
|
|
begin
|
|
sock.close
|
|
rescue ::Exception
|
|
end
|
|
true
|
|
}
|
|
end
|
|
|
|
protected
|
|
|
|
#
|
|
# The list of sockets established by this exploit.
|
|
#
|
|
attr_accessor :sockets
|
|
|
|
end
|
|
|
|
#
|
|
# Load all of the exploit mixins
|
|
#
|
|
require 'msf/core/exploit/mixins'
|
|
|
|
#
|
|
# Returns an array of all of the exploit mixins. Lame algorithm right now.
|
|
# We search the Msf::Exploit namespace for all modules that do not have any
|
|
# constants in them. In the future we can replace this with a better
|
|
# algorithm. It's just important that it returns an array of all of the
|
|
# mixin modules.
|
|
#
|
|
# @return [Array]
|
|
def self.mixins
|
|
mixins = []
|
|
wl = [ Msf::Exploit ]
|
|
visited = {}
|
|
|
|
until wl.length == 0
|
|
wl.delete_if { |mod|
|
|
mod.constants.each { |const|
|
|
child = mod.const_get(const)
|
|
|
|
next if child.to_s !~ /^Msf::Exploit/
|
|
|
|
next if visited[child]
|
|
|
|
next if child.kind_of?(::Module) == false
|
|
|
|
visited[child] = true
|
|
|
|
if child.constants.length > 0
|
|
wl << child
|
|
else
|
|
mixins << child
|
|
end
|
|
}
|
|
|
|
true
|
|
}
|
|
end
|
|
|
|
return mixins
|
|
end
|
|
|
|
#
|
|
# Creates an instance of the exploit module. Mad skillz.
|
|
#
|
|
def initialize(info = {})
|
|
|
|
# Ghetto compat mirroring for payload compatibilities. This mirrors
|
|
#
|
|
# Payload => Compat => xyz
|
|
#
|
|
# to
|
|
#
|
|
# Compat => Payload => xyz
|
|
if (info['Payload'] and info['Payload']['Compat'])
|
|
info['Compat'] = Hash.new if (info['Compat'] == nil)
|
|
info['Compat']['Payload'] = Hash.new if (info['Compat']['Payload'] == nil)
|
|
info['Compat']['Payload'].update(info['Payload']['Compat'])
|
|
end
|
|
|
|
# Call the parent constructor after making any necessary modifications
|
|
# to the information hash.
|
|
super(info)
|
|
|
|
self.targets = Rex::Transformer.transform(info['Targets'], Array,
|
|
[ Target ], 'Targets')
|
|
self.default_target = info['DefaultTarget']
|
|
self.payload_info = info['Payload'] || {}
|
|
self.successful = false
|
|
self.session_count = 0
|
|
self.active_timeout = 120
|
|
self.fail_reason = Failure::None
|
|
|
|
if (info['Payload'] and info['Payload']['ActiveTimeout'])
|
|
self.active_timeout = info['Payload']['ActiveTimeout'].to_i
|
|
end
|
|
|
|
# All exploits can increase the delay when waiting for a session.
|
|
# However, this only applies to aggressive exploits.
|
|
if aggressive?
|
|
register_advanced_options(
|
|
[
|
|
OptInt.new('WfsDelay', [ false, "Additional delay when waiting for a session", 0 ])
|
|
], Msf::Exploit)
|
|
end
|
|
|
|
# Allow all exploits to leverage context keyed encoding
|
|
register_advanced_options(
|
|
[
|
|
OptBool.new('EnableContextEncoding', [ false, "Use transient context when encoding payloads", false ]),
|
|
OptPath.new('ContextInformationFile', [ false, "The information file that contains context information", nil ])
|
|
], Msf::Exploit)
|
|
|
|
# Allow all exploits to disable their payload handlers
|
|
register_advanced_options(
|
|
[
|
|
OptBool.new('DisablePayloadHandler', [ false, "Disable the handler code for the selected payload", false ])
|
|
], Msf::Exploit)
|
|
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.
|
|
#
|
|
##
|
|
|
|
#
|
|
# 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.
|
|
#
|
|
# This method is designed to be overriden by exploit modules.
|
|
#
|
|
def exploit
|
|
end
|
|
|
|
#
|
|
# Performs last-minute sanity checking of auxiliary parameters. This method
|
|
# is called during automated exploitation attempts and allows an
|
|
# auxiliary module to filter bad attempts, obtain more information, and choose
|
|
# better parameters based on the available data. Returning anything that
|
|
# evaluates to "false" will cause this specific auxiliary attempt to
|
|
# be skipped. This method can and will change datastore values and
|
|
# may interact with the backend database.
|
|
#
|
|
def autofilter
|
|
true
|
|
end
|
|
|
|
#
|
|
# Provides a list of ports that can be used for matching this module
|
|
# against target systems.
|
|
#
|
|
def autofilter_ports
|
|
@autofilter_ports || []
|
|
end
|
|
|
|
#
|
|
# Provides a list of services that can be used for matching this module
|
|
# against target systems.
|
|
#
|
|
def autofilter_services
|
|
@autofilter_services || []
|
|
end
|
|
|
|
#
|
|
# Adds a port into the list of ports
|
|
#
|
|
def register_autofilter_ports(ports=[])
|
|
@autofilter_ports ||= []
|
|
@autofilter_ports << ports
|
|
@autofilter_ports.flatten!
|
|
@autofilter_ports.uniq!
|
|
end
|
|
|
|
def register_autofilter_services(services=[])
|
|
@autofilter_services ||= []
|
|
@autofilter_services << services
|
|
@autofilter_services.flatten!
|
|
@autofilter_services.uniq!
|
|
end
|
|
|
|
#
|
|
# Prepares the module for exploitation, initializes any state, and starts
|
|
# the payload handler.
|
|
#
|
|
def setup
|
|
# Reset the session counts to zero.
|
|
reset_session_counts
|
|
|
|
return if not payload_instance
|
|
return if not handler_enabled?
|
|
|
|
# Configure the payload handler
|
|
payload_instance.exploit_config = {
|
|
'active_timeout' => self.active_timeout
|
|
}
|
|
|
|
# Set up the payload handlers
|
|
payload_instance.setup_handler
|
|
|
|
# Start the payload handler
|
|
payload_instance.start_handler
|
|
|
|
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 and handler_enabled?)
|
|
payload_instance.cleanup_handler
|
|
end
|
|
self.abort_sockets if self.respond_to?(:abort_sockets)
|
|
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
|
|
|
|
#
|
|
# Allows arbitrary shellcode to be encoded from within an exploit
|
|
#
|
|
def encode_shellcode_stub(code, badchars=payload_badchars)
|
|
platform = self.platform
|
|
if(self.payload_instance)
|
|
self.payload_instance.platform
|
|
end
|
|
compatible_encoders.each do |name, mod|
|
|
begin
|
|
enc = framework.encoders.create(name)
|
|
raw = enc.encode(code, badchars, nil, platform)
|
|
return raw if raw
|
|
rescue ::Exception
|
|
end
|
|
end
|
|
nil
|
|
end
|
|
|
|
#
|
|
# Allows the payload handler to spawn a new monitor
|
|
#
|
|
def add_handler(opts={})
|
|
return if not payload_instance
|
|
return if not handler_enabled?
|
|
payload_instance.add_handler(opts)
|
|
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, platform = nil, arch = nil, explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_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
|
|
|
|
# If this is a generic payload, then we should specify the platform
|
|
# and architecture so that it knows how to pass things on.
|
|
if real_payload.kind_of?(Msf::Payload::Generic)
|
|
# Convert the architecture specified into an array.
|
|
if arch and arch.kind_of?(String)
|
|
arch = [ arch ]
|
|
end
|
|
|
|
# Define the explicit platform and architecture information only if
|
|
# it's been specified.
|
|
if platform
|
|
real_payload.explicit_platform = Msf::Module::PlatformList.transform(platform)
|
|
end
|
|
|
|
if arch
|
|
real_payload.explicit_arch = arch
|
|
end
|
|
|
|
# Force it to reset so that it will find updated information.
|
|
real_payload.reset
|
|
end
|
|
|
|
# Duplicate the exploit payload requirements
|
|
reqs = self.payload_info.dup
|
|
|
|
# Pass save register requirements to the NOP generator
|
|
reqs['Space'] = payload_space(explicit_target)
|
|
reqs['SaveRegisters'] = nop_save_registers(explicit_target)
|
|
reqs['Prepend'] = payload_prepend(explicit_target)
|
|
reqs['PrependEncoder'] = payload_prepend_encoder(explicit_target)
|
|
reqs['BadChars'] = payload_badchars(explicit_target)
|
|
reqs['Append'] = payload_append(explicit_target)
|
|
reqs['AppendEncoder'] = payload_append_encoder(explicit_target)
|
|
reqs['MaxNops'] = payload_max_nops(explicit_target)
|
|
reqs['MinNops'] = payload_min_nops(explicit_target)
|
|
reqs['Encoder'] = datastore['ENCODER']
|
|
reqs['Nop'] = datastore['NOP']
|
|
reqs['EncoderType'] = payload_encoder_type(explicit_target)
|
|
reqs['EncoderOptions'] = payload_encoder_options(explicit_target)
|
|
reqs['ExtendedOptions'] = payload_extended_options(explicit_target)
|
|
reqs['Exploit'] = self
|
|
|
|
# Pass along the encoder don't fall through flag
|
|
reqs['EncoderDontFallThrough'] = datastore['EncoderDontFallThrough']
|
|
|
|
# Incorporate any context encoding requirements that are needed
|
|
define_context_encoding_reqs(reqs)
|
|
|
|
# Call the encode begin routine.
|
|
encode_begin(real_payload, reqs)
|
|
|
|
# Generate the encoded payload.
|
|
encoded = EncodedPayload.create(real_payload, reqs)
|
|
|
|
# Call the encode end routine which is expected to return the actual
|
|
# encoded payload instance.
|
|
return encode_end(real_payload, reqs, encoded)
|
|
end
|
|
|
|
#
|
|
# Re-generates an encoded payload, typically called after something in the
|
|
# datastore has changed. An optional platform and architecture can be
|
|
# supplied as well.
|
|
#
|
|
def regenerate_payload(platform = nil, arch = nil, explicit_target = nil)
|
|
generate_single_payload(nil, platform, arch, explicit_target)
|
|
end
|
|
|
|
#
|
|
# Called prior to encoding a payload.
|
|
#
|
|
def encode_begin(real_payload, reqs)
|
|
end
|
|
|
|
#
|
|
# Called after an encoded payload has been generated. This gives exploits
|
|
# or mixins a chance to alter the encoded payload.
|
|
#
|
|
def encode_end(real_payload, reqs, encoded)
|
|
encoded
|
|
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.
|
|
#
|
|
##
|
|
|
|
#
|
|
# Returns true if the exploit module supports the check method.
|
|
#
|
|
def supports_check?
|
|
derived_implementor?(Msf::Exploit, 'check')
|
|
end
|
|
|
|
#
|
|
# Returns true if the exploit module supports the exploit method.
|
|
#
|
|
def supports_exploit?
|
|
derived_implementor?(Msf::Exploit, 'exploit')
|
|
end
|
|
|
|
#
|
|
# Returns a hash of the capabilities this exploit module has support for,
|
|
# such as whether or not it supports check and exploit.
|
|
#
|
|
def capabilities
|
|
{
|
|
'check' => supports_check?,
|
|
'exploit' => supports_exploit?
|
|
}
|
|
end
|
|
|
|
##
|
|
#
|
|
# Getters/Setters
|
|
#
|
|
# Querying and set interfaces for some of the exploit's attributes.
|
|
#
|
|
##
|
|
|
|
#
|
|
# Returns MODULE_EXPLOIT to indicate that this is an exploit module.
|
|
#
|
|
def self.type
|
|
Msf::MODULE_EXPLOIT
|
|
end
|
|
|
|
#
|
|
# Returns MODULE_EXPLOIT to indicate that this is an exploit module.
|
|
#
|
|
def type
|
|
Msf::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 true if the exploit has an aggressive stance.
|
|
#
|
|
def aggressive?
|
|
(stance == Stance::Aggressive || stance.include?(Stance::Aggressive))
|
|
end
|
|
|
|
#
|
|
# Returns if the exploit has a passive stance.
|
|
#
|
|
def passive?
|
|
(stance == Stance::Passive || stance.include?(Stance::Passive))
|
|
end
|
|
|
|
#
|
|
# Returns the active target for this exploit. If not target has been
|
|
# defined, nil is returned. If no target was defined but there is a
|
|
# default target, that one will be automatically used.
|
|
#
|
|
def target
|
|
target_idx = target_index
|
|
|
|
return (target_idx) ? targets[target_idx.to_i] : nil
|
|
end
|
|
|
|
#
|
|
# The target index that has been selected.
|
|
#
|
|
def target_index
|
|
target_idx = datastore['TARGET']
|
|
|
|
# Use the default target if one was not supplied.
|
|
if (target_idx == nil and default_target and default_target >= 0)
|
|
target_idx = default_target
|
|
end
|
|
return (target_idx) ? target_idx.to_i : nil
|
|
end
|
|
|
|
#
|
|
# Returns the target's platform, or the one assigned to the module itself.
|
|
#
|
|
def target_platform
|
|
(target and target.platform) ? target.platform : platform
|
|
end
|
|
|
|
#
|
|
# Returns the target's architecture, or the one assigned to the module
|
|
# itself.
|
|
#
|
|
def target_arch
|
|
(target and target.arch) ? target.arch : (arch == []) ? nil : arch
|
|
end
|
|
|
|
|
|
#
|
|
# Returns whether the requested payload is compatible with the module.
|
|
#
|
|
# @param [String] payload_name The payload name
|
|
# @return [TrueClass] Payload is compatible.
|
|
# @return [FalseClass] Payload is not compatible.
|
|
#
|
|
def is_payload_compatible?(payload_name)
|
|
payload_names = compatible_payloads.collect { |entry| entry[0] }
|
|
|
|
payload_names.include?(payload_name)
|
|
end
|
|
|
|
#
|
|
# Returns a list of compatible payloads based on platform, architecture,
|
|
# and size requirements.
|
|
#
|
|
def compatible_payloads
|
|
payloads = []
|
|
|
|
|
|
c_platform = (target and target.platform) ? target.platform : platform
|
|
c_arch = (target and target.arch) ? target.arch : (arch == []) ? nil : arch
|
|
c_arch ||= [ ARCH_X86 ]
|
|
|
|
framework.payloads.each_module(
|
|
'Platform' => c_platform,
|
|
'Arch' => c_arch ) { |name, mod|
|
|
|
|
# Skip over payloads that are too big
|
|
if ((payload_space) and
|
|
(framework.payloads.sizes[name]) and
|
|
(framework.payloads.sizes[name] > payload_space))
|
|
dlog("#{refname}: Skipping payload #{name} for being too large", 'core',
|
|
LEV_1)
|
|
next
|
|
end
|
|
|
|
# Are we compatible in terms of conventions and connections and
|
|
# what not?
|
|
next if (compatible?(framework.payloads.instance(name)) == false)
|
|
|
|
# If the payload is privileged but the exploit does not give
|
|
# privileged access, then fail it.
|
|
next if (self.privileged == false and framework.payloads.instance(name).privileged == true)
|
|
|
|
# This one be compatible!
|
|
payloads << [ name, mod ]
|
|
}
|
|
|
|
return payloads;
|
|
end
|
|
|
|
#
|
|
# Returns a list of compatible encoders based on architecture
|
|
#
|
|
def compatible_encoders
|
|
encoders = []
|
|
|
|
c_platform = (target and target.platform) ? target.platform : platform
|
|
c_arch = (target and target.arch) ? target.arch : (arch == []) ? nil : arch
|
|
|
|
framework.encoders.each_module_ranked(
|
|
'Arch' => c_arch, 'Platform' => c_platform) { |name, mod|
|
|
|
|
encoders << [ name, mod ]
|
|
}
|
|
|
|
return encoders;
|
|
end
|
|
|
|
#
|
|
# This method returns the number of bytes that should be adjusted to the
|
|
# stack pointer prior to executing any code. The number of bytes to adjust
|
|
# is indicated to the routine through the payload 'StackAdjustment'
|
|
# attribute or through a target's payload 'StackAdjustment' attribute.
|
|
#
|
|
def stack_adjustment
|
|
if (target and target.payload_stack_adjustment)
|
|
adj = target.payload_stack_adjustment
|
|
else
|
|
adj = payload_info['StackAdjustment']
|
|
end
|
|
|
|
# Get the architecture for the current target or use the one specific to
|
|
# this exploit
|
|
arch = (target and target.arch) ? target.arch : self.arch
|
|
|
|
# Default to x86 if we can't find a list of architectures
|
|
if (arch and arch.empty? == false)
|
|
arch = arch.join(", ")
|
|
else
|
|
arch = 'x86'
|
|
end
|
|
|
|
(adj != nil) ? Rex::Arch::adjust_stack_pointer(arch, adj) || '' : ''
|
|
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. This automatically takes into
|
|
# account any require stack adjustments.
|
|
#
|
|
def payload_prepend(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_prepend)
|
|
p = explicit_target.payload_prepend
|
|
else
|
|
p = payload_info['Prepend'] || ''
|
|
end
|
|
|
|
stack_adjustment + p
|
|
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(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_append)
|
|
explicit_target.payload_append
|
|
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(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_prepend_encoder)
|
|
p = explicit_target.payload_prepend_encoder
|
|
else
|
|
p = payload_info['PrependEncoder'] || ''
|
|
end
|
|
|
|
p
|
|
end
|
|
|
|
#
|
|
# Return any text that should be appended 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_append_encoder(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_append_encoder)
|
|
p = explicit_target.payload_append_encoder
|
|
else
|
|
p = payload_info['AppendEncoder'] || ''
|
|
end
|
|
|
|
p
|
|
end
|
|
|
|
#
|
|
# Maximum number of nops to use as a hint to the framework.
|
|
# Nil signifies that the framework should decide.
|
|
#
|
|
def payload_max_nops(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_max_nops)
|
|
explicit_target.payload_max_nops
|
|
else
|
|
payload_info['MaxNops'] || nil
|
|
end
|
|
end
|
|
|
|
#
|
|
# Minimum number of nops to use as a hint to the framework.
|
|
# Nil signifies that the framework should decide.
|
|
#
|
|
def payload_min_nops(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_min_nops)
|
|
explicit_target.payload_min_nops
|
|
else
|
|
payload_info['MinNops'] || nil
|
|
end
|
|
end
|
|
|
|
#
|
|
# Returns the maximum amount of room the exploit has for a payload.
|
|
#
|
|
def payload_space(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_space)
|
|
explicit_target.payload_space
|
|
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(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_badchars)
|
|
explicit_target.payload_badchars
|
|
else
|
|
payload_info['BadChars']
|
|
end
|
|
end
|
|
|
|
#
|
|
# Returns the payload encoder type that is associated with either the
|
|
# current target of the exploit in general.
|
|
#
|
|
def payload_encoder_type(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_encoder_type)
|
|
explicit_target.payload_encoder_type
|
|
else
|
|
payload_info['EncoderType']
|
|
end
|
|
end
|
|
|
|
#
|
|
# Returns the payload encoder option hash that is used to initialize the
|
|
# datastore of the encoder that is selected when generating an encoded
|
|
# payload.
|
|
#
|
|
def payload_encoder_options(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.payload_encoder_options)
|
|
explicit_target.payload_encoder_options
|
|
else
|
|
payload_info['EncoderOptions']
|
|
end
|
|
end
|
|
|
|
#
|
|
# Returns the payload extended options hash which is used to provide
|
|
# a location to store extended information that may be useful to
|
|
# a particular type of payload or mixin.
|
|
#
|
|
def payload_extended_options(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if explicit_target and explicit_target.payload_extended_options
|
|
explicit_target.payload_extended_options
|
|
else
|
|
payload_info['ExtendedOptions']
|
|
end
|
|
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(explicit_target = nil)
|
|
explicit_target ||= target
|
|
|
|
if (explicit_target and explicit_target.save_registers)
|
|
return explicit_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)
|
|
# If we're debugging, then make_nops will return a safe sled. We
|
|
# currently assume x86.
|
|
if debugging?
|
|
return "\x90" * count
|
|
end
|
|
|
|
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.copy_ui(self)
|
|
|
|
nop_sled = nop.generate_sled(count,
|
|
'BadChars' => payload_badchars || '',
|
|
'SaveRegisters' => save_regs)
|
|
|
|
if nop_sled && nop_sled.length == count
|
|
break
|
|
else
|
|
wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit", 'core', LEV_0)
|
|
end
|
|
rescue
|
|
wlog("#{self.refname}: Nop generator #{nop.refname} failed to generate sled for exploit: #{$!}",
|
|
'core', LEV_0)
|
|
end
|
|
}
|
|
|
|
nop_sled
|
|
end
|
|
|
|
##
|
|
#
|
|
# Utility methods for generating random text that implicitly uses the
|
|
# exploit's bad character set.
|
|
#
|
|
##
|
|
|
|
#
|
|
# Generate random text characters avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text(length, bad=payload_badchars)
|
|
if debugging?
|
|
"A" * length
|
|
else
|
|
Rex::Text.rand_text(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate random english-like avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text_english(length, bad=payload_badchars)
|
|
if debugging?
|
|
"A" * length
|
|
else
|
|
Rex::Text.rand_text_english(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate random high ascii characters avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text_highascii(length, bad=payload_badchars)
|
|
if debugging?
|
|
"A" * length
|
|
else
|
|
Rex::Text.rand_text_highascii(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate random alpha characters avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text_alpha(length, bad=payload_badchars)
|
|
if debugging?
|
|
"A" * length
|
|
else
|
|
Rex::Text.rand_text_alpha(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate random alpha upper characters avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text_alpha_upper(length, bad=payload_badchars)
|
|
if debugging?
|
|
"A" * length
|
|
else
|
|
Rex::Text.rand_text_alpha_upper(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate random alpha lower characters avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text_alpha_lower(length, bad=payload_badchars)
|
|
if debugging?
|
|
"a" * length
|
|
else
|
|
Rex::Text.rand_text_alpha_lower(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate random alphanumeric characters avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text_alphanumeric(length, bad=payload_badchars)
|
|
if debugging?
|
|
"A" * length
|
|
else
|
|
Rex::Text.rand_text_alphanumeric(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate random numeric characters avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_text_numeric(length, bad=payload_badchars)
|
|
if debugging?
|
|
"0" * length
|
|
else
|
|
Rex::Text.rand_text_numeric(length, bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate a random character avoiding the exploit's bad
|
|
# characters.
|
|
#
|
|
def rand_char(bad=payload_badchars)
|
|
if debugging?
|
|
"A"
|
|
else
|
|
Rex::Text.rand_char(bad)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Generate a non-repeating static random string
|
|
#
|
|
def pattern_create(length, sets = nil)
|
|
Rex::Text.pattern_create(length, sets)
|
|
end
|
|
|
|
#
|
|
# The default "wait for session" delay is zero for all exploits.
|
|
#
|
|
def wfs_delay
|
|
(datastore['WfsDelay'] || 0).to_i
|
|
end
|
|
|
|
#
|
|
# Allow the user to disable the payload handler
|
|
#
|
|
def handler_enabled?
|
|
not datastore['DisablePayloadHandler']
|
|
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 if not payload_instance
|
|
return if not handler_enabled?
|
|
return payload_instance.handler(*args)
|
|
end
|
|
|
|
##
|
|
#
|
|
# Session tracking
|
|
#
|
|
##
|
|
|
|
#
|
|
# This is called by the payload when a new session is created
|
|
#
|
|
def on_new_session(session)
|
|
self.session_count += 1
|
|
self.successful = true
|
|
end
|
|
|
|
#
|
|
# A boolean for whether a session has been created yet
|
|
#
|
|
def session_created?
|
|
(self.session_count > 0) ? true : false
|
|
end
|
|
|
|
#
|
|
# Reset the session counter to zero (which occurs during set up of the
|
|
# exploit prior to calling exploit).
|
|
#
|
|
def reset_session_counts
|
|
self.session_count = 0
|
|
end
|
|
|
|
|
|
##
|
|
# Failure tracking
|
|
##
|
|
|
|
# Raises a Msf::Exploit::Failed exception. It overrides the fail_with method
|
|
# in lib/msf/core/module.rb
|
|
#
|
|
# @param reason [String] A constant from Msf::Module::Failure.
|
|
# If the reason does not come from there, then it will default to
|
|
# Msf::Module::Failure::Unknown.
|
|
# @param msg [String] (Optional) A message about the failure.
|
|
# @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception.
|
|
# @return [void]
|
|
# @see Msf::Module::Failure
|
|
# @see Msf::Module#fail_with
|
|
# @example
|
|
# fail_with(Msf::Module::Failure::NoAccess, 'Unable to login to upload payload')
|
|
def fail_with(reason,msg=nil)
|
|
# The reason being registered here will be used later on, so it's important we don't actually
|
|
# provide a made-up one.
|
|
allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)}
|
|
if allowed_values.include?(reason)
|
|
self.fail_reason = reason
|
|
else
|
|
self.fail_reason = Msf::Module::Failure::Unknown
|
|
end
|
|
|
|
self.fail_detail = msg
|
|
raise Msf::Exploit::Failed, (msg || "No failure message given")
|
|
end
|
|
|
|
def setup_fail_detail_from_exception e
|
|
# Build a user-friendly error message
|
|
msg = "#{e}"
|
|
unless e.class == Msf::Exploit::Failed
|
|
msg = "#{e.class} #{e}"
|
|
end
|
|
|
|
self.error = e
|
|
|
|
# Record the detailed reason
|
|
self.fail_detail ||= e.to_s
|
|
msg
|
|
end
|
|
|
|
#
|
|
# Handle the exception
|
|
#
|
|
def handle_exception e
|
|
msg = setup_fail_detail_from_exception e
|
|
|
|
case e
|
|
when Msf::Exploit::Complete
|
|
# Nothing to show in this case
|
|
return
|
|
|
|
when Msf::Exploit::Failed
|
|
self.print_error("Exploit aborted due to failure: #{self.fail_reason}: #{msg}")
|
|
|
|
# The caller should have already set self.fail_reason
|
|
if self.fail_reason == Msf::Exploit::Failure::None
|
|
self.fail_reason = Msf::Exploit::Failure::Unknown
|
|
end
|
|
|
|
when Rex::ConnectionError
|
|
self.fail_reason = Msf::Exploit::Failure::Unreachable
|
|
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
|
|
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
|
|
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
|
|
|
|
when Rex::BindFailed
|
|
self.fail_reason = Msf::Exploit::Failure::BadConfig
|
|
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
|
|
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
|
|
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
|
|
|
|
when Timeout::Error
|
|
self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
|
|
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
|
|
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
|
|
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
|
|
else
|
|
|
|
# Compare as a string since not all error classes may be loaded
|
|
case msg
|
|
when /access.denied|Login Failed/i # Covers SMB as well as some generic errors
|
|
self.fail_reason = Msf::Exploit::Failure::NoAccess
|
|
when /connection reset/i
|
|
self.fail_reason = Msf::Exploit::Failure::Disconnected
|
|
when /connection timed out|SSL_connect|unreachable|connection was refused/i
|
|
self.fail_reason = Msf::Exploit::Failure::Unreachable
|
|
when /unable.*target/i
|
|
self.fail_reason = Msf::Exploit::Failure::NoTarget
|
|
when /execution expired/i
|
|
self.fail_reason = Msf::Exploit::Failure::TimeoutExpired
|
|
when /(doesn.t|not).*vulnerable|may.*patched/i
|
|
self.fail_reason = Msf::Exploit::Failure::NotVulnerable
|
|
end
|
|
|
|
# The caller should have already set self.fail_reason
|
|
if self.fail_reason == Msf::Exploit::Failure::None
|
|
self.fail_reason = Msf::Exploit::Failure::Unknown
|
|
end
|
|
|
|
if self.fail_reason == Msf::Exploit::Failure::Unknown
|
|
self.print_error("Exploit failed: #{msg}")
|
|
else
|
|
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
|
|
end
|
|
|
|
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
|
|
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
|
|
end
|
|
|
|
# Record the error to various places
|
|
self.framework.events.on_module_error(self, msg)
|
|
|
|
# Report the failure (and attempt) in the database
|
|
self.report_failure
|
|
end
|
|
|
|
def report_failure
|
|
return unless framework.db and framework.db.active
|
|
|
|
info = {
|
|
:timestamp => Time.now.utc,
|
|
:workspace => framework.db.find_workspace(self.workspace),
|
|
:module => self.fullname,
|
|
:fail_reason => self.fail_reason,
|
|
:fail_detail => self.fail_detail,
|
|
:target_name => self.target.name,
|
|
:username => self.owner,
|
|
:refs => self.references,
|
|
:run_id => self.datastore['RUN_ID']
|
|
}
|
|
|
|
if self.datastore['RHOST'] and self.options['RHOST']
|
|
info[:host] = self.datastore['RHOST']
|
|
end
|
|
|
|
if self.datastore['RPORT'] and self.options['RPORT']
|
|
info[:port] = self.datastore['RPORT']
|
|
if self.class.ancestors.include?(Msf::Exploit::Remote::Tcp)
|
|
info[:proto] = 'tcp'
|
|
end
|
|
end
|
|
|
|
framework.db.report_exploit_failure(info)
|
|
end
|
|
|
|
##
|
|
#
|
|
# Aliases
|
|
#
|
|
# These allow access to methods inside this class, even if exploits use mixins that
|
|
# override them.
|
|
#
|
|
##
|
|
|
|
# Give exploits the ability to use the original +regenerate_payload+ so
|
|
# they can avoid needing additional arguments added by overridden versions.
|
|
# Used specifically by things that include +TcpServer+ (or a descendant)
|
|
# but which are active exploits.
|
|
alias :exploit_regenerate_payload :regenerate_payload
|
|
|
|
|
|
##
|
|
#
|
|
# Attributes
|
|
#
|
|
##
|
|
|
|
#
|
|
# The reason why the exploit was not successful (one of Msf::Module::Failure)
|
|
#
|
|
attr_accessor :fail_reason
|
|
|
|
#
|
|
# Detailed exception string indicating why the exploit was not successful
|
|
#
|
|
attr_accessor :fail_detail
|
|
|
|
#
|
|
# 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
|
|
|
|
#
|
|
# The number of active sessions created by this instance
|
|
#
|
|
attr_reader :session_count
|
|
|
|
#
|
|
# The boolean indicating whether the exploit succeeded
|
|
#
|
|
attr_reader :successful
|
|
|
|
|
|
protected
|
|
|
|
#
|
|
# Writable copy of the list of targets.
|
|
#
|
|
attr_writer :targets
|
|
#
|
|
# Writable copy of the default target.
|
|
#
|
|
attr_writer :default_target
|
|
#
|
|
# Writable copy of the payload requirement hash.
|
|
#
|
|
attr_writer :payload_info
|
|
#
|
|
# Number of sessions created by this exploit instance.
|
|
#
|
|
attr_writer :session_count
|
|
#
|
|
# Maximum number of seconds for active handlers
|
|
#
|
|
attr_accessor :active_timeout
|
|
|
|
#
|
|
# Boolean indicating whether the exploit succeeded
|
|
#
|
|
attr_writer :successful
|
|
|
|
|
|
#
|
|
# Overrides the base class method and serves to initialize default
|
|
# compatibilities for exploits
|
|
#
|
|
def init_compat
|
|
super
|
|
|
|
#
|
|
# Merge in payload compatible defaults
|
|
#
|
|
p = module_info['Compat']['Payload']
|
|
|
|
CompatDefaults::Payload.each_pair { |k,v|
|
|
(p[k]) ? p[k] << " #{v}" : p[k] = v
|
|
}
|
|
|
|
#
|
|
# Set the default save registers if none have been explicitly
|
|
# specified.
|
|
#
|
|
if (module_info['SaveRegisters'] == nil)
|
|
module_info['SaveRegisters'] = [ 'esp', 'ebp' ]
|
|
end
|
|
end
|
|
|
|
#
|
|
# Gets the memory map file and other context information that is
|
|
# required when wanting to support context keyed encoding
|
|
#
|
|
def define_context_encoding_reqs(reqs)
|
|
return if datastore['EnableContextEncoding'] != true
|
|
|
|
# At present, we don't support any automatic methods of obtaining
|
|
# context information. In the future, we might support obtaining
|
|
# temporal information remotely.
|
|
|
|
# Pass along the information specified in our exploit datastore as
|
|
# encoder options
|
|
reqs['EncoderOptions'] = {} if reqs['EncoderOptions'].nil?
|
|
reqs['EncoderOptions']['EnableContextEncoding'] = datastore['EnableContextEncoding']
|
|
reqs['EncoderOptions']['ContextInformationFile'] = datastore['ContextInformationFile']
|
|
end
|
|
|
|
end
|
|
|
|
end
|