Merge pull request #13 from rapid7/feature/login_scanner/mysql

Add LoginScanner for MySQL

MSP-9676 #land
bug/bundler_fix
Trevor Rosen 2014-05-08 15:05:24 -05:00
commit c77412d373
3 changed files with 191 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -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