Vulnerability Description: CWE-78: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Software Version: 7.1.3
CVSS: 7.8
Severity: High
NVD: https://nvd.nist.gov/vuln/detail/cve-2026-16524
Credits: Francisco Alisson Bezerra, Lucas Gabriel Alves, Massimiliano Brolli
A command injection vulnerability exists in the linux_sockets PMDA through the writable metric network.persocket.filter. The validation helper sockets_check_filter() returns 1 for safe input and 0 for unsafe input, but the store handler uses an inverted condition: safe expressions are rejected while malicious ones containing shell metacharacters are silently accepted. The attacker-controlled filter value is later incorporated into a shell command executed via popen() whenever socket metrics are refreshed, enabling arbitrary command execution as the PMDA process user.
Prerequisites:
Observation: pmcd (TCP 44321) binds to localhost only by default (PMCD_LOCAL=1 in /etc/default/pmcd on Debian/Ubuntu), limiting direct exploitation to local access. However, pmproxy (TCP 44322) binds to all interfaces by default (PMPROXY_LOCAL unset) and exposes an unauthenticated HTTP REST API that internally calls pmStore()/pmFetch(), enabling full remote exploitation without any local access.
Step-by-step instructions and PoC
The linux_sockets PMDA collects socket-level metrics by spawning the system ss utility and parsing its output. It exposes the writable metric network.persocket.filter that allows operators to set BPF filter expressions passed without sanitization to ss. The vulnerability originates from a logic inversion in the store handler: sockets_check_filter() returns 1 for safe input and 0 for input containing shell metacharacters. The guard in sockets_store() tests if (sockets_check_filter(av.cp)), which causes safe input to be rejected and malicious input to pass unchecked into popen(). Because popen() invokes /bin/sh, any shell metacharacter embedded in the filter is executed as an OS command with the privileges of the PMDA process (typically user pcp).
Affected Source Files and Lines
src/pmdas/linux_sockets/pmda.c:160-176: sockets_check_filter() - correct logic, safe=1 / unsafe=0
src/pmdas/linux_sockets/pmda.c:188-200: sockets_store() - inverted guard: line 194 is the root cause
src/pmdas/linux_sockets/ss_stream.c:43-55: ss_open_stream() - popen() execution sink at lines 48-49
Image 1 - sockets_check_filter() (pmda.c:160). Returns 1 for safe input and 0 for metacharacters - correct logic.
Image 2 - Inverted guard in sockets_store() (pmda.c:194, highlighted). Safe input is rejected; payload with metacharacters is accepted.
Image 3 - Execution sink in ss_open_stream() (ss_stream.c:48-49). Attacker-controlled ss_filter is passed directly to popen().
Affected Endpoints
pmcd TCP 44321 (localhost only by default): network.persocket.filter via pmstore
pmproxy TCP 44322 (all interfaces by default): POST /pmapi/{ctx}/store - no authentication
The proof-of-concept (poc.py) sends three unauthenticated HTTP requests to pmproxy (port 44322): it opens a context via GET /pmapi/context, stores a shell metacharacter payload (;id>/tmp/poc_rce_sockets.txt;echo) to network.persocket.filter via POST, then fetches network.persocket.netid to trigger execution. The linux_sockets PMDA passes the filter value verbatim to popen(), running the injected command as user pcp. Successful execution is confirmed by the output file created on the target.
Image 4 - Files in temp folder before the exploit execution.
Image 5 - Execution of the proof-of-concept exploit.
Security Impact