This commit is contained in:
commit
98a6147403
|
@ -69,7 +69,7 @@ module Auxiliary
|
|||
end
|
||||
|
||||
run_uuid = Rex::Text.rand_text_alphanumeric(24)
|
||||
mod.framework.job_state_tracker.waiting run_uuid
|
||||
mod.framework.ready << run_uuid
|
||||
ctx = [mod, run_uuid]
|
||||
if(mod.passive? or opts['RunAsJob'])
|
||||
mod.job_id = mod.framework.jobs.start_bg_job(
|
||||
|
@ -122,7 +122,7 @@ module Auxiliary
|
|||
|
||||
|
||||
run_uuid = Rex::Text.rand_text_alphanumeric(24)
|
||||
mod.framework.job_state_tracker.waiting run_uuid
|
||||
mod.framework.ready << run_uuid
|
||||
ctx = [mod, run_uuid]
|
||||
|
||||
if opts['RunAsJob']
|
||||
|
@ -169,12 +169,15 @@ protected
|
|||
mod.setup
|
||||
mod.framework.events.on_module_run(mod)
|
||||
begin
|
||||
mod.framework.job_state_tracker.start run_uuid
|
||||
mod.framework.running << run_uuid
|
||||
mod.framework.ready.delete run_uuid
|
||||
result = block.call(mod)
|
||||
mod.framework.job_state_tracker.completed(run_uuid, result)
|
||||
mod.framework.results[run_uuid] = {result: result}
|
||||
rescue ::Exception => e
|
||||
mod.framework.job_state_tracker.failed(run_uuid, e)
|
||||
mod.framework.results[run_uuid] = {error: e.to_s}
|
||||
raise
|
||||
ensure
|
||||
mod.framework.running.delete run_uuid
|
||||
end
|
||||
rescue Msf::Auxiliary::Complete
|
||||
mod.cleanup
|
||||
|
@ -191,7 +194,7 @@ protected
|
|||
return
|
||||
rescue ::Interrupt => e
|
||||
mod.error = e
|
||||
mod.print_error("Stopping running against current target...")
|
||||
mod.print_error("Stopping running againest current target...")
|
||||
mod.cleanup
|
||||
mod.print_status("Control-C again to force quit all targets.")
|
||||
begin
|
||||
|
|
|
@ -190,7 +190,7 @@ module Exploit
|
|||
mod.validate
|
||||
|
||||
run_uuid = Rex::Text.rand_text_alphanumeric(24)
|
||||
mod.framework.job_state_tracker.waiting run_uuid
|
||||
mod.framework.ready << run_uuid
|
||||
ctx = [mod, run_uuid]
|
||||
|
||||
if opts['RunAsJob']
|
||||
|
@ -220,12 +220,15 @@ module Exploit
|
|||
run_uuid = ctx[1]
|
||||
begin
|
||||
mod.setup
|
||||
mod.framework.job_state_tracker.start run_uuid
|
||||
mod.framework.running << run_uuid
|
||||
mod.framework.ready.delete run_uuid
|
||||
result = mod.has_check? ? mod.check : Msf::Exploit::CheckCode::Unsupported
|
||||
mod.framework.job_state_tracker.completed(run_uuid, result)
|
||||
mod.framework.results[run_uuid] = {result: result}
|
||||
rescue => e
|
||||
mod.framework.job_state_tracker.failed(run_uuid, e)
|
||||
mod.framework.results[run_uuid] = {error: e.to_s}
|
||||
mod.handle_exception e
|
||||
ensure
|
||||
mod.framework.running.delete run_uuid
|
||||
end
|
||||
|
||||
return result
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/base/simple'
|
||||
require 'msf/base/simple/framework/module_paths'
|
||||
require 'msf/base/simple/job_state_tracker'
|
||||
|
||||
module Msf
|
||||
module Simple
|
||||
|
@ -151,7 +150,9 @@ module Framework
|
|||
#
|
||||
def init_simplified
|
||||
self.stats = Statistics.new(self)
|
||||
self.job_state_tracker = JobStateTracker.new
|
||||
self.ready = Set.new
|
||||
self.running = Set.new
|
||||
self.results = Hash.new
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -173,12 +174,6 @@ module Framework
|
|||
#
|
||||
attr_reader :stats
|
||||
|
||||
|
||||
#
|
||||
# JobStateTracker
|
||||
#
|
||||
attr_reader :job_state_tracker
|
||||
|
||||
#
|
||||
# Boolean indicating whether the cache is initialized yet
|
||||
#
|
||||
|
@ -188,14 +183,32 @@ module Framework
|
|||
# Thread of the running rebuild operation
|
||||
#
|
||||
attr_reader :cache_thread
|
||||
|
||||
#
|
||||
# {Set<String>} of module run/check UUIDs waiting to be kicked off
|
||||
#
|
||||
attr_reader :ready
|
||||
|
||||
#
|
||||
# {Hash<String,Hash>} of module run/check results, by UUID. Successful runs
|
||||
# look like `{result: check_code}` and errors like `{error: message}`.
|
||||
#
|
||||
attr_reader :results
|
||||
|
||||
#
|
||||
# {Set<String>} of module run/check UUIDs currently in progress
|
||||
#
|
||||
attr_reader :running
|
||||
attr_writer :cache_initialized # :nodoc:
|
||||
attr_writer :cache_thread # :nodoc:
|
||||
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :ready # :nodoc:
|
||||
attr_writer :results # :nodoc:
|
||||
attr_writer :running # :nodoc:
|
||||
attr_writer :stats # :nodoc:
|
||||
attr_writer :job_state_tracker # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
require 'monitor'
|
||||
|
||||
class JobStateTracker
|
||||
|
||||
include MonitorMixin
|
||||
|
||||
def initialize(result_ttl=nil)
|
||||
self.ready = Set.new
|
||||
self.running = Set.new
|
||||
# Can be expanded upon later to allow the option of a MemCacheStore being backed by redis for example
|
||||
self.results = ResultsMemoryStore.new(expires_in: result_ttl || 5.minutes)
|
||||
end
|
||||
|
||||
def waiting(id)
|
||||
ready << id
|
||||
end
|
||||
|
||||
def start(id)
|
||||
running << id
|
||||
ready.delete(id)
|
||||
end
|
||||
|
||||
def completed(id, result, ttl=nil)
|
||||
begin
|
||||
# ttl of nil means it will take the default expiry time
|
||||
results.write(id, {result: result}, ttl)
|
||||
ensure
|
||||
running.delete(id)
|
||||
end
|
||||
end
|
||||
|
||||
def failed(id, error, ttl=nil)
|
||||
begin
|
||||
# ttl of nil means it will take the default expiry time
|
||||
results.write(id, {error: error.to_s}, ttl)
|
||||
ensure
|
||||
running.delete(id)
|
||||
end
|
||||
end
|
||||
|
||||
def running?(id)
|
||||
running.include? id
|
||||
end
|
||||
|
||||
def waiting?(id)
|
||||
ready.include? id
|
||||
end
|
||||
|
||||
def finished?(id)
|
||||
results.exist? id
|
||||
end
|
||||
|
||||
def result(id)
|
||||
results.fetch(id)
|
||||
end
|
||||
|
||||
def delete(id)
|
||||
results.delete(id)
|
||||
end
|
||||
|
||||
def results_size
|
||||
results.size
|
||||
end
|
||||
|
||||
def waiting_size
|
||||
ready.size
|
||||
end
|
||||
|
||||
def running_size
|
||||
running.size
|
||||
end
|
||||
|
||||
alias :ack :delete
|
||||
|
||||
private
|
||||
|
||||
attr_accessor :ready, :running, :results
|
||||
|
||||
class ResultsMemoryStore < ActiveSupport::Cache::MemoryStore
|
||||
def size
|
||||
@data.size
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,10 +9,10 @@ class RPC_Base
|
|||
#
|
||||
# return [void]
|
||||
def initialize(service)
|
||||
self.service = service
|
||||
self.framework = service.framework
|
||||
self.tokens = service.tokens
|
||||
self.users = service.users
|
||||
self.service = service
|
||||
self.framework = service.framework
|
||||
self.tokens = service.tokens
|
||||
self.users = service.users
|
||||
end
|
||||
|
||||
# Raises an Msf::RPC Exception.
|
||||
|
|
|
@ -412,9 +412,9 @@ class RPC_Module < RPC_Base
|
|||
# rpc.call('module.running_stats')
|
||||
def rpc_running_stats
|
||||
{
|
||||
"waiting" => self.framework.job_state_tracker.waiting_size,
|
||||
"running" => self.framework.job_state_tracker.running_size,
|
||||
"results" => self.framework.job_state_tracker.results_size,
|
||||
"ready" => self.framework.ready.size,
|
||||
"running" => self.framework.running.size,
|
||||
"results" => self.framework.results.size,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -523,7 +523,7 @@ class RPC_Module < RPC_Base
|
|||
# TODO: expand these to take a list of UUIDs or stream with event data if
|
||||
# required for performance
|
||||
def rpc_results(uuid)
|
||||
if (r = self.framework.job_state_tracker.result(uuid))
|
||||
if r = self.framework.results[uuid]
|
||||
if r[:error]
|
||||
{"status" => "errored", "error" => r[:error]}
|
||||
else
|
||||
|
@ -537,9 +537,9 @@ class RPC_Module < RPC_Base
|
|||
{"status" => "completed", "result" => r[:result]}
|
||||
end
|
||||
end
|
||||
elsif self.framework.job_state_tracker.running? uuid
|
||||
elsif self.framework.running.include? uuid
|
||||
{"status" => "running"}
|
||||
elsif self.framework.job_state_tracker.waiting uuid
|
||||
elsif self.framework.ready.include? uuid
|
||||
{"status" => "ready"}
|
||||
else
|
||||
error(404, "Results not found for module instance #{uuid}")
|
||||
|
@ -547,7 +547,7 @@ class RPC_Module < RPC_Base
|
|||
end
|
||||
|
||||
def rpc_ack(uuid)
|
||||
{"success" => !!self.framework.job_state_tracker.ack(uuid)}
|
||||
{"success" => !!self.framework.results.delete(uuid)}
|
||||
end
|
||||
|
||||
# Returns a list of executable format names.
|
||||
|
@ -740,7 +740,7 @@ private
|
|||
end
|
||||
|
||||
def _run_auxiliary(mod, opts)
|
||||
uuid, job = Msf::Simple::Auxiliary.run_simple(mod,{
|
||||
uuid, job = Msf::Simple::Auxiliary.run_simple(mod, {
|
||||
'Action' => opts['ACTION'],
|
||||
'RunAsJob' => true,
|
||||
'Options' => opts
|
||||
|
@ -752,7 +752,7 @@ private
|
|||
end
|
||||
|
||||
def _check_exploit(mod, opts)
|
||||
uuid, job = Msf::Simple::Exploit.check_simple(mod,{
|
||||
uuid, job = Msf::Simple::Exploit.check_simple(mod, {
|
||||
'RunAsJob' => true,
|
||||
'Options' => opts
|
||||
})
|
||||
|
@ -763,7 +763,7 @@ private
|
|||
end
|
||||
|
||||
def _check_auxiliary(mod, opts)
|
||||
uuid, job = Msf::Simple::Auxiliary.check_simple(mod,{
|
||||
uuid, job = Msf::Simple::Auxiliary.check_simple(mod, {
|
||||
'Action' => opts['ACTION'],
|
||||
'RunAsJob' => true,
|
||||
'Options' => opts
|
||||
|
|
|
@ -15,6 +15,7 @@ require 'msf/core/rpc/v10/rpc_plugin'
|
|||
require 'msf/core/rpc/v10/rpc_job'
|
||||
require 'msf/core/rpc/v10/rpc_db'
|
||||
|
||||
|
||||
module Msf
|
||||
module RPC
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Msf::Simple::Framework do
|
||||
include_context 'Msf::Simple::Framework'
|
||||
|
||||
subject do
|
||||
framework
|
||||
end
|
||||
|
||||
it_should_behave_like 'Msf::Simple::Framework::ModulePaths'
|
||||
|
||||
describe "#ready" do
|
||||
let(:run_uuid) { Rex::Text.rand_text_alphanumeric 24 }
|
||||
it "should start out empty" do
|
||||
expect(subject.ready).to_not include, run_uuid
|
||||
end
|
||||
it "should remember things that are ready to run" do
|
||||
subject.ready << run_uuid
|
||||
expect(subject.ready).to include, run_uuid
|
||||
end
|
||||
it "should forget things that are running" do
|
||||
subject.ready << run_uuid
|
||||
subject.ready.delete run_uuid
|
||||
expect(subject.ready).to_not include, run_uuid
|
||||
end
|
||||
end
|
||||
|
||||
describe "#running" do
|
||||
let(:run_uuid) { Rex::Text.rand_text_alphanumeric 24 }
|
||||
it "should start out empty" do
|
||||
expect(subject.running).to_not include, run_uuid
|
||||
end
|
||||
it "should remember things that are running" do
|
||||
subject.running << run_uuid
|
||||
expect(subject.running).to include, run_uuid
|
||||
end
|
||||
it "should forget things that are done" do
|
||||
subject.running << run_uuid
|
||||
subject.running.delete run_uuid
|
||||
expect(subject.running).to_not include, run_uuid
|
||||
end
|
||||
end
|
||||
|
||||
describe "#results" do
|
||||
let(:run_uuid) { Rex::Text.rand_text_alphanumeric 24 }
|
||||
it "should start out empty" do
|
||||
expect(subject.results.keys).to_not include, run_uuid
|
||||
end
|
||||
it "should remember results" do
|
||||
subject.results[run_uuid] = {}
|
||||
expect(subject.results.keys).to include, run_uuid
|
||||
end
|
||||
it "should forget things that have been acknowleged" do
|
||||
subject.results[run_uuid] = {}
|
||||
subject.results.delete run_uuid
|
||||
expect(subject.results.keys).to_not include, run_uuid
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,171 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
require 'msf/base/simple/job_state_tracker.rb'
|
||||
|
||||
RSpec.describe JobStateTracker do
|
||||
|
||||
context "With a 1 second ttl " do
|
||||
let(:job_state_tracker) { described_class.new(result_ttl=1) }
|
||||
let(:job_id) { "super_random_job_id" }
|
||||
|
||||
|
||||
before(:each) do
|
||||
allow(ActiveSupport::Cache::MemoryStore).to receive(:new).and_call_original
|
||||
end
|
||||
|
||||
context "A job has completed" do
|
||||
before(:each) do
|
||||
generic_result = "This is a job that has finished"
|
||||
job_state_tracker.completed(job_id, generic_result)
|
||||
end
|
||||
|
||||
it 'should show as finished' do
|
||||
expect(job_state_tracker).to be_finished(job_id)
|
||||
end
|
||||
|
||||
it { expect(ActiveSupport::Cache::MemoryStore).to have_received(:new).with(expires_in: 1) }
|
||||
end
|
||||
end
|
||||
|
||||
context "With default options" do
|
||||
let(:job_state_tracker) { described_class.new }
|
||||
let(:job_id) { "super_random_job_id" }
|
||||
let(:good_result) { 'yay_success' }
|
||||
let(:bad_result) { 'boo_fail' }
|
||||
|
||||
context 'A job is waiting' do
|
||||
before(:each) do
|
||||
job_id = "super_random_job_id"
|
||||
job_state_tracker.waiting(job_id)
|
||||
end
|
||||
|
||||
it 'should show as waiting' do
|
||||
expect(job_state_tracker).to be_waiting(job_id)
|
||||
expect(job_state_tracker.waiting_size).to be(1)
|
||||
end
|
||||
|
||||
it 'should not show as running' do
|
||||
expect(job_state_tracker).not_to be_running(job_id)
|
||||
expect(job_state_tracker.running_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should not show as finished' do
|
||||
expect(job_state_tracker).not_to be_finished(job_id)
|
||||
expect(job_state_tracker.results_size).to be(0)
|
||||
end
|
||||
|
||||
context "The job is started" do
|
||||
before(:each) do
|
||||
job_state_tracker.start(job_id)
|
||||
end
|
||||
|
||||
it 'should no longer show as waiting' do
|
||||
expect(job_state_tracker).not_to be_waiting(job_id)
|
||||
expect(job_state_tracker.waiting_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should now show as running' do
|
||||
expect(job_state_tracker).to be_running(job_id)
|
||||
expect(job_state_tracker.running_size).to be(1)
|
||||
end
|
||||
|
||||
it 'should not show as finished' do
|
||||
expect(job_state_tracker).not_to be_finished(job_id)
|
||||
expect(job_state_tracker.results_size).to be(0)
|
||||
end
|
||||
|
||||
context "The job completes successfully" do
|
||||
before(:each) do
|
||||
job_state_tracker.completed(job_id, good_result)
|
||||
end
|
||||
|
||||
it 'should not show as waiting' do
|
||||
expect(job_state_tracker).not_to be_waiting(job_id)
|
||||
expect(job_state_tracker.waiting_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should no longer show as running' do
|
||||
expect(job_state_tracker).not_to be_running(job_id)
|
||||
expect(job_state_tracker.running_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should show as finished' do
|
||||
expect(job_state_tracker).to be_finished(job_id)
|
||||
expect(job_state_tracker.results_size).to be(1)
|
||||
end
|
||||
|
||||
it 'should have a retrievable result' do
|
||||
expect(job_state_tracker.result job_id).to eq({result: good_result})
|
||||
end
|
||||
|
||||
context "The job is acknowledged" do
|
||||
before(:each) do
|
||||
job_state_tracker.ack(job_id)
|
||||
end
|
||||
|
||||
it 'should not show as waiting' do
|
||||
expect(job_state_tracker).not_to be_waiting(job_id)
|
||||
expect(job_state_tracker.waiting_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should not show as running' do
|
||||
expect(job_state_tracker).not_to be_running(job_id)
|
||||
expect(job_state_tracker.running_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should no longer show as finished' do
|
||||
expect(job_state_tracker).not_to be_finished(job_id)
|
||||
expect(job_state_tracker.results_size).to be(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "The job fails" do
|
||||
before(:each) do
|
||||
job_state_tracker.failed(job_id, bad_result)
|
||||
end
|
||||
|
||||
it 'should not show as waiting' do
|
||||
expect(job_state_tracker).not_to be_waiting(job_id)
|
||||
expect(job_state_tracker.waiting_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should no longer show as running' do
|
||||
expect(job_state_tracker).not_to be_running(job_id)
|
||||
expect(job_state_tracker.running_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should show as finished' do
|
||||
expect(job_state_tracker).to be_finished(job_id)
|
||||
expect(job_state_tracker.results_size).to be(1)
|
||||
end
|
||||
|
||||
it 'should have a retrievable result' do
|
||||
expect(job_state_tracker.result job_id).to eq({error: bad_result})
|
||||
end
|
||||
|
||||
context "The job is acknowledged" do
|
||||
before(:each) do
|
||||
job_state_tracker.ack(job_id)
|
||||
end
|
||||
|
||||
it 'should not show as waiting' do
|
||||
expect(job_state_tracker).not_to be_waiting(job_id)
|
||||
expect(job_state_tracker.waiting_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should not show as running' do
|
||||
expect(job_state_tracker).not_to be_running(job_id)
|
||||
expect(job_state_tracker.running_size).to be(0)
|
||||
end
|
||||
|
||||
it 'should no longer show as finished' do
|
||||
expect(job_state_tracker).not_to be_finished(job_id)
|
||||
expect(job_state_tracker.results_size).to be(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue