From 1a10b21e7ffa821c6498a16b89623881c533c3d9 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 21 Jul 2014 13:39:17 -0500 Subject: [PATCH 01/15] Use metasploit-credential with association search MSP-10029 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index b050e0f2c7..9aa8de5c9c 100755 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ group :db do # Needed for Msf::DbManager gem 'activerecord', '>= 3.0.0', '< 4.0.0' # Metasploit::Credential database models - gem 'metasploit-credential', '~> 0.7.8' + gem 'metasploit-credential', '>= 0.7.9.pre.core.pre.search', '< 0.7.10' # Database models shared between framework and Pro. gem 'metasploit_data_models', '>= 0.18.0', '< 0.19' # Needed for module caching in Mdm::ModuleDetails diff --git a/Gemfile.lock b/Gemfile.lock index e110fa0d85..c42bd8a4b2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -58,7 +58,7 @@ GEM json (1.8.1) metasploit-concern (0.1.1) activesupport (~> 3.0, >= 3.0.0) - metasploit-credential (0.7.8) + metasploit-credential (0.7.9.pre.core.pre.search) metasploit-concern (~> 0.1.0) metasploit-model (>= 0.25.6) metasploit_data_models (>= 0.18.0.pre.compatibility, < 0.19) @@ -156,7 +156,7 @@ DEPENDENCIES factory_girl (>= 4.1.0) factory_girl_rails fivemat (= 1.2.1) - metasploit-credential (~> 0.7.8) + metasploit-credential (>= 0.7.9.pre.core.pre.search, < 0.7.10) metasploit-framework! metasploit_data_models (>= 0.18.0, < 0.19) network_interface (~> 0.0.1) From 9db951cadc9c7013b364880baaf630dad5a7bf97 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Mon, 21 Jul 2014 14:57:28 -0500 Subject: [PATCH 02/15] Add sane defaults for HTTP method and path. --- lib/metasploit/framework/login_scanner/http.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/metasploit/framework/login_scanner/http.rb b/lib/metasploit/framework/login_scanner/http.rb index 6a909ed2ac..4518ef2ef5 100644 --- a/lib/metasploit/framework/login_scanner/http.rb +++ b/lib/metasploit/framework/login_scanner/http.rb @@ -96,6 +96,8 @@ module Metasploit self.connection_timeout ||= 20 self.max_send_size = 0 if self.max_send_size.nil? self.send_delay = 0 if self.send_delay.nil? + self.uri = '/' if self.uri.blank? + self.method = 'GET' if self.method.blank? # Note that this doesn't cover the case where ssl is unset and # port is something other than a default. In that situtation, From a2a75ffb03dc9eaa96ee377ce44ac0f6fb148bec Mon Sep 17 00:00:00 2001 From: James Lee Date: Mon, 21 Jul 2014 17:58:27 -0500 Subject: [PATCH 03/15] Fix typo and full path issue Previously, the JtR library was prepending the path to data/john/ for shipped bins; without it, modules weren't finding the executables. --- lib/metasploit/framework/jtr/cracker.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/metasploit/framework/jtr/cracker.rb b/lib/metasploit/framework/jtr/cracker.rb index a075eb3b85..ed0ead2663 100644 --- a/lib/metasploit/framework/jtr/cracker.rb +++ b/lib/metasploit/framework/jtr/cracker.rb @@ -199,11 +199,11 @@ module Metasploit # This method tries to identify the correct version of the pre-shipped # JtR binaries to use based on the platform. # - # @return [NilClass] if the correct bianry could not be determined + # @return [NilClass] if the correct binary could not be determined # @return [String] the path to the selected binary def select_shipped_binary cpuinfo_base = ::File.join(Msf::Config.data_directory, "cpuinfo") - runpath = nil + run_path = nil if File.directory?(cpuinfo_base) data = nil @@ -215,11 +215,11 @@ module Metasploit end case data when /sse2/ - run_path ||= "run.win32.sse2/john.exe" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.sse2", "john.exe") when /mmx/ - run_path ||= "run.win32.mmx/john.exe" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.mmx", "john.exe") else - run_path ||= "run.win32.any/john.exe" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.any", "john.exe") end when /x86_64-linux/ fname = "#{cpuinfo_base}/cpuinfo.ia64.bin" @@ -229,9 +229,9 @@ module Metasploit end case data when /mmx/ - run_path ||= "run.linux.x64.mmx/john" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x64.mmx", "john") else - run_path ||= "run.linux.x86.any/john" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.any", "john") end when /i[\d]86-linux/ fname = "#{cpuinfo_base}/cpuinfo.ia32.bin" @@ -241,15 +241,15 @@ module Metasploit end case data when /sse2/ - run_path ||= "run.linux.x86.sse2/john" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.sse2", "john") when /mmx/ - run_path ||= "run.linux.x86.mmx/john" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.mmx", "john") else - run_path ||= "run.linux.x86.any/john" + run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.any", "john") end end end - runpath + run_path end From 917d2c718b1deea3dcb24cdbad9895bb97ffa931 Mon Sep 17 00:00:00 2001 From: James Lee Date: Mon, 21 Jul 2014 18:24:35 -0500 Subject: [PATCH 04/15] Use All4 instead of LanMan ... Which was the original behavior. A full incremental LanMan can take many hours instead of the few seconds this module was intended to run. --- modules/auxiliary/analyze/jtr_crack_fast.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/analyze/jtr_crack_fast.rb b/modules/auxiliary/analyze/jtr_crack_fast.rb index 7fcaacc21d..051610d2b3 100644 --- a/modules/auxiliary/analyze/jtr_crack_fast.rb +++ b/modules/auxiliary/analyze/jtr_crack_fast.rb @@ -30,7 +30,7 @@ class Metasploit3 < Msf::Auxiliary def run cracker = new_john_cracker - #generate our wordlist and close the file handle + # generate our wordlist and close the file handle wordlist = wordlist_file wordlist.close print_status "Wordlist file written out to #{wordlist.path}" @@ -53,10 +53,10 @@ class Metasploit3 < Msf::Auxiliary end if format == 'lm' - print_status "Cracking #{format} hashes in incremental mode (LanMan)..." + print_status "Cracking #{format} hashes in incremental mode (All4)..." cracker_instance.rules = nil cracker_instance.wordlist = nil - cracker_instance.incremental = 'LanMan' + cracker_instance.incremental = 'All4' cracker_instance.crack do |line| print_status line.chomp end From addecb63113f65a84f8158aafc130500f6ce27bd Mon Sep 17 00:00:00 2001 From: James Lee Date: Mon, 21 Jul 2014 18:26:50 -0500 Subject: [PATCH 05/15] Fix running shipped bins by using a config file This should get everything working again. MSP-10817 --- lib/metasploit/framework/jtr/cracker.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/metasploit/framework/jtr/cracker.rb b/lib/metasploit/framework/jtr/cracker.rb index ed0ead2663..52cd44ff7f 100644 --- a/lib/metasploit/framework/jtr/cracker.rb +++ b/lib/metasploit/framework/jtr/cracker.rb @@ -119,6 +119,8 @@ module Metasploit if config.present? cmd << ( "--config=" + config ) + else + cmd << ( "--config=" + john_config_file ) end if pot.present? @@ -162,6 +164,13 @@ module Metasploit end end + # This method returns the path to a default john.conf file. + # + # @return [String] the path to the default john.conf file + def john_config_file + ::File.join( ::Msf::Config.data_directory, "john", "confs", "john.conf" ) + end + # This method returns the path to a default john.pot file. # # @return [String] the path to the default john.pot file @@ -189,6 +198,8 @@ module Metasploit if config cmd << "--config=#{config}" + else + cmd << ( "--config=" + john_config_file ) end cmd << hash_path From f8af435df6eb2b128baee7c94f5d13468d321c4e Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 21 Jul 2014 19:44:20 -0500 Subject: [PATCH 06/15] Fix version restrictions on metasploit-credential MSP-10029 The '<' version should have been '0.8' and not '0.7.10' because '0.8' is the next incompatible version number. --- Gemfile | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 9aa8de5c9c..42706fd750 100755 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ group :db do # Needed for Msf::DbManager gem 'activerecord', '>= 3.0.0', '< 4.0.0' # Metasploit::Credential database models - gem 'metasploit-credential', '>= 0.7.9.pre.core.pre.search', '< 0.7.10' + gem 'metasploit-credential', '>= 0.7.9.pre.core.pre.search', '< 0.8' # Database models shared between framework and Pro. gem 'metasploit_data_models', '>= 0.18.0', '< 0.19' # Needed for module caching in Mdm::ModuleDetails diff --git a/Gemfile.lock b/Gemfile.lock index c42bd8a4b2..a0b78f94e0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -156,7 +156,7 @@ DEPENDENCIES factory_girl (>= 4.1.0) factory_girl_rails fivemat (= 1.2.1) - metasploit-credential (>= 0.7.9.pre.core.pre.search, < 0.7.10) + metasploit-credential (>= 0.7.9.pre.core.pre.search, < 0.8) metasploit-framework! metasploit_data_models (>= 0.18.0, < 0.19) network_interface (~> 0.0.1) From f3eb708dd14ecbacc2d93d9ca051c3cc5a5ae75b Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 22 Jul 2014 09:07:18 -0500 Subject: [PATCH 07/15] Update metasploit_data_models and metasploit-credential for tag search MSP-10029 Use metasploit_data_models that supports searching Mdm::Tag and a compatible metasploit-credential. Needed so Metasploit::Credential::Core#tags can be searched in Pro. --- Gemfile | 4 ++-- Gemfile.lock | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index 42706fd750..da51f4999b 100755 --- a/Gemfile +++ b/Gemfile @@ -7,9 +7,9 @@ group :db do # Needed for Msf::DbManager gem 'activerecord', '>= 3.0.0', '< 4.0.0' # Metasploit::Credential database models - gem 'metasploit-credential', '>= 0.7.9.pre.core.pre.search', '< 0.8' + gem 'metasploit-credential', '>= 0.7.10.pre.core.pre.search', '< 0.8' # Database models shared between framework and Pro. - gem 'metasploit_data_models', '>= 0.18.0', '< 0.19' + gem 'metasploit_data_models', '~> 0.19' # Needed for module caching in Mdm::ModuleDetails gem 'pg', '>= 0.11' end diff --git a/Gemfile.lock b/Gemfile.lock index a0b78f94e0..c36333f202 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,6 +41,8 @@ GEM i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) arel (3.0.3) + arel-helpers (2.0.0) + activerecord (>= 3.1.0, < 5) bcrypt (3.1.7) builder (3.0.4) coderay (1.1.0) @@ -58,18 +60,19 @@ GEM json (1.8.1) metasploit-concern (0.1.1) activesupport (~> 3.0, >= 3.0.0) - metasploit-credential (0.7.9.pre.core.pre.search) + metasploit-credential (0.7.10.pre.core.pre.search) metasploit-concern (~> 0.1.0) metasploit-model (>= 0.25.6) - metasploit_data_models (>= 0.18.0.pre.compatibility, < 0.19) + metasploit_data_models (~> 0.19) pg rubyntlm rubyzip (~> 1.1) metasploit-model (0.25.6) activesupport - metasploit_data_models (0.18.0) + metasploit_data_models (0.19.0) activerecord (>= 3.2.13, < 4.0.0) activesupport + arel-helpers metasploit-concern (~> 0.1.0) metasploit-model (>= 0.25.1, < 0.26) pg @@ -156,9 +159,9 @@ DEPENDENCIES factory_girl (>= 4.1.0) factory_girl_rails fivemat (= 1.2.1) - metasploit-credential (>= 0.7.9.pre.core.pre.search, < 0.8) + metasploit-credential (>= 0.7.10.pre.core.pre.search, < 0.8) metasploit-framework! - metasploit_data_models (>= 0.18.0, < 0.19) + metasploit_data_models (~> 0.19) network_interface (~> 0.0.1) pcaprub pg (>= 0.11) From 14fa49cdeb82025aef84296546ff792711ab3c03 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 22 Jul 2014 09:47:35 -0500 Subject: [PATCH 08/15] Update spec to handle Mdm::Service#proto sequence MSP-10029 Mdm::Service factories were changed in metasploit_data_models 0.19.0 to use a sequence that cycles between 'tcp' and 'udp'. To make the spec clearer, just hard-code the protos under test instead of relying on default behavior. --- spec/lib/msf/ui/command_dispatcher/db_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/lib/msf/ui/command_dispatcher/db_spec.rb b/spec/lib/msf/ui/command_dispatcher/db_spec.rb index ee192a5c85..3498dcd25b 100644 --- a/spec/lib/msf/ui/command_dispatcher/db_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/db_spec.rb @@ -73,9 +73,9 @@ describe Msf::Ui::Console::CommandDispatcher::Db do describe "-p" do before(:each) do host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") - FactoryGirl.create(:mdm_service, :host => host, :port => 1024, name: 'Service1') - FactoryGirl.create(:mdm_service, :host => host, :port => 1025, name: 'Service2') - FactoryGirl.create(:mdm_service, :host => host, :port => 1026, name: 'Service3') + FactoryGirl.create(:mdm_service, :host => host, :port => 1024, name: 'Service1', proto: 'udp') + FactoryGirl.create(:mdm_service, :host => host, :port => 1025, name: 'Service2', proto: 'tcp') + FactoryGirl.create(:mdm_service, :host => host, :port => 1026, name: 'Service3', proto: 'udp') end it "should list services that are on a given port" do db.cmd_services "-p", "1024,1025" @@ -85,8 +85,8 @@ describe Msf::Ui::Console::CommandDispatcher::Db do "", "host port proto name state info", "---- ---- ----- ---- ----- ----", - "192.168.0.1 1024 snmp Service1 open ", - "192.168.0.1 1025 snmp Service2 open " + "192.168.0.1 1024 udp Service1 open ", + "192.168.0.1 1025 tcp Service2 open " ] end end From 0eb4fc0ed17bf0c2b20747aadf0493eaf9571248 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Tue, 22 Jul 2014 11:44:31 -0500 Subject: [PATCH 09/15] community string collection add community string collection to handle snmp loginscanner credentials --- lib/metasploit/framework/community_string_collection.rb | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 lib/metasploit/framework/community_string_collection.rb diff --git a/lib/metasploit/framework/community_string_collection.rb b/lib/metasploit/framework/community_string_collection.rb new file mode 100644 index 0000000000..436bf0f139 --- /dev/null +++ b/lib/metasploit/framework/community_string_collection.rb @@ -0,0 +1,2 @@ +class CommunityStringCollection +end \ No newline at end of file From c553fcac73edb15be250a791e48aac7dcae28a96 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Tue, 22 Jul 2014 11:46:22 -0500 Subject: [PATCH 10/15] start refacotirng snmp_login --- .../framework/community_string_collection.rb | 71 ++++- modules/auxiliary/scanner/snmp/snmp_login.rb | 247 +----------------- 2 files changed, 70 insertions(+), 248 deletions(-) diff --git a/lib/metasploit/framework/community_string_collection.rb b/lib/metasploit/framework/community_string_collection.rb index 436bf0f139..b06409ce94 100644 --- a/lib/metasploit/framework/community_string_collection.rb +++ b/lib/metasploit/framework/community_string_collection.rb @@ -1,2 +1,69 @@ -class CommunityStringCollection -end \ No newline at end of file +require 'metasploit/framework/credential' + +module Metasploit + module Framework + + # This class is responsible for taking datastore options from the snmp_login module + # and yielding appropriate {Metasploit::Framework::Credential}s to the {Metasploit::Framework::LoginScanner::SNMP}. + # This one has to be different from {credentialCollection} as it will only have a {Metasploit::Framework::Credential#public} + # It may be slightly confusing that the attribues are called password and pass_file, because this is what the legacy + # module used. However, community Strings are now considered more to be public credentials than private ones. + class CommunityStringCollection + # @!attribute pass_file + # Path to a file containing passwords, one per line + # @return [String] + attr_accessor :pass_file + + # @!attribute password + # @return [String] + attr_accessor :password + + # @!attribute prepended_creds + # List of credentials to be tried before any others + # + # @see #prepend_cred + # @return [Array] + attr_accessor :prepended_creds + + # @option opts [String] :pass_file See {#pass_file} + # @option opts [String] :password See {#password} + # @option opts [Array] :prepended_creds ([]) See {#prepended_creds} + def initialize(opts = {}) + opts.each do |attribute, value| + public_send("#{attribute}=", value) + end + self.prepended_creds ||= [] + end + + # Combines all the provided credential sources into a stream of {Credential} + # objects, yielding them one at a time + # + # @yieldparam credential [Metasploit::Framework::Credential] + # @return [void] + def each + if pass_file.present? + pass_fd = File.open(pass_file, 'r:binary') + pass_fd.each_line do |line| + line.chomp! + yield Metasploit::Framework::Credential.new(public: line, paired: false) + end + end + + if password.present? + yield Metasploit::Framework::Credential.new(public: password, paired: false) + end + end + + # Add {Credential credentials} that will be yielded by {#each} + # + # @see prepended_creds + # @param cred [Credential] + # @return [self] + def prepend_cred(cred) + prepended_creds.unshift cred + self + end + + end + end +end diff --git a/modules/auxiliary/scanner/snmp/snmp_login.rb b/modules/auxiliary/scanner/snmp/snmp_login.rb index d16e1cfbe6..4b149db1ee 100644 --- a/modules/auxiliary/scanner/snmp/snmp_login.rb +++ b/modules/auxiliary/scanner/snmp/snmp_login.rb @@ -49,260 +49,15 @@ class Metasploit3 < Msf::Auxiliary # Operate on an entire batch of hosts at once def run_batch(batch) - @found = {} - @tried = [] + batch.each do |ip| - begin - udp_sock = nil - idx = 0 - # Create an unbound UDP socket if no CHOST is specified, otherwise - # create a UDP socket bound to CHOST (in order to avail of pivoting) - udp_sock = Rex::Socket::Udp.create( { 'LocalHost' => datastore['CHOST'] || nil, 'Context' => {'Msf' => framework, 'MsfExploit' => self} }) - add_socket(udp_sock) - each_user_pass do |user, pass| - comm = pass - - data1 = create_probe_snmp1(comm) - data2 = create_probe_snmp2(comm) - - batch.each do |ip| - fq_pass = [ip,pass] - next if @tried.include? fq_pass - @tried << fq_pass - vprint_status "#{ip}:#{datastore['RPORT']} - SNMP - Trying #{(pass.nil? || pass.empty?) ? "" : pass}..." - - begin - udp_sock.sendto(data1, ip, datastore['RPORT'].to_i, 0) - udp_sock.sendto(data2, ip, datastore['RPORT'].to_i, 0) - rescue ::Interrupt - raise $! - rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused - nil - end - - if (idx % 10 == 0) - while (r = udp_sock.recvfrom(65535, 0.25) and r[1]) - parse_reply(r) - end - end - - idx += 1 - - end - end - - idx = 0 - while (r = udp_sock.recvfrom(65535, 3) and r[1] and idx < 500) - parse_reply(r) - idx += 1 - end - - if @found.keys.length > 0 - print_status("Validating scan results from #{@found.keys.length} hosts...") - end - - # Review all successful communities and determine write access - @found.keys.sort.each do |host| - fake_comm = Rex::Text.rand_text_alphanumeric(8) - anycomm_ro = false - anycomm_rw = false - comms_ro = [] - comms_rw = [] - finished = false - versions = ["1", "2"] - - versions.each do |version| - comms_todo = @found[host].keys.sort - comms_todo.unshift(fake_comm) - - comms_todo.each do |comm| - begin - sval = nil - snmp = snmp_client(host, datastore['RPORT'].to_i, version, udp_sock, comm) - resp = snmp.get("sysName.0") - resp.each_varbind { |var| sval = var.value } - next if not sval - - svar = ::SNMP::VarBind.new("1.3.6.1.2.1.1.5.0", ::SNMP::OctetString.new(sval)) - resp = snmp.set(svar) - - if resp.error_status == :noError - comms_rw << comm - print_status("Host #{host} provides READ-WRITE access with community '#{comm}'") - if comm == fake_comm - anycomm_rw = true - finished = true - break - end - else - comms_ro << comm - print_status("Host #{host} provides READ-ONLY access with community '#{comm}'") - if comm == fake_comm - anycomm_ro = true - finished = true - break - end - end - - # Used to flag whether this version was compatible - finished = true - - rescue ::SNMP::UnsupportedPduTag, ::SNMP::InvalidPduTag, ::SNMP::ParseError, - ::SNMP::InvalidErrorStatus, ::SNMP::InvalidTrapVarbind, ::SNMP::InvalidGenericTrap, - ::SNMP::BER::OutOfData, ::SNMP::BER::InvalidLength, ::SNMP::BER::InvalidTag, - ::SNMP::BER::InvalidObjectId, ::SNMP::MIB::ModuleNotLoadedError, - ::SNMP::UnsupportedValueTag - next - - rescue ::SNMP::UnsupportedVersion - break - rescue ::SNMP::RequestTimeout - next - end - end - - break if finished - end - - # Report on the results - comms_ro = ["anything"] if anycomm_ro - comms_rw = ["anything"] if anycomm_rw - - comms_rw.each do |comm| - report_auth_info( - :host => host, - :port => datastore['RPORT'].to_i, - :proto => 'udp', - :sname => 'snmp', - :user => '', - :pass => comm, - :duplicate_ok => true, - :active => true, - :source_type => "user_supplied", - :type => "password" - ) - end - - comms_ro.each do |comm| - report_auth_info( - :host => host, - :port => datastore['RPORT'].to_i, - :proto => 'udp', - :sname => 'snmp', - :user => '', - :pass => comm, - :duplicate_ok => true, - :active => true, - :source_type => "user_supplied", - :type => "password_ro" - ) - end - end - - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("Unknown error: #{e.class} #{e}") end end - # - # Allocate a SNMP client using the existing socket - # - def snmp_client(host, port, version, socket, community) - version = :SNMPv1 if version == "1" - version = :SNMPv2c if version == "2c" - - snmp = ::SNMP::Manager.new( - :Host => host, - :Port => port, - :Community => community, - :Version => version, - :Timeout => 1, - :Retries => 2, - :Transport => SNMP::RexUDPTransport, - :Socket => socket - ) - end - - # - # The response parsers - # - def parse_reply(pkt) - - return if not pkt[1] - - if(pkt[1] =~ /^::ffff:/) - pkt[1] = pkt[1].sub(/^::ffff:/, '') - end - - asn = OpenSSL::ASN1.decode(pkt[0]) rescue nil - return if not asn - - snmp_error = asn.value[0].value rescue nil - snmp_comm = asn.value[1].value rescue nil - snmp_data = asn.value[2].value[3].value[0] rescue nil - snmp_oid = snmp_data.value[0].value rescue nil - snmp_info = snmp_data.value[1].value rescue nil - - return if not (snmp_error and snmp_comm and snmp_data and snmp_oid and snmp_info) - snmp_info = snmp_info.to_s.gsub(/\s+/, ' ') - - inf = snmp_info - com = snmp_comm - - if(com) - @found[pkt[1]]||={} - if(not @found[pkt[1]][com]) - print_good("SNMP: #{pkt[1]} community string: '#{com}' info: '#{inf}'") - @found[pkt[1]][com] = inf - end - - report_service( - :host => pkt[1], - :port => pkt[2], - :proto => 'udp', - :name => 'snmp', - :info => inf, - :state => "open" - ) - end - end - def create_probe_snmp1(name) - xid = rand(0x100000000) - pdu = - "\x02\x01\x00" + - "\x04" + [name.length].pack('c') + name + - "\xa0\x1c" + - "\x02\x04" + [xid].pack('N') + - "\x02\x01\x00" + - "\x02\x01\x00" + - "\x30\x0e\x30\x0c\x06\x08\x2b\x06\x01\x02\x01" + - "\x01\x01\x00\x05\x00" - head = "\x30" + [pdu.length].pack('C') - data = head + pdu - data - end - - def create_probe_snmp2(name) - xid = rand(0x100000000) - pdu = - "\x02\x01\x01" + - "\x04" + [name.length].pack('c') + name + - "\xa1\x19" + - "\x02\x04" + [xid].pack('N') + - "\x02\x01\x00" + - "\x02\x01\x00" + - "\x30\x0b\x30\x09\x06\x05\x2b\x06\x01\x02\x01" + - "\x05\x00" - head = "\x30" + [pdu.length].pack('C') - data = head + pdu - data - end end From e54f5e8ee7c877939509ade1e3e64037b41ea2ff Mon Sep 17 00:00:00 2001 From: David Maloney Date: Tue, 22 Jul 2014 12:44:21 -0500 Subject: [PATCH 11/15] working snmp_login module --- .../framework/community_string_collection.rb | 21 ++++--- modules/auxiliary/scanner/snmp/snmp_login.rb | 57 ++++++++++++++++++- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/lib/metasploit/framework/community_string_collection.rb b/lib/metasploit/framework/community_string_collection.rb index b06409ce94..fdeddb4a96 100644 --- a/lib/metasploit/framework/community_string_collection.rb +++ b/lib/metasploit/framework/community_string_collection.rb @@ -41,16 +41,21 @@ module Metasploit # @yieldparam credential [Metasploit::Framework::Credential] # @return [void] def each - if pass_file.present? - pass_fd = File.open(pass_file, 'r:binary') - pass_fd.each_line do |line| - line.chomp! - yield Metasploit::Framework::Credential.new(public: line, paired: false) + begin + if pass_file.present? + pass_fd = File.open(pass_file, 'r:binary') + pass_fd.each_line do |line| + line.chomp! + yield Metasploit::Framework::Credential.new(public: line, paired: false) + end end - end - if password.present? - yield Metasploit::Framework::Credential.new(public: password, paired: false) + if password.present? + yield Metasploit::Framework::Credential.new(public: password, paired: false) + end + + ensure + pass_fd.close if pass_fd && !pass_fd.closed? end end diff --git a/modules/auxiliary/scanner/snmp/snmp_login.rb b/modules/auxiliary/scanner/snmp/snmp_login.rb index 4b149db1ee..5f5e25ef66 100644 --- a/modules/auxiliary/scanner/snmp/snmp_login.rb +++ b/modules/auxiliary/scanner/snmp/snmp_login.rb @@ -5,8 +5,8 @@ require 'msf/core' -require 'openssl' -require 'snmp' +require 'metasploit/framework/community_string_collection' +require 'metasploit/framework/login_scanner/snmp' class Metasploit3 < Msf::Auxiliary @@ -50,11 +50,64 @@ class Metasploit3 < Msf::Auxiliary def run_batch(batch) batch.each do |ip| + collection = Metasploit::Framework::CommunityStringCollection.new( + pass_file: datastore['PASS_FILE'], + password: datastore['PASSWORD'] + ) + scanner = Metasploit::Framework::LoginScanner::SNMP.new( + host: ip, + port: rport, + cred_details: collection, + stop_on_success: datastore['STOP_ON_SUCCESS'], + connection_timeout: 2 + ) + service_data = { + address: ip, + port: rport, + service_name: 'snmp', + protocol: 'udp', + workspace_id: myworkspace_id + } + scanner.scan! do |result| + if result.success? + credential_data = { + module_fullname: self.fullname, + origin_type: :service, + username: result.credential.public + } + credential_data.merge!(service_data) + + credential_core = create_credential(credential_data) + + login_data = { + core: credential_core, + last_attempted_at: DateTime.now, + status: Metasploit::Model::Login::Status::SUCCESSFUL + } + login_data.merge!(service_data) + + create_credential_login(login_data) + print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}" + else + invalidate_data = { + public: result.credential.public, + private: result.credential.private, + realm_key: result.credential.realm_key, + realm_value: result.credential.realm, + status: result.status + } .merge(service_data) + invalidate_login(invalidate_data) + print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})" + end + end end + end + def rport + datastore['RPORT'] end From b7d15d0b089d9b08571c620250f7552751e997c7 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 23 Jul 2014 12:07:57 -0500 Subject: [PATCH 12/15] simple fix to mysql loginscanner typo caused connection_timeout default to not get set --- lib/metasploit/framework/login_scanner/mysql.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metasploit/framework/login_scanner/mysql.rb b/lib/metasploit/framework/login_scanner/mysql.rb index 7ebed038df..6212cd8b12 100644 --- a/lib/metasploit/framework/login_scanner/mysql.rb +++ b/lib/metasploit/framework/login_scanner/mysql.rb @@ -78,7 +78,7 @@ module Metasploit # This method sets the sane defaults for things # like timeouts and TCP evasion options def set_sane_defaults - self.connection_timeout || 30 + self.connection_timeout ||= 30 self.port ||= DEFAULT_PORT self.max_send_size ||= 0 self.send_delay ||= 0 From eee72a86babdf84d37a0ba5693611d0ecf16b504 Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 23 Jul 2014 15:25:32 -0500 Subject: [PATCH 13/15] Fix the case when john cracks only half of LM --- lib/msf/core/auxiliary/jtr.rb | 6 ++++++ modules/auxiliary/analyze/jtr_crack_fast.rb | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/msf/core/auxiliary/jtr.rb b/lib/msf/core/auxiliary/jtr.rb index fffb228640..b430f4b935 100644 --- a/lib/msf/core/auxiliary/jtr.rb +++ b/lib/msf/core/auxiliary/jtr.rb @@ -41,6 +41,12 @@ module Auxiliary::JohnTheRipper end + # @param pwd [String] Password recovered from cracking an LM hash + # @param hash [String] NTLM hash for this password + # @return [String] `pwd` converted to the correct case to match the + # given NTLM hash + # @return [nil] if no case matches the NT hash. This can happen when + # `pwd` came from a john run that only cracked half of the LM hash def john_lm_upper_to_ntlm(pwd, hash) pwd = pwd.upcase hash = hash.upcase diff --git a/modules/auxiliary/analyze/jtr_crack_fast.rb b/modules/auxiliary/analyze/jtr_crack_fast.rb index 051610d2b3..57d02cb2bc 100644 --- a/modules/auxiliary/analyze/jtr_crack_fast.rb +++ b/modules/auxiliary/analyze/jtr_crack_fast.rb @@ -98,6 +98,12 @@ class Metasploit3 < Msf::Auxiliary end end password = john_lm_upper_to_ntlm(password, nt_hash) + # password can be nil if the hash is broken (i.e., the NT and + # LM sides don't actually match) or if john was only able to + # crack one half of the LM hash. In the latter case, we'll + # have a line like: + # username:???????WORD:...:...::: + next if password.nil? end print_good "#{username}:#{password}:#{core_id}" From 064d624322ddd4890ebd392bdffea80a61d353f8 Mon Sep 17 00:00:00 2001 From: darkbushido Date: Wed, 23 Jul 2014 16:17:09 -0500 Subject: [PATCH 14/15] changing Credential == operator it should no longer raise no method errors when comparing a credential to an object that doesnt respond to public, private, or realm --- lib/metasploit/framework/credential.rb | 4 +++- spec/lib/metasploit/framework/credential_spec.rb | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/metasploit/framework/credential.rb b/lib/metasploit/framework/credential.rb index be9cc0809b..800a526344 100644 --- a/lib/metasploit/framework/credential.rb +++ b/lib/metasploit/framework/credential.rb @@ -78,7 +78,9 @@ module Metasploit end def ==(other) - other.public == self.public && other.private == self.private && other.realm == self.realm + other.respond_to?(:public) && other.public == self.public && + other.respond_to?(:private) && other.private == self.private && + other.respond_to?(:realm) && other.realm == self.realm end def to_credential diff --git a/spec/lib/metasploit/framework/credential_spec.rb b/spec/lib/metasploit/framework/credential_spec.rb index 78f71673ac..2856966b96 100644 --- a/spec/lib/metasploit/framework/credential_spec.rb +++ b/spec/lib/metasploit/framework/credential_spec.rb @@ -132,6 +132,11 @@ describe Metasploit::Framework::Credential do expect(other).not_to eq(cred_detail) end end - + context "when comparing to a different object" do + let(:other) {'a string'} + specify do + expect(other).not_to eq(cred_detail) + end + end end end From 7a49f218d47b4d78bb728a54a408b0cd9ce5b699 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Thu, 24 Jul 2014 13:59:04 -0500 Subject: [PATCH 15/15] update .yardopts --- .yardopts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.yardopts b/.yardopts index eb3cff1cc2..b58b0bda2b 100644 --- a/.yardopts +++ b/.yardopts @@ -3,6 +3,8 @@ --exclude \.ut\.rb/ --exclude \.ts\.rb/ --files CONTRIBUTING.md,COPYING,HACKING,LICENSE +app/**/*.rb lib/msf/**/*.rb +lib/metasploit/**/*.rb lib/rex/**/*.rb plugins/**/*.rb