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
|
private
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# This method sets the sane defaults for things
|
# This method sets the sane defaults for things
|
||||||
# like timeouts and TCP evasion options
|
# like timeouts and TCP evasion options
|
||||||
def set_sane_defaults
|
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