refactor to use Result class

we now use a Result class to handle
all result codes from the login attempt
bug/bundler_fix
David Maloney 2014-04-21 15:35:16 -05:00
parent d313047532
commit 2e11f80a98
No known key found for this signature in database
GPG Key ID: DEDBA9DC3A913DB2
2 changed files with 72 additions and 31 deletions

View File

@ -1,4 +1,5 @@
require 'metasploit/framework/login_scanner/invalid' require 'metasploit/framework/login_scanner/invalid'
require 'metasploit/framework/login_scanner/result'
require 'net/ssh' require 'net/ssh'
module Metasploit module Metasploit
@ -15,6 +16,9 @@ module Metasploit
# @!attribute cred_details # @!attribute cred_details
# @return [Array] An array of hashes containing the cred # @return [Array] An array of hashes containing the cred
attr_accessor :cred_details attr_accessor :cred_details
# @!attribute successes
# @return [Array] Array of credential hashes that filed to log in
attr_accessor :failures
# @!attribute host # @!attribute host
# @return [String] The IP address or hostname to connect to # @return [String] The IP address or hostname to connect to
attr_accessor :host attr_accessor :host
@ -27,6 +31,9 @@ module Metasploit
# @!attribute stop_on_success # @!attribute stop_on_success
# @return [Boolean] Whether the scanner should stop when it has found one working Credential # @return [Boolean] Whether the scanner should stop when it has found one working Credential
attr_accessor :stop_on_success attr_accessor :stop_on_success
# @!attribute successes
# @return [Array] Array of credential hashes that successfully logged in
attr_accessor :successes
# @!attribute verbosity # @!attribute verbosity
# @return [Symbol] The verbosity level for the SSH client. # @return [Symbol] The verbosity level for the SSH client.
attr_accessor :verbosity attr_accessor :verbosity
@ -66,6 +73,8 @@ module Metasploit
attributes.each do |attribute, value| attributes.each do |attribute, value|
public_send("#{attribute}=", value) public_send("#{attribute}=", value)
end end
public_send("successes=", [])
public_send("failures=", [])
end end
def attempt_login(user, pass) def attempt_login(user, pass)
@ -87,28 +96,56 @@ module Metasploit
opt_hash opt_hash
) )
end end
rescue Rex::ConnectionError, Rex::AddressInUse rescue Rex::ConnectionError, Rex::AddressInUse, Net::SSH::Disconnect, ::EOFError, ::Timeout::Error
return :connection_error return ::Metasploit::Framework::LoginScanner::Result.new(
rescue Net::SSH::Disconnect, ::EOFError private: pass,
return :connection_disconnect proof: nil,
rescue ::Timeout::Error public: user,
return :connection_disconnect realm: nil,
status: :connection_error
)
rescue Net::SSH::Exception rescue Net::SSH::Exception
return [:fail,nil] # For whatever reason. Can't tell if passwords are on/off without timing responses. return ::Metasploit::Framework::LoginScanner::Result.new(
private: pass,
proof: nil,
public: user,
realm: nil,
status: :failed
)
end end
if ssh_socket if ssh_socket
proof = gather_proof proof = gather_proof
[:success, proof] ::Metasploit::Framework::LoginScanner::Result.new(
private: pass,
proof: proof,
public: user,
realm: nil,
status: :success
)
else else
[:fail, nil] ::Metasploit::Framework::LoginScanner::Result.new(
private: pass,
proof: nil,
public: user,
realm: nil,
status: :failed
)
end end
end end
def scan! def scan!
valid! valid!
cred_details.each do |credential|
result = attempt_login(credential[:public], credential[:private])
if result[0] == :success
successes << credential.merge(status: result[0], proof: result[1])
break if stop_on_success
else
failures << credential.merge(status: result[0], proof: result[1])
end
end
end end
# @raise [Metasploit::Framework::LoginScanner::Invalid] if the attributes are not valid on the scanner # @raise [Metasploit::Framework::LoginScanner::Invalid] if the attributes are not valid on the scanner

View File

@ -2,7 +2,8 @@ require 'spec_helper'
require 'metasploit/framework/login_scanner/ssh' require 'metasploit/framework/login_scanner/ssh'
describe Metasploit::Framework::LoginScanner::SSH do describe Metasploit::Framework::LoginScanner::SSH do
let(:public) { 'root' }
let(:private) { 'toor' }
subject(:ssh_scanner) { subject(:ssh_scanner) {
described_class.new described_class.new
} }
@ -14,6 +15,8 @@ describe Metasploit::Framework::LoginScanner::SSH do
it { should respond_to :verbosity } it { should respond_to :verbosity }
it { should respond_to :stop_on_success } it { should respond_to :stop_on_success }
it { should respond_to :valid! } it { should respond_to :valid! }
it { should respond_to :scan! }
context 'validations' do context 'validations' do
context 'port' do context 'port' do
@ -126,35 +129,35 @@ describe Metasploit::Framework::LoginScanner::SSH do
end end
it 'is not valid if any of the elements are missing a public component' do it 'is not valid if any of the elements are missing a public component' do
detail = { private: 'toor'} detail = { private: private}
ssh_scanner.cred_details = [detail] ssh_scanner.cred_details = [detail]
expect(ssh_scanner).to_not be_valid expect(ssh_scanner).to_not be_valid
expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, missing public component #{detail}" expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, missing public component #{detail}"
end end
it 'is not valid if any of the elements have an invalid public component' do it 'is not valid if any of the elements have an invalid public component' do
detail = { public: 5, private: 'toor'} detail = { public: 5, private: private}
ssh_scanner.cred_details = [detail] ssh_scanner.cred_details = [detail]
expect(ssh_scanner).to_not be_valid expect(ssh_scanner).to_not be_valid
expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, invalid public component #{detail}" expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, invalid public component #{detail}"
end end
it 'is not valid if any of the elements are missing a public component' do it 'is not valid if any of the elements are missing a public component' do
detail = { public: 'root'} detail = { public: public}
ssh_scanner.cred_details = [detail] ssh_scanner.cred_details = [detail]
expect(ssh_scanner).to_not be_valid expect(ssh_scanner).to_not be_valid
expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, missing private component #{detail}" expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, missing private component #{detail}"
end end
it 'is not valid if any of the elements have an invalid public component' do it 'is not valid if any of the elements have an invalid public component' do
detail = { public: 'root', private: []} detail = { public: public, private: []}
ssh_scanner.cred_details = [detail] ssh_scanner.cred_details = [detail]
expect(ssh_scanner).to_not be_valid expect(ssh_scanner).to_not be_valid
expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, invalid private component #{detail}" expect(ssh_scanner.errors[:cred_details]).to include "has invalid element, invalid private component #{detail}"
end end
it 'is valid if all of the lements are properly formed hashes' do it 'is valid if all of the lements are properly formed hashes' do
detail = { public: 'root', private: 'toor'} detail = { public: public, private: private}
ssh_scanner.cred_details = [detail] ssh_scanner.cred_details = [detail]
expect(ssh_scanner.errors[:cred_details]).to be_empty expect(ssh_scanner.errors[:cred_details]).to be_empty
end end
@ -281,12 +284,12 @@ describe Metasploit::Framework::LoginScanner::SSH do
ssh_scanner.connection_timeout = 30 ssh_scanner.connection_timeout = 30
ssh_scanner.verbosity = :fatal ssh_scanner.verbosity = :fatal
ssh_scanner.stop_on_success = true ssh_scanner.stop_on_success = true
ssh_scanner.cred_details = [ { public: 'root', private: 'toor' }] ssh_scanner.cred_details = [ { public: public, private: private}]
end end
it 'creates a Timeout based on the connection_timeout' do it 'creates a Timeout based on the connection_timeout' do
::Timeout.should_receive(:timeout).with(ssh_scanner.connection_timeout) ::Timeout.should_receive(:timeout).with(ssh_scanner.connection_timeout)
ssh_scanner.attempt_login('root', 'toor') ssh_scanner.attempt_login(public, private)
end end
it 'calls Net::SSH with the correct arguments' do it 'calls Net::SSH with the correct arguments' do
@ -294,52 +297,53 @@ describe Metasploit::Framework::LoginScanner::SSH do
:auth_methods => ['password','keyboard-interactive'], :auth_methods => ['password','keyboard-interactive'],
:port => ssh_scanner.port, :port => ssh_scanner.port,
:disable_agent => true, :disable_agent => true,
:password => 'toor', :password => private,
:config => false, :config => false,
:verbose => ssh_scanner.verbosity :verbose => ssh_scanner.verbosity
} }
Net::SSH.should_receive(:start).with( Net::SSH.should_receive(:start).with(
ssh_scanner.host, ssh_scanner.host,
'root', public,
opt_hash opt_hash
) )
ssh_scanner.attempt_login('root', 'toor') ssh_scanner.attempt_login(public, private)
end end
context 'when it fails' do context 'when it fails' do
it 'returns :connection_error for a Rex::ConnectionError' do it 'returns :connection_error for a Rex::ConnectionError' do
Net::SSH.should_receive(:start) { raise Rex::ConnectionError } Net::SSH.should_receive(:start) { raise Rex::ConnectionError }
expect(ssh_scanner.attempt_login('root', 'toor')).to eq :connection_error expect(ssh_scanner.attempt_login(public, private).status).to eq :connection_error
end end
it 'returns :connection_error for a Rex::AddressInUse' do it 'returns :connection_error for a Rex::AddressInUse' do
Net::SSH.should_receive(:start) { raise Rex::AddressInUse } Net::SSH.should_receive(:start) { raise Rex::AddressInUse }
expect(ssh_scanner.attempt_login('root', 'toor')).to eq :connection_error expect(ssh_scanner.attempt_login(public, private).status).to eq :connection_error
end end
it 'returns :connection_disconnect for a Net::SSH::Disconnect' do it 'returns :connection_disconnect for a Net::SSH::Disconnect' do
Net::SSH.should_receive(:start) { raise Net::SSH::Disconnect } Net::SSH.should_receive(:start) { raise Net::SSH::Disconnect }
expect(ssh_scanner.attempt_login('root', 'toor')).to eq :connection_disconnect expect(ssh_scanner.attempt_login(public, private).status).to eq :connection_error
end end
it 'returns :connection_disconnect for a ::EOFError' do it 'returns :connection_disconnect for a ::EOFError' do
Net::SSH.should_receive(:start) { raise ::EOFError } Net::SSH.should_receive(:start) { raise ::EOFError }
expect(ssh_scanner.attempt_login('root', 'toor')).to eq :connection_disconnect expect(ssh_scanner.attempt_login(public, private).status).to eq :connection_error
end end
it 'returns :connection_disconnect for a ::Timeout::Error' do it 'returns :connection_disconnect for a ::Timeout::Error' do
Net::SSH.should_receive(:start) { raise ::Timeout::Error } Net::SSH.should_receive(:start) { raise ::Timeout::Error }
expect(ssh_scanner.attempt_login('root', 'toor')).to eq :connection_disconnect expect(ssh_scanner.attempt_login(public, private).status).to eq :connection_error
end end
it 'returns [:fail,nil] for a Net::SSH::Exception' do it 'returns [:fail,nil] for a Net::SSH::Exception' do
Net::SSH.should_receive(:start) { raise Net::SSH::Exception } Net::SSH.should_receive(:start) { raise Net::SSH::Exception }
expect(ssh_scanner.attempt_login('root', 'toor')).to eq [:fail,nil] expect(ssh_scanner.attempt_login(public, private).status).to eq :failed
end end
it 'returns [:fail,nil] if no socket returned' do it 'returns [:fail,nil] if no socket returned' do
Net::SSH.should_receive(:start).and_return nil Net::SSH.should_receive(:start).and_return nil
expect(ssh_scanner.attempt_login('root', 'toor')).to eq [:fail,nil] expect(ssh_scanner.attempt_login(public, private).status).to eq :failed
end end
end end
@ -349,14 +353,14 @@ describe Metasploit::Framework::LoginScanner::SSH do
Net::SSH.should_receive(:start) {"fake_socket"} Net::SSH.should_receive(:start) {"fake_socket"}
my_scanner = ssh_scanner my_scanner = ssh_scanner
my_scanner.should_receive(:gather_proof) my_scanner.should_receive(:gather_proof)
my_scanner.attempt_login('root', 'toor') my_scanner.attempt_login(public, private)
end end
it 'returns a success code and proof' do it 'returns a success code and proof' do
Net::SSH.should_receive(:start) {"fake_socket"} Net::SSH.should_receive(:start) {"fake_socket"}
my_scanner = ssh_scanner my_scanner = ssh_scanner
my_scanner.should_receive(:gather_proof).and_return('root') my_scanner.should_receive(:gather_proof).and_return(public)
expect(my_scanner.attempt_login('root', 'toor')).to eq [:success, 'root'] expect(my_scanner.attempt_login(public, private).status).to eq :success
end end
end end
end end