287 lines
8.0 KiB
Ruby
287 lines
8.0 KiB
Ruby
# -*- 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
|
|
|