# -*- 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) if payload_instance && handler_enabled? payload_instance.handler(*args) end end def interrupt_handler if payload_instance && handler_enabled? && payload_instance.respond_to?(:interrupt_wait_for_session) payload_instance.interrupt_wait_for_session() end 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 # Interrupt any session waiters in the handler self.interrupt_handler 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 unless datastore['EnableContextEncoding'] # 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