diff --git a/Gemfile b/Gemfile index 2ce6643e21..0262173312 100755 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,8 @@ source 'https://rubygems.org' # spec.add_runtime_dependency '', [] gemspec name: 'metasploit-framework' +gem 'metasploit_data_models', git: 'https://github.com/rapid7/metasploit_data_models.git', branch: 'staging/single-vuln-push' + # separate from test as simplecov is not run on travis-ci group :coverage do # code coverage for tests diff --git a/Gemfile.lock b/Gemfile.lock index a1d14ffc1e..e7138383d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,18 @@ +GIT + remote: https://github.com/rapid7/metasploit_data_models.git + revision: 6835d9cf61ad998db4184405ab21a92ef92caaa5 + branch: staging/single-vuln-push + specs: + metasploit_data_models (0.23.2.pre.single.pre.vuln.pre.push) + activerecord (>= 3.2.13, < 4.0.0) + activesupport + arel-helpers + metasploit-concern (~> 0.3.0) + metasploit-model (~> 0.29.0) + pg + railties (< 4.0.0) + recog (~> 1.0) + PATH remote: . specs: @@ -123,15 +138,6 @@ GEM metasploit-model (0.29.0) activesupport railties (< 4.0.0) - metasploit_data_models (0.23.1) - activerecord (>= 3.2.13, < 4.0.0) - activesupport - arel-helpers - metasploit-concern (~> 0.3.0) - metasploit-model (~> 0.29.0) - pg - railties (< 4.0.0) - recog (~> 1.0) meterpreter_bins (0.0.17) method_source (0.8.2) mime-types (1.25.1) @@ -236,6 +242,7 @@ DEPENDENCIES metasploit-framework! metasploit-framework-db! metasploit-framework-pcap! + metasploit_data_models! pry rake (>= 10.0.0) redcarpet diff --git a/db/schema.rb b/db/schema.rb index dc45813e0b..5113661fa2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20150212214222) do +ActiveRecord::Schema.define(:version => 20150312155312) do create_table "api_keys", :force => true do |t| t.text "token" @@ -19,6 +19,47 @@ ActiveRecord::Schema.define(:version => 20150212214222) do t.datetime "updated_at", :null => false end + create_table "automatic_exploitation_match_results", :force => true do |t| + t.integer "match_id" + t.integer "run_id" + t.string "state", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "automatic_exploitation_match_sets", :force => true do |t| + t.integer "workspace_id" + t.integer "user_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "automatic_exploitation_match_sets", ["user_id"], :name => "index_automatic_exploitation_match_sets_on_user_id" + add_index "automatic_exploitation_match_sets", ["workspace_id"], :name => "index_automatic_exploitation_match_sets_on_workspace_id" + + create_table "automatic_exploitation_matches", :force => true do |t| + t.integer "module_detail_id" + t.string "state" + t.integer "nexpose_data_vulnerability_definition_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.integer "match_set_id" + t.string "matchable_type" + t.integer "matchable_id" + t.text "module_fullname" + end + + add_index "automatic_exploitation_matches", ["module_detail_id"], :name => "index_automatic_exploitation_matches_on_ref_id" + add_index "automatic_exploitation_matches", ["module_fullname"], :name => "index_automatic_exploitation_matches_on_module_fullname" + + create_table "automatic_exploitation_runs", :force => true do |t| + t.integer "workspace_id" + t.integer "user_id" + t.integer "match_set_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "clients", :force => true do |t| t.integer "host_id" t.datetime "created_at" @@ -155,19 +196,22 @@ ActiveRecord::Schema.define(:version => 20150212214222) do end create_table "loots", :force => true do |t| - t.integer "workspace_id", :default => 1, :null => false + t.integer "workspace_id", :default => 1, :null => false t.integer "host_id" t.integer "service_id" - t.string "ltype", :limit => 512 - t.string "path", :limit => 1024 + t.string "ltype", :limit => 512 + t.string "path", :limit => 1024 t.text "data" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "content_type" t.text "name" t.text "info" + t.integer "module_run_id" end + add_index "loots", ["module_run_id"], :name => "index_loots_on_module_run_id" + create_table "macros", :force => true do |t| t.datetime "created_at", :null => false t.datetime "updated_at", :null => false @@ -359,6 +403,24 @@ ActiveRecord::Schema.define(:version => 20150212214222) do add_index "module_refs", ["detail_id"], :name => "index_module_refs_on_module_detail_id" add_index "module_refs", ["name"], :name => "index_module_refs_on_name" + create_table "module_runs", :force => true do |t| + t.datetime "attempted_at" + t.text "fail_detail" + t.string "fail_reason" + t.integer "module_detail_id" + t.text "module_full_name" + t.integer "port" + t.string "proto" + t.integer "session_id" + t.string "status" + t.integer "trackable_id" + t.string "trackable_type" + t.integer "user_id" + t.string "username" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "module_targets", :force => true do |t| t.integer "detail_id" t.integer "index" @@ -481,13 +543,16 @@ ActiveRecord::Schema.define(:version => 20150212214222) do t.integer "port" t.string "platform" t.text "datastore" - t.datetime "opened_at", :null => false + t.datetime "opened_at", :null => false t.datetime "closed_at" t.string "close_reason" t.integer "local_id" t.datetime "last_seen" + t.integer "module_run_id" end + add_index "sessions", ["module_run_id"], :name => "index_sessions_on_module_run_id" + create_table "tags", :force => true do |t| t.integer "user_id" t.string "name", :limit => 1024 diff --git a/lib/msf/core/db_manager/exploit_attempt.rb b/lib/msf/core/db_manager/exploit_attempt.rb index 9f864ec296..dd2cb95000 100644 --- a/lib/msf/core/db_manager/exploit_attempt.rb +++ b/lib/msf/core/db_manager/exploit_attempt.rb @@ -209,4 +209,4 @@ module Msf::DBManager::ExploitAttempt host.exploit_attempts.create(attempt_info) } end -end \ No newline at end of file +end diff --git a/lib/msf/core/db_manager/session.rb b/lib/msf/core/db_manager/session.rb index 56b4a0a436..3460465d73 100644 --- a/lib/msf/core/db_manager/session.rb +++ b/lib/msf/core/db_manager/session.rb @@ -121,7 +121,6 @@ module Msf::DBManager::Session else raise ArgumentError.new("Missing option :session or :host") end - ret = {} # Truncate the session data if necessary if sess_data[:desc] @@ -145,61 +144,80 @@ module Msf::DBManager::Session end end - if opts[:session] session.db_record = s - end - - # If this is a live session, we know the host is vulnerable to something. - if opts[:session] and session.via_exploit - mod = framework.modules.create(session.via_exploit) - - if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] - mod_fullname = sess_data[:datastore]['ParentModule'] - mod_name = ::Mdm::Module::Detail.find_by_fullname(mod_fullname).name - else - mod_name = mod.name - mod_fullname = mod.fullname + if session.assoc_exploit.user_data_is_match? + # do some shit with the match + MetasploitDataModels::AutomaticExploitation::MatchResult.create!( + match: session.assoc_exploit.user_data[:match], + match_set: session.assoc_exploit.user_data[:match_set], + run: session.assoc_exploit.user_data[:run], + state: 'succeeded', + ) + elsif session.via_exploit + # This is a live session, we know the host is vulnerable to something. + infer_vuln_from_session(session, wspace) end - - vuln_info = { - :host => host.address, - :name => mod_name, - :refs => mod.references, - :workspace => wspace, - :exploited_at => Time.now.utc, - :info => "Exploited by #{mod_fullname} to create Session #{s.id}" - } - - port = session.exploit_datastore["RPORT"] - service = (port ? host.services.find_by_port(port.to_i) : nil) - - vuln_info[:service] = service if service - - vuln = framework.db.report_vuln(vuln_info) - - if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] - via_exploit = sess_data[:datastore]['ParentModule'] - else - via_exploit = session.via_exploit - end - attempt_info = { - :timestamp => Time.now.utc, - :workspace => wspace, - :module => via_exploit, - :username => session.username, - :refs => mod.references, - :session_id => s.id, - :host => host, - :service => service, - :vuln => vuln - } - - framework.db.report_exploit_success(attempt_info) - end s } end -end \ No newline at end of file + + protected + + # @param session [Msf::Session] A session with a {db_record Msf::Session#db_record} + # @param wspace [Mdm::Workspace] + # @return [void] + def infer_vuln_from_session(session, wspace) + s = session.db_record + host = session.db_record.host + + mod = framework.modules.create(session.via_exploit) + + if session.via_exploit == "exploit/multi/handler" and session.exploit_datastore['ParentModule'] + mod_fullname = session.exploit_datastore['ParentModule'] + mod_name = ::Mdm::Module::Detail.find_by_fullname(mod_fullname).name + else + mod_name = mod.name + mod_fullname = mod.fullname + end + + vuln_info = { + :host => host.address, + :name => mod_name, + :refs => mod.references, + :workspace => wspace, + :exploited_at => Time.now.utc, + :info => "Exploited by #{mod_fullname} to create Session #{s.id}" + } + + port = session.exploit_datastore["RPORT"] + service = (port ? host.services.find_by_port(port.to_i) : nil) + + vuln_info[:service] = service if service + + vuln = framework.db.report_vuln(vuln_info) + + if session.via_exploit == "exploit/multi/handler" and session.exploit_datastore['ParentModule'] + via_exploit = session.exploit_datastore['ParentModule'] + else + via_exploit = session.via_exploit + end + attempt_info = { + :timestamp => Time.now.utc, + :workspace => wspace, + :module => via_exploit, + :username => session.username, + :refs => mod.references, + :session_id => s.id, + :host => host, + :service => service, + :vuln => vuln + } + + framework.db.report_exploit_success(attempt_info) + + end + +end diff --git a/lib/msf/core/module.rb b/lib/msf/core/module.rb index e9e7365028..4b6f946294 100644 --- a/lib/msf/core/module.rb +++ b/lib/msf/core/module.rb @@ -278,6 +278,14 @@ class Module raise RuntimeError, "#{reason.to_s}: #{msg}" end + # Whether {user_data} contains everything necessary to make a + # `MetasploitDataModels::AutomaticExploitation::MatchResult` + # + # @return [bool] + def user_data_is_match? + user_data.kind_of?(Hash) && user_data.keys == [ :match, :match_set, :run ] + end + ## # # Just some handy quick checks @@ -295,6 +303,7 @@ class Module # The array of zero or more platforms. # attr_reader :platform + # # The reference count for the module. # @@ -315,6 +324,13 @@ class Module # attr_accessor :error + # An opaque bag of data to attach to a module. This is useful for attaching + # some piece of identifying info on to a module before calling + # {Msf::Simple::Exploit#exploit_simple} or + # {Msf::Simple::Auxiliary#run_simple} for correlating where modules came + # from. + attr_accessor :user_data + protected # diff --git a/spec/lib/msf/core/module_spec.rb b/spec/lib/msf/core/module_spec.rb index 35b83bc7b1..6666933e45 100644 --- a/spec/lib/msf/core/module_spec.rb +++ b/spec/lib/msf/core/module_spec.rb @@ -46,6 +46,17 @@ describe Msf::Module do it { is_expected.to respond_to :is_usable } end + describe '#user_data_is_match?' do + subject(:msf_module) { + msf_module = described_class.new + msf_module.user_data = { match: 'match', match_set: 'match_set', run: 'run' } + msf_module + } + specify do + expect(msf_module.user_data_is_match?).to eq(true) + end + end + describe "cloning modules into replicants" do module MsfExtensionTestFoo; def my_test1; true; end; end; module MsfExtensionTestBar; def my_test2; true; end; end; diff --git a/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb b/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb index 28eeeaacf8..10c82b2705 100644 --- a/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb +++ b/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb @@ -3,4 +3,37 @@ shared_examples_for 'Msf::DBManager::ExploitAttempt' do 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 } -end \ No newline at end of file + + describe '#report_exploit_success' do + + let(:workspace) do + FactoryGirl.create(:mdm_workspace) + end + + let(:host) do + FactoryGirl.create(:mdm_host, workspace: workspace) + end + + let(:refs) do + [ FactoryGirl.create(:mdm_ref) ] + end + + let(:vuln) do + FactoryGirl.create(:mdm_vuln) + end + + let(:opts) do + { + workspace: workspace, + refs: refs, + host: host, + vuln: vuln, + } + end + + specify do + expect{ subject.report_exploit_success(opts) }.to change{ Mdm::VulnAttempt.count }.by(1) + end + + end +end diff --git a/spec/support/shared/examples/msf/db_manager/session.rb b/spec/support/shared/examples/msf/db_manager/session.rb index 36b86fd84f..ba28e52925 100644 --- a/spec/support/shared/examples/msf/db_manager/session.rb +++ b/spec/support/shared/examples/msf/db_manager/session.rb @@ -39,12 +39,15 @@ shared_examples_for 'Msf::DBManager::Session' do let(:module_instance) do name = 'multi/handler' - double( - 'Msf::Module', + d = double( + 'Msf::Exploit', + :user_data => user_data, :fullname => "exploit/#{name}", :framework => framework, :name => name ) + allow(d).to receive(:user_data_is_match?).and_return(false) + d end let(:options_workspace) do @@ -65,6 +68,7 @@ shared_examples_for 'Msf::DBManager::Session' do let(:session) do session_class.new.tap do |session| + session.assoc_exploit = module_instance session.exploit_datastore = exploit_datastore session.info = 'Info' session.platform = 'Platform' @@ -81,6 +85,7 @@ shared_examples_for 'Msf::DBManager::Session' do Class.new do include Msf::Session + attr_accessor :assoc_exploit attr_accessor :datastore attr_accessor :platform attr_accessor :type @@ -117,153 +122,339 @@ shared_examples_for 'Msf::DBManager::Session' do ) end - context 'with :workspace' do - before(:each) do - options[:workspace] = options_workspace + context 'with a match in user_data' do + let(:user_data) do + { + match: FactoryGirl.create(:automatic_exploitation_match), + match_set: FactoryGirl.create(:automatic_exploitation_match_set), + run: FactoryGirl.create(:automatic_exploitation_run), + } end - it 'should not find workspace from session' do - db_manager.should_not_receive(:find_workspace) + before do + allow(module_instance).to receive(:user_data_is_match?).and_return(true) + end - report_session + it 'should make a MatchResult' do + expect { report_session }.to change(MetasploitDataModels::AutomaticExploitation::MatchResult, :count).by(1) 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 + context 'without user_data' do + let(:user_data) { nil } + context 'with :workspace' do before(:each) do - session.stub(:arch => arch) + options[:workspace] = options_workspace end - it 'should pass :arch to #find_or_create_host' do + 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( - :arch => arch + :workspace => session_workspace ) - ).and_call_original + ).and_return(host) report_session end end - context 'without session responds to arch' do - it 'should not pass :arch to #find_or_create_host' do + 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_excluding( - :arch + hash_including( + :host => normalized_host ) - ).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 + ).and_return(host) 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 + context 'with session responds to arch' do + let(:arch) do + FactoryGirl.generate :mdm_host_arch end before(:each) do - Timecop.freeze + session.stub(:arch => arch) + end - session.exploit_datastore['RPORT'] = rport + 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 + + 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 + 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 + + 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 + + it { expect(subject.service).to eq(service) } + end + + context 'without RPORT' do + 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 + + it { expect(subject.attempted_at).to be_within(1.second).of(Time.now.utc) } + # @todo https://www.pivotaltracker.com/story/show/48362615 + 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 + 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 + 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 + + 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(:vuln) do - Mdm::Vuln.last + subject(:mdm_session) do + report_session end - 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) } + # + # 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 + + 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 { 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) } + it "should have session.exploit_datastore['ParentModule']" do + session.exploit_datastore['ParentModule'].should_not be_nil + end + + it { expect(subject.via_exploit).to eq(parent_module_fullname) } end end context "without session.via_exploit 'exploit/multi/handler'" do - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - before(:each) do + reference_name = 'windows/smb/ms08_067_netapi' path = File.join( parent_path, 'exploits', @@ -287,176 +478,12 @@ shared_examples_for 'Msf::DBManager::Session' do session.via_exploit = "#{type}/#{reference_name}" end - 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 + it "should not have session.via_exploit of 'exploit/multi/handler'" do + session.via_exploit.should_not == 'exploit/multi/handler' end - let(:service) do - FactoryGirl.create( - :mdm_service, - :host => host - ) - end - - it { expect(subject.service).to eq(service) } + it { expect(subject.via_exploit).to eq(session.via_exploit) } end - - context 'without RPORT' do - 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 - - it { expect(subject.attempted_at).to be_within(1.second).of(Time.now.utc) } - # @todo https://www.pivotaltracker.com/story/show/48362615 - 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 - 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 - 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 - - 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 - - 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 - - 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 - - it { expect(subject.via_exploit).to eq(session.via_exploit) } end end end @@ -641,4 +668,4 @@ shared_examples_for 'Msf::DBManager::Session' do end end end -end \ No newline at end of file +end