Skip to content

strongswan-connections plugin connects as root to an unvalidated --socket path, enabling DoS and monitoring-data injection

Moderate
markuslf published GHSA-cw8h-7h72-79v8 Sep 25, 2026

Software

Linuxfabrik/monitoring-plugins

Affected versions

<= 8.0.0

Patched versions

>= 8.0.1

Description

Summary

The strongswan-connections monitoring plugin accepts an arbitrary --socket path from the command line and connects to it as root without any validation. Both shipped sudoers files (assets/sudoers/Debian.sudoers and assets/sudoers/RedHat.sudoers) authorize the monitoring service account to run this plugin via sudo as a bare command path with no argument restriction, so a low-privilege account that runs monitoring checks can point the root process at an attacker-controlled Unix socket. The plugin then creates an AF_UNIX socket and connects to the supplied path, deserializing all VICI protocol data the rogue socket returns. This yields a reliable denial of service against the root-owned check process and lets the attacker inject arbitrary data into the monitoring output.

Root Cause

The plugin defines --socket as an unconstrained string argument and passes its value straight to socket.connect() running as root, with no confinement to the trusted runtime directories where a legitimate VICI socket lives (/run, /var/run). A sibling plugin in the same repository (check-plugins/fail2ban/fail2ban) already treats this exact pattern as a root-code-execution risk and confines its own --socket argument with a realpath containment check; that confinement was never applied to strongswan-connections.

check-plugins/strongswan-connections/strongswan-connections
  - Line 122-128: argparse defines --socket (dest=SOCKET) with no type, choices, or validation.
  - Line 442-444: s = socket.socket(socket.AF_UNIX); s.connect(args.SOCKET)  # runs as root, no path check
  - Line 455-460: vici.Session(s).list_conns()/list_sas() deserialize all data from the socket.
No is_within / realpath / allowlist confinement exists anywhere in the plugin.

Impact

An attacker who holds the low-privilege monitoring service account (the account the shipped sudoers files authorize) can run:

sudo /usr/lib64/nagios/plugins/strongswan-connections --socket /tmp/evil.vici

This redirects the root-owned process to a socket the attacker created in an attacker-writable directory. The demonstrated capabilities are:

  • Denial of service (root process): the plugin sets no socket timeout and imposes no bound on the VICI response length. A rogue socket can hold the connection open to hang the process indefinitely, or send an oversized 4-byte length prefix to drive an unbounded multi-gigabyte accumulation, crashing the root process or the monitoring check.
  • Monitoring data injection: the attacker controls every VICI response field (connection names, states, hosts, encryption parameters, traffic counters) that the plugin formats into its output, so false "all established" results can mask real VPN outages and false "down" results can trigger spurious incident response.

The monitoring account gains control over a root process's data intake and execution lifetime, capabilities it does not otherwise possess. The plausible further risk of code execution through untrusted deserialization (noted by the sibling plugin's own fix rationale) is not demonstrated here and is not relied on for this report.

Proof of Concept

Preconditions:
- A host that deploys one of the shipped sudoers files (assets/sudoers/Debian.sudoers
  or assets/sudoers/RedHat.sudoers), which is the documented deployment mechanism.
- Attacker holds the monitoring service account (e.g. nagios/icinga).

Reproduction steps:

  1. As the monitoring account, create a listening AF_UNIX socket at an attacker-writable path, for example /tmp/evil.vici, that either never sends payload data (to hang) or answers the VICI handshake with a 4-byte big-endian length prefix of 0xFFFFFFFF followed by a data flood (to exhaust memory).
  2. Invoke the plugin through sudo, pointing it at that socket:
sudo /usr/lib64/nagios/plugins/strongswan-connections --socket /tmp/evil.vici
Observed behavior:
- The root process connects to /tmp/evil.vici and enters the VICI receive loop.
- Hang variant: the process blocks in recv() with no timeout until killed by the check timeout.
- Exhaustion variant: the process accumulates unbounded bytes until OOM/MemoryError terminates it.
- Injection variant: any VICI connection/SA data the socket returns is trusted and emitted as the check result.

Attack Chain

  1. Exposure: The shipped sudoers files list /usr/lib64/nagios/plugins/strongswan-connections for the monitoring account as a bare path (line 51 in both Debian.sudoers and RedHat.sudoers). Per sudoers(5), a command without argument specification permits any arguments, so the account can pass an arbitrary --socket value.
  2. Control: The attacker fully controls the --socket string. It is parsed by argparse and used verbatim, with no normalization, allowlist, or realpath containment.
  3. Path: args.SOCKET flows directly from argparse (line 127) to socket.connect(args.SOCKET) (line 444) inside the plugin's main, with no intermediate function or check. The root process then deserializes all VICI data from that socket (lines 455-460).
  4. Guard: No guard exists. An exhaustive search of the plugin for is_within, realpath, allowlist roots, and validation/sanitization patterns returns nothing; the sibling fail2ban plugin's confinement was not propagated here.
  5. Primitive: A root-owned process connects to and trusts an attacker-controlled Unix socket, with no receive timeout and no response-length bound.
  6. Result: Reliable denial of service of the root monitoring process (hang or memory exhaustion) and injection of arbitrary attacker-chosen data into the monitoring output.

Bypass Evidence

The relevant guard is per-plugin socket-path confinement. The sibling check-plugins/fail2ban/fail2ban plugin implements it (ALLOWED_SOCKET_ROOTS = ('/run', '/var/run') at line 33; lib.disk.is_within(os.path.realpath(args.SOCKET), ALLOWED_SOCKET_ROOTS) at line 169, rejecting paths that resolve outside the runtime directories). This guard is entirely contained in the fail2ban file and does not touch strongswan-connections, which has no os import, no realpath call, no allowlist, and no is_within call.

Disproof attempts and outcomes:

  • Precondition equivalence ("the monitoring account is already privileged"): rejected. Without this flaw the account can only run the plugin against the legitimate socket; with it, the account can crash a root process on demand and control all data that process trusts, a genuine capability delta.
  • Duplicate of the seed advisory (CVE-2026-52817, apt-get argument injection): rejected. Different plugin, different sink (Unix socket connect versus apt-get Pre-Invoke shell), different weakness class, and a different fix. Restricting apt-get arguments does not confine this plugin's socket path.
  • Availability claim depends on an unverifiable third-party library: rejected. The absence of any socket timeout in the plugin's own code independently produces an indefinite hang, and the pinned dependency version (verified in lockfiles/py311/requirements.txt) confirms the unbounded receive path as well.

Affected Versions

  • Ecosystem: other (GitHub release distribution)
  • Package: Linuxfabrik/monitoring-plugins
  • Confirmed affected range: <= 8.0.0
  • Fix status: fixed in v8.0.1

The plugin ships since release 2023051201 and is authorized by the shipped sudoers files in every release since, so every release up to and including v8.0.0 is affected. v8.0.1 accepts --socket only within the system runtime directories and only if nobody but root can replace the socket or a directory above it, connects to the resolved path, and stops waiting after --timeout seconds with a WARN (monitoring-plugins commit c68d48d). The plugin does not bound the VICI length prefix itself; with the socket confined, only charon can send it.

Suggested Fix

Enforce that the resolved socket path stays within the trusted runtime directories before connecting, applying the same confinement the fail2ban plugin already uses: resolve --socket with os.path.realpath and reject it unless lib.disk.is_within confirms it lies under an allowlist such as ('/run', '/var/run'). Additionally set an explicit receive timeout and enforce the VICI transport's MAX_SEGMENT bound so a rogue or slow socket cannot hang or exhaust the process. As an interim mitigation without a code change, operators can pin the socket argument in the sudoers entry (for example authorizing only strongswan-connections --socket /run/strongswan/charon.vici) so an arbitrary path can no longer be supplied.

Reported by zx (GitHub: @manus-pi).

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
Low
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H

CVE ID

No known CVE

Weaknesses

Absolute Path Traversal

The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize absolute path sequences such as /abs/path that can resolve to a location that is outside of that directory. Learn more on MITRE.

Credits