ff295aa5bc
This updates the #validate methods to check if the selected payload is in the modules list of compatible payloads instead of just checking the platform.
283 lines
8.1 KiB
Ruby
283 lines
8.1 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
module Msf
|
|
|
|
###
|
|
#
|
|
# This class drives the exploitation process from start to finish for a given
|
|
# exploit module instance. It's responsible for payload generation, encoding,
|
|
# and padding as well as initialization handlers and finally launching the
|
|
# exploit.
|
|
#
|
|
###
|
|
class ExploitDriver
|
|
|
|
#
|
|
# Initializes the exploit driver using the supplied framework instance.
|
|
#
|
|
def initialize(framework)
|
|
self.payload = nil
|
|
self.exploit = nil
|
|
self.use_job = false
|
|
self.job_id = nil
|
|
self.force_wait_for_session = false
|
|
self.keep_handler = false
|
|
self.semaphore = Mutex.new
|
|
end
|
|
|
|
#
|
|
# Specification of the exploit target index.
|
|
#
|
|
def target_idx=(target_idx)
|
|
if (target_idx)
|
|
# Make sure the target index is valid
|
|
if (target_idx >= exploit.targets.length)
|
|
raise Rex::ArgumentError, "Invalid target index.", caller
|
|
end
|
|
end
|
|
|
|
# Set the active target
|
|
@target_idx = target_idx
|
|
end
|
|
|
|
#
|
|
# This method returns the currently selected target index.
|
|
#
|
|
def target_idx
|
|
@target_idx
|
|
end
|
|
|
|
#
|
|
# Checks to see if the supplied payload is compatible with the
|
|
# current exploit. Assumes that target_idx is valid.
|
|
#
|
|
def compatible_payload?(payload)
|
|
!exploit.compatible_payloads.find { |refname, _| refname == payload.refname }.nil?
|
|
end
|
|
|
|
##
|
|
#
|
|
# Exploit execution
|
|
#
|
|
##
|
|
|
|
#
|
|
# Makes sure everything's in tip-top condition prior to launching the
|
|
# exploit. For things that aren't good to go, an exception is thrown.
|
|
#
|
|
def validate
|
|
# First, validate that a target has been selected
|
|
if (target_idx == nil)
|
|
raise MissingTargetError,
|
|
"A payload cannot be selected until a target is specified.",
|
|
caller
|
|
end
|
|
|
|
# Next, validate that a payload has been selected
|
|
if (payload == nil)
|
|
raise MissingPayloadError,
|
|
"A payload has not been selected.", caller
|
|
end
|
|
|
|
# Make sure the payload is compatible after all
|
|
unless compatible_payload?(payload)
|
|
raise IncompatiblePayloadError.new(payload.refname), "#{payload.refname} is not a compatible payload.", caller
|
|
end
|
|
|
|
unless exploit.respond_to?(:allow_no_cleanup) && exploit.allow_no_cleanup
|
|
# Being able to cleanup requires a session to be created from a handler, and for that
|
|
# session to be able to be able to clean up files
|
|
can_cleanup = payload.handler_klass != Msf::Handler::None && payload&.session&.can_cleanup_files
|
|
if exploit.needs_cleanup && !can_cleanup
|
|
raise IncompatiblePayloadError.new(payload.refname), "#{payload.refname} cannot cleanup files created during exploit. To run anyway, set AllowNoCleanup to true"
|
|
end
|
|
|
|
if exploit.needs_cleanup && !exploit.handler_enabled?
|
|
raise ValidationError.new('Cannot cleanup files created during exploit if payload handler is disabled. To run anyway, set AllowNoCleanup to true')
|
|
end
|
|
end
|
|
|
|
# Associate the payload instance with the exploit
|
|
payload.assoc_exploit = exploit
|
|
|
|
# Finally, validate options on the exploit module to ensure that things
|
|
# are ready to operate as they should.
|
|
exploit.options.validate(exploit.datastore)
|
|
|
|
# Validate the payload's options. The payload's datastore is
|
|
# most likely shared against the exploit's datastore, but in case it
|
|
# isn't.
|
|
payload.options.validate(payload.datastore)
|
|
|
|
return true
|
|
end
|
|
|
|
#
|
|
# Kicks off an exploitation attempt and performs the following four major
|
|
# operations:
|
|
#
|
|
# - Generates the payload
|
|
# - Initializes & monitors the handler
|
|
# - Launches the exploit
|
|
# - Cleans up the handler
|
|
#
|
|
def run
|
|
# First thing's first -- validate the state. Make sure all requirement
|
|
# parameters are set, including those that are derived from the
|
|
# datastore.
|
|
validate()
|
|
|
|
# After validation has occurred, it's time to set some values on the
|
|
# exploit instance and begin preparing the payload
|
|
exploit.datastore['TARGET'] = target_idx
|
|
|
|
# Default the session to nil
|
|
self.session = nil
|
|
|
|
# Explicitly clear the module's job_id in case it was set in a previous
|
|
# run
|
|
exploit.job_id = nil
|
|
|
|
# If we are being instructed to run as a job then let's create that job
|
|
# like a good person.
|
|
if (use_job or exploit.passive?)
|
|
# Since references to the exploit and payload will hang around for
|
|
# awhile in the job, make sure we copy them so further changes to
|
|
# the datastore don't alter settings in existing jobs
|
|
e = exploit.replicant
|
|
p = payload.replicant
|
|
|
|
# Assign the correct exploit instance to the payload
|
|
p.assoc_exploit = e
|
|
|
|
# Generate the encoded version of the supplied payload for the
|
|
# newly copied exploit module instance
|
|
e.generate_payload(p)
|
|
ctx = [ e, p ]
|
|
|
|
e.job_id = e.framework.jobs.start_bg_job(
|
|
"Exploit: #{e.refname}",
|
|
ctx,
|
|
Proc.new { |ctx_| job_run_proc(ctx_) },
|
|
Proc.new { |ctx_| job_cleanup_proc(ctx_) }
|
|
)
|
|
self.job_id = e.job_id
|
|
else
|
|
# Generate the encoded version of the supplied payload on the
|
|
# exploit module instance
|
|
exploit.generate_payload(payload)
|
|
|
|
# No need to copy since we aren't creating a job. We wait until
|
|
# they're finished running to do anything else with them, so
|
|
# nothing should be able to modify their datastore or other
|
|
# settings until after they're done.
|
|
ctx = [ exploit, payload ]
|
|
|
|
begin
|
|
job_run_proc(ctx)
|
|
rescue ::Interrupt
|
|
job_cleanup_proc(ctx)
|
|
raise $!
|
|
ensure
|
|
# For multi exploit targets.
|
|
# Keep the payload handler until last target or interrupt
|
|
job_cleanup_proc(ctx) unless keep_handler
|
|
end
|
|
end
|
|
|
|
return session
|
|
end
|
|
|
|
attr_accessor :exploit # :nodoc:
|
|
attr_accessor :payload # :nodoc:
|
|
attr_accessor :use_job # :nodoc:
|
|
#
|
|
# The identifier of the job this exploit is launched as, if it's run as a
|
|
# job.
|
|
#
|
|
attr_accessor :job_id
|
|
attr_accessor :force_wait_for_session # :nodoc:
|
|
attr_accessor :session # :nodoc:
|
|
attr_accessor :keep_handler # :nodoc:
|
|
|
|
# To synchronize threads cleaning up the exploit and the handler
|
|
attr_accessor :semaphore
|
|
|
|
protected
|
|
|
|
#
|
|
# Job run proc, sets up the exploit and kicks it off.
|
|
#
|
|
def job_run_proc(ctx)
|
|
begin
|
|
exploit, payload = ctx
|
|
# Default session wait time..
|
|
delay = payload.wfs_delay + exploit.wfs_delay
|
|
delay = nil if exploit.passive?
|
|
|
|
# Set the exploit up the bomb
|
|
exploit.setup
|
|
|
|
exploit.framework.events.on_module_run(exploit)
|
|
|
|
# Launch the exploit
|
|
exploit.exploit
|
|
|
|
rescue ::Exception => e
|
|
if [::RuntimeError, ::Interrupt].include?(e.class)
|
|
# Wait for session, but don't wait long.
|
|
delay = 0.01
|
|
end
|
|
|
|
fail_reason = exploit.handle_exception(e)
|
|
end
|
|
|
|
# Start bind handlers after exploit completion
|
|
payload.start_handler if exploit.handler_bind?
|
|
|
|
# Wait the payload to acquire a session if this isn't a passive-style
|
|
# exploit.
|
|
return if not delay
|
|
|
|
if (force_wait_for_session == true) or
|
|
(exploit.passive? == false and exploit.handler_enabled?)
|
|
begin
|
|
self.session = payload.wait_for_session(delay)
|
|
rescue ::Interrupt
|
|
# Don't let interrupt pass upward
|
|
end
|
|
end
|
|
|
|
return self.session if self.session
|
|
|
|
if exploit.fail_reason == Msf::Exploit::Failure::None
|
|
exploit.fail_reason = Msf::Exploit::Failure::PayloadFailed
|
|
exploit.fail_detail = "No session created"
|
|
exploit.report_failure
|
|
end
|
|
|
|
if fail_reason && fail_reason == Msf::Exploit::Failure::UserInterrupt
|
|
raise ::Interrupt
|
|
end
|
|
end
|
|
|
|
#
|
|
# Clean up the exploit and the handler after the job completes.
|
|
#
|
|
def job_cleanup_proc(ctx)
|
|
exploit, payload = ctx
|
|
|
|
# Ensure that, no matter what, clean up of the handler occurs
|
|
semaphore.synchronize { payload.stop_handler }
|
|
|
|
exploit.framework.events.on_module_complete(exploit)
|
|
|
|
# Allow the exploit to cleanup after itself, that messy bugger.
|
|
semaphore.synchronize { exploit.cleanup }
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|