CVE-2026-16526 - Performance Co-Pilot

CVE-2026-16526 - Performance Co-Pilot (PCP)

Vulnerability Description: CWE-403: Exposure of File Descriptor to Unintended Control Sphere ('File Descriptor Leak')

Software Version: 7.1.3

CVSS: 8.8

Severity:  High

NVD: https://nvd.nist.gov/vuln/detail/cve-2026-16526

Credits: Francisco Alisson Bezerra, Lucas Gabriel Alves, Massimiliano Brolli

When the linux_sockets PMDA is loaded as a DSO inside PMCD, code execution can be escalated from user pcp to root. The pmdarootfd Unix socket connection to pmdaroot is created without O_CLOEXEC, causing all child processes spawned by popen() to inherit it. pmdaroot runs as root and processes PDUROOT_STARTPMDA_REQ without per-request authentication, executing attacker-controlled arguments via execvp() as root.

Prerequisites:

  • linux_sockets command injection required for initial code execution - exploitable either locally via pmcd (TCP 44321, localhost only by default) or remotely via the pmproxy REST API (TCP 44322, all interfaces by default, no authentication required). linux_sockets
  • PMDA loaded as DSO in PMCD (requires explicit configuration in pmcd.conf; default is daemon mode).
  • ss binary present.

Image 1 - PMDA sockets configured as DSO in pmcd.conf.

Click to Enlarge

Step-by-step Instructions and PoC

When the linux_sockets PMDA is loaded as a DSO inside PMCD, code execution can be escalated to root. The underlying condition is that the pmdarootfd Unix socket - the connection between PMCD and the pmdaroot daemon (which runs as root) - is created without the O_CLOEXEC / FD_CLOEXEC flag.

The __pmInitSocket() function in auxconnect.c applies FD_CLOEXEC via __pmConnectRestoreFlags() only for TCP connections. For AF_UNIX sockets, the function returns early (line 517) before reaching the flag-setting code. As a result, every child process spawned by popen() in the PMDA inherits the open file descriptor. An exploit running inside the popen() child can identify the inherited fd by scanning /proc/self/fd, then send a PDUROOT_STARTPMDA_REQ PDU with attacker-controlled arguments. pmdaroot processes this request without per-operation authentication and executes the arguments via fork() + execvp() as root.

Since Vulnerability 1 is exploitable remotely via the pmproxy REST API (TCP 44322, no authentication, exposed on all interfaces by default), this full escalation chain - from unauthenticated remote attacker to root - is achievable without any local access when pmproxy is running, which represents the default configuration of networked PCP installations.

Affected Source Files and Lines

src/libpcp/src/auxconnect.c:514-525: __pmInitSocket() - early return at line 517 skips FD_CLOEXEC for AF_UNIX

src/libpcp_pmda/src/root.c: pmdaRootConnect() - fd returned without FD_CLOEXEC applied

Image 2 - __pmInitSocket() (auxconnect.c:517, highlighted). The early return for AF_UNIX skips all subsequent socket hardening.

Click to Enlarge

Affected Endpoints

Unix socket (inherited fd): /var/lib/pcp/pmcd/root.socket

pmproxy TCP 44322 (all interfaces, no auth): Initial access via POST /pmapi/{ctx}/store 

The proof-of-concept (rce_remote.py) chains three vulnerabilities into a full remote-to-root exploit: it creates an unauthenticated pmproxy context, injects a base64-encoded Python stage-2 payload into network.persocket.filter, then triggers its execution via a fetch request. The stage-2 payload, running as user pcp, enumerates inherited file descriptors, locates the open pmdaroot Unix socket leaked without FD_CLOEXEC, and sends a PDUROOT_STARTPMDA_REQ PDU to execute an arbitrary shell command as root.

The exploit chains Vulnerability CVE-2026-16524 to achieve root. The initial code execution can be triggered locally (pmcd, TCP 44321, localhost only) or remotely (pmproxy REST API, TCP 44322, all interfaces, no authentication). Step 1 plants the payload via the inverted guard - either via pmstore locally or via POST /pmapi/{ctx}/store remotely. Step 2 triggers popen(); the child inherits pmdarootfd and sends a privileged PDU:

Image 3 - start netcat listener to wait for connection.

Click to Enlarge

Image 4 - run the remote exploit that will spawn a shell at our listener.

Click to Enlarge

Image 5 - connection received from the victim.

Click to Enlarge

Security Impact

Full privilege escalation from user pcp to root in a single HTTP request to pmproxy when combined with CVE-2026-16527 (missing authentication in pmproxy REST API), or via two local commands (pmstore + pminfo).