diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index b724cef4cc..8d0ea39a78 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -25,7 +25,17 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(subject).to receive(:framework).and_return(framework) end - def create_fake_note(tag, data) + + def mock_report_note(arg) + framework = double('Msf::Framework', datastore: {}) + notes = [create_fake_note('bap.clicks')] + db = double('db') + allow(db).to receive(:notes).and_return(notes) + allow(framework).to receive(:db).and_return(db) + allow(subject).to receive(:framework).and_return(framework) + end + + def create_fake_note(tag, data='') note = double('note') allow(note).to receive(:ntype).and_return(tag) @@ -53,6 +63,8 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do disclosure_date = opts[:disclosure_date] || 'Dec 21 2014' compat_payloads = opts[:compat_payloads] || [] datastore_options = opts[:datastore_options] || {} + job_id = opts[:job_id] || 0 + requirements = opts[:requirements] || {} mod = Msf::Exploit.new mod.extend(Msf::Exploit::Remote::BrowserExploitServer) @@ -62,66 +74,104 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(mod).to receive(:disclosure_date).and_return(disclosure_date) allow(mod).to receive(:compatible_payloads).and_return(compat_payloads) allow(mod).to receive(:datastore).and_return(datastore_options) + allow(mod).to receive(:job_id).and_return(job_id) + allow(mod).to receive(:exploit_simple) mod end def create_fake_ms14_064 - compat_payloads = ['windows/meterpreter/reverse_tcp'] + compat_payloads = [ + [windows_meterpreter_reverse_tcp, create_fake_windows_meterpreter] + ] create_fake_exploit( full_name: 'windows/browser/ms14_064_ole_code_execution', rank: 600, disclosure_date: 'Nov 13 2014', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/ms14_064'} + datastore_options: {'URI'=>'/ms14_064'}, + job_id: 0, + requirements: {os_name: windows_81_regex} ) end def create_fake_flash_net_connection_confusion - compat_payloads = ['windows/meterpreter/reverse_tcp', 'linux/x86/meterpreter/reverse_tcp'] + compat_payloads = [ + [windows_meterpreter_reverse_tcp, create_fake_windows_meterpreter], + [linux_meterpreter_reverse_tcp, create_fake_linux_meterpreter] + ] create_fake_exploit( full_name: 'multi/browser/adobe_flash_net_connection_confusion', rank: 500, disclosure_date: 'Mar 12 2015', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/flash1'} + datastore_options: {'URI'=>'/flash1'}, + job_id: 1, + requirements: {os_name: windows_81_regex} ) end def create_fake_flash_uncompress_zlib_uaf - compat_payloads = ['windows/meterpreter/reverse_tcp', 'linux/x86/meterpreter/reverse_tcp'] + compat_payloads = [windows_meterpreter_reverse_tcp, linux_meterpreter_reverse_tcp] create_fake_exploit( full_name: 'multi/browser/adobe_flash_uncompress_zlib_uaf', rank: 500, disclosure_date: 'Apr 28 2014', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/flash2'} + datastore_options: {'URI'=>'/flash2'}, + job_id: 2, + requirements: {os_name: windows_81_regex} ) end - def create_fake_windows_meterpreter + def create_fake_payload(opts={}) + platforms = opts[:platforms] + archs = opts[:archs] + datastores = opts[:datastore_options] + fullname = opts[:fullname] + shortname = opts[:shortname] + p = Msf::Payload.new - p.platform.platforms << Msf::Module::Platform::Windows - p.arch << 'x86' - p.datastore['LPORT'] = '4444' - allow(p).to receive(:fullname).and_return('windows/meterpreter/reverse_tcp') - allow(p).to receive(:shortname).and_return('reverse_tcp') + + platforms.each do |platform| + p.platform.platforms << platform + end + + archs.each do |arch| + p.arch << arch + end + + datastores.each_pair do |key, value| + p.datastore[key] = value + end + + allow(p).to receive(:fullname).and_return(fullname) + allow(p).to receive(:shoftname).and_return(shortname) p end - def create_fake_linux_meterpreter - p = Msf::Payload.new - p.platform.platforms << Msf::Module::Platform::Linux - p.arch << 'x86' - p.datastore['LPORT'] = '4445' - allow(p).to receive(:fullname).and_return('linux/x86/meterpreter/reverse_tcp') - allow(p).to receive(:shortname).and_return('reverse_tcp') + def create_fake_windows_meterpreter + create_fake_payload( + platforms: [Msf::Module::Platform::Windows], + archs: ['x86'], + datastore_options: {'LPORT'=>'4444'}, + fullname: windows_meterpreter_reverse_tcp, + shortname: 'reverse_tcp' + ) + end - p + def create_fake_linux_meterpreter + create_fake_payload( + platforms: [Msf::Module::Platform::Linux], + archs: ['x86'], + datastore_options: {'LPORT'=>'4445'}, + fullname: linux_meterpreter_reverse_tcp, + shortname: 'reverse_tcp' + ) end def mock_payload_create(full_name) @@ -140,6 +190,18 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do nil end + let(:windows_meterpreter_reverse_tcp) do + 'windows/meterpreter/reverse_tcp' + end + + let(:linux_meterpreter_reverse_tcp) do + 'linux/x86/meterpreter/reverse_tcp' + end + + let(:windows_81_regex) do + /^(?:Microsoft )?Windows 8\.1/ + end + let(:available_exploits) do @exploits ||= lambda { exploits = [] @@ -178,7 +240,9 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do 'SRVHOST' => '0.0.0.0', 'SRVPORT' => 8080, 'MaxExploits' => 20, + 'LHOST' => '127.0.0.1', 'MaxSessions' => -1, + 'Custom404' => 'http://example.com', 'PAYLOAD_ANDROID' => 'android/meterpreter/reverse_tcp', 'PAYLOAD_FIREFOX' => 'firefox/shell_reverse_tcp', 'PAYLOAD_GENERIC' => 'generic/shell_reverse_tcp', @@ -237,7 +301,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do # Prepare framework.db db = double('db') - allow(db).to receive(:report_note).with(kind_of(Hash)) { mock_report_note } + allow(db).to receive(:report_note).with(kind_of(Hash)) { |arg| mock_report_note(arg) } allow(db).to receive(:notes).and_return(notes) allow(db).to receive(:active).and_return(true) allow(framework).to receive(:db).and_return(db) @@ -262,6 +326,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(framework).to receive(:payloads).and_return(payloads) allow_any_instance_of(described_class).to receive(:framework).and_return(framework) + allow_any_instance_of(described_class).to receive(:report_note) { |arg| mock_report_note(arg) } end subject do @@ -351,7 +416,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do describe '#set_exploit_options' do before(:each) do payload_info = { - payload_name: 'windows/meterpreter/reverse_tcp', + payload_name: windows_meterpreter_reverse_tcp, payload_lport: 4444 } @@ -463,36 +528,137 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do describe '#get_selected_payload_name' do context 'when windows platform is given' do it 'returns windows/meterpreter/reverse_tcp' do - expect(subject.get_selected_payload_name('win')).to eq('windows/meterpreter/reverse_tcp') + expect(subject.get_selected_payload_name('win')).to eq(windows_meterpreter_reverse_tcp) end end end describe '#get_selected_payload_lport' do + context 'when windows platform is given' do + it 'returns 4444' do + expect(subject.get_selected_payload_lport('win')).to eq(4444) + end + end end describe '#get_payload_lhost' do + it 'returns LHOST' do + expect(subject.get_payload_lhost).to eq(autopwn_datastore_options['LHOST']) + end end describe '#start_payload_listeners' do end describe '#parse_rank' do + context 'when rank is 600' do + it 'returns Excellent' do + expect(subject.parse_rank(600)).to eq('Excellent') + end + end + + context 'when rank is 500' do + it 'returns Great' do + expect(subject.parse_rank(500)).to eq('Great') + end + end + + context 'when rank is 400' do + it 'returns Good' do + expect(subject.parse_rank(400)).to eq('Good') + end + end + + context 'when rank is 300' do + it 'returns Good' do + expect(subject.parse_rank(300)).to eq('Normal') + end + end + + context 'when rank is 200' do + it 'returns Average' do + expect(subject.parse_rank(200)).to eq('Average') + end + end + + context 'when rank is 100' do + it 'returns Low' do + expect(subject.parse_rank(100)).to eq('Low') + end + end + + context 'when rank is 0' do + it 'returns Manual' do + expect(subject.parse_rank(0)).to eq('Manual') + end + end end describe '#is_payload_platform_compatible?' do + let(:windows_payload) { create_fake_windows_meterpreter } + + context 'when a valid platform is given' do + it 'returns true' do + expect(subject.is_payload_platform_compatible?(windows_payload, 'win')).to be_truthy + end + end + + context 'when an invalid platform is given' do + it 'returns false' do + expect(subject.is_payload_platform_compatible?(windows_payload, 'linux')).to be_falsey + end + end end describe '#is_payload_compatible?' do + let(:windows_exploit) { create_fake_ms14_064 } + + context 'when a valid payload name is given' do + it 'returns true' do + expect(subject.is_payload_compatible?(windows_exploit, windows_meterpreter_reverse_tcp)).to be_truthy + end + end + + context 'when an invalid payload name is given' do + it 'returns false' do + expect(subject.is_payload_compatible?(windows_exploit, linux_meterpreter_reverse_tcp)).to be_falsey + end + end end describe '#is_multi_platform_exploit?' do + context 'when a windows exploit is given' do + it 'returns false' do + windows_exploit = create_fake_ms14_064 + expect(subject.is_multi_platform_exploit?(windows_exploit)).to be_falsey + end + end + + context 'when a multi-platform flash exploit is given' do + it 'returns true' do + flash_exploit = create_fake_flash_net_connection_confusion + expect(subject.is_multi_platform_exploit?(flash_exploit)).to be_truthy + end + end end describe '#select_payload' do end describe '#start_exploits' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@exploit_job_ids, []) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + end + + it 'returns job IDs of the exploits started' do + subject.start_exploits + available_exploits.each do |x| + expect(subject.instance_variable_get(:@exploit_job_ids)).to include(x.job_id) + end + end end describe '#show_ready_exploits' do @@ -508,9 +674,24 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end describe '#get_suitable_exploits' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + allow(subject).to receive(:retrieve_tag) + allow(subject).to receive(:get_profile_info) + end end describe '#log_click' do + let(:ip) { '192.168.1.123' } + + context 'when a link is clicked' do + it 'reports a bap.clicks note' do + subject.log_click(ip) + expect(subject.framework.db.notes.first.ntype).to eq('bap.clicks') + end + end end describe '#show_real_list' do @@ -529,6 +710,9 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end describe '#get_custom_404_url' do + it 'returns a custom 404' do + expect(subject.get_custom_404_url).to eq(autopwn_datastore_options['Custom404']) + end end describe '#build_html' do