refactor to use Result class
we now use a Result class to handle all result codes from the login attemptbug/bundler_fix
parent
d313047532
commit
2e11f80a98
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue