Land #15382, Add tests for aux and exploit cmd_check and cmd_run
This commit is contained in:
commit
6817d0a0ee
|
@ -21,6 +21,8 @@ Gemfile.local.lock
|
|||
config/database.yml
|
||||
# target config file for testing
|
||||
features/support/targets.yml
|
||||
# Generated test files
|
||||
spec/dummy
|
||||
# simplecov coverage data
|
||||
coverage
|
||||
doc/
|
||||
|
@ -94,6 +96,5 @@ docker-compose.local*
|
|||
*.pyc
|
||||
rspec.failures
|
||||
|
||||
|
||||
#Ignore any base disk store files
|
||||
db/modules_metadata_base.pstore
|
||||
db/modules_metadata_base.pstore
|
||||
|
|
|
@ -51,7 +51,7 @@ class Auxiliary
|
|||
jobify = true
|
||||
end
|
||||
|
||||
rhosts = datastore['RHOSTS']
|
||||
rhosts = mod.datastore['RHOSTS']
|
||||
begin
|
||||
# Check if this is a scanner module or doesn't target remote hosts
|
||||
if rhosts.blank? || mod.class.included_modules.include?(Msf::Auxiliary::Scanner)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 12
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::CommandShellOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
merge_info(
|
||||
info,
|
||||
'Name' => 'mock payload which gives no session',
|
||||
'Description' => 'mock payload which gives no session',
|
||||
'Author' => ['unknown'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix'],
|
||||
'Arch' => ARCH_CMD,
|
||||
# 'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::CommandShell,
|
||||
'PayloadType' => 'cmd',
|
||||
'Payload' => { 'Offsets' => {}, 'Payload' => '' }
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def wait_for_session(_t = wfs_delay)
|
||||
# noop
|
||||
end
|
||||
|
||||
def generate
|
||||
'mock payload'
|
||||
end
|
||||
end
|
|
@ -1,23 +1,734 @@
|
|||
require 'spec_helper'
|
||||
|
||||
|
||||
RSpec.describe Msf::Ui::Console::CommandDispatcher::Auxiliary do
|
||||
include_context 'Msf::DBManager'
|
||||
include_context 'Msf::UIDriver'
|
||||
include_context 'Rex::Job#start run inline'
|
||||
include_context 'Msf::Framework#threads cleaner', verify_cleanup_required: false
|
||||
|
||||
subject(:aux) do
|
||||
described_class.new(driver)
|
||||
let(:aux_mod) do
|
||||
mod_klass = Class.new(Msf::Auxiliary) do
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'mock module',
|
||||
'Description' => 'mock module',
|
||||
'Author' => ['Unknown'],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::Opt::RHOSTS,
|
||||
Msf::Opt::RPORT(3000),
|
||||
Msf::OptFloat.new('FloatValue', [false, 'A FloatValue which should be normalized before framework runs this module', 3.5])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
print_status("Checking for target #{datastore['RHOSTS']}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Running for target #{datastore['RHOSTS']}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
def cleanup
|
||||
print_status("Cleanup for target #{datastore['RHOSTS']}:#{datastore['RPORT']}")
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod_klass.new
|
||||
datastore = Msf::ModuleDataStore.new(mod)
|
||||
allow(mod).to receive(:framework).and_return(framework)
|
||||
allow(mod).to receive(:datastore).and_return(datastore)
|
||||
datastore.import_options(mod.options)
|
||||
Msf::Simple::Framework.simplify_module(mod, false)
|
||||
mod
|
||||
end
|
||||
|
||||
describe "#cmd_run" do
|
||||
let(:smb_scanner_run_host_mod) do
|
||||
mod_klass = Class.new(Msf::Auxiliary) do
|
||||
include Msf::Exploit::Remote::DCERPC
|
||||
include Msf::Exploit::Remote::SMB::Client
|
||||
|
||||
# Scanner mixin should be near last
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'mock smb module',
|
||||
'Description' => 'mock smb module',
|
||||
'Author' => ['Unknown'],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::Opt::RPORT(445),
|
||||
Msf::OptFloat.new('FloatValue', [false, 'A FloatValue which should be normalized before framework runs this module', 3.5])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check_host(_ip)
|
||||
print_status("Checking for target #{datastore['RHOSTS']}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
def run_host(_ip)
|
||||
print_status("Running for target #{datastore['RHOSTS']}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
def cleanup
|
||||
print_status("Cleanup for target #{datastore['RHOSTS']}:#{datastore['RPORT']}")
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod_klass.new
|
||||
datastore = Msf::ModuleDataStore.new(mod)
|
||||
allow(mod).to receive(:framework).and_return(framework)
|
||||
allow(mod).to receive(:datastore).and_return(datastore)
|
||||
datastore.import_options(mod.options)
|
||||
Msf::Simple::Framework.simplify_module(mod, false)
|
||||
mod
|
||||
end
|
||||
|
||||
describe "#cmd_rerun" do
|
||||
let(:smb_scanner_run_batch_mod) do
|
||||
mod_klass = Class.new(Msf::Auxiliary) do
|
||||
include Msf::Exploit::Remote::DCERPC
|
||||
include Msf::Exploit::Remote::SMB::Client
|
||||
|
||||
# Scanner mixin should be near last
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'mock smb module',
|
||||
'Description' => 'mock smb module',
|
||||
'Author' => ['Unknown'],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::Opt::RPORT(445),
|
||||
Msf::OptFloat.new('FloatValue', [false, 'A FloatValue which should be normalized before framework runs this module', 3.5])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check_host(_ip)
|
||||
print_status("Checking for target #{datastore['RHOSTS']}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
def run_batch(batch)
|
||||
print_status("Running batch #{batch.inspect}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
def run_batch_size
|
||||
2
|
||||
end
|
||||
|
||||
def cleanup
|
||||
print_status("Cleanup for target #{datastore['RHOSTS']}:#{datastore['RPORT']}")
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod_klass.new
|
||||
datastore = Msf::ModuleDataStore.new(mod)
|
||||
allow(mod).to receive(:framework).and_return(framework)
|
||||
allow(mod).to receive(:datastore).and_return(datastore)
|
||||
datastore.import_options(mod.options)
|
||||
Msf::Simple::Framework.simplify_module(mod, false)
|
||||
mod
|
||||
end
|
||||
|
||||
describe "#cmd_exploit" do
|
||||
subject do
|
||||
instance = described_class.new(driver)
|
||||
instance
|
||||
end
|
||||
|
||||
describe "#cmd_reload" do
|
||||
before(:each) do
|
||||
run_rex_jobs_inline!
|
||||
allow(driver).to receive(:input).and_return(driver_input)
|
||||
allow(driver).to receive(:output).and_return(driver_output)
|
||||
current_mod.init_ui(driver_input, driver_output)
|
||||
allow(subject).to receive(:mod).and_return(current_mod)
|
||||
end
|
||||
|
||||
describe '#cmd_check' do
|
||||
context 'when running a run_host scanner module' do
|
||||
let(:current_mod) { smb_scanner_run_host_mod }
|
||||
|
||||
it 'reports missing RHOST values' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = ''
|
||||
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Check failed: Msf::OptionValidateError One or more options failed to validate: RHOSTS.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs a single RHOST value' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Checking for target 192.0.2.1:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Check failed: The state could not be determined.',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs multiple RHOST values' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Checking for target 192.0.2.1:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Check failed: The state could not be determined.',
|
||||
'Checked 1 of 2 hosts (050% complete)',
|
||||
'192.0.2.2:445 - Checking for target 192.0.2.2:445 with normalized datastore value 3.5',
|
||||
'192.0.2.2:445 - Cleanup for target 192.0.2.2:445',
|
||||
'192.0.2.2:445 - Check failed: The state could not be determined.',
|
||||
'Checked 2 of 2 hosts (100% complete)',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:445'
|
||||
]
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalizes the datastore before running' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Checking for target 192.0.2.1:445 with normalized datastore value 5.0',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Check failed: The state could not be determined.',
|
||||
'Checked 1 of 2 hosts (050% complete)',
|
||||
'192.0.2.2:445 - Checking for target 192.0.2.2:445 with normalized datastore value 5.0',
|
||||
'192.0.2.2:445 - Cleanup for target 192.0.2.2:445',
|
||||
'192.0.2.2:445 - Check failed: The state could not be determined.',
|
||||
'Checked 2 of 2 hosts (100% complete)',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:445'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
pending("cmd_check doesn't support inline methods, only cmd_run")
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check('RHOSTS=192.0.2.5', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'192.0.2.5:445 - Checking for target 192.0.2.5:445 with normalized datastore value 10.0',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'192.0.2.5:445 - Check failed: The state could not be determined.',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports multiple RHOST inline options' do
|
||||
pending("cmd_check doesn't support inline methods, only cmd_run")
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check('RHOSTS=192.0.2.5 192.0.2.6', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'192.0.2.5:445 - Checking for target 192.0.2.5:445 with normalized datastore value 10.0',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'192.0.2.5:445 - Check failed: The state could not be determined.',
|
||||
'Checked 1 of 2 hosts (050% complete)',
|
||||
'192.0.2.6:445 - Checking for target 192.0.2.6:445 with normalized datastore value 10.0',
|
||||
'192.0.2.6:445 - Cleanup for target 192.0.2.6:445',
|
||||
'192.0.2.6:445 - Check failed: The state could not be determined.',
|
||||
'Checked 2 of 2 hosts (100% complete)',
|
||||
'Cleanup for target 192.0.2.5 192.0.2.6:445'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports targeting a single host as an inline argument' do
|
||||
subject.cmd_check('192.0.2.5')
|
||||
expected_output = [
|
||||
'192.0.2.5:445 - Checking for target 192.0.2.5:445 with normalized datastore value 3.5',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'192.0.2.5:445 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target :445'
|
||||
]
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports targeting multiple hosts as an inline argument' do
|
||||
subject.cmd_check('192.0.2.5 192.0.2.6')
|
||||
|
||||
expected_output = [
|
||||
'192.0.2.5:445 - Checking for target 192.0.2.5:445 with normalized datastore value 3.5',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'192.0.2.5:445 - Check failed: The state could not be determined.',
|
||||
'Checked 1 of 2 hosts (050% complete)',
|
||||
'192.0.2.6:445 - Checking for target 192.0.2.6:445 with normalized datastore value 3.5',
|
||||
'192.0.2.6:445 - Cleanup for target 192.0.2.6:445',
|
||||
'192.0.2.6:445 - Check failed: The state could not be determined.',
|
||||
'Checked 2 of 2 hosts (100% complete)',
|
||||
'Cleanup for target :445'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'incorrectly handles unknown flags, and inadvertently run the exploit with the old rhosts value' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_check('-unknown-flag')
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Checking for target 192.0.2.1:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Check failed: The state could not be determined.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when running an auxiliary module' do
|
||||
let(:current_mod) { aux_mod }
|
||||
|
||||
it 'reports missing RHOST values' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = ''
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Check failed: Msf::OptionValidateError One or more options failed to validate: RHOSTS.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'runs a single RHOST value' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.1:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs multiple RHOST values' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.',
|
||||
'Checking for target 192.0.2.2:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.2:3000',
|
||||
'192.0.2.2:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalizes the datastore before running' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.',
|
||||
'Checking for target 192.0.2.2:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.2:3000',
|
||||
'192.0.2.2:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
pending('cmd_check does not support inline values yet')
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check('RHOSTS=192.0.2.5', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.5:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5:3000',
|
||||
'192.0.2.5:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.5:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports multiple inlined RHOST values' do
|
||||
pending('pending as inline module options are evaluated too late, and the module is therefore treated as a scanner')
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check('RHOSTS=192.0.2.5 192.0.2.6', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.5:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5:3000',
|
||||
'192.0.2.5:3000 - Check failed: The state could not be determined.',
|
||||
'Checking for target 192.0.2.6:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.6:3000',
|
||||
'192.0.2.6:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.5 192.0.2.6:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'ignores the -j flag, and the module is not run as a job' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_check('-j')
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#cmd_run' do
|
||||
context 'when running a scanner run_host module' do
|
||||
let(:current_mod) { smb_scanner_run_host_mod }
|
||||
|
||||
it 'reports missing RHOST values' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = ''
|
||||
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Auxiliary failed: Msf::OptionValidateError One or more options failed to validate: RHOSTS.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'runs a single RHOST value' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Running for target 192.0.2.1:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Scanned 1 of 1 hosts (100% complete)',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs multiple RHOST values' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Running for target 192.0.2.1:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Scanned 1 of 2 hosts (50% complete)',
|
||||
'192.0.2.2:445 - Running for target 192.0.2.2:445 with normalized datastore value 3.5',
|
||||
'192.0.2.2:445 - Cleanup for target 192.0.2.2:445',
|
||||
'192.0.2.2:445 - Scanned 2 of 2 hosts (100% complete)',
|
||||
'192.0.2.2:445 - Cleanup for target 192.0.2.2:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalizes the datastore before running' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Running for target 192.0.2.1:445 with normalized datastore value 5.0',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Scanned 1 of 2 hosts (50% complete)',
|
||||
'192.0.2.2:445 - Running for target 192.0.2.2:445 with normalized datastore value 5.0',
|
||||
'192.0.2.2:445 - Cleanup for target 192.0.2.2:445',
|
||||
'192.0.2.2:445 - Scanned 2 of 2 hosts (100% complete)',
|
||||
'192.0.2.2:445 - Cleanup for target 192.0.2.2:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'192.0.2.5:445 - Running for target 192.0.2.5:445 with normalized datastore value 10.0',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'192.0.2.5:445 - Scanned 1 of 1 hosts (100% complete)',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports multiple RHOST inline options' do
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5 192.0.2.6', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'192.0.2.5:445 - Running for target 192.0.2.5:445 with normalized datastore value 10.0',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'192.0.2.5:445 - Scanned 1 of 2 hosts (50% complete)',
|
||||
'192.0.2.6:445 - Running for target 192.0.2.6:445 with normalized datastore value 10.0',
|
||||
'192.0.2.6:445 - Cleanup for target 192.0.2.6:445',
|
||||
'192.0.2.6:445 - Scanned 2 of 2 hosts (100% complete)',
|
||||
'192.0.2.6:445 - Cleanup for target 192.0.2.6:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs the scanner as a background job when the -j flag is used' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run('-j')
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Running rex job 0 inline',
|
||||
'192.0.2.1:445 - Running for target 192.0.2.1:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Scanned 1 of 1 hosts (100% complete)',
|
||||
'Auxiliary module running as background job 0.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when running a scanner run_batch module' do
|
||||
let(:current_mod) { smb_scanner_run_batch_mod }
|
||||
|
||||
it 'reports missing RHOST values' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = ''
|
||||
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Auxiliary failed: Msf::OptionValidateError One or more options failed to validate: RHOSTS.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'runs a single RHOST value' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Running batch ["192.0.2.1"]:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Scanned 1 of 1 hosts (100% complete)',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs multiple RHOST values' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running batch ["192.0.2.1", "192.0.2.2"]:445 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:445',
|
||||
'Scanned 2 of 2 hosts (100% complete)',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalizes the datastore before running' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running batch ["192.0.2.1", "192.0.2.2"]:445 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:445',
|
||||
'Scanned 2 of 2 hosts (100% complete)',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'192.0.2.5:445 - Running batch ["192.0.2.5"]:445 with normalized datastore value 10.0',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'192.0.2.5:445 - Scanned 1 of 1 hosts (100% complete)',
|
||||
'192.0.2.5:445 - Cleanup for target 192.0.2.5:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports multiple RHOST inline options' do
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5 192.0.2.6', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Running batch ["192.0.2.5", "192.0.2.6"]:445 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5 192.0.2.6:445',
|
||||
'Scanned 2 of 2 hosts (100% complete)',
|
||||
'Cleanup for target 192.0.2.5 192.0.2.6:445',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs the scanner as a background job when the -j flag is used' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run('-j')
|
||||
expected_output = [
|
||||
'192.0.2.1:445 - Running rex job 0 inline',
|
||||
'192.0.2.1:445 - Running batch ["192.0.2.1"]:445 with normalized datastore value 3.5',
|
||||
'192.0.2.1:445 - Cleanup for target 192.0.2.1:445',
|
||||
'192.0.2.1:445 - Scanned 1 of 1 hosts (100% complete)',
|
||||
'Auxiliary module running as background job 0.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when running an auxiliary module' do
|
||||
let(:current_mod) { aux_mod }
|
||||
|
||||
it 'reports missing RHOST values' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = ''
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Auxiliary failed: Msf::OptionValidateError One or more options failed to validate: RHOSTS.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'runs a single RHOST value' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running module against 192.0.2.1',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs multiple RHOST values' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running module against 192.0.2.1',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'Running module against 192.0.2.2',
|
||||
'Running for target 192.0.2.2:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.2:3000',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalizes the datastore before running' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running module against 192.0.2.1',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'Running module against 192.0.2.2',
|
||||
'Running for target 192.0.2.2:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.2:3000',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Running for target 192.0.2.5:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5:3000',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports multiple inlined RHOST values' do
|
||||
pending('fails as inline module options are evaluated too late, and the module is therefore treated as a scanner')
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5 192.0.2.6', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Running module against 192.0.2.5',
|
||||
'Running for target 192.0.2.5:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5:3000',
|
||||
'Running module against 192.0.2.6',
|
||||
'Running for target 192.0.2.6:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.6:3000',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'ignores the -j flag, and the module is not run as a job' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run('-j')
|
||||
expected_output = [
|
||||
'Running module against 192.0.2.1',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'Auxiliary module execution completed'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#cmd_rerun' do
|
||||
end
|
||||
|
||||
describe '#cmd_exploit' do
|
||||
end
|
||||
|
||||
describe '#cmd_reload' do
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,30 +1,450 @@
|
|||
require 'spec_helper'
|
||||
|
||||
|
||||
RSpec.describe Msf::Ui::Console::CommandDispatcher::Exploit do
|
||||
include_context 'Msf::DBManager'
|
||||
include_context 'Msf::UIDriver'
|
||||
include_context 'Rex::Job#start run inline'
|
||||
include_context 'Msf::Framework#threads cleaner', verify_cleanup_required: false
|
||||
|
||||
subject(:exp) do
|
||||
described_class.new(driver)
|
||||
let(:remote_exploit_mod) do
|
||||
mod_klass = Class.new(Msf::Exploit) do
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'mock module',
|
||||
'Description' => 'mock module',
|
||||
'Author' => ['Unknown'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Arch' => ARCH_CMD,
|
||||
'Platform' => ['unix'],
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::Opt::RHOSTS,
|
||||
Msf::Opt::RPORT(3000),
|
||||
Msf::OptFloat.new('FloatValue', [false, 'A FloatValue which should be normalized before framework runs this module', 3.5])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
print_status("Checking for target #{datastore['RHOSTS']}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Running for target #{datastore['RHOSTS']}:#{datastore['RPORT']} with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
alias_method :exploit, :run
|
||||
|
||||
def cleanup
|
||||
print_status("Cleanup for target #{datastore['RHOSTS']}:#{datastore['RPORT']}")
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod_klass.new
|
||||
datastore = Msf::ModuleDataStore.new(mod)
|
||||
allow(mod).to receive(:framework).and_return(framework)
|
||||
allow(mod).to receive(:datastore).and_return(datastore)
|
||||
datastore.import_options(mod.options)
|
||||
Msf::Simple::Framework.simplify_module(mod, false)
|
||||
mod
|
||||
end
|
||||
|
||||
describe "#cmd_exploit" do
|
||||
let(:non_remote_exploit_mod) do
|
||||
mod_klass = Class.new(Msf::Exploit) do
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'mock module',
|
||||
'Description' => 'mock module',
|
||||
'Author' => ['Unknown'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Arch' => ARCH_CMD,
|
||||
'Platform' => ['unix'],
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::OptFloat.new('FloatValue', [false, 'A FloatValue which should be normalized before framework runs this module', 3.5])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Running with normalized datastore value #{datastore['FloatValue'].inspect}")
|
||||
end
|
||||
|
||||
alias_method :exploit, :run
|
||||
|
||||
def cleanup
|
||||
print_status('Cleanup')
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod_klass.new
|
||||
datastore = Msf::ModuleDataStore.new(mod)
|
||||
allow(mod).to receive(:framework).and_return(framework)
|
||||
allow(mod).to receive(:datastore).and_return(datastore)
|
||||
datastore.import_options(mod.options)
|
||||
Msf::Simple::Framework.simplify_module(mod, false)
|
||||
mod
|
||||
end
|
||||
|
||||
describe "#cmd_rcheck" do
|
||||
subject do
|
||||
instance = described_class.new(driver)
|
||||
instance
|
||||
end
|
||||
|
||||
describe "#cmd_rexploit" do
|
||||
def set_default_payload(mod)
|
||||
mod.datastore['PAYLOAD'] = 'generic/no_session_payload'
|
||||
mod.datastore['LHOST'] = '127.0.0.1'
|
||||
end
|
||||
|
||||
describe "#cmd_reload" do
|
||||
before do
|
||||
run_rex_jobs_inline!
|
||||
|
||||
allow(driver).to receive(:input).and_return(driver_input)
|
||||
allow(driver).to receive(:output).and_return(driver_output)
|
||||
current_mod.init_ui(driver_input, driver_output)
|
||||
allow(subject).to receive(:mod).and_return(current_mod)
|
||||
|
||||
framework.modules.add_module_path(File.join(FILE_FIXTURES_PATH, 'modules'))
|
||||
end
|
||||
|
||||
describe "#cmd_run" do
|
||||
describe '#cmd_check' do
|
||||
context 'when checking a remote exploit module' do
|
||||
let(:current_mod) { remote_exploit_mod }
|
||||
|
||||
it 'reports missing RHOST values' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = ''
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Check failed: Msf::OptionValidateError One or more options failed to validate: RHOSTS.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'runs a single RHOST value' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs multiple RHOST values' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.',
|
||||
'Checking for target 192.0.2.2:3000 with normalized datastore value 3.5',
|
||||
'192.0.2.2:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalizes the datastore before running' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 5.0',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.',
|
||||
'Checking for target 192.0.2.2:3000 with normalized datastore value 5.0',
|
||||
'192.0.2.2:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.1 192.0.2.2:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
pending('cmd_check does not support inline values yet')
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check('RHOSTS=192.0.2.5', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.5:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5:3000',
|
||||
'192.0.2.5:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.5:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports multiple inlined RHOST values' do
|
||||
pending('pending as inline module options are evaluated too late, and the module is therefore treated as a scanner')
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_check('RHOSTS=192.0.2.5 192.0.2.6', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.5:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5:3000',
|
||||
'192.0.2.5:3000 - Check failed: The state could not be determined.',
|
||||
'Checking for target 192.0.2.6:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.6:3000',
|
||||
'192.0.2.6:3000 - Check failed: The state could not be determined.',
|
||||
'Cleanup for target 192.0.2.5 192.0.2.6:3000'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'ignores the -j flag, and the module is not run as a job' do
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_check('-j')
|
||||
expected_output = [
|
||||
'Checking for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'192.0.2.1:3000 - Check failed: The state could not be determined.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when checking a non remote exploit module' do
|
||||
let(:current_mod) { non_remote_exploit_mod }
|
||||
|
||||
it 'notifies the user that this module does not support check' do
|
||||
subject.cmd_check
|
||||
expected_output = [
|
||||
'Check failed: NoMethodError This module does not support check.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#cmd_rerun" do
|
||||
describe '#cmd_run' do
|
||||
before do
|
||||
set_default_payload(current_mod)
|
||||
end
|
||||
|
||||
context 'when running a remote exploit module' do
|
||||
let(:current_mod) { remote_exploit_mod }
|
||||
|
||||
it 'reports missing RHOST values' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = nil
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Exploit failed: One or more options failed to validate: RHOSTS.',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'attempts to run modules with blank RHOSTS' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['RHOSTS'] = ''
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Exploit failed: One or more options failed to validate: RHOSTS.',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'reports a missing payload value' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['PAYLOAD'] = nil
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Exploit failed: A payload has not been selected.',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'runs a single RHOST value' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'runs multiple RHOST values' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Exploiting target {:address=>"192.0.2.1", :hostname=>nil}',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'Exploiting target {:address=>"192.0.2.2", :hostname=>nil}',
|
||||
'Running for target 192.0.2.2:3000 with normalized datastore value 3.5',
|
||||
'Cleanup for target 192.0.2.2:3000',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalizes the datastore before running' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Exploiting target {:address=>"192.0.2.1", :hostname=>nil}',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.1:3000',
|
||||
'Exploiting target {:address=>"192.0.2.2", :hostname=>nil}',
|
||||
'Running for target 192.0.2.2:3000 with normalized datastore value 5.0',
|
||||
'Cleanup for target 192.0.2.2:3000',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Running for target 192.0.2.5:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5:3000',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports multiple inlined RHOST values' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run('RHOSTS=192.0.2.5 192.0.2.6', 'FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Running for target 192.0.2.5 192.0.2.6:3000 with normalized datastore value 10.0',
|
||||
'Cleanup for target 192.0.2.5 192.0.2.6:3000',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'honors the -j flag, and the module is run as a job' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run('-j')
|
||||
expected_output = [
|
||||
'Running rex job 0 inline',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Exploit running as background job 0.',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'honors the -j flag, and the module is run as a job when there are multiple hosts' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1 192.0.2.2'
|
||||
subject.cmd_run('-j')
|
||||
expected_output = [
|
||||
'Exploiting target {:address=>"192.0.2.1", :hostname=>nil}',
|
||||
'Running rex job 0 inline',
|
||||
'Running for target 192.0.2.1:3000 with normalized datastore value 3.5',
|
||||
'Exploiting target {:address=>"192.0.2.2", :hostname=>nil}',
|
||||
'Running rex job 1 inline',
|
||||
'Running for target 192.0.2.2:3000 with normalized datastore value 3.5',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when running a non remote exploit module' do
|
||||
let(:current_mod) { non_remote_exploit_mod }
|
||||
|
||||
it 'reports a missing payload value' do
|
||||
allow(current_mod).to receive(:run).and_call_original
|
||||
current_mod.datastore['PAYLOAD'] = nil
|
||||
current_mod.datastore['RHOSTS'] = '192.0.2.1'
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Exploit failed: A payload has not been selected.',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
expect(subject.mod).not_to have_received(:run)
|
||||
end
|
||||
|
||||
it 'runs when a payload is set' do
|
||||
set_default_payload(current_mod)
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running with normalized datastore value 3.5',
|
||||
'Cleanup',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'normalized the datastore before running' do
|
||||
set_default_payload(current_mod)
|
||||
current_mod.datastore.store('FloatValue', '5.0')
|
||||
subject.cmd_run
|
||||
expected_output = [
|
||||
'Running with normalized datastore value 5.0',
|
||||
'Cleanup',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
|
||||
it 'supports inline options' do
|
||||
set_default_payload(current_mod)
|
||||
subject.cmd_run('FloatValue=10.0')
|
||||
expected_output = [
|
||||
'Running with normalized datastore value 10.0',
|
||||
'Cleanup',
|
||||
'Exploit completed, but no session was created.'
|
||||
]
|
||||
|
||||
expect(@combined_output).to match_array(expected_output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#cmd_rerun' do
|
||||
end
|
||||
|
||||
describe '#cmd_exploit' do
|
||||
end
|
||||
|
||||
describe '#cmd_reload' do
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
RSpec.shared_context 'Msf::Framework#threads cleaner' do
|
||||
RSpec.shared_context 'Msf::Framework#threads cleaner' do |options = {}|
|
||||
after(:example) do |example|
|
||||
unless framework.threads?
|
||||
if options.fetch(:verify_cleanup_required, true) && !framework.threads?
|
||||
fail RuntimeError.new(
|
||||
"framework.threads was never initialized. There are no threads to clean up. " \
|
||||
"Remove `include_context Msf::Framework#threads cleaner` from context around " \
|
||||
"'#{example.metadata.full_description}'"
|
||||
"'#{example.metadata[:full_description]}'"
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -21,4 +21,4 @@ RSpec.shared_context 'Msf::Framework#threads cleaner' do
|
|||
# ensure killed thread is cleaned up by VM
|
||||
thread_manager.monitor.join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,26 +1,45 @@
|
|||
RSpec.shared_context 'Msf::UIDriver' do
|
||||
let(:driver) do
|
||||
double(
|
||||
'Driver',
|
||||
:framework => framework
|
||||
).tap { |driver|
|
||||
allow(driver).to receive(:on_command_proc=).with(kind_of(Proc))
|
||||
allow(driver).to receive(:print_line).with(kind_of(String)) do |string|
|
||||
@output ||= []
|
||||
@output.concat string.split("\n")
|
||||
end
|
||||
allow(driver).to receive(:print_status).with(kind_of(String)) do |string|
|
||||
@output ||= []
|
||||
@output.concat string.split("\n")
|
||||
end
|
||||
allow(driver).to receive(:print_error).with(kind_of(String)) do |string|
|
||||
@error ||= []
|
||||
@error.concat string.split("\n")
|
||||
end
|
||||
allow(driver).to receive(:print_bad).with(kind_of(String)) do |string|
|
||||
@error ||= []
|
||||
@error.concat string.split("\n")
|
||||
end
|
||||
}
|
||||
instance = double('Driver', framework: framework)
|
||||
allow(instance).to receive(:on_command_proc=).with(kind_of(Proc))
|
||||
capture_logging(instance)
|
||||
instance
|
||||
end
|
||||
|
||||
let(:driver_input) do
|
||||
double(Rex::Ui::Text::Input)
|
||||
end
|
||||
|
||||
let(:driver_output) do
|
||||
instance = double(
|
||||
Rex::Ui::Text::Output,
|
||||
prompting?: false
|
||||
)
|
||||
|
||||
capture_logging(instance)
|
||||
instance
|
||||
end
|
||||
|
||||
def capture_logging(target)
|
||||
append_output = proc do |string|
|
||||
lines = string.split("\n")
|
||||
@output ||= []
|
||||
@output.concat(lines)
|
||||
@combined_output ||= []
|
||||
@combined_output.concat(lines)
|
||||
end
|
||||
append_error = proc do |string|
|
||||
lines = string.split("\n")
|
||||
@error ||= []
|
||||
@error.concat(lines)
|
||||
@combined_output ||= []
|
||||
@combined_output.concat(lines)
|
||||
end
|
||||
|
||||
allow(target).to receive(:print_line).with(kind_of(String), &append_output)
|
||||
allow(target).to receive(:print_status).with(kind_of(String), &append_output)
|
||||
allow(target).to receive(:print_warning).with(kind_of(String), &append_error)
|
||||
allow(target).to receive(:print_error).with(kind_of(String), &append_error)
|
||||
allow(target).to receive(:print_bad).with(kind_of(String), &append_error)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
RSpec.shared_context 'Rex::Job#start run inline' do
|
||||
# Intercepts calls to Rex::Job objects, and ensures that async rex jobs are immediately run inline instead of having
|
||||
# their execution deferred until later. This ensures that Jobs deterministically complete during a test run.
|
||||
def run_rex_jobs_inline!
|
||||
allow_any_instance_of(Rex::Job).to receive(:start).and_wrap_original do |original_method, original_async_value|
|
||||
original_receiver = original_method.receiver
|
||||
ctx = original_receiver.ctx
|
||||
if ctx.first.is_a?(Msf::Module)
|
||||
mod = ctx.first
|
||||
mod.print_status("Running rex job #{original_receiver.jid} inline")
|
||||
end
|
||||
expect(original_async_value).to be(true)
|
||||
new_async_value = false
|
||||
original_method.call(new_async_value)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue