# -*- coding: binary -*- require 'msf/base' module Msf module Simple ### # # A simplified exploit wrapper. # ### module Exploit include Module # # Wraps the exploitation process in a simple single method. The options # hash can have the following values passed in it: # # Encoder # # The encoder module that should be used. # # Payload # # The payload module name that should be used. # # Target # # The selected target index. # # Nop # # The NOP generator that should be used in preference. # # OptionStr # # A string of comma separated option values that should be imported into # the datastore. # # Options # # A hash of values to be imported directly into the datastore. # # LocalInput # # The local input handle that data can be read in from. # # LocalOutput # # The local output through which data can be displayed. # # RunAsJob # # Whether or not the exploit should be run in the context of a background # job. # def self.exploit_simple(oexploit, opts, &block) # Trap and print errors here (makes them UI-independent) begin # Clone the module to prevent changes to the original instance exploit = oexploit.replicant Msf::Simple::Framework.simplify_module( exploit, false ) yield(exploit) if block_given? # Import options from the OptionStr or Option hash. exploit._import_extra_options(opts) # Make sure parameters are valid. if (opts['Payload'] == nil) raise MissingPayloadError.new, caller end # Verify the options exploit.options.validate(exploit.datastore) # Start it up driver = ExploitDriver.new(exploit.framework) # Initialize the driver instance driver.exploit = exploit driver.payload = exploit.framework.payloads.create(opts['Payload']) # Set the force wait for session flag if the caller requested force # blocking. This is so that passive exploits can be blocked on from # things like the cli. driver.force_wait_for_session = true if (opts['ForceBlocking'] == true) # Was the payload valid? if (driver.payload == nil) raise MissingPayloadError, "You specified an invalid payload: #{opts['Payload']}", caller end # Use the supplied encoder, if any. If one was not specified, then # nil will be assigned causing the exploit to default to picking the # best encoder. exploit.datastore['ENCODER'] = opts['Encoder'] if opts['Encoder'] # Force the payload to share the exploit's datastore driver.payload.share_datastore(driver.exploit.datastore) # Verify the payload options driver.payload.options.validate(driver.payload.datastore) # If we still have no target index, try to use the datastore's index target_idx = opts['Target'] || exploit.default_target # Convert it to an integer if it's valid if (target_idx) target_idx = target_idx.to_i end if (target_idx == nil or target_idx < 0) raise MissingTargetError, "You must select a target.", caller end driver.target_idx = target_idx # Set the payload and exploit's subscriber values if ! opts['Quiet'] driver.exploit.init_ui(opts['LocalInput'] || exploit.user_input, opts['LocalOutput'] || exploit.user_output) driver.payload.init_ui(opts['LocalInput'] || exploit.user_input, opts['LocalOutput'] || exploit.user_output) else driver.exploit.init_ui(nil, nil) driver.payload.init_ui(nil, nil) end if (opts['RunAsJob']) driver.use_job = true end # Let's rock this party driver.run # Save the job identifier this exploit is running as exploit.job_id = driver.job_id # Propagate this back to the caller for console mgmt oexploit.job_id = exploit.job_id rescue ::Interrupt exploit.error = $! raise $! rescue ::Exception => e exploit.error = e exploit.print_error("Exploit failed: #{e}") elog("Exploit failed (#{exploit.refname}): #{e}", 'core', LEV_0) dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3) end return driver.session if driver nil 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 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 # # Calls the class method. # def exploit_simple(opts, &block) Msf::Simple::Exploit.exploit_simple(self, opts, &block) end # # Initiates a check, setting up the exploit to be used. The following # options can be specified: # # LocalInput # # The local input handle that data can be read in from. # # LocalOutput # # The local output through which data can be displayed. # def self.check_simple(mod, opts) if opts['LocalInput'] mod.init_ui(opts['LocalInput'], opts['LocalOutput']) end # Validate the option container state so that options will # be normalized mod.validate mod.setup # Run check mod.check end # # Calls the class method. # def check_simple(opts) Msf::Simple::Exploit.check_simple(self, opts) end end end end