metasploit-framework/spec/lib/msf/db_manager_spec.rb

2023 lines
61 KiB
Ruby
Raw Normal View History

#
# Specs
#
require 'spec_helper'
#
# Project
#
require 'metasploit/framework/database'
require 'msf/core'
describe Msf::DBManager do
include_context 'Msf::DBManager'
subject do
db_manager
end
it_should_behave_like 'Msf::DBManager::Migration'
it_should_behave_like 'Msf::DBManager::ImportMsfXml'
context 'CONSTANTS' do
context 'ADAPTER' do
subject(:adapter) {
described_class::ADAPTER
}
it { is_expected.to eq('postgresql') }
end
end
it { is_expected.to respond_to :active }
context '#add_rails_engine_migration_paths' do
def add_rails_engine_migration_paths
db_manager.add_rails_engine_migration_paths
end
it 'should not add duplicate paths to ActiveRecord::Migrator.migrations_paths' do
add_rails_engine_migration_paths
expect {
add_rails_engine_migration_paths
}.to_not change {
ActiveRecord::Migrator.migrations_paths.length
}
ActiveRecord::Migrator.migrations_paths.uniq.should == ActiveRecord::Migrator.migrations_paths
end
end
it { is_expected.to respond_to :add_workspace }
it { is_expected.to respond_to :after_establish_connection }
it { is_expected.to respond_to :check }
it { is_expected.to respond_to :connect }
it { is_expected.to respond_to :connection_established? }
it { is_expected.to respond_to :create_db }
it { is_expected.to respond_to :create_request }
it { is_expected.to respond_to :create_target }
it { is_expected.to respond_to :creds }
it { is_expected.to respond_to :default_workspace }
it { is_expected.to respond_to :dehex }
it { is_expected.to respond_to :del_host }
it { is_expected.to respond_to :del_service }
it { is_expected.to respond_to :delete_all_targets }
it { is_expected.to respond_to :disconnect }
it { is_expected.to respond_to :driver }
it { is_expected.to respond_to :drivers }
it { is_expected.to respond_to :drivers= }
it { is_expected.to respond_to :each_cred }
it { is_expected.to respond_to :each_distinct_target }
it { is_expected.to respond_to :each_exploited_host }
it { is_expected.to respond_to :each_host }
it { is_expected.to respond_to :each_loot }
it { is_expected.to respond_to :each_note }
it { is_expected.to respond_to :each_request }
it { is_expected.to respond_to :each_request_target }
it { is_expected.to respond_to :each_request_target_with_body }
it { is_expected.to respond_to :each_request_target_with_headers }
it { is_expected.to respond_to :each_request_target_with_path }
it { is_expected.to respond_to :each_request_target_with_query }
it { is_expected.to respond_to :each_service }
it { is_expected.to respond_to :each_target }
it { is_expected.to respond_to :each_vuln }
it { is_expected.to respond_to :emit }
it { is_expected.to respond_to :error }
it { is_expected.to respond_to :events }
it { is_expected.to respond_to :exploited_hosts }
it { is_expected.to respond_to :find_or_create_client }
it { is_expected.to respond_to :find_or_create_cred }
it { is_expected.to respond_to :find_or_create_host }
it { is_expected.to respond_to :find_or_create_loot }
it { is_expected.to respond_to :find_or_create_note }
it { is_expected.to respond_to :find_or_create_ref }
it { is_expected.to respond_to :find_or_create_report }
it { is_expected.to respond_to :find_or_create_service }
it { is_expected.to respond_to :find_or_create_task }
it { is_expected.to respond_to :find_or_create_vuln }
it { is_expected.to respond_to :find_qualys_asset_ports }
it { is_expected.to respond_to :find_qualys_asset_vuln_refs }
it { is_expected.to respond_to :find_qualys_asset_vulns }
it { is_expected.to respond_to :find_vuln_by_details }
it { is_expected.to respond_to :find_vuln_by_refs }
it { is_expected.to respond_to :find_workspace }
it { is_expected.to respond_to :get_client }
it { is_expected.to respond_to :get_host }
it { is_expected.to respond_to :get_ref }
it { is_expected.to respond_to :get_service }
it { is_expected.to respond_to :get_session }
it { is_expected.to respond_to :get_target }
it { is_expected.to respond_to :get_vuln }
it { is_expected.to respond_to :has_host? }
it { is_expected.to respond_to :has_ref? }
it { is_expected.to respond_to :has_vuln? }
it { is_expected.to respond_to :hosts }
it { is_expected.to respond_to :import }
it { is_expected.to respond_to :import_acunetix_noko_stream }
it { is_expected.to respond_to :import_acunetix_xml }
it { is_expected.to respond_to :import_amap_log }
it { is_expected.to respond_to :import_amap_log_file }
it { is_expected.to respond_to :import_amap_mlog }
it { is_expected.to respond_to :import_appscan_noko_stream }
it { is_expected.to respond_to :import_appscan_xml }
it { is_expected.to respond_to :import_burp_session_noko_stream }
it { is_expected.to respond_to :import_burp_session_xml }
it { is_expected.to respond_to :import_ci_noko_stream }
it { is_expected.to respond_to :import_ci_xml }
it { is_expected.to respond_to :import_file }
it { is_expected.to respond_to :import_filetype_detect }
it { is_expected.to respond_to :import_foundstone_noko_stream }
it { is_expected.to respond_to :import_foundstone_xml }
it { is_expected.to respond_to :import_fusionvm_xml }
it { is_expected.to respond_to :import_ip360_aspl_xml }
it { is_expected.to respond_to :import_ip360_xml_file }
it { is_expected.to respond_to :import_ip360_xml_v3 }
it { is_expected.to respond_to :import_ip_list }
it { is_expected.to respond_to :import_ip_list_file }
it { is_expected.to respond_to :import_libpcap }
it { is_expected.to respond_to :import_libpcap_file }
it { is_expected.to respond_to :import_mbsa_noko_stream }
it { is_expected.to respond_to :import_mbsa_xml }
it { is_expected.to respond_to :import_msf_collateral }
it { is_expected.to respond_to :import_msf_cred_dump }
it { is_expected.to respond_to :import_msf_cred_dump_zip }
it { is_expected.to respond_to :import_msf_file }
it { is_expected.to respond_to :import_msf_pwdump }
it { is_expected.to respond_to :import_msf_zip }
it { is_expected.to respond_to :import_nessus_nbe }
it { is_expected.to respond_to :import_nessus_nbe_file }
it { is_expected.to respond_to :import_nessus_xml }
it { is_expected.to respond_to :import_nessus_xml_file }
it { is_expected.to respond_to :import_nessus_xml_v2 }
it { is_expected.to respond_to :import_netsparker_xml }
it { is_expected.to respond_to :import_netsparker_xml_file }
it { is_expected.to respond_to :import_nexpose_noko_stream }
it { is_expected.to respond_to :import_nexpose_raw_noko_stream }
it { is_expected.to respond_to :import_nexpose_rawxml }
it { is_expected.to respond_to :import_nexpose_rawxml_file }
it { is_expected.to respond_to :import_nexpose_simplexml }
it { is_expected.to respond_to :import_nexpose_simplexml_file }
it { is_expected.to respond_to :import_nikto_xml }
it { is_expected.to respond_to :import_nmap_noko_stream }
it { is_expected.to respond_to :import_nmap_xml }
it { is_expected.to respond_to :import_nmap_xml_file }
it { is_expected.to respond_to :import_openvas_new_xml }
it { is_expected.to respond_to :import_openvas_new_xml_file }
it { is_expected.to respond_to :import_openvas_xml }
it { is_expected.to respond_to :import_outpost24_noko_stream }
it { is_expected.to respond_to :import_outpost24_xml }
it { is_expected.to respond_to :import_qualys_asset_xml }
it { is_expected.to respond_to :import_qualys_scan_xml }
it { is_expected.to respond_to :import_qualys_scan_xml_file }
it { is_expected.to respond_to :import_report }
it { is_expected.to respond_to :import_retina_xml }
it { is_expected.to respond_to :import_retina_xml_file }
it { is_expected.to respond_to :import_spiceworks_csv }
it { is_expected.to respond_to :import_wapiti_xml }
it { is_expected.to respond_to :import_wapiti_xml_file }
it { is_expected.to respond_to :initialize_adapter }
it { is_expected.to respond_to :initialize_database_support }
it { is_expected.to respond_to :initialize_sink }
it { is_expected.to respond_to :inspect_single_packet }
it { is_expected.to respond_to :inspect_single_packet_http }
it { is_expected.to respond_to :ipv46_validator }
it { is_expected.to respond_to :ipv4_validator }
it { is_expected.to respond_to :ipv6_validator }
it { is_expected.to respond_to :loots }
it { is_expected.to respond_to :modules_cached }
it { is_expected.to respond_to :modules_cached }
it { is_expected.to respond_to :modules_cached= }
it { is_expected.to respond_to :modules_cached= }
it { is_expected.to respond_to :msf_import_timestamps }
it { is_expected.to respond_to :netsparker_method_map }
it { is_expected.to respond_to :netsparker_params_map }
it { is_expected.to respond_to :netsparker_pname_map }
it { is_expected.to respond_to :netsparker_vulnerability_map }
it { is_expected.to respond_to :nexpose_host_from_rawxml }
it { is_expected.to respond_to :nexpose_refs_to_struct }
it { is_expected.to respond_to :nils_for_nulls }
it { is_expected.to respond_to :nmap_msf_service_map }
it { is_expected.to respond_to :normalize_host }
it { is_expected.to respond_to :notes }
context '#purge_all_module_details' do
def purge_all_module_details
db_manager.purge_all_module_details
end
let(:migrated) do
false
end
let(:module_detail_count) do
2
end
let!(:module_details) do
FactoryGirl.create_list(
:mdm_module_detail,
module_detail_count
)
end
before(:each) do
db_manager.stub(:migrated => migrated)
end
context 'with migrated' do
let(:migrated) do
true
end
let(:modules_caching) do
false
end
before(:each) do
db_manager.stub(:modules_caching => modules_caching)
end
context 'with modules_caching' do
let(:modules_caching) do
true
end
it 'should not destroy Mdm::Module::Details' do
expect {
purge_all_module_details
}.to_not change(Mdm::Module::Detail, :count)
end
end
context 'without modules_caching' do
it 'should destroy all Mdm::Module::Details' do
expect {
purge_all_module_details
}.to change(Mdm::Module::Detail, :count).by(-module_detail_count)
end
end
end
context 'without migrated' do
it 'should not destroy Mdm::Module::Details' do
expect {
purge_all_module_details
}.to_not change(Mdm::Module::Detail, :count)
end
end
end
it { is_expected.to respond_to :queue }
2014-10-07 20:25:00 +00:00
context '#remove_module_details' do
def remove_module_details
db_manager.remove_module_details(mtype, refname)
end
let(:migrated) do
false
end
let(:mtype) do
FactoryGirl.generate :mdm_module_detail_mtype
end
let(:refname) do
FactoryGirl.generate :mdm_module_detail_refname
end
let!(:module_detail) do
FactoryGirl.create(
:mdm_module_detail
)
end
before(:each) do
db_manager.stub(:migrated => migrated)
end
context 'with migrated' do
let(:migrated) do
true
end
let!(:module_detail) do
FactoryGirl.create(:mdm_module_detail)
end
context 'with matching Mdm::Module::Detail' do
let(:mtype) do
module_detail.mtype
end
let(:refname) do
module_detail.refname
end
it 'should destroy Mdm::Module::Detail' do
expect {
remove_module_details
}.to change(Mdm::Module::Detail, :count).by(-1)
end
end
context 'without matching Mdm::Module::Detail' do
it 'should not destroy Mdm::Module::Detail' do
expect {
remove_module_details
}.to_not change(Mdm::Module::Detail, :count)
end
end
end
context 'without migrated' do
it 'should not destroy Mdm::Module::Detail' do
expect {
remove_module_details
}.to_not change(Mdm::Module::Detail, :count)
end
end
end
it { is_expected.to respond_to :report_artifact }
it { is_expected.to respond_to :report_auth }
it { is_expected.to respond_to :report_auth_info }
it { is_expected.to respond_to :report_client }
it { is_expected.to respond_to :report_cred }
it { is_expected.to respond_to :report_event }
it { is_expected.to respond_to :report_exploit }
it { is_expected.to respond_to :report_exploit_attempt }
it { is_expected.to respond_to :report_exploit_failure }
it { is_expected.to respond_to :report_exploit_success }
it { is_expected.to respond_to :report_host }
it { is_expected.to respond_to :report_host_details }
it { is_expected.to respond_to :report_host_tag }
it { is_expected.to respond_to :report_import_note }
it { is_expected.to respond_to :report_loot }
it { is_expected.to respond_to :report_note }
it { is_expected.to respond_to :report_report }
it { is_expected.to respond_to :report_service }
context '#report_session' do
let(:options) do
{}
end
subject(:report_session) do
db_manager.report_session(options)
end
context 'with active' do
let(:active) do
true
end
context 'with :session' do
before(:each) do
options[:session] = session
end
context 'with Msf::Session' do
let(:exploit_datastore) do
Msf::ModuleDataStore.new(module_instance).tap do |datastore|
datastore['ParentModule'] = parent_module_fullname
remote_port = rand(2 ** 16 - 1)
datastore['RPORT'] = remote_port
end
end
let(:host) do
FactoryGirl.create(:mdm_host, :workspace => session_workspace)
end
let(:module_instance) do
name = 'multi/handler'
double(
'Msf::Module',
:fullname => "exploit/#{name}",
:framework => framework,
:name => name
)
end
let(:options_workspace) do
FactoryGirl.create(:mdm_workspace)
end
let(:parent_module_fullname) do
"exploit/#{parent_module_name}"
end
let(:parent_module_name) do
'windows/smb/ms08_067_netapi'
end
let(:parent_path) do
Metasploit::Framework.root.join('modules').to_path
end
let(:session) do
session_class.new.tap do |session|
session.exploit_datastore = exploit_datastore
session.info = 'Info'
session.platform = 'Platform'
session.session_host = host.address
session.sid = rand(100)
session.type = 'Session Type'
session.via_exploit = 'exploit/multi/handler'
session.via_payload = 'payload/single/windows/metsvc_bind_tcp'
session.workspace = session_workspace.name
end
end
let(:session_class) do
Class.new do
include Msf::Session
attr_accessor :datastore
attr_accessor :platform
attr_accessor :type
attr_accessor :via_exploit
attr_accessor :via_payload
end
end
let(:session_workspace) do
FactoryGirl.create(:mdm_workspace)
end
before(:each) do
reference_name = 'multi/handler'
path = File.join(parent_path, 'exploits', reference_name)
# fake cache data for exploit/multi/handler so it can be loaded
framework.modules.send(
:module_info_by_path=,
{
path =>
{
:parent_path => parent_path,
:reference_name => reference_name,
:type => 'exploit',
}
}
)
FactoryGirl.create(
:mdm_module_detail,
:fullname => parent_module_fullname,
:name => parent_module_name
)
end
context 'with :workspace' do
before(:each) do
options[:workspace] = options_workspace
end
it 'should not find workspace from session' do
db_manager.should_not_receive(:find_workspace)
report_session
end
end
context 'without :workspace' do
it 'should find workspace from session' do
db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original
report_session
end
it 'should pass session.workspace to #find_or_create_host' do
db_manager.should_receive(:find_or_create_host).with(
hash_including(
:workspace => session_workspace
)
).and_return(host)
report_session
end
end
context 'with workspace from either :workspace or session' do
it 'should pass normalized host from session as :host to #find_or_create_host' do
normalized_host = double('Normalized Host')
db_manager.stub(:normalize_host).with(session).and_return(normalized_host)
# stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere.
db_manager.stub(:report_vuln)
db_manager.should_receive(:find_or_create_host).with(
hash_including(
:host => normalized_host
)
).and_return(host)
report_session
end
context 'with session responds to arch' do
let(:arch) do
FactoryGirl.generate :mdm_host_arch
end
before(:each) do
session.stub(:arch => arch)
end
it 'should pass :arch to #find_or_create_host' do
db_manager.should_receive(:find_or_create_host).with(
hash_including(
:arch => arch
)
).and_call_original
report_session
end
end
context 'without session responds to arch' do
it 'should not pass :arch to #find_or_create_host' do
db_manager.should_receive(:find_or_create_host).with(
hash_excluding(
:arch
)
).and_call_original
report_session
end
end
it 'should create an Mdm::Session' do
expect {
report_session
}.to change(Mdm::Session, :count).by(1)
end
it { should be_an Mdm::Session }
it 'should set session.db_record to created Mdm::Session' do
mdm_session = report_session
session.db_record.should == mdm_session
end
context 'with session.via_exploit' do
it 'should create session.via_exploit module' do
framework.modules.should_receive(:create).with(session.via_exploit).and_call_original
report_session
end
it 'should create Mdm::Vuln' do
expect {
report_session
}.to change(Mdm::Vuln, :count).by(1)
end
context 'created Mdm::Vuln' do
let(:mdm_session) do
Mdm::Session.last
end
let(:rport) do
nil
end
before(:each) do
Timecop.freeze
session.exploit_datastore['RPORT'] = rport
report_session
end
after(:each) do
Timecop.return
end
subject(:vuln) do
Mdm::Vuln.last
end
2014-09-02 17:46:50 +00:00
it { expect(subject.host).to eq(Mdm::Host.last) }
it { expect(subject.refs).to eq([]) }
it { expect(subject.exploited_at).to be_within(1.second).of(Time.now.utc) }
context "with session.via_exploit 'exploit/multi/handler'" do
context "with session.exploit_datastore['ParentModule']" do
2014-09-02 17:46:50 +00:00
it { expect(subject.info).to eq("Exploited by #{parent_module_fullname} to create Session #{mdm_session.id}") }
it { expect(subject.name).to eq(parent_module_name) }
end
end
context "without session.via_exploit 'exploit/multi/handler'" do
let(:reference_name) do
'windows/smb/ms08_067_netapi'
end
before(:each) do
path = File.join(
parent_path,
'exploits',
"#{reference_name}.rb"
)
type = 'exploit'
# fake cache data for ParentModule so it can be loaded
framework.modules.send(
:module_info_by_path=,
{
path =>
{
:parent_path => parent_path,
:reference_name => reference_name,
:type => type,
}
}
)
session.via_exploit = "#{type}/#{reference_name}"
end
2014-09-02 17:46:50 +00:00
it { expect(subject.info).to eq("Exploited by #{session.via_exploit} to create Session #{mdm_session.id}") }
it { expect(subject.name).to eq(reference_name) }
end
context 'with RPORT' do
let(:rport) do
# use service.port instead of having service use rport so
# that service is forced to exist before call to
# report_service, which happens right after using rport in
# outer context's before(:each)
service.port
end
let(:service) do
FactoryGirl.create(
:mdm_service,
:host => host
)
end
2014-09-02 17:46:50 +00:00
it { expect(subject.service).to eq(service) }
end
context 'without RPORT' do
2014-09-02 17:46:50 +00:00
it { expect(subject.service).to be_nil }
end
end
context 'created Mdm::ExploitAttempt' do
let(:rport) do
nil
end
before(:each) do
Timecop.freeze
session.exploit_datastore['RPORT'] = rport
report_session
end
after(:each) do
Timecop.return
end
subject(:exploit_attempt) do
Mdm::ExploitAttempt.last
end
2014-09-02 17:46:50 +00:00
it { expect(subject.attempted_at).to be_within(1.second).of(Time.now.utc) }
# @todo https://www.pivotaltracker.com/story/show/48362615
2014-09-02 17:46:50 +00:00
it { expect(subject.session_id).to eq(Mdm::Session.last.id) }
it { expect(subject.exploited).to be_truthy }
# @todo https://www.pivotaltracker.com/story/show/48362615
2014-09-02 17:46:50 +00:00
it { expect(subject.vuln_id).to eq(Mdm::Vuln.last.id) }
context "with session.via_exploit 'exploit/multi/handler'" do
context "with session.datastore['ParentModule']" do
2014-09-02 17:46:50 +00:00
it { expect(subject.module).to eq(parent_module_fullname) }
end
end
context "without session.via_exploit 'exploit/multi/handler'" do
before(:each) do
session.via_exploit = parent_module_fullname
end
2014-09-02 17:46:50 +00:00
it { expect(subject.module).to eq(session.via_exploit) }
end
end
end
context 'returned Mdm::Session' do
before(:each) do
Timecop.freeze
end
after(:each) do
Timecop.return
end
subject(:mdm_session) do
report_session
end
#
# Ensure session has attributes present so its on mdm_session are
# not just comparing nils.
#
it 'should have session.info present' do
session.info.should be_present
end
it 'should have session.sid present' do
session.sid.should be_present
end
it 'should have session.platform present' do
session.platform.should be_present
end
it 'should have session.type present' do
session.type.should be_present
end
it 'should have session.via_exploit present' do
session.via_exploit.should be_present
end
it 'should have session.via_payload present' do
session.via_exploit.should be_present
end
2014-09-02 17:46:50 +00:00
it { expect(subject.datastore).to eq(session.exploit_datastore.to_h) }
it { expect(subject.desc).to eq(session.info) }
it { expect(subject.host_id).to eq(Mdm::Host.last.id) }
it { expect(subject.last_seen).to be_within(1.second).of(Time.now.utc) }
it { expect(subject.local_id).to eq(session.sid) }
it { expect(subject.opened_at).to be_within(1.second).of(Time.now.utc) }
it { expect(subject.platform).to eq(session.platform) }
it { expect(subject.routes).to eq([]) }
it { expect(subject.stype).to eq(session.type) }
it { expect(subject.via_payload).to eq(session.via_payload) }
context "with session.via_exploit 'exploit/multi/handler'" do
it "should have session.via_exploit of 'exploit/multi/handler'" do
session.via_exploit.should == 'exploit/multi/handler'
end
context "with session.exploit_datastore['ParentModule']" do
it "should have session.exploit_datastore['ParentModule']" do
session.exploit_datastore['ParentModule'].should_not be_nil
end
2014-09-02 17:46:50 +00:00
it { expect(subject.via_exploit).to eq(parent_module_fullname) }
end
end
context "without session.via_exploit 'exploit/multi/handler'" do
before(:each) do
reference_name = 'windows/smb/ms08_067_netapi'
path = File.join(
parent_path,
'exploits',
"#{reference_name}.rb"
)
type = 'exploit'
# fake cache data for ParentModule so it can be loaded
framework.modules.send(
:module_info_by_path=,
{
path =>
{
:parent_path => parent_path,
:reference_name => reference_name,
:type => type,
}
}
)
session.via_exploit = "#{type}/#{reference_name}"
end
it "should not have session.via_exploit of 'exploit/multi/handler'" do
session.via_exploit.should_not == 'exploit/multi/handler'
end
2014-09-02 17:46:50 +00:00
it { expect(subject.via_exploit).to eq(session.via_exploit) }
end
end
end
end
context 'without Msf::Session' do
let(:session) do
double('Not a Msf::Session')
end
it 'should raise ArgumentError' do
expect {
report_session
}.to raise_error(ArgumentError, "Invalid :session, expected Msf::Session")
end
end
end
context 'without :session' do
context 'with :host' do
before(:each) do
options[:host] = host
end
context 'with Mdm::Host' do
let(:host) do
FactoryGirl.create(:mdm_host)
end
context 'created Mdm::Session' do
let(:closed_at) do
nil
end
let(:close_reason) do
'Closed because...'
end
let(:description) do
'Session Description'
end
let(:exploit_full_name) do
'exploit/windows/smb/ms08_067_netapi'
end
let(:last_seen) do
nil
end
let(:opened_at) do
Time.now.utc - 5.minutes
end
let(:payload_full_name) do
'payload/singles/windows/metsvc_reverse_tcp'
end
let(:platform) do
'Host Platform'
end
let(:routes) do
nil
end
let(:session_type) do
'Session Type'
end
before(:each) do
options[:closed_at] = closed_at
options[:close_reason] = close_reason
options[:desc] = description
options[:last_seen] = last_seen
options[:opened_at] = opened_at
options[:platform] = platform
options[:routes] = routes
options[:stype] = session_type
options[:via_payload] = payload_full_name
options[:via_exploit] = exploit_full_name
end
subject(:mdm_session) do
report_session
end
2014-09-02 17:46:50 +00:00
it { expect(subject.close_reason).to eq(close_reason) }
it { expect(subject.desc).to eq(description) }
it { expect(subject.host).to eq(host) }
it { expect(subject.platform).to eq(platform) }
it { expect(subject.stype).to eq(session_type) }
it { expect(subject.via_exploit).to eq(exploit_full_name) }
it { expect(subject.via_payload).to eq(payload_full_name) }
context 'with :last_seen' do
let(:last_seen) do
opened_at
end
2014-09-02 17:46:50 +00:00
it { expect(subject.last_seen).to eq(last_seen) }
end
context 'with :closed_at' do
let(:closed_at) do
opened_at + 1.minute
end
2014-09-02 17:46:50 +00:00
it { expect(subject.closed_at).to eq(closed_at) }
end
context 'without :closed_at' do
2014-09-02 17:46:50 +00:00
it { expect(subject.closed_at).to be_nil }
end
context 'without :last_seen' do
context 'with :closed_at' do
let(:closed_at) do
opened_at + 1.minute
end
2014-09-02 17:46:50 +00:00
it { expect(subject.last_seen).to eq(closed_at) }
end
context 'without :closed_at' do
2014-09-02 17:46:50 +00:00
it { expect(subject.last_seen).to be_nil }
end
end
context 'with :routes' do
let(:routes) do
FactoryGirl.build_list(
:mdm_route,
1,
:session => nil
)
end
2014-09-02 17:46:50 +00:00
it { expect(subject.routes).to eq(routes) }
end
context 'without :routes' do
2014-09-02 17:46:50 +00:00
it { expect(subject.routes).to eq([]) }
end
end
end
context 'without Mdm::Host' do
let(:host) do
'192.168.0.1'
end
it 'should raise ArgumentError' do
expect {
report_session
}.to raise_error(ArgumentError, "Invalid :host, expected Host object")
end
end
end
context 'without :host' do
it 'should raise ArgumentError' do
expect {
report_session
}.to raise_error(ArgumentError)
end
end
end
end
context 'without active' do
let(:active) do
false
end
it { should be_nil }
it 'should not create a connection' do
ActiveRecord::Base.connection_pool.should_not_receive(:with_connection)
report_session
end
end
end
it { is_expected.to respond_to :report_session_event }
it { is_expected.to respond_to :report_session_route }
it { is_expected.to respond_to :report_session_route_remove }
it { is_expected.to respond_to :report_task }
it { is_expected.to respond_to :report_vuln }
it { is_expected.to respond_to :report_vuln_attempt }
it { is_expected.to respond_to :report_vuln_details }
it { is_expected.to respond_to :report_web_form }
it { is_expected.to respond_to :report_web_page }
it { is_expected.to respond_to :report_web_site }
it { is_expected.to respond_to :report_web_vuln }
it { is_expected.to respond_to :reports }
it { is_expected.to respond_to :request_distinct_targets }
it { is_expected.to respond_to :request_sql }
it { is_expected.to respond_to :requests }
it { is_expected.to respond_to :rexmlify }
it { is_expected.to respond_to :rfc3330_reserved }
context '#search_modules' do
subject(:search_modules) do
db_manager.search_modules(search_string)
end
let(:module_details) do
search_modules.to_a
end
context 'with app keyword' do
let(:search_string) do
"app:#{app}"
end
before(:each) do
Mdm::Module::Detail::STANCES.each do |stance|
FactoryGirl.create(:mdm_module_detail, :stance => stance)
end
end
context 'with client' do
let(:app) do
'client'
end
it "should match Mdm::Module::Detail#stance 'passive'" do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.stance == 'passive'
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with server' do
let(:app) do
'server'
end
it "should match Mdm::Module::Detail#stance 'aggressive'" do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.stance == 'aggressive'
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
end
context 'with author keyword' do
let(:search_string) do
# us inspect so strings with spaces are quoted correctly
"author:#{author}"
end
let!(:module_authors) do
FactoryGirl.create_list(:mdm_module_author, 2)
end
let(:target_module_author) do
module_authors.first
end
context 'with Mdm::Module::Author#email' do
let(:author) do
target_module_author.email
end
it 'should match Mdm::Module::Author#email' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.authors.any? { |module_author|
module_author.email == target_module_author.email
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with Mdm::Module::Author#name' do
let(:author) do
# use inspect to quote space in name
target_module_author.name.inspect
end
it 'should match Mdm::Module::Author#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.authors.any? { |module_author|
module_author.name == target_module_author.name
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
end
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :bid
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :cve
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :edb
context 'with name keyword' do
let(:search_string) do
"name:#{name}"
end
let!(:existing_module_details) do
FactoryGirl.create_list(:mdm_module_detail, 2)
end
let(:target_module_detail) do
existing_module_details.first
end
context 'with Mdm::Module::Detail#fullname' do
let(:name) do
target_module_detail.fullname
end
it 'should match Mdm::Module::Detail#fullname' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.fullname == target_module_detail.fullname
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with Mdm::Module::Detail#name' do
let(:name) do
# use inspect so spaces are inside quotes
target_module_detail.name.inspect
end
it 'should match Mdm::Module::Detail#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.name == target_module_detail.name
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
end
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :os
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :osvdb
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :platform
context 'with ref keyword' do
let(:ref) do
FactoryGirl.generate :mdm_module_ref_name
end
let(:search_string) do
# use inspect to quote spaces in string
"ref:#{ref.inspect}"
end
let!(:module_ref) do
FactoryGirl.create(:mdm_module_ref)
end
context 'with Mdm::Module::Ref#name' do
let(:ref) do
module_ref.name
end
it 'should match Mdm::Module::Ref#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.refs.any? { |module_ref|
module_ref.name == ref
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'without Mdm::Module::Ref#name' do
it 'should not match Mdm::Module::Ref#name' do
module_details.count.should == 0
end
end
end
context 'with type keyword' do
let(:type) do
FactoryGirl.generate :mdm_module_detail_mtype
end
let(:search_string) do
"type:#{type}"
end
let(:target_module_detail) do
all_module_details.first
end
let!(:all_module_details) do
FactoryGirl.create_list(:mdm_module_detail, 2)
end
context 'with Mdm::Module::Ref#name' do
let(:type) do
target_module_detail.mtype
end
it 'should match Mdm::Module::Detail#mtype' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.mtype == type
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'without Mdm::Module::Detail#mtype' do
it 'should not match Mdm::Module::Detail#mtype' do
module_details.count.should == 0
end
end
end
context 'without keyword' do
context 'with Mdm::Module::Action#name' do
let(:search_string) do
module_action.name
end
let!(:module_action) do
FactoryGirl.create(:mdm_module_action)
end
it 'should match Mdm::Module::Action#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.actions.any? { |module_action|
module_action.name == search_string
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with Mdm::Module::Arch#name' do
let(:search_string) do
module_arch.name
end
let!(:module_arch) do
FactoryGirl.create(:mdm_module_arch)
end
it 'should match Mdm::Module::Arch#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.archs.any? { |module_arch|
module_arch.name == search_string
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with Mdm::Module::Author#name' do
let(:search_string) do
module_author.name
end
let!(:module_author) do
FactoryGirl.create(:mdm_module_author)
end
it 'should match Mdm::Module::Author#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.authors.any? { |module_author|
module_author.name == search_string
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with Mdm::Module::Detail' do
let(:target_module_detail) do
all_module_details.first
end
let!(:all_module_details) do
FactoryGirl.create_list(:mdm_module_detail, 3)
end
context 'with #description' do
let(:search_string) do
# use inspect to quote spaces in string
target_module_detail.description.inspect
end
it 'should match Mdm::Module::Detail#description' do
module_details.count.should == 1
module_details.all? { |module_detail|
module_detail.description == target_module_detail.description
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with #fullname' do
let(:search_string) do
target_module_detail.fullname
end
it 'should match Mdm::Module::Detail#fullname' do
module_details.count.should == 1
module_details.all? { |module_detail|
module_detail.fullname == search_string
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with #name' do
let(:search_string) do
# use inspect to quote spaces in string
target_module_detail.name.inspect
end
it 'should match Mdm::Module::Detail#name' do
module_details.count.should == 1
module_details.all? { |module_detail|
module_detail.name == target_module_detail.name
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
end
context 'with Mdm::Module::Platform#name' do
let(:search_string) do
module_platform.name
end
let!(:module_platform) do
FactoryGirl.create(:mdm_module_platform)
end
it 'should match Mdm::Module::Platform#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.platforms.any? { |module_platform|
module_platform.name == search_string
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with Mdm::Module::Ref#name' do
let(:search_string) do
module_ref.name
end
let!(:module_ref) do
FactoryGirl.create(:mdm_module_ref)
end
it 'should match Mdm::Module::Ref#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.refs.any? { |module_ref|
module_ref.name == search_string
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
context 'with Mdm::Module::Target#name' do
let(:search_string) do
module_target.name
end
let!(:module_target) do
FactoryGirl.create(:mdm_module_target)
end
it 'should match Mdm::Module::Target#name' do
module_details.count.should > 0
module_details.all? { |module_detail|
module_detail.targets.any? { |module_target|
module_target.name == search_string
}
2014-08-25 23:13:38 +00:00
}.should be_truthy
end
end
end
end
it { is_expected.to respond_to :selected_host }
it { is_expected.to respond_to :selected_id }
it { is_expected.to respond_to :selected_port }
it { is_expected.to respond_to :selected_ssl }
it { is_expected.to respond_to :selected_wmap_target }
it { is_expected.to respond_to :service_name_map }
it { is_expected.to respond_to :services }
it { is_expected.to respond_to :sink }
it { is_expected.to respond_to :sql_query }
it { is_expected.to respond_to :sync }
it { is_expected.to respond_to :target_requests }
it { is_expected.to respond_to :targets }
it { is_expected.to respond_to :tasks }
it { is_expected.to respond_to :unserialize_object }
context '#update_all_module_details' do
def update_all_module_details
db_manager.update_all_module_details
end
let(:migrated) do
false
end
before(:each) do
db_manager.stub(:migrated => migrated)
end
context 'with migrated' do
let(:migrated) do
true
end
let(:modules_caching) do
true
end
before(:each) do
db_manager.stub(:modules_caching => modules_caching)
end
context 'with modules_caching' do
it 'should not update module details' do
db_manager.should_not_receive(:update_module_details)
update_all_module_details
end
end
context 'without modules_caching' do
let(:modules_caching) do
false
end
it 'should set framework.cache_thread to current thread and then nil' do
framework.should_receive(:cache_thread=).with(Thread.current).ordered
framework.should_receive(:cache_thread=).with(nil).ordered
update_all_module_details
end
it 'should set modules_cached to false and then true' do
db_manager.should_receive(:modules_cached=).with(false).ordered
db_manager.should_receive(:modules_cached=).with(true).ordered
update_all_module_details
end
it 'should set modules_caching to true and then false' do
db_manager.should_receive(:modules_caching=).with(true).ordered
db_manager.should_receive(:modules_caching=).with(false).ordered
update_all_module_details
end
context 'with Mdm::Module::Details' do
let(:module_pathname) do
parent_pathname.join(
'exploits',
"#{reference_name}.rb"
)
end
let(:modification_time) do
module_pathname.mtime
end
let(:parent_pathname) do
Metasploit::Framework.root.join('modules')
end
let(:reference_name) do
'windows/smb/ms08_067_netapi'
end
let(:type) do
'exploit'
end
let!(:module_detail) do
# needs to reference a real module so that it can be loaded
FactoryGirl.create(
:mdm_module_detail,
:file => module_pathname.to_path,
:mtime => modification_time,
:mtype => type,
:ready => ready,
:refname => reference_name
)
end
context '#ready' do
context 'false' do
let(:ready) do
false
end
it_should_behave_like 'Msf::DBManager#update_all_module_details refresh'
end
context 'true' do
let(:ready) do
true
end
context 'with existing Mdm::Module::Detail#file' do
context 'with same Mdm::Module::Detail#mtime and File.mtime' do
it 'should not update module details' do
db_manager.should_not_receive(:update_module_details)
update_all_module_details
end
end
context 'without same Mdm::Module::Detail#mtime and File.mtime' do
let(:modification_time) do
# +1 as rand can return 0 and the time must be different for
# this context.
super() - (rand(1.day) + 1)
end
it_should_behave_like 'Msf::DBManager#update_all_module_details refresh'
end
end
# Emulates a module being removed or renamed
context 'without existing Mdm::Module::Detail#file' do
# have to compute modification manually since the
# `module_pathname` refers to a non-existent file and
# `module_pathname.mtime` would error.
let(:modification_time) do
Time.now.utc - 1.day
end
let(:module_pathname) do
parent_pathname.join('exploits', 'deleted.rb')
end
it 'should not update module details' do
db_manager.should_not_receive(:update_module_details)
update_all_module_details
end
end
end
end
end
end
end
context 'without migrated' do
it 'should not update module details' do
db_manager.should_not_receive(:update_module_details)
update_all_module_details
end
end
end
it { is_expected.to respond_to :update_host_via_sysinfo }
context '#update_module_details' do
def update_module_details
db_manager.update_module_details(module_instance)
end
let(:loader) do
loader = framework.modules.send(:loaders).find { |loader|
loader.loadable?(parent_path)
}
# Override load_error so that rspec will print it instead of going to framework log
def loader.load_error(module_path, error)
raise error
end
loader
end
let(:migrated) do
false
end
let(:module_instance) do
# make sure the module is loaded into the module_set
loaded = loader.load_module(parent_path, module_type, module_reference_name)
unless loaded
module_path = loader.module_path(parent_path, type, module_reference_name)
fail "#{description} failed to load: #{module_path}"
end
module_set.create(module_reference_name)
end
let(:module_set) do
framework.modules.module_set(module_type)
end
let(:module_type) do
'exploit'
end
let(:module_reference_name) do
'windows/smb/ms08_067_netapi'
end
let(:parent_path) do
parent_pathname.to_path
end
let(:parent_pathname) do
Metasploit::Framework.root.join('modules')
end
let(:type_directory) do
'exploits'
end
before(:each) do
db_manager.stub(:migrated => migrated)
end
context 'with migrated' do
let(:migrated) do
true
end
it 'should call module_to_details_hash to get Mdm::Module::Detail attributes and association attributes' do
db_manager.should_receive(:module_to_details_hash).and_call_original
update_module_details
end
it 'should create an Mdm::Module::Detail' do
expect {
update_module_details
}.to change(Mdm::Module::Detail, :count).by(1)
end
context 'module_to_details_hash' do
let(:module_to_details_hash) do
{
:mtype => module_type,
:privileged => privileged,
:rank => rank,
:refname => module_reference_name,
:stance => stance
}
end
let(:privileged) do
FactoryGirl.generate :mdm_module_detail_privileged
end
let(:rank) do
FactoryGirl.generate :mdm_module_detail_rank
end
let(:stance) do
FactoryGirl.generate :mdm_module_detail_stance
end
before(:each) do
db_manager.stub(
:module_to_details_hash
).with(
module_instance
).and_return(
module_to_details_hash
)
end
context 'Mdm::Module::Detail' do
subject(:module_detail) do
Mdm::Module::Detail.last
end
before(:each) do
update_module_details
end
2014-09-02 17:46:50 +00:00
it { expect(subject.mtype).to eq(module_type) }
it { expect(subject.privileged).to eq(privileged) }
it { expect(subject.rank).to eq(rank) }
it { expect(subject.ready).to be_truthy }
it { expect(subject.refname).to eq(module_reference_name) }
it { expect(subject.stance).to eq(stance) }
end
context 'with :bits' do
let(:bits) do
[]
end
before(:each) do
module_to_details_hash[:bits] = bits
end
context 'with :action' do
let(:name) do
FactoryGirl.generate :mdm_module_action_name
end
let(:bits) do
super() << [
:action,
{
:name => name
}
]
end
it 'should create an Mdm::Module::Action' do
expect {
update_module_details
}.to change(Mdm::Module::Action, :count).by(1)
end
context 'Mdm::Module::Action' do
subject(:module_action) do
module_detail.actions.last
end
let(:module_detail) do
Mdm::Module::Detail.last
end
before(:each) do
update_module_details
end
2014-09-02 17:46:50 +00:00
it { expect(subject.name).to eq(name) }
end
end
context 'with :arch' do
let(:name) do
FactoryGirl.generate :mdm_module_arch_name
end
let(:bits) do
super() << [
:arch,
{
:name => name
}
]
end
it 'should create an Mdm::Module::Arch' do
expect {
update_module_details
}.to change(Mdm::Module::Arch, :count).by(1)
end
context 'Mdm::Module::Arch' do
subject(:module_arch) do
module_detail.archs.last
end
let(:module_detail) do
Mdm::Module::Detail.last
end
before(:each) do
update_module_details
end
2014-09-02 17:46:50 +00:00
it { expect(subject.name).to eq(name) }
end
end
context 'with :author' do
let(:email) do
FactoryGirl.generate :mdm_module_author_email
end
let(:name) do
FactoryGirl.generate :mdm_module_author_name
end
let(:bits) do
super() << [
:author,
{
:email => email,
:name => name
}
]
end
it 'should create an Mdm::Module::Author' do
expect {
update_module_details
}.to change(Mdm::Module::Author, :count).by(1)
end
context 'Mdm::Module::Author' do
subject(:module_author) do
module_detail.authors.last
end
let(:module_detail) do
Mdm::Module::Detail.last
end
before(:each) do
update_module_details
end
2014-09-02 17:46:50 +00:00
it { expect(subject.name).to eq(name) }
it { expect(subject.email).to eq(email) }
end
end
context 'with :platform' do
let(:bits) do
super() << [
:platform,
{
:name => name
}
]
end
let(:name) do
FactoryGirl.generate :mdm_module_platform_name
end
it 'should create an Mdm::Module::Platform' do
expect {
update_module_details
}.to change(Mdm::Module::Platform, :count).by(1)
end
context 'Mdm::Module::Platform' do
subject(:module_platform) do
module_detail.platforms.last
end
let(:module_detail) do
Mdm::Module::Detail.last
end
before(:each) do
update_module_details
end
2014-09-02 17:46:50 +00:00
it { expect(subject.name).to eq(name) }
end
end
context 'with :ref' do
let(:bits) do
super() << [
:ref,
{
:name => name
}
]
end
let(:name) do
FactoryGirl.generate :mdm_module_ref_name
end
it 'should create an Mdm::Module::Ref' do
expect {
update_module_details
}.to change(Mdm::Module::Ref, :count).by(1)
end
context 'Mdm::Module::Ref' do
subject(:module_ref) do
module_detail.refs.last
end
let(:module_detail) do
Mdm::Module::Detail.last
end
before(:each) do
update_module_details
end
2014-09-02 17:46:50 +00:00
it { expect(subject.name).to eq(name) }
end
end
context 'with :target' do
let(:bits) do
super() << [
:target,
{
:index => index,
:name => name
}
]
end
let(:index) do
FactoryGirl.generate :mdm_module_target_index
end
let(:name) do
FactoryGirl.generate :mdm_module_target_name
end
it 'should create an Mdm::Module::Target' do
expect {
update_module_details
}.to change(Mdm::Module::Target, :count).by(1)
end
context 'Mdm::Module::Target' do
subject(:module_target) do
module_detail.targets.last
end
let(:module_detail) do
Mdm::Module::Detail.last
end
before(:each) do
update_module_details
end
2014-09-02 17:46:50 +00:00
it { expect(subject.index).to eq(index) }
it { expect(subject.name).to eq(name) }
end
end
end
end
it_should_behave_like 'Msf::DBManager#update_module_details with module',
:reference_name => 'admin/2wire/xslt_password_reset',
:type => 'auxiliary'
it_should_behave_like 'Msf::DBManager#update_module_details with module',
:reference_name => 'generic/none',
:type => 'encoder'
it_should_behave_like 'Msf::DBManager#update_module_details with module',
:reference_name => 'windows/smb/ms08_067_netapi',
:type => 'exploit'
it_should_behave_like 'Msf::DBManager#update_module_details with module',
:reference_name => 'x64/simple',
:type => 'nop'
# @todo determine how to load a single payload to test payload type outside of msfconsole
it_should_behave_like 'Msf::DBManager#update_module_details with module',
:reference_name => 'windows/escalate/screen_unlock',
:type => 'post'
end
context 'without migrated' do
it 'should not create an Mdm::Module::Detail' do
expect {
update_module_details
}.to_not change(Mdm::Module::Detail, :count)
end
end
end
it { is_expected.to respond_to :update_vuln_details }
it { is_expected.to respond_to :usable }
it { is_expected.to respond_to :usable= }
it { is_expected.to respond_to :validate_import_file }
it { is_expected.to respond_to :validate_ips }
it { is_expected.to respond_to :vulns }
it { is_expected.to respond_to :warn_about_rubies }
it { is_expected.to respond_to :workspace }
it { is_expected.to respond_to :workspace= }
it { is_expected.to respond_to :workspaces }
end