diff --git a/lib/metasploit/framework/login_scanner/postgres.rb b/lib/metasploit/framework/login_scanner/postgres.rb index f5807e959da0..6baf981a9214 100644 --- a/lib/metasploit/framework/login_scanner/postgres.rb +++ b/lib/metasploit/framework/login_scanner/postgres.rb @@ -11,7 +11,7 @@ module LoginScanner class Postgres include Metasploit::Framework::LoginScanner::Base - # @returns [Boolean] If a login is successful and this attribute is true - a PostgreSQL::Client instance is used as proof, + # @returns [Boolean] If a login is successful and this attribute is true - a Msf::Db::PostgresPR::Connection instance is used as proof, # and the socket is not immediately closed attr_accessor :use_client_as_proof diff --git a/lib/msf/base/sessions/mssql.rb b/lib/msf/base/sessions/mssql.rb index df42b1546314..68df784899e2 100644 --- a/lib/msf/base/sessions/mssql.rb +++ b/lib/msf/base/sessions/mssql.rb @@ -4,10 +4,6 @@ class Msf::Sessions::MSSQL < Msf::Sessions::Sql - # @return [String] The address MSSQL is running on - attr_accessor :address - # @return [Integer] The port MSSQL is running on - attr_accessor :port attr_reader :framework def initialize(rstream, opts = {}) @@ -40,18 +36,4 @@ def self.can_cleanup_files def desc 'MSSQL' end - - def address - return @address if @address - - @address, @port = client.sock.peerinfo.split(':') - @address - end - - def port - return @port if @port - - @address, @port = client.sock.peerinfo.split(':') - @port - end end diff --git a/lib/msf/base/sessions/mysql.rb b/lib/msf/base/sessions/mysql.rb index 5e1f445abfd8..13353c4192f3 100644 --- a/lib/msf/base/sessions/mysql.rb +++ b/lib/msf/base/sessions/mysql.rb @@ -19,7 +19,7 @@ def bootstrap(datastore = {}, handler = nil) session = self session.init_ui(user_input, user_output) - @info = "MySQL #{datastore['USERNAME']} @ #{client.socket.peerinfo}" + @info = "MySQL #{datastore['USERNAME']} @ #{client.peerinfo}" end # @return [String] The type of the session @@ -36,20 +36,4 @@ def self.can_cleanup_files def desc 'MySQL' end - - # @return [Object] The peer address - def address - return @address if @address - - @address, @port = @client.socket.peerinfo.split(':') - @address - end - - # @return [Object] The peer host - def port - return @port if @port - - @address, @port = @client.socket.peerinfo.split(':') - @port - end end diff --git a/lib/msf/base/sessions/postgresql.rb b/lib/msf/base/sessions/postgresql.rb index 1341f0748fd6..37c72ed8b1e4 100644 --- a/lib/msf/base/sessions/postgresql.rb +++ b/lib/msf/base/sessions/postgresql.rb @@ -6,7 +6,7 @@ class Msf::Sessions::PostgreSQL < Msf::Sessions::Sql # @param[Rex::IO::Stream] rstream # @param [Hash] opts - # @param opts [PostgreSQL::Client] :client + # @param opts [Msf::Db::PostgresPR::Connection] :client def initialize(rstream, opts = {}) @client = opts.fetch(:client) @console = ::Rex::Post::PostgreSQL::Ui::Console.new(self) @@ -39,18 +39,4 @@ def self.can_cleanup_files def desc 'PostgreSQL' end - - def address - return @address if @address - - @address, @port = @client.conn.peerinfo.split(':') - @address - end - - def port - return @port if @port - - @address, @port = @client.conn.peerinfo.split(':') - @port - end end diff --git a/lib/msf/base/sessions/smb.rb b/lib/msf/base/sessions/smb.rb index 5d9366dd25cf..d8fded2d287c 100644 --- a/lib/msf/base/sessions/smb.rb +++ b/lib/msf/base/sessions/smb.rb @@ -75,17 +75,11 @@ def desc end def address - return @address if @address - - @address, @port = self.client.dispatcher.tcp_socket.peerinfo.split(':') - @address + @address ||= simple_client.peerhost end def port - return @port if @port - - @address, @port = self.client.dispatcher.tcp_socket.peerinfo.split(':') - @port + @port ||= simple_client.peerport end ## diff --git a/lib/msf/base/sessions/sql.rb b/lib/msf/base/sessions/sql.rb index 23e338803182..6b2834fb6d52 100644 --- a/lib/msf/base/sessions/sql.rb +++ b/lib/msf/base/sessions/sql.rb @@ -73,14 +73,14 @@ def desc raise ::NotImplementedError end - # @return [Object] The peer address + # @return [String] The peer address def address - raise ::NotImplementedError + client.peerhost end - # @return [Object] The peer host + # @return [Integer] The peer port def port - raise ::NotImplementedError + client.peerport end # Initializes the console's I/O handles. diff --git a/lib/rex/post/mssql/ui/console.rb b/lib/rex/post/mssql/ui/console.rb index 08ced5661c9b..afa96e2a6c38 100644 --- a/lib/rex/post/mssql/ui/console.rb +++ b/lib/rex/post/mssql/ui/console.rb @@ -29,7 +29,7 @@ def initialize(session, opts={}) self.session = session self.client = session.client envchange = ::Rex::Proto::MSSQL::ClientMixin::ENVCHANGE - prompt = "%undMSSQL @ #{client.sock.peerinfo} (#{client.initial_info_for_envchange(envchange: envchange::DATABASE)[:new]})%clr" + prompt = "%undMSSQL @ #{client.peerinfo} (#{client.initial_info_for_envchange(envchange: envchange::DATABASE)[:new]})%clr" history_manager = Msf::Config.mssql_session_history super(prompt, '>', history_manager, nil, :mssql) diff --git a/lib/rex/post/mysql/ui/console.rb b/lib/rex/post/mysql/ui/console.rb index 197f2fc167aa..d27e3311025f 100644 --- a/lib/rex/post/mysql/ui/console.rb +++ b/lib/rex/post/mysql/ui/console.rb @@ -24,8 +24,7 @@ def initialize(session) # The mysql client context self.session = session self.client = session.client - self.client.socket ||= self.client.io - prompt = "%undMySQL @ #{client.socket.peerinfo} (#{current_database})%clr" + prompt = "%undMySQL @ #{client.peerinfo} (#{current_database})%clr" history_manager = Msf::Config.mysql_session_history super(prompt, '>', history_manager, nil, :mysql) diff --git a/lib/rex/post/postgresql/ui/console.rb b/lib/rex/post/postgresql/ui/console.rb index efc6a4ec004a..87aa882c45f4 100644 --- a/lib/rex/post/postgresql/ui/console.rb +++ b/lib/rex/post/postgresql/ui/console.rb @@ -28,7 +28,7 @@ def initialize(session) # The postgresql client context self.session = session self.client = session.client - prompt = "%undPostgreSQL @ #{client.conn.peerinfo} (#{current_database})%clr" + prompt = "%undPostgreSQL @ #{client.peerinfo} (#{current_database})%clr" history_manager = Msf::Config.postgresql_session_history super(prompt, '>', history_manager, nil, :postgresql) diff --git a/lib/rex/proto/mssql/client.rb b/lib/rex/proto/mssql/client.rb index 25c961aaef24..4fa380efc36b 100644 --- a/lib/rex/proto/mssql/client.rb +++ b/lib/rex/proto/mssql/client.rb @@ -68,6 +68,7 @@ def initialize(framework_module, framework, rhost, rport = 1433, proxies = nil) @rhost = rhost @rport = rport @proxies = proxies + @current_database = '' end # diff --git a/lib/rex/proto/smb/simple_client.rb b/lib/rex/proto/smb/simple_client.rb index 3a19a20d803e..0347fec4fa52 100644 --- a/lib/rex/proto/smb/simple_client.rb +++ b/lib/rex/proto/smb/simple_client.rb @@ -19,11 +19,13 @@ class SimpleClient DEFAULT_VERSIONS = [1, 2, 3].freeze # Public accessors - attr_accessor :last_error, :server_max_buffer_size, :address, :port + attr_accessor :last_error, :server_max_buffer_size # Private accessors attr_accessor :socket, :client, :direct, :shares, :last_share, :versions + attr_reader :address, :port + # Pass the socket object and a boolean indicating whether the socket is netbios or cifs def initialize(socket, direct = false, versions = DEFAULT_VERSIONS, always_encrypt: true, backend: nil, client: nil) self.socket = socket @@ -258,6 +260,19 @@ def negotiated_smb_version self.client.negotiated_smb_version || -1 end + alias peerhost address + + def peerport + port.to_i + end + + def peerinfo + "#{peerhost}:#{peerport}" + end + + private + + attr_writer :address, :port end end end diff --git a/modules/auxiliary/scanner/mysql/mysql_login.rb b/modules/auxiliary/scanner/mysql/mysql_login.rb index aeaaba020c78..49bf6b4f7d0d 100644 --- a/modules/auxiliary/scanner/mysql/mysql_login.rb +++ b/modules/auxiliary/scanner/mysql/mysql_login.rb @@ -200,9 +200,7 @@ def int_version(str) def session_setup(result, client) return unless (result && client) - rstream = client.socket || client.io - - my_session = Msf::Sessions::MySQL.new(rstream, { client: client }) + my_session = Msf::Sessions::MySQL.new(client.io, { client: client }) merging = { 'USERPASS_FILE' => nil, 'USER_FILE' => nil, diff --git a/spec/lib/msf/base/sessions/mssql_spec.rb b/spec/lib/msf/base/sessions/mssql_spec.rb index 25babcc79aca..406ecf3c831d 100644 --- a/spec/lib/msf/base/sessions/mssql_spec.rb +++ b/spec/lib/msf/base/sessions/mssql_spec.rb @@ -4,7 +4,6 @@ require 'rex/post/mssql' RSpec.describe Msf::Sessions::MSSQL do - let(:rstream) { instance_double(::Rex::Socket) } let(:client) { instance_double(Rex::Proto::MSSQL::Client) } let(:opts) { { client: client } } let(:console_class) { Rex::Post::MSSQL::Ui::Console } @@ -16,7 +15,7 @@ let(:description) { 'MSSQL' } let(:can_cleanup_files) { false } let(:address) { '192.0.2.1' } - let(:port) { '1433' } + let(:port) { 1433 } let(:peer_info) { "#{address}:#{port}" } let(:console) do console = Rex::Post::MSSQL::Ui::Console.new(session) @@ -28,127 +27,11 @@ before(:each) do allow(user_input).to receive(:intrinsic_shell?).and_return(true) allow(user_input).to receive(:output=) - allow(client).to receive(:sock).and_return(rstream) allow(client).to receive(:initial_info_for_envchange).with({ envchange: 1 }).and_return(envchange_result) - allow(rstream).to receive(:peerinfo).and_return(peer_info) + allow(client).to receive(:peerinfo).and_return(peer_info) + allow(client).to receive(:peerport).and_return(port) + allow(client).to receive(:peerhost).and_return(address) end - subject(:session) do - mssql_session = described_class.new(rstream, opts) - mssql_session.user_input = user_input - mssql_session.user_output = user_output - mssql_session.name = name - mssql_session - end - - describe '.type' do - it 'should have the correct type' do - expect(described_class.type).to eq(type) - end - end - - describe '.can_cleanup_files' do - it 'should be able to cleanup files' do - expect(described_class.can_cleanup_files).to eq(can_cleanup_files) - end - end - - describe '#desc' do - it 'should have the correct description' do - expect(subject.desc).to eq(description) - end - end - - describe '#type' do - it 'should have the correct type' do - expect(subject.type).to eq(type) - end - end - - describe '#initialize' do - context 'without a client' do - let(:opts) { {} } - - it 'raises a KeyError' do - expect { subject }.to raise_exception(KeyError) - end - end - context 'with a client' do - it 'does not raise an exception' do - expect { subject }.not_to raise_exception - end - end - - it 'creates a new console' do - expect(subject.console).to be_a(console_class) - end - end - - describe '#bootstrap' do - subject { session.bootstrap } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'sets the console input' do - expect { subject }.to change(session.console, :input).to(user_input) - end - - it 'sets the console output' do - expect { subject }.to change(session.console, :output).to(user_output) - end - - it 'sets the log source' do - expect { subject }.to change(session.console, :log_source).to(log_source) - end - end - - describe '#reset_ui' do - before(:each) do - session.bootstrap - end - - subject { session.reset_ui } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'resets the console input' do - expect { subject }.to change(session.console, :input).from(user_input).to(nil) - end - - it 'resets the console output' do - expect { subject }.to change(session.console, :output).from(user_output).to(nil) - end - end - - describe '#exit' do - subject { session.exit } - - it 'exits the session' do - expect { subject }.to change(session.console, :stopped?).from(false).to(true) - end - end - - describe '#address' do - subject { session.address } - - it { is_expected.to eq(address) } - end - - describe '#port' do - subject { session.port } - - it { is_expected.to eq(port) } - end + it_behaves_like 'client session' end diff --git a/spec/lib/msf/base/sessions/mysql_spec.rb b/spec/lib/msf/base/sessions/mysql_spec.rb index 47ce65e03a7e..48a46ecc02a6 100644 --- a/spec/lib/msf/base/sessions/mysql_spec.rb +++ b/spec/lib/msf/base/sessions/mysql_spec.rb @@ -4,7 +4,6 @@ require 'rex/proto/mysql/client' RSpec.describe Msf::Sessions::MySQL do - let(:rstream) { instance_double(::Rex::Socket) } let(:client) { instance_double(::Rex::Proto::MySQL::Client) } let(:opts) { { client: client } } let(:console_class) { Rex::Post::MySQL::Ui::Console } @@ -16,135 +15,19 @@ let(:description) { 'MySQL' } let(:can_cleanup_files) { false } let(:address) { '192.0.2.1' } - let(:port) { '3306' } + let(:port) { 3306 } let(:peerinfo) { "#{address}:#{port}" } let(:current_database) { 'database_name' } before(:each) do allow(user_input).to receive(:output=) allow(user_input).to receive(:intrinsic_shell?).and_return(true) - allow(rstream).to receive(:peerinfo).and_return(peerinfo) - allow(client).to receive(:socket).and_return(rstream) + allow(client).to receive(:peerinfo).and_return(peerinfo) + allow(client).to receive(:peerport).and_return(port) + allow(client).to receive(:peerhost).and_return(address) allow(client).to receive(:current_database).and_return(current_database) allow(::Rex::Proto::MySQL::Client).to receive(:connect).and_return(client) end - subject(:session) do - mysql_session = described_class.new(rstream, opts) - mysql_session.user_input = user_input - mysql_session.user_output = user_output - mysql_session.name = name - mysql_session - end - - describe '.type' do - it 'should have the correct type' do - expect(described_class.type).to eq(type) - end - end - - describe '.can_cleanup_files' do - it 'should be able to cleanup files' do - expect(described_class.can_cleanup_files).to eq(can_cleanup_files) - end - end - - describe '#desc' do - it 'should have the correct description' do - expect(subject.desc).to eq(description) - end - end - - describe '#type' do - it 'should have the correct type' do - expect(subject.type).to eq(type) - end - end - - describe '#initialize' do - context 'without a client' do - let(:opts) { {} } - - it 'raises a KeyError' do - expect { subject }.to raise_exception(KeyError) - end - end - context 'with a client' do - it 'does not raise an exception' do - expect { subject }.not_to raise_exception - end - end - - it 'creates a new console' do - expect(subject.console).to be_a(console_class) - end - end - - describe '#bootstrap' do - subject { session.bootstrap } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'sets the console input' do - expect { subject }.to change(session.console, :input).to(user_input) - end - - it 'sets the console output' do - expect { subject }.to change(session.console, :output).to(user_output) - end - - it 'sets the log source' do - expect { subject }.to change(session.console, :log_source).to(log_source) - end - end - - describe '#reset_ui' do - before(:each) do - session.bootstrap - end - - subject { session.reset_ui } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'resets the console input' do - expect { subject }.to change(session.console, :input).from(user_input).to(nil) - end - - it 'resets the console output' do - expect { subject }.to change(session.console, :output).from(user_output).to(nil) - end - end - - describe '#exit' do - subject { session.exit } - - it 'exits the session' do - expect { subject }.to change(session.console, :stopped?).from(false).to(true) - end - end - - describe '#address' do - subject { session.address } - - it { is_expected.to eq(address) } - end - - describe '#port' do - subject { session.port } - - it { is_expected.to eq(port) } - end + it_behaves_like 'client session' end diff --git a/spec/lib/msf/base/sessions/postgresql_spec.rb b/spec/lib/msf/base/sessions/postgresql_spec.rb index 5c762a5a5624..e655bc7a4c90 100644 --- a/spec/lib/msf/base/sessions/postgresql_spec.rb +++ b/spec/lib/msf/base/sessions/postgresql_spec.rb @@ -4,7 +4,6 @@ require 'postgres/postgres-pr/connection' RSpec.describe Msf::Sessions::PostgreSQL do - let(:rstream) { instance_double(::Rex::Socket) } let(:client) { instance_double(Msf::Db::PostgresPR::Connection) } let(:opts) { { client: client } } let(:console_class) { Rex::Post::PostgreSQL::Ui::Console } @@ -16,135 +15,19 @@ let(:description) { 'PostgreSQL' } let(:can_cleanup_files) { false } let(:address) { '192.0.2.1' } - let(:port) { '5432' } + let(:port) { 5432 } let(:peer_info) { "#{address}:#{port}" } let(:current_database) { 'template1' } before(:each) do allow(user_input).to receive(:intrinsic_shell?).and_return(true) allow(user_input).to receive(:output=) - allow(rstream).to receive(:peerinfo).and_return(peer_info) - allow(client).to receive(:conn).and_return(rstream) + allow(client).to receive(:peerinfo).and_return(peer_info) + allow(client).to receive(:peerhost).and_return(address) + allow(client).to receive(:peerport).and_return(port) allow(client).to receive(:params).and_return({ 'database' => current_database }) allow(client).to receive(:current_database).and_return(current_database) end - subject(:session) do - postgresql_session = described_class.new(rstream, opts) - postgresql_session.user_input = user_input - postgresql_session.user_output = user_output - postgresql_session.name = name - postgresql_session - end - - describe '.type' do - it 'should have the correct type' do - expect(described_class.type).to eq(type) - end - end - - describe '.can_cleanup_files' do - it 'should be able to cleanup files' do - expect(described_class.can_cleanup_files).to eq(can_cleanup_files) - end - end - - describe '#desc' do - it 'should have the correct description' do - expect(subject.desc).to eq(description) - end - end - - describe '#type' do - it 'should have the correct type' do - expect(subject.type).to eq(type) - end - end - - describe '#initialize' do - context 'without a client' do - let(:opts) { {} } - - it 'raises a KeyError' do - expect { subject }.to raise_exception(KeyError) - end - end - context 'with a client' do - it 'does not raise an exception' do - expect { subject }.not_to raise_exception - end - end - - it 'creates a new console' do - expect(subject.console).to be_a(console_class) - end - end - - describe '#bootstrap' do - subject { session.bootstrap } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'sets the console input' do - expect { subject }.to change(session.console, :input).to(user_input) - end - - it 'sets the console output' do - expect { subject }.to change(session.console, :output).to(user_output) - end - - it 'sets the log source' do - expect { subject }.to change(session.console, :log_source).to(log_source) - end - end - - describe '#reset_ui' do - before(:each) do - session.bootstrap - end - - subject { session.reset_ui } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'resets the console input' do - expect { subject }.to change(session.console, :input).from(user_input).to(nil) - end - - it 'resets the console output' do - expect { subject }.to change(session.console, :output).from(user_output).to(nil) - end - end - - describe '#exit' do - subject { session.exit } - - it 'exits the session' do - expect { subject }.to change(session.console, :stopped?).from(false).to(true) - end - end - - describe '#address' do - subject { session.address } - - it { is_expected.to eq(address) } - end - - describe '#port' do - subject { session.port } - - it { is_expected.to eq(port) } - end + it_behaves_like 'client session' end diff --git a/spec/lib/msf/base/sessions/smb_spec.rb b/spec/lib/msf/base/sessions/smb_spec.rb index ee24e450c816..e3027340ca20 100644 --- a/spec/lib/msf/base/sessions/smb_spec.rb +++ b/spec/lib/msf/base/sessions/smb_spec.rb @@ -16,7 +16,7 @@ let(:description) { 'SMB' } let(:can_cleanup_files) { false } let(:address) { '192.0.2.1' } - let(:port) { '1337' } + let(:port) { 1337 } let(:peer_info) { "#{address}:#{port}" } before(:each) do @@ -27,122 +27,5 @@ allow(dispatcher).to receive(:tcp_socket).and_return(rstream) end - subject(:session) do - smb_session = described_class.new(rstream, opts) - smb_session.user_input = user_input - smb_session.user_output = user_output - smb_session.name = name - smb_session - end - - describe '.type' do - it 'should have the correct type' do - expect(described_class.type).to eq(type) - end - end - - describe '.can_cleanup_files' do - it 'should be able to cleanup files' do - expect(described_class.can_cleanup_files).to eq(can_cleanup_files) - end - end - - describe '#desc' do - it 'should have the correct description' do - expect(subject.desc).to eq(description) - end - end - - describe '#type' do - it 'should have the correct type' do - expect(subject.type).to eq(type) - end - end - - describe '#initialize' do - context 'without a client' do - let(:opts) { {} } - - it 'raises a KeyError' do - expect { subject }.to raise_exception(KeyError) - end - end - context 'with a client' do - it 'does not raise an exception' do - expect { subject }.not_to raise_exception - end - end - - it 'creates a new console' do - expect(subject.console).to be_a(console_class) - end - end - - describe '#bootstrap' do - subject { session.bootstrap } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'sets the console input' do - expect { subject }.to change(session.console, :input).to(user_input) - end - - it 'sets the console output' do - expect { subject }.to change(session.console, :output).to(user_output) - end - - it 'sets the log source' do - expect { subject }.to change(session.console, :log_source).to(log_source) - end - end - - describe '#reset_ui' do - before(:each) do - session.bootstrap - end - - subject { session.reset_ui } - - it 'keeps the sessions user input' do - expect { subject }.not_to change(session, :user_input).from(user_input) - end - - it 'keeps the sessions user output' do - expect { subject }.not_to change(session, :user_output).from(user_output) - end - - it 'resets the console input' do - expect { subject }.to change(session.console, :input).from(user_input).to(nil) - end - - it 'resets the console output' do - expect { subject }.to change(session.console, :output).from(user_output).to(nil) - end - end - - describe '#exit' do - subject { session.exit } - - it 'exits the session' do - expect { subject }.to change(session.console, :stopped?).from(false).to(true) - end - end - - describe '#address' do - subject { session.address } - - it { is_expected.to eq(address) } - end - - describe '#port' do - subject { session.port } - - it { is_expected.to eq(port) } - end + it_behaves_like 'client session' end diff --git a/spec/lib/msf/core/option_group_spec.rb b/spec/lib/msf/core/option_group_spec.rb index 152370887e6e..d6669b942974 100644 --- a/spec/lib/msf/core/option_group_spec.rb +++ b/spec/lib/msf/core/option_group_spec.rb @@ -66,7 +66,7 @@ end it 'validates the options in the group' do - expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + expect { subject.validate(options, datastore) }.not_to raise_error end end @@ -78,7 +78,7 @@ end it 'validates the options in the group' do - expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + expect { subject.validate(options, datastore) }.not_to raise_error end end @@ -89,7 +89,7 @@ end it 'does not attempt to validate the options' do - expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + expect { subject.validate(options, datastore) }.not_to raise_error end end end @@ -118,7 +118,7 @@ end it 'validates the options in the group' do - expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + expect { subject.validate(options, datastore) }.not_to raise_error end end @@ -129,7 +129,7 @@ end it 'does not attempt to validate the options' do - expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + expect { subject.validate(options, datastore) }.not_to raise_error end end end diff --git a/spec/lib/rex/post/mssql/ui/console/command_dispatcher/core_spec.rb b/spec/lib/rex/post/mssql/ui/console/command_dispatcher/core_spec.rb index b93a797c2186..37fbf4ff4a36 100644 --- a/spec/lib/rex/post/mssql/ui/console/command_dispatcher/core_spec.rb +++ b/spec/lib/rex/post/mssql/ui/console/command_dispatcher/core_spec.rb @@ -4,7 +4,6 @@ require 'rex/post/mssql' RSpec.describe Rex::Post::MSSQL::Ui::Console::CommandDispatcher::Core do - let(:rstream) { instance_double(::Rex::Socket) } let(:client) { instance_double(Rex::Proto::MSSQL::Client) } let(:session) { Msf::Sessions::MSSQL.new(nil, { client: client }) } let(:address) { '192.0.2.1' } @@ -18,9 +17,8 @@ let(:envchange_result) { { type: 1, old: 'master', new: 'master' } } before(:each) do - allow(client).to receive(:sock).and_return(rstream) allow(client).to receive(:initial_info_for_envchange).with({ envchange: 1 }).and_return(envchange_result) - allow(rstream).to receive(:peerinfo).and_return(peer_info) + allow(client).to receive(:peerinfo).and_return(peer_info) allow(session).to receive(:client).and_return(client) allow(session).to receive(:console).and_return(console) allow(session).to receive(:name).and_return('test client name') diff --git a/spec/lib/rex/post/mysql/ui/console/command_dispatcher/core_spec.rb b/spec/lib/rex/post/mysql/ui/console/command_dispatcher/core_spec.rb index ef4937499746..bb389ed81ab4 100644 --- a/spec/lib/rex/post/mysql/ui/console/command_dispatcher/core_spec.rb +++ b/spec/lib/rex/post/mysql/ui/console/command_dispatcher/core_spec.rb @@ -5,13 +5,12 @@ require 'rex/proto/mysql/client' RSpec.describe Rex::Post::MySQL::Ui::Console::CommandDispatcher::Core do - let(:rstream) { instance_double(::Rex::Socket) } let(:client) { instance_double(::Rex::Proto::MySQL::Client) } let(:current_database) { 'database_name' } let(:address) { '192.0.2.1' } let(:port) { '3306' } let(:peerinfo) { "#{address}:#{port}" } - let(:session) { Msf::Sessions::MySQL.new(rstream, { client: client }) } + let(:session) { Msf::Sessions::MySQL.new(nil, { client: client }) } let(:console) do console = Rex::Post::MySQL::Ui::Console.new(session) console.disable_output = true @@ -19,9 +18,8 @@ end before(:each) do - allow(rstream).to receive(:peerinfo).and_return(peerinfo) + allow(client).to receive(:peerinfo).and_return(peerinfo) allow(client).to receive(:current_database).and_return(current_database) - allow(client).to receive(:socket).and_return(rstream) allow(session).to receive(:console).and_return(console) allow(session).to receive(:name).and_return('test client name') allow(session).to receive(:sid).and_return('test client sid') diff --git a/spec/lib/rex/post/postgresql/ui/console/command_dispatcher/core_spec.rb b/spec/lib/rex/post/postgresql/ui/console/command_dispatcher/core_spec.rb index 6279750949f6..6e2f051f7dc3 100644 --- a/spec/lib/rex/post/postgresql/ui/console/command_dispatcher/core_spec.rb +++ b/spec/lib/rex/post/postgresql/ui/console/command_dispatcher/core_spec.rb @@ -5,7 +5,6 @@ require 'postgres/postgres-pr/connection' RSpec.describe Rex::Post::PostgreSQL::Ui::Console::CommandDispatcher::Core do - let(:rstream) { instance_double(::Rex::Socket) } let(:client) { instance_double(Msf::Db::PostgresPR::Connection) } let(:address) { '192.0.2.1' } let(:port) { '5432' } @@ -19,10 +18,9 @@ end before(:each) do - allow(client).to receive(:conn).and_return(rstream) allow(client).to receive(:params).and_return({ 'database' => current_database }) allow(client).to receive(:current_database).and_return(current_database) - allow(rstream).to receive(:peerinfo).and_return(peer_info) + allow(client).to receive(:peerinfo).and_return(peer_info) allow(session).to receive(:client).and_return(client) allow(session).to receive(:console).and_return(console) allow(session).to receive(:name).and_return('test client name') diff --git a/spec/lib/rex/proto/mssql/client_spec.rb b/spec/lib/rex/proto/mssql/client_spec.rb new file mode 100644 index 000000000000..0954eaba10f6 --- /dev/null +++ b/spec/lib/rex/proto/mssql/client_spec.rb @@ -0,0 +1,32 @@ +# -*- coding: binary -*- + +require 'spec_helper' +require 'rex/proto/mysql/client' + +RSpec.describe Rex::Proto::MSSQL::Client do + let(:host) { '127.0.0.1' } + let(:port) { 1234 } + let(:info) { "#{host}:#{port}" } + let(:db_name) { 'my_db_name' } + let(:framework_module) { ::Msf::Module.new } + + subject do + client = described_class.new(framework_module, nil, host, port) + client.current_database = db_name + client + end + + it_behaves_like 'session compatible SQL client' + + describe '#current_database' do + context 'we have not selected a database yet' do + subject do + described_class.new(framework_module, nil, host, port) + end + + it 'returns an empty database name' do + expect(subject.current_database).to eq('') + end + end + end +end diff --git a/spec/lib/rex/proto/mysql/client_spec.rb b/spec/lib/rex/proto/mysql/client_spec.rb index d85d10dc0895..9813ef863164 100644 --- a/spec/lib/rex/proto/mysql/client_spec.rb +++ b/spec/lib/rex/proto/mysql/client_spec.rb @@ -4,13 +4,32 @@ require 'rex/proto/mysql/client' RSpec.describe Rex::Proto::MySQL::Client do + let(:host) { '127.0.0.1' } + let(:port) { 1234 } + let(:info) { "#{host}:#{port}" } + let(:db_name) { 'my_db_name' } + + subject do + addr_info = instance_double(Addrinfo, ip_address: host, ip_port: port) + socket = instance_double(Socket, remote_address: addr_info) + client = described_class.new(io: socket) + allow(client).to receive(:session_track).and_return({ 1 => [db_name] }) + client + end + it { is_expected.to be_a ::Mysql } - [ - { method: :peerhost, return_type: String }, - { method: :peerport, return_type: Integer }, - { method: :current_database, return_type: String } - ].each do |method_hash| - it { is_expected.to respond_to method_hash[:method] } + it_behaves_like 'session compatible SQL client' + + describe '#current_database' do + context 'we have not selected a database yet' do + before(:each) do + allow(subject).to receive(:session_track).and_return({}) + end + + it 'returns an empty database name' do + expect(subject.current_database).to eq('') + end + end end end diff --git a/spec/lib/rex/proto/postgresql/client_spec.rb b/spec/lib/rex/proto/postgresql/client_spec.rb new file mode 100644 index 000000000000..90d6847ca1df --- /dev/null +++ b/spec/lib/rex/proto/postgresql/client_spec.rb @@ -0,0 +1,23 @@ +# -*- coding: binary -*- + +require 'spec_helper' +require 'postgres/postgres-pr/connection' + +RSpec.describe Msf::Db::PostgresPR::Connection do + let(:host) { '127.0.0.1' } + let(:port) { 1234 } + let(:info) { "#{host}:#{port}" } + let(:db_name) { 'my_db_name' } + let(:socket) { double(Rex::Socket, peerhost: host, peerport: port) } + let(:message) { Msf::Db::PostgresPR::ReadyForQuery.new('') } + + subject do + allow(socket).to receive(:<<) + allow(Msf::Db::PostgresPR::Message).to receive(:read).and_return(message) + allow(Rex::Socket).to receive(:create).and_return(socket) + client = described_class.new(db_name, 'username', 'password', "tcp://#{host}:#{port}") + client + end + + it_behaves_like 'session compatible SQL client' +end diff --git a/spec/lib/rex/proto/smb/simple_client_spec.rb b/spec/lib/rex/proto/smb/simple_client_spec.rb new file mode 100644 index 000000000000..025c0a0861eb --- /dev/null +++ b/spec/lib/rex/proto/smb/simple_client_spec.rb @@ -0,0 +1,18 @@ +# -*- coding: binary -*- + +require 'spec_helper' +require 'rex/proto/smb/simple_client' + +RSpec.describe Rex::Proto::SMB::SimpleClient do + let(:host) { '127.0.0.1' } + let(:port) { 1234 } + let(:info) { "#{host}:#{port}" } + + subject do + socket = instance_double(Rex::Socket, peerinfo: info) + client = described_class.new(socket) + client + end + + it_behaves_like 'session compatible client' +end diff --git a/spec/support/shared/examples/msf/sessions.rb b/spec/support/shared/examples/msf/sessions.rb new file mode 100644 index 000000000000..a98280934f4e --- /dev/null +++ b/spec/support/shared/examples/msf/sessions.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'client session' do + subject(:session) do + session = described_class.new(nil, opts) + session.user_input = user_input + session.user_output = user_output + session.name = name + session + end + + describe '.type' do + it 'should have the correct type' do + expect(described_class.type).to eq(type) + end + end + + describe '.can_cleanup_files' do + it 'should be able to cleanup files' do + expect(described_class.can_cleanup_files).to eq(can_cleanup_files) + end + end + + describe '#desc' do + it 'should have the correct description' do + expect(subject.desc).to eq(description) + end + end + + describe '#type' do + it 'should have the correct type' do + expect(subject.type).to eq(type) + end + end + + describe '#initialize' do + context 'without a client' do + let(:opts) { {} } + + it 'raises a KeyError' do + expect { subject }.to raise_exception(KeyError) + end + end + context 'with a client' do + it 'does not raise an exception' do + expect { subject }.not_to raise_exception + end + end + + it 'creates a new console' do + expect(subject.console).to be_a(console_class) + end + end + + describe '#bootstrap' do + subject { session.bootstrap } + + it 'keeps the sessions user input' do + expect { subject }.not_to change(session, :user_input).from(user_input) + end + + it 'keeps the sessions user output' do + expect { subject }.not_to change(session, :user_output).from(user_output) + end + + it 'sets the console input' do + expect { subject }.to change(session.console, :input).to(user_input) + end + + it 'sets the console output' do + expect { subject }.to change(session.console, :output).to(user_output) + end + + it 'sets the log source' do + expect { subject }.to change(session.console, :log_source).to(log_source) + end + end + + describe '#reset_ui' do + before(:each) do + session.bootstrap + end + + subject { session.reset_ui } + + it 'keeps the sessions user input' do + expect { subject }.not_to change(session, :user_input).from(user_input) + end + + it 'keeps the sessions user output' do + expect { subject }.not_to change(session, :user_output).from(user_output) + end + + it 'resets the console input' do + expect { subject }.to change(session.console, :input).from(user_input).to(nil) + end + + it 'resets the console output' do + expect { subject }.to change(session.console, :output).from(user_output).to(nil) + end + end + + describe '#exit' do + subject { session.exit } + + it 'exits the session' do + expect { subject }.to change(session.console, :stopped?).from(false).to(true) + end + end + + describe '#address' do + subject { session.address } + + it { is_expected.to eq(address) } + end + + describe '#port' do + subject { session.port } + + it { is_expected.to eq(port) } + end +end diff --git a/spec/support/shared/examples/rex/proto/session_clients.rb b/spec/support/shared/examples/rex/proto/session_clients.rb new file mode 100644 index 000000000000..407b84e17d2e --- /dev/null +++ b/spec/support/shared/examples/rex/proto/session_clients.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'session compatible client' do + it { is_expected.to respond_to(:peerhost).with(0).arguments } + it { is_expected.to respond_to(:peerport).with(0).arguments } + it { is_expected.to respond_to(:peerinfo).with(0).arguments } + + describe '#peerhost' do + it 'returns the ip address' do + expect(subject.peerhost).to eq(host) + end + end + + describe '#peerport' do + it 'returns the port number' do + expect(subject.peerport).to eq(port) + end + end + + describe '#peerinfo' do + it 'returns the peer info' do + expect(subject.peerinfo).to eq(info) + end + end +end + +RSpec.shared_examples 'session compatible SQL client' do + it_behaves_like 'session compatible client' + + it { is_expected.to respond_to(:current_database).with(0).arguments } + + describe '#current_database' do + it 'returns the database name' do + expect(subject.current_database).to eq(db_name) + end + end +end