diff --git a/lib/msf/base/sessions/mssql.rb b/lib/msf/base/sessions/mssql.rb index 68df784899e2..4787f72909b0 100644 --- a/lib/msf/base/sessions/mssql.rb +++ b/lib/msf/base/sessions/mssql.rb @@ -8,6 +8,8 @@ class Msf::Sessions::MSSQL < Msf::Sessions::Sql def initialize(rstream, opts = {}) @client = opts.fetch(:client) + self.platform = opts.fetch(:platform) + self.arch = opts.fetch(:arch) self.console = ::Rex::Post::MSSQL::Ui::Console.new(self, opts) super(rstream, opts) diff --git a/lib/rex/proto/mssql/client.rb b/lib/rex/proto/mssql/client.rb index 4fa380efc36b..d4beb01ee6df 100644 --- a/lib/rex/proto/mssql/client.rb +++ b/lib/rex/proto/mssql/client.rb @@ -71,6 +71,57 @@ def initialize(framework_module, framework, rhost, rport = 1433, proxies = nil) @current_database = '' end + # MS SQL Server only supports Windows and Linux + def map_compile_os_to_platform(server_info) + return '' if server_info.blank? + + os_data = server_info.downcase.encode(::Encoding::BINARY) + + if os_data.match?('linux') + platform = Msf::Platform::Linux.realname + elsif os_data.match?('windows') + platform = Msf::Platform::Windows.realname + elsif os_data.match?('win') + platform = Msf::Platform::Windows.realname + else + platform = os_data + end + platform + end + + # MS SQL Server currently only supports 64 bit but older installs may be x86 + def map_compile_arch_to_architecture(server_info) + return '' if server_info.blank? + + arch_data = server_info.downcase.encode(::Encoding::BINARY) + + if arch_data.match?('x64') + arch = ARCH_X86_64 + elsif arch_data.match?('x86') + arch = ARCH_X86 + elsif arch_data.match?('64') + arch = ARCH_X86_64 + elsif arch_data.match?('32-bit') + arch = ARCH_X86 + else + arch = arch_data + end + arch + end + + # @return [Hash] Detect the platform and architecture of the MSSQL server: + # * :arch [String] The server architecture. + # * :platform [String] The server platform. + def detect_platform_and_arch + result = {} + + server_vars = query('select @@version')[:rows][0][0] + + result[:arch] = map_compile_arch_to_architecture(server_vars) + result[:platform] = map_compile_os_to_platform(server_vars) + result + end + # # This method connects to the server over TCP and attempts # to authenticate with the supplied username and password diff --git a/lib/rex/proto/mysql/client.rb b/lib/rex/proto/mysql/client.rb index be543ddd3eab..692250228b60 100644 --- a/lib/rex/proto/mysql/client.rb +++ b/lib/rex/proto/mysql/client.rb @@ -32,25 +32,25 @@ def current_database # List of supported MySQL platforms & architectures: # https://www.mysql.com/support/supportedplatforms/database.html def map_compile_os_to_platform(compile_os) - return Msf::Platform::Unknown.realname if compile_os.blank? + return '' if compile_os.blank? compile_os = compile_os.downcase.encode(::Encoding::BINARY) if compile_os.match?('linux') - platform = Msf::Platform::Linux + platform = Msf::Platform::Linux.realname elsif compile_os.match?('unix') - platform = Msf::Platform::Unix + platform = Msf::Platform::Unix.realname elsif compile_os.match?(/(darwin|mac|osx)/) - platform = Msf::Platform::OSX + platform = Msf::Platform::OSX.realname elsif compile_os.match?('win') - platform = Msf::Platform::Windows + platform = Msf::Platform::Windows.realname elsif compile_os.match?('solaris') - platform = Msf::Platform::Solaris + platform = Msf::Platform::Solaris.realname else - platform = Msf::Platform::Unknown + platform = compile_os end - platform.realname + platform end def map_compile_arch_to_architecture(compile_arch) @@ -75,7 +75,7 @@ def map_compile_arch_to_architecture(compile_arch) elsif compile_arch.match?('86') || compile_arch.match?('i686') arch = ARCH_X86 else - arch = '' + arch = compile_arch end arch diff --git a/modules/auxiliary/scanner/mssql/mssql_login.rb b/modules/auxiliary/scanner/mssql/mssql_login.rb index 68c603ee853d..20c854ed3c59 100644 --- a/modules/auxiliary/scanner/mssql/mssql_login.rb +++ b/modules/auxiliary/scanner/mssql/mssql_login.rb @@ -153,7 +153,7 @@ def run_host(ip) def session_setup(result) return unless (result.connection && result.proof) - my_session = Msf::Sessions::MSSQL.new(result.connection, { client: result.proof }) + my_session = Msf::Sessions::MSSQL.new(result.connection, { client: result.proof, **result.proof.detect_platform_and_arch }) merge_me = { '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 406ecf3c831d..75f4764811c2 100644 --- a/spec/lib/msf/base/sessions/mssql_spec.rb +++ b/spec/lib/msf/base/sessions/mssql_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Msf::Sessions::MSSQL do let(:client) { instance_double(Rex::Proto::MSSQL::Client) } - let(:opts) { { client: client } } + let(:opts) { { client: client, platform: Msf::Platform::Linux.realname, arch: ARCH_X86_64 } } let(:console_class) { Rex::Post::MSSQL::Ui::Console } let(:user_input) { instance_double(Rex::Ui::Text::Input::Readline) } let(:user_output) { instance_double(Rex::Ui::Text::Output::Stdio) } 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 37fbf4ff4a36..d24002f8f77c 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 @@ -5,7 +5,7 @@ RSpec.describe Rex::Post::MSSQL::Ui::Console::CommandDispatcher::Core do let(:client) { instance_double(Rex::Proto::MSSQL::Client) } - let(:session) { Msf::Sessions::MSSQL.new(nil, { client: client }) } + let(:session) { Msf::Sessions::MSSQL.new(nil, { client: client, platform: Msf::Platform::Linux.realname, arch: ARCH_X86_64 }) } let(:address) { '192.0.2.1' } let(:port) { '1433' } let(:peer_info) { "#{address}:#{port}" } diff --git a/spec/lib/rex/proto/mssql/client_spec.rb b/spec/lib/rex/proto/mssql/client_spec.rb index 0954eaba10f6..fa68f461e87d 100644 --- a/spec/lib/rex/proto/mssql/client_spec.rb +++ b/spec/lib/rex/proto/mssql/client_spec.rb @@ -18,6 +18,46 @@ it_behaves_like 'session compatible SQL client' + describe '#map_compile_os_to_platform' do + [ + { info: 'linux', expected: Msf::Platform::Linux.realname }, + { info: 'windows', expected: Msf::Platform::Windows.realname }, + { info: 'win', expected: Msf::Platform::Windows.realname }, + ].each do |test| + it "correctly identifies '#{test[:info]}' as '#{test[:expected]}'" do + expect(subject.map_compile_os_to_platform(test[:info])).to eq(test[:expected]) + end + end + end + + describe '#map_compile_arch_to_architecture' do + [ + { info: 'x64', expected: ARCH_X86_64 }, + { info: 'x86', expected: ARCH_X86 }, + { info: '64', expected: ARCH_X86_64 }, + { info: '32-bit', expected: ARCH_X86 }, + ].each do |test| + it "correctly identifies '#{test[:info]}' as '#{test[:expected]}'" do + expect(subject.map_compile_arch_to_architecture(test[:info])).to eq(test[:expected]) + end + end + end + + describe '#detect_platform_and_arch' do + [ + { version: 'Microsoft SQL Server 2022 (RTM-CU12) (KB5033663) - 16.0.4115.5 (X64) Mar 4 2024 08:56:10 Copyright (C) 2022 Microsoft Corporation Developer Edition (64-bit) on Linux (Ubuntu 22.04.4 LTS) ', expected: { arch: 'x86_64', platform: 'Linux' } }, + { version: 'Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64) Oct 8 2022 05:58:25 Copyright (C) 2022 Microsoft Corporation Developer Edition (64-bit) on Windows Server 2022 Standard 10.0 (Build 20348: ) (Hypervisor)', expected: { arch: 'x86_64', platform: 'Windows' } }, + ].each do |test| + context "when the database is version #{test[:version]}" do + it "returns #{test[:expected]}" do + mock_query_result = { rows: [[test[:version]]] } + allow(subject).to receive(:query).with('select @@version').and_return(mock_query_result) + + expect(subject.detect_platform_and_arch).to eq test[:expected] + end + end + end + end describe '#current_database' do context 'we have not selected a database yet' do subject do diff --git a/spec/lib/rex/proto/mysql/client_spec.rb b/spec/lib/rex/proto/mysql/client_spec.rb index 3f81ad179c20..e57d83816b56 100644 --- a/spec/lib/rex/proto/mysql/client_spec.rb +++ b/spec/lib/rex/proto/mysql/client_spec.rb @@ -45,9 +45,9 @@ { info: 'macos', expected: Msf::Platform::OSX.realname }, { info: 'unix', expected: Msf::Platform::Unix.realname }, { info: 'solaris', expected: Msf::Platform::Solaris.realname }, - { info: '', expected: Msf::Platform::Unknown.realname }, - { info: 'blank', expected: Msf::Platform::Unknown.realname }, - { info: nil, expected: Msf::Platform::Unknown.realname }, + { info: '', expected: '' }, + { info: 'blank', expected: 'blank' }, + { info: nil, expected: '' }, ].each do |test| it "correctly identifies '#{test[:info]}' as '#{test[:expected]}'" do expect(subject.map_compile_os_to_platform(test[:info])).to eq(test[:expected]) @@ -69,7 +69,7 @@ { info: 'sparc', expected: ARCH_SPARC }, { info: 'sparc64', expected: ARCH_SPARC64 }, { info: '', expected: '' }, - { info: 'blank', expected: '' }, + { info: 'blank', expected: 'blank' }, { info: nil, expected: '' }, ].each do |test| it "correctly identifies '#{test[:info]}' as '#{test[:expected]}'" do