diff --git a/lib/msf/base/sessions/postgresql.rb b/lib/msf/base/sessions/postgresql.rb index 37c72ed8b1e4..984a02743dec 100644 --- a/lib/msf/base/sessions/postgresql.rb +++ b/lib/msf/base/sessions/postgresql.rb @@ -9,6 +9,8 @@ class Msf::Sessions::PostgreSQL < Msf::Sessions::Sql # @param opts [Msf::Db::PostgresPR::Connection] :client def initialize(rstream, opts = {}) @client = opts.fetch(:client) + self.platform = opts.fetch(:platform) + self.arch = opts.fetch(:arch) @console = ::Rex::Post::PostgreSQL::Ui::Console.new(self) super(rstream, opts) end diff --git a/lib/postgres/postgres-pr/connection.rb b/lib/postgres/postgres-pr/connection.rb index c894e291d181..8047c438c291 100644 --- a/lib/postgres/postgres-pr/connection.rb +++ b/lib/postgres/postgres-pr/connection.rb @@ -129,12 +129,100 @@ def peerport @conn.peerport end + def peerinfo + "#{peerhost}:#{peerport}" + end + def current_database @params['database'] end - def peerinfo - "#{peerhost}:#{peerport}" + # List of supported PostgreSQL platforms & architectures: + # https://postgrespro.com/docs/postgresql/16/supported-platforms + def map_compile_os_to_platform(compile_os) + return Msf::Platform::Unknown.realname if compile_os.blank? + + compile_os = compile_os.downcase.encode(::Encoding::BINARY) + + if compile_os.match?('linux') + platform = Msf::Platform::Linux + elsif compile_os.match?(/(darwin|mac|osx)/) + platform = Msf::Platform::OSX + elsif compile_os.match?('win') + platform = Msf::Platform::Windows + elsif compile_os.match?('free') + platform = Msf::Platform::FreeBSD + elsif compile_os.match?('net') + platform = Msf::Platform::NetBSD + elsif compile_os.match?('open') + platform = Msf::Platform::OpenBSD + elsif compile_os.match?('solaris') + platform = Msf::Platform::Solaris + elsif compile_os.match?('aix') + platform = Msf::Platform::AIX + elsif compile_os.match?('hpux') + platform = Msf::Platform::HPUX + elsif compile_os.match?('irix') + platform = Msf::Platform::Irix + else + # Return the query result if the value can't be mapped + return compile_os + end + + platform.realname + end + + # List of supported PostgreSQL platforms & architectures: + # https://postgrespro.com/docs/postgresql/16/supported-platforms + def map_compile_arch_to_architecture(compile_arch) + return '' if compile_arch.blank? + + compile_arch = compile_arch.downcase.encode(::Encoding::BINARY) + + if compile_arch.match?('sparc') + if compile_arch.include?('64') + arch = ARCH_SPARC64 + else + arch = ARCH_SPARC + end + elsif compile_arch.include?('mips') + arch = ARCH_MIPS + elsif compile_arch.include?('ppc') + arch = ARCH_PPC + elsif compile_arch.match?('arm') + if compile_arch.match?('64') + arch = ARCH_AARCH64 + elsif compile_arch.match?('arm') + arch = ARCH_ARMLE + end + elsif compile_arch.match?('64') + arch = ARCH_X86_64 + elsif compile_arch.match?('86') || compile_arch.match?('i686') + arch = ARCH_X86 + else + # Return the query result if the value can't be mapped + arch = compile_arch + end + + arch + end + + # @return [Hash] Detect the platform and architecture of the PostgreSQL server: + # * :arch [String] The server architecture. + # * :platform [String] The server platform. + def detect_platform_and_arch + result = {} + + query_result = query('select version()').rows.join.match(/on (?\w+)-\w+-(?\w+)/) + server_vars = { + 'version_compile_machine' => query_result[:architecture], + 'version_compile_os' => query_result[:platform] + } + + result[:arch] = map_compile_arch_to_architecture(server_vars['version_compile_machine']) + result[:platform] = map_compile_os_to_platform(server_vars['version_compile_os']) + + result end def close diff --git a/lib/rex/proto/mysql/client.rb b/lib/rex/proto/mysql/client.rb index fc31d82d2d89..be543ddd3eab 100644 --- a/lib/rex/proto/mysql/client.rb +++ b/lib/rex/proto/mysql/client.rb @@ -65,7 +65,11 @@ def map_compile_arch_to_architecture(compile_arch) arch = ARCH_SPARC end elsif compile_arch.match?('arm') - arch = ARCH_AARCH64 + if compile_arch.match?('64') + arch = ARCH_AARCH64 + elsif compile_arch.match?('arm') + arch = ARCH_ARMLE + end elsif compile_arch.match?('64') arch = ARCH_X86_64 elsif compile_arch.match?('86') || compile_arch.match?('i686') diff --git a/modules/auxiliary/scanner/postgres/postgres_login.rb b/modules/auxiliary/scanner/postgres/postgres_login.rb index 644e6bdbd3cb..a7f9cdd225fb 100644 --- a/modules/auxiliary/scanner/postgres/postgres_login.rb +++ b/modules/auxiliary/scanner/postgres/postgres_login.rb @@ -147,7 +147,7 @@ def rport def session_setup(result) return unless (result.connection && result.proof) - my_session = Msf::Sessions::PostgreSQL.new(result.connection, { client: result.proof }) + my_session = Msf::Sessions::PostgreSQL.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/postgresql_spec.rb b/spec/lib/msf/base/sessions/postgresql_spec.rb index e655bc7a4c90..96b7c246079d 100644 --- a/spec/lib/msf/base/sessions/postgresql_spec.rb +++ b/spec/lib/msf/base/sessions/postgresql_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Msf::Sessions::PostgreSQL do let(:client) { instance_double(Msf::Db::PostgresPR::Connection) } - let(:opts) { { client: client } } + let(:opts) { { client: client, platform: Msf::Platform::Linux.realname, arch: ARCH_X86_64 } } let(:console_class) { Rex::Post::PostgreSQL::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/postgresql/ui/console/command_dispatcher/core_spec.rb b/spec/lib/rex/post/postgresql/ui/console/command_dispatcher/core_spec.rb index 6e2f051f7dc3..dc47c356ebb8 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 @@ -10,7 +10,7 @@ let(:port) { '5432' } let(:current_database) { 'template1' } let(:peer_info) { "#{address}:#{port}" } - let(:session) { Msf::Sessions::PostgreSQL.new(nil, { client: client }) } + let(:session) { Msf::Sessions::PostgreSQL.new(nil, { client: client, platform: Msf::Platform::Linux.realname, arch: ARCH_X86_64 }) } let(:console) do console = Rex::Post::PostgreSQL::Ui::Console.new(session) console.disable_output = true diff --git a/spec/lib/rex/proto/mysql/client_spec.rb b/spec/lib/rex/proto/mysql/client_spec.rb index d1e534bf1ede..3f81ad179c20 100644 --- a/spec/lib/rex/proto/mysql/client_spec.rb +++ b/spec/lib/rex/proto/mysql/client_spec.rb @@ -65,7 +65,7 @@ { info: '86', expected: ARCH_X86 }, { info: 'i686', expected: ARCH_X86 }, { info: 'arm64', expected: ARCH_AARCH64 }, - { info: 'arm', expected: ARCH_AARCH64 }, + { info: 'arm', expected: ARCH_ARMLE }, { info: 'sparc', expected: ARCH_SPARC }, { info: 'sparc64', expected: ARCH_SPARC64 }, { info: '', expected: '' }, diff --git a/spec/lib/rex/proto/postgresql/client_spec.rb b/spec/lib/rex/proto/postgresql/client_spec.rb index 90d6847ca1df..e7f4a2faeef4 100644 --- a/spec/lib/rex/proto/postgresql/client_spec.rb +++ b/spec/lib/rex/proto/postgresql/client_spec.rb @@ -20,4 +20,64 @@ end it_behaves_like 'session compatible SQL client' + + describe '#map_compile_os_to_platform' do + [ + { info: 'linux', expected: Msf::Platform::Linux.realname }, + { info: 'linux2.6', expected: Msf::Platform::Linux.realname }, + { info: 'debian-linux-gnu', expected: Msf::Platform::Linux.realname }, + { info: 'win', expected: Msf::Platform::Windows.realname }, + { info: 'windows', expected: Msf::Platform::Windows.realname }, + { info: 'darwin', expected: Msf::Platform::OSX.realname }, + { info: 'osx', expected: Msf::Platform::OSX.realname }, + { info: 'macos', expected: Msf::Platform::OSX.realname }, + { info: 'solaris', expected: Msf::Platform::Solaris.realname }, + { info: 'aix', expected: Msf::Platform::AIX.realname }, + { info: 'hpux', expected: Msf::Platform::HPUX.realname }, + { info: 'irix', expected: Msf::Platform::Irix.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: 'x86_64', expected: ARCH_X86_64 }, + { info: 'x86_x64', expected: ARCH_X86_64 }, + { info: 'x64', expected: ARCH_X86_64 }, + { info: '64', expected: ARCH_X86_64 }, + { info: 'x86', expected: ARCH_X86 }, + { info: '86', expected: ARCH_X86 }, + { info: 'i686', expected: ARCH_X86 }, + { info: 'arm64', expected: ARCH_AARCH64 }, + { info: 'arm', expected: ARCH_ARMLE }, + { info: 'sparc', expected: ARCH_SPARC }, + { info: 'sparc64', expected: ARCH_SPARC64 }, + { info: 'ppc', expected: ARCH_PPC }, + { info: 'mips', expected: ARCH_MIPS }, + ].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: 'PostgreSQL 9.4.26 on x86_64-pc-linux-gnu (Debian 9.4.26-1.pgdg90+1), compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit', expected: { arch: 'x86_64', platform: 'Linux' } }, + { version: 'PostgreSQL 14.11 (Debian 14.11-1.pgdg120+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit', expected: { arch: 'x86_64', platform: 'Linux' } }, + { version: 'PostgreSQL 14.11 (Homebrew) on x86_64-apple-darwin22.6.0, compiled by Apple clang version 15.0.0 (clang-1500.1.0.2.5), 64-bit', expected: { arch: 'x86_64', platform: 'OSX' } } + ].each do |test| + context "when the database is version #{test[:version]}" do + it "returns #{test[:expected]}" do + mock_query_result = instance_double Msf::Db::PostgresPR::Connection::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 end