From 5e6f57f7113cefca3906ac5009c58cb9f7f2b13b Mon Sep 17 00:00:00 2001 From: David Maloney Date: Thu, 1 May 2014 12:10:51 -0500 Subject: [PATCH] fix up some more specs some spec cleanup and added basic specs to the HTTP LoginScanner --- .../framework/login_scanner/http.rb | 103 ++---------------- .../framework/login_scanner/http_spec.rb | 15 +++ .../login_scanner/login_scanner_base.rb | 6 +- .../framework/login_scanner/rex_socket.rb | 44 ++++---- 4 files changed, 52 insertions(+), 116 deletions(-) create mode 100644 spec/lib/metasploit/framework/login_scanner/http_spec.rb diff --git a/lib/metasploit/framework/login_scanner/http.rb b/lib/metasploit/framework/login_scanner/http.rb index 535979f78f..a5ca3c5faf 100644 --- a/lib/metasploit/framework/login_scanner/http.rb +++ b/lib/metasploit/framework/login_scanner/http.rb @@ -1,5 +1,6 @@ require 'rex/proto/http' -require 'metasploit/framework/login_scanner' +require 'metasploit/framework/login_scanner/base' +require 'metasploit/framework/login_scanner/rex_socket' module Metasploit module Framework @@ -8,75 +9,17 @@ module Metasploit # HTTP-specific login scananer. # class HTTP - include ActiveModel::Validations + include Metasploit::Framework::LoginScanner::Base + include Metasploit::Framework::LoginScanner::RexSocket - # @!attribute connection_timeout - # @return [Numeric] The timeout in seconds for a single connection - attr_accessor :connection_timeout - - # @!attribute cred_details - # @return [Array] An array of {Credential} objects - attr_accessor :cred_details - - # @!attribute failures - # @return [Array] Array of {Result} objects that failed - attr_accessor :failures - - # @!attribute host - # @return [String] The IP address or hostname to connect to - attr_accessor :host - - # @!attribute port - # @return [Fixnum] The port to connect to - attr_accessor :port - - # @!attribute ssl - # @return [Boolean] Whether this client makes SSL connections - attr_accessor :ssl - - # @!attribute ssl_version - # @return [Symbol] The version of SSL/TLS to use when connecting - attr_accessor :ssl_version - - # @!attribute stop_on_success - # @return [Boolean] Whether the scanner should stop when it has - # found one working {Credential} - attr_accessor :stop_on_success - - # @!attribute successes - # @return [Array] Array of {Result} objects that succeded - attr_accessor :successes # @!attribute uri # @return [String] The path and query string on the server to # authenticate to. attr_accessor :uri - validates :connection_timeout, presence: true - - validates :cred_details, presence: true - - validates :host, presence: true - - validates :port, - presence: true, - numericality: { - only_integer: true, - greater_than_or_equal_to: 1, - less_than_or_equal_to: 0xffff - } - validates :uri, presence: true, length: { minimum: 1 } - # @param attributes [Hash{Symbol => String,nil}] - def initialize(attributes = {}) - attributes.each do |attribute, value| - public_send("#{attribute}=", value) - end - self.successes = [] - self.failures = [] - end - # Attempt a single login with a single credential against the target. # # @param credential [Credential] The credential object to attempt to @@ -126,39 +69,13 @@ module Metasploit Result.new(result_opts) end - # Run all the login attempts against the target. - # - # This method calls {#attempt_login} once for each credential in - # {#cred_details}. Results are stored in {#successes} and {#failures}. - # If a block is given, each result will be yielded as we go. - # - # @yieldparam result [Result] The frozen {Result} object associated - # with each attempt - # @yieldreturn [void] - # @return [void] - def scan! - valid! - cred_details.each do |credential| - result = attempt_login(credential) - result.freeze + private - yield result if block_given? - - if result.success? - successes << result - break if stop_on_success - else - failures << result - end - end - end - - # @return [void] - # @raise [Invalid] if this scanner's attributes are not valid - def valid! - unless valid? - raise LoginScanner::Invalid.new(self) - end + # This method sets the sane defaults for things + # like timeouts and TCP evasion options + def set_sane_defaults + self.max_send_size = 0 if self.max_send_size.nil? + self.send_delay = 0 if self.send_delay.nil? end end diff --git a/spec/lib/metasploit/framework/login_scanner/http_spec.rb b/spec/lib/metasploit/framework/login_scanner/http_spec.rb new file mode 100644 index 0000000000..bd550fbefc --- /dev/null +++ b/spec/lib/metasploit/framework/login_scanner/http_spec.rb @@ -0,0 +1,15 @@ + +require 'spec_helper' +require 'metasploit/framework/login_scanner/http' + +describe Metasploit::Framework::LoginScanner::HTTP do + + subject(:http_scanner) { described_class.new } + + it_behaves_like 'Metasploit::Framework::LoginScanner::Base' + it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket' + + it { should respond_to :uri } + + +end \ No newline at end of file diff --git a/spec/support/shared/examples/lib/metasploit/framework/login_scanner/login_scanner_base.rb b/spec/support/shared/examples/lib/metasploit/framework/login_scanner/login_scanner_base.rb index 8d96ab4fc9..e00e59f6df 100644 --- a/spec/support/shared/examples/lib/metasploit/framework/login_scanner/login_scanner_base.rb +++ b/spec/support/shared/examples/lib/metasploit/framework/login_scanner/login_scanner_base.rb @@ -276,13 +276,14 @@ shared_examples_for 'Metasploit::Framework::LoginScanner::Base' do it 'calls valid! before running' do my_scanner = login_scanner - my_scanner.should_receive(:valid!).and_call_original + my_scanner.should_receive(:valid!) my_scanner.should_receive(:attempt_login).at_least(:once).and_return success my_scanner.scan! end it 'call attempt_login once for each cred_detail' do my_scanner = login_scanner + my_scanner.should_receive(:valid!) my_scanner.should_receive(:attempt_login).once.with(pub_blank).and_return success my_scanner.should_receive(:attempt_login).once.with(pub_pub).and_return success my_scanner.should_receive(:attempt_login).once.with(pub_pri).and_return success @@ -291,6 +292,7 @@ shared_examples_for 'Metasploit::Framework::LoginScanner::Base' do it 'adds the failed results to the failures attribute' do my_scanner = login_scanner + my_scanner.should_receive(:valid!) my_scanner.should_receive(:attempt_login).once.with(pub_blank).and_return failure_blank my_scanner.should_receive(:attempt_login).once.with(pub_pub).and_return success my_scanner.should_receive(:attempt_login).once.with(pub_pri).and_return failure @@ -301,6 +303,7 @@ shared_examples_for 'Metasploit::Framework::LoginScanner::Base' do it 'adds the success results to the successes attribute' do my_scanner = login_scanner + my_scanner.should_receive(:valid!) my_scanner.should_receive(:attempt_login).once.with(pub_blank).and_return failure_blank my_scanner.should_receive(:attempt_login).once.with(pub_pub).and_return success my_scanner.should_receive(:attempt_login).once.with(pub_pri).and_return failure @@ -319,6 +322,7 @@ shared_examples_for 'Metasploit::Framework::LoginScanner::Base' do it 'stops after the first successful login' do my_scanner = login_scanner + my_scanner.should_receive(:valid!) my_scanner.should_receive(:attempt_login).once.with(pub_blank).and_return failure_blank my_scanner.should_receive(:attempt_login).once.with(pub_pub).and_return success my_scanner.should_not_receive(:attempt_login).with(pub_pri) diff --git a/spec/support/shared/examples/lib/metasploit/framework/login_scanner/rex_socket.rb b/spec/support/shared/examples/lib/metasploit/framework/login_scanner/rex_socket.rb index 0c481d6090..6cf9eeb9fb 100644 --- a/spec/support/shared/examples/lib/metasploit/framework/login_scanner/rex_socket.rb +++ b/spec/support/shared/examples/lib/metasploit/framework/login_scanner/rex_socket.rb @@ -8,51 +8,51 @@ shared_examples_for 'Metasploit::Framework::LoginScanner::RexSocket' do context 'send_delay' do it 'is not valid for a non-number' do - ftp_scanner.send_delay = "a" - expect(ftp_scanner).to_not be_valid - expect(ftp_scanner.errors[:send_delay]).to include "is not a number" + login_scanner.send_delay = "a" + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:send_delay]).to include "is not a number" end it 'is not valid for a floating point' do - ftp_scanner.send_delay = 5.76 - expect(ftp_scanner).to_not be_valid - expect(ftp_scanner.errors[:send_delay]).to include "must be an integer" + login_scanner.send_delay = 5.76 + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:send_delay]).to include "must be an integer" end it 'is not valid for a negative number' do - ftp_scanner.send_delay = -8 - expect(ftp_scanner).to_not be_valid - expect(ftp_scanner.errors[:send_delay]).to include "must be greater than or equal to 0" + login_scanner.send_delay = -8 + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:send_delay]).to include "must be greater than or equal to 0" end it 'is valid for a legitimate number' do - ftp_scanner.send_delay = rand(1000) + 1 - expect(ftp_scanner.errors[:send_delay]).to be_empty + login_scanner.send_delay = rand(1000) + 1 + expect(login_scanner.errors[:send_delay]).to be_empty end end context 'max_send_size' do it 'is not valid for a non-number' do - ftp_scanner.max_send_size = "a" - expect(ftp_scanner).to_not be_valid - expect(ftp_scanner.errors[:max_send_size]).to include "is not a number" + login_scanner.max_send_size = "a" + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:max_send_size]).to include "is not a number" end it 'is not valid for a floating point' do - ftp_scanner.max_send_size = 5.76 - expect(ftp_scanner).to_not be_valid - expect(ftp_scanner.errors[:max_send_size]).to include "must be an integer" + login_scanner.max_send_size = 5.76 + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:max_send_size]).to include "must be an integer" end it 'is not valid for a negative number' do - ftp_scanner.max_send_size = -8 - expect(ftp_scanner).to_not be_valid - expect(ftp_scanner.errors[:max_send_size]).to include "must be greater than or equal to 0" + login_scanner.max_send_size = -8 + expect(login_scanner).to_not be_valid + expect(login_scanner.errors[:max_send_size]).to include "must be greater than or equal to 0" end it 'is valid for a legitimate number' do - ftp_scanner.max_send_size = rand(1000) + 1 - expect(ftp_scanner.errors[:max_send_size]).to be_empty + login_scanner.max_send_size = rand(1000) + 1 + expect(login_scanner.errors[:max_send_size]).to be_empty end end