From a862b1628609b702b235f7e996e050e0a1dc05ea Mon Sep 17 00:00:00 2001 From: sjanusz-r7 Date: Thu, 28 Mar 2024 12:30:34 +0000 Subject: [PATCH] Add MySQL Arch & Platform detection by query --- lib/msf/base/sessions/mysql.rb | 2 + lib/rex/proto/mysql/client.rb | 61 +++++++++++++++++++ .../auxiliary/scanner/mysql/mysql_login.rb | 2 +- spec/lib/msf/base/sessions/mysql_spec.rb | 2 +- .../console/command_dispatcher/core_spec.rb | 2 +- spec/lib/rex/proto/mysql/client_spec.rb | 45 ++++++++++++++ 6 files changed, 111 insertions(+), 3 deletions(-) diff --git a/lib/msf/base/sessions/mysql.rb b/lib/msf/base/sessions/mysql.rb index 13353c4192f3..c3268772c576 100644 --- a/lib/msf/base/sessions/mysql.rb +++ b/lib/msf/base/sessions/mysql.rb @@ -8,6 +8,8 @@ class Msf::Sessions::MySQL < Msf::Sessions::Sql # @param [Hash] opts def initialize(rstream, opts = {}) @client = opts.fetch(:client) + self.platform = opts.fetch(:platform) + self.arch = opts.fetch(:arch) self.console = ::Rex::Post::MySQL::Ui::Console.new(self) super(rstream, opts) end diff --git a/lib/rex/proto/mysql/client.rb b/lib/rex/proto/mysql/client.rb index 31ac3311e927..fc31d82d2d89 100644 --- a/lib/rex/proto/mysql/client.rb +++ b/lib/rex/proto/mysql/client.rb @@ -28,6 +28,67 @@ def current_database # Current database is stored as an array under the type 1 key. session_track.fetch(1, ['']).first end + + # 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? + + compile_os = compile_os.downcase.encode(::Encoding::BINARY) + + if compile_os.match?('linux') + platform = Msf::Platform::Linux + elsif compile_os.match?('unix') + platform = Msf::Platform::Unix + elsif compile_os.match?(/(darwin|mac|osx)/) + platform = Msf::Platform::OSX + elsif compile_os.match?('win') + platform = Msf::Platform::Windows + elsif compile_os.match?('solaris') + platform = Msf::Platform::Solaris + else + platform = Msf::Platform::Unknown + end + + platform.realname + end + + 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.match?('arm') + arch = ARCH_AARCH64 + elsif compile_arch.match?('64') + arch = ARCH_X86_64 + elsif compile_arch.match?('86') || compile_arch.match?('i686') + arch = ARCH_X86 + else + arch = '' + end + + arch + end + + # @return [Hash] Detect the platform and architecture of the MySQL server: + # * :arch [String] The server architecture. + # * :platform [String] The server platform. + def detect_platform_and_arch + result = {} + + server_vars = query("show variables where variable_name in ('version_compile_machine', 'version_compile_os')").entries.to_h + 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 end end end diff --git a/modules/auxiliary/scanner/mysql/mysql_login.rb b/modules/auxiliary/scanner/mysql/mysql_login.rb index 0c9486965b9c..ccaf698ae794 100644 --- a/modules/auxiliary/scanner/mysql/mysql_login.rb +++ b/modules/auxiliary/scanner/mysql/mysql_login.rb @@ -198,7 +198,7 @@ def int_version(str) def session_setup(result) return unless (result.connection && result.proof) - my_session = Msf::Sessions::MySQL.new(result.connection, { client: result.proof }) + my_session = Msf::Sessions::MySQL.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/mysql_spec.rb b/spec/lib/msf/base/sessions/mysql_spec.rb index 48a46ecc02a6..8ec31d2a50c9 100644 --- a/spec/lib/msf/base/sessions/mysql_spec.rb +++ b/spec/lib/msf/base/sessions/mysql_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Msf::Sessions::MySQL do let(:client) { instance_double(::Rex::Proto::MySQL::Client) } - let(:opts) { { client: client } } + let(:opts) { { client: client, platform: Msf::Platform::Linux.realname, arch: ARCH_X86_64 } } let(:console_class) { Rex::Post::MySQL::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/mysql/ui/console/command_dispatcher/core_spec.rb b/spec/lib/rex/post/mysql/ui/console/command_dispatcher/core_spec.rb index bb389ed81ab4..1bd28eeb9144 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 @@ -10,7 +10,7 @@ let(:address) { '192.0.2.1' } let(:port) { '3306' } let(:peerinfo) { "#{address}:#{port}" } - let(:session) { Msf::Sessions::MySQL.new(nil, { client: client }) } + let(:session) { Msf::Sessions::MySQL.new(nil, { client: client, platform: Msf::Platform::Linux.realname, arch: ARCH_X86_64 }) } let(:console) do console = Rex::Post::MySQL::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 9813ef863164..d1e534bf1ede 100644 --- a/spec/lib/rex/proto/mysql/client_spec.rb +++ b/spec/lib/rex/proto/mysql/client_spec.rb @@ -32,4 +32,49 @@ end end end + + 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: '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 }, + ].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_AARCH64 }, + { info: 'sparc', expected: ARCH_SPARC }, + { info: 'sparc64', expected: ARCH_SPARC64 }, + { info: '', expected: '' }, + { info: 'blank', expected: '' }, + { info: nil, expected: '' }, + ].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 end