Merge pull request #13 from rapid7/feature/login_scanner/mysql
Add LoginScanner for MySQL MSP-9676 #landbug/bundler_fix
commit
c77412d373
|
@ -55,8 +55,6 @@ module Metasploit
|
|||
|
||||
private
|
||||
|
||||
|
||||
|
||||
# This method sets the sane defaults for things
|
||||
# like timeouts and TCP evasion options
|
||||
def set_sane_defaults
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
require 'metasploit/framework/tcp/client'
|
||||
require 'rbmysql'
|
||||
require 'metasploit/framework/login_scanner/base'
|
||||
require 'metasploit/framework/login_scanner/rex_socket'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module LoginScanner
|
||||
|
||||
# This is the LoginScanner class for dealing with MySQL Database servers.
|
||||
# It is responsible for taking a single target, and a list of credentials
|
||||
# and attempting them. It then saves the results.
|
||||
class MySQL
|
||||
include Metasploit::Framework::LoginScanner::Base
|
||||
include Metasploit::Framework::LoginScanner::RexSocket
|
||||
include Metasploit::Framework::Tcp::Client
|
||||
|
||||
def attempt_login(credential)
|
||||
result_options = {
|
||||
credential: credential
|
||||
}
|
||||
|
||||
# manage our behind the scenes socket. Close any existing one and open a new one
|
||||
disconnect if self.sock
|
||||
connect
|
||||
|
||||
begin
|
||||
::RbMysql.connect({
|
||||
:host => host,
|
||||
:port => port,
|
||||
:read_timeout => 300,
|
||||
:write_timeout => 300,
|
||||
:socket => sock,
|
||||
:user => credential.public,
|
||||
:password => credential.private,
|
||||
:db => ''
|
||||
})
|
||||
rescue Errno::ECONNREFUSED
|
||||
result_options.merge!({
|
||||
status: :connection_error,
|
||||
proof: "Connection refused"
|
||||
})
|
||||
rescue RbMysql::ClientError
|
||||
result_options.merge!({
|
||||
status: :connection_error,
|
||||
proof: "Connection timeout"
|
||||
})
|
||||
rescue Errno::ETIMEDOUT
|
||||
result_options.merge!({
|
||||
status: :connection_error,
|
||||
proof: "Operation Timed out"
|
||||
})
|
||||
rescue RbMysql::HostNotPrivileged
|
||||
result_options.merge!({
|
||||
status: :connection_error,
|
||||
proof: "Unable to login from this host due to policy"
|
||||
})
|
||||
rescue RbMysql::AccessDeniedError
|
||||
result_options.merge!({
|
||||
status: :failed,
|
||||
proof: "Access Denied"
|
||||
})
|
||||
end
|
||||
|
||||
unless result_options[:status]
|
||||
result_options[:status] = :success
|
||||
end
|
||||
|
||||
::Metasploit::Framework::LoginScanner::Result.new(result_options)
|
||||
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
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,108 @@
|
|||
require 'spec_helper'
|
||||
require 'metasploit/framework/login_scanner/mysql'
|
||||
|
||||
describe Metasploit::Framework::LoginScanner::MySQL do
|
||||
let(:public) { 'root' }
|
||||
let(:private) { 'toor' }
|
||||
let(:pub_blank) {
|
||||
Metasploit::Framework::LoginScanner::Credential.new(
|
||||
paired: true,
|
||||
public: public,
|
||||
private: ''
|
||||
)
|
||||
}
|
||||
|
||||
let(:pub_pub) {
|
||||
Metasploit::Framework::LoginScanner::Credential.new(
|
||||
paired: true,
|
||||
public: public,
|
||||
private: public
|
||||
)
|
||||
}
|
||||
|
||||
let(:pub_pri) {
|
||||
Metasploit::Framework::LoginScanner::Credential.new(
|
||||
paired: true,
|
||||
public: public,
|
||||
private: private
|
||||
)
|
||||
}
|
||||
|
||||
subject(:login_scanner) { described_class.new }
|
||||
|
||||
it_behaves_like 'Metasploit::Framework::LoginScanner::Base'
|
||||
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
|
||||
|
||||
context '#attempt_login' do
|
||||
|
||||
context 'when the attempt is successful' do
|
||||
it 'returns a result object with a status of :success' do
|
||||
::RbMysql.should_receive(:connect).and_return "fake mysql handle"
|
||||
expect(login_scanner.attempt_login(pub_pri).status).to eq :success
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the attempt is unsuccessful' do
|
||||
context 'due to connection refused' do
|
||||
it 'returns a result with a status of :connection_error' do
|
||||
::RbMysql.should_receive(:connect).and_raise Errno::ECONNREFUSED
|
||||
expect(login_scanner.attempt_login(pub_pub).status).to eq :connection_error
|
||||
end
|
||||
|
||||
it 'returns a result with the proof containing an appropriate error message' do
|
||||
::RbMysql.should_receive(:connect).and_raise Errno::ECONNREFUSED
|
||||
expect(login_scanner.attempt_login(pub_pub).proof).to eq "Connection refused"
|
||||
end
|
||||
end
|
||||
|
||||
context 'due to connection timeout' do
|
||||
it 'returns a result with a status of :connection_error' do
|
||||
::RbMysql.should_receive(:connect).and_raise RbMysql::ClientError
|
||||
expect(login_scanner.attempt_login(pub_pub).status).to eq :connection_error
|
||||
end
|
||||
|
||||
it 'returns a result with the proof containing an appropriate error message' do
|
||||
::RbMysql.should_receive(:connect).and_raise RbMysql::ClientError
|
||||
expect(login_scanner.attempt_login(pub_pub).proof).to eq "Connection timeout"
|
||||
end
|
||||
end
|
||||
|
||||
context 'due to operation timeout' do
|
||||
it 'returns a result with a status of :connection_error' do
|
||||
::RbMysql.should_receive(:connect).and_raise Errno::ETIMEDOUT
|
||||
expect(login_scanner.attempt_login(pub_pub).status).to eq :connection_error
|
||||
end
|
||||
|
||||
it 'returns a result with the proof containing an appropriate error message' do
|
||||
::RbMysql.should_receive(:connect).and_raise Errno::ETIMEDOUT
|
||||
expect(login_scanner.attempt_login(pub_pub).proof).to eq "Operation Timed out"
|
||||
end
|
||||
end
|
||||
|
||||
context 'due to not being allowed to connect from this host' do
|
||||
it 'returns a result with a status of :connection_error' do
|
||||
::RbMysql.should_receive(:connect).and_raise RbMysql::HostNotPrivileged, "Host not privileged"
|
||||
expect(login_scanner.attempt_login(pub_pub).status).to eq :connection_error
|
||||
end
|
||||
|
||||
it 'returns a result with the proof containing an appropriate error message' do
|
||||
::RbMysql.should_receive(:connect).and_raise RbMysql::HostNotPrivileged, "Host not privileged"
|
||||
expect(login_scanner.attempt_login(pub_pub).proof).to eq "Unable to login from this host due to policy"
|
||||
end
|
||||
end
|
||||
|
||||
context 'due to access denied' do
|
||||
it 'returns a result with a status of :failed' do
|
||||
::RbMysql.should_receive(:connect).and_raise RbMysql::AccessDeniedError, "Access Denied"
|
||||
expect(login_scanner.attempt_login(pub_pub).status).to eq :failed
|
||||
end
|
||||
|
||||
it 'returns a result with the proof containing an appropriate error message' do
|
||||
::RbMysql.should_receive(:connect).and_raise RbMysql::AccessDeniedError, "Access Denied"
|
||||
expect(login_scanner.attempt_login(pub_pub).proof).to eq "Access Denied"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue