From e025fa179179d326154adce1a523ee990f9af669 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Wed, 7 May 2014 18:32:36 -0500 Subject: [PATCH] final touches and specs add finishing touches to postgres Loginscanner and add specs to cover the behaviour --- .../framework/login_scanner/postgres.rb | 71 +++++++++++++++++ .../framework/login_scanner/postgres_spec.rb | 78 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 lib/metasploit/framework/login_scanner/postgres.rb create mode 100644 spec/lib/metasploit/framework/login_scanner/postgres_spec.rb diff --git a/lib/metasploit/framework/login_scanner/postgres.rb b/lib/metasploit/framework/login_scanner/postgres.rb new file mode 100644 index 0000000000..b25440045a --- /dev/null +++ b/lib/metasploit/framework/login_scanner/postgres.rb @@ -0,0 +1,71 @@ +require 'metasploit/framework/login_scanner/base' +require 'postgres_msf' + +module Metasploit + module Framework + module LoginScanner + + # This is the LoginScanner class for dealing with PostgreSQL database servers. + # It is responsible for taking a single target, and a list of credentials + # and attempting them. It then saves the results. + class Postgres + include Metasploit::Framework::LoginScanner::Base + + # This method attempts a single login with a single credential against the target + # @param credential [Credential] The credential object to attmpt to login with + # @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object + def attempt_login(credential) + result_options = { + credential: credential + } + + db_name = credential.realm || 'template1' + + if ::Rex::Socket.is_ipv6?(host) + uri = "tcp://[#{host}]:#{port}" + else + uri = "tcp://#{host}:#{port}" + end + + pg_conn = nil + + begin + pg_conn = Msf::Db::PostgresPR::Connection.new(db_name,credential.public,credential.private,uri) + rescue RuntimeError => e + case e.to_s.split("\t")[1] + when "C3D000" + result_options.merge!({ + status: :failed, + proof: "C3D000, Creds were good but database was bad" + }) + when "C28000", "C28P01" + result_options.merge!({ + status: :failed, + proof: "Invalid username or password" + }) + else + result_options.merge!({ + status: :failed, + proof: e.message + }) + end + end + + if pg_conn + pg_conn.close + result_options[:status] = :success + else + result_options[:status] = :failed + end + + ::Metasploit::Framework::LoginScanner::Result.new(result_options) + + end + + + end + + + end + end +end \ No newline at end of file diff --git a/spec/lib/metasploit/framework/login_scanner/postgres_spec.rb b/spec/lib/metasploit/framework/login_scanner/postgres_spec.rb new file mode 100644 index 0000000000..e055d45e03 --- /dev/null +++ b/spec/lib/metasploit/framework/login_scanner/postgres_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' +require 'metasploit/framework/login_scanner/postgres' + +describe Metasploit::Framework::LoginScanner::Postgres do + let(:public) { 'root' } + let(:private) { 'toor' } + let(:realm) { 'template1' } + + let(:full_cred) { + Metasploit::Framework::LoginScanner::Credential.new( + paired: true, + public: public, + private: private, + realm: realm + ) + } + + let(:cred_no_realm) { + Metasploit::Framework::LoginScanner::Credential.new( + paired: true, + public: public, + private: private + ) + } + + + + subject(:login_scanner) { described_class.new } + + it_behaves_like 'Metasploit::Framework::LoginScanner::Base' + + context '#attempt_login' do + context 'when the login is successful' do + it 'returns a result object with a status of success' do + fake_conn = "fake_connection" + Msf::Db::PostgresPR::Connection.should_receive(:new).and_return fake_conn + fake_conn.should_receive(:close) + expect(login_scanner.attempt_login(full_cred).status).to eq :success + end + end + + context 'when there is no realm on the credential' do + it 'uses template1 as the default realm' do + Msf::Db::PostgresPR::Connection.should_receive(:new).with('template1', 'root', 'toor', 'tcp://:') + login_scanner.attempt_login(cred_no_realm) + end + end + + context 'when the realm is invalid but the rest of the credential is not' do + it 'includes the details in the result proof' do + Msf::Db::PostgresPR::Connection.should_receive(:new).and_raise RuntimeError, "blah\tC3D000" + result = login_scanner.attempt_login(cred_no_realm) + expect(result.status).to eq :failed + expect(result.proof).to eq "C3D000, Creds were good but database was bad" + end + end + + context 'when the username or password is invalid' do + it 'includes a message in proof, indicating why it failed' do + Msf::Db::PostgresPR::Connection.should_receive(:new).and_raise RuntimeError, "blah\tC28000" + result = login_scanner.attempt_login(cred_no_realm) + expect(result.status).to eq :failed + expect(result.proof).to eq "Invalid username or password" + end + end + + context 'when any other type of error occurs' do + it 'returns a failure with the error message in the proof' do + Msf::Db::PostgresPR::Connection.should_receive(:new).and_raise RuntimeError, "unknown error" + result = login_scanner.attempt_login(cred_no_realm) + expect(result.status).to eq :failed + expect(result.proof).to eq "unknown error" + end + end + + end + +end \ No newline at end of file