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:
- 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).
- 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
- 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.
- Control: The attacker fully controls the
--socket string. It is parsed by argparse and used verbatim, with no normalization, allowlist, or realpath containment.
- 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).
- 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.
- Primitive: A root-owned process connects to and trusts an attacker-controlled Unix socket, with no receive timeout and no response-length bound.
- 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).
Summary
The
strongswan-connectionsmonitoring plugin accepts an arbitrary--socketpath from the command line and connects to it as root without any validation. Both shipped sudoers files (assets/sudoers/Debian.sudoersandassets/sudoers/RedHat.sudoers) authorize the monitoring service account to run this plugin viasudoas 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 anAF_UNIXsocket 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
--socketas an unconstrained string argument and passes its value straight tosocket.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--socketargument with a realpath containment check; that confinement was never applied tostrongswan-connections.Impact
An attacker who holds the low-privilege monitoring service account (the account the shipped sudoers files authorize) can run:
This redirects the root-owned process to a socket the attacker created in an attacker-writable directory. The demonstrated capabilities are:
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
Reproduction steps:
AF_UNIXsocket 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 of0xFFFFFFFFfollowed by a data flood (to exhaust memory).Attack Chain
/usr/lib64/nagios/plugins/strongswan-connectionsfor the monitoring account as a bare path (line 51 in bothDebian.sudoersandRedHat.sudoers). Per sudoers(5), a command without argument specification permits any arguments, so the account can pass an arbitrary--socketvalue.--socketstring. It is parsed by argparse and used verbatim, with no normalization, allowlist, or realpath containment.args.SOCKETflows directly from argparse (line 127) tosocket.connect(args.SOCKET)(line 444) inside the plugin'smain, with no intermediate function or check. The root process then deserializes all VICI data from that socket (lines 455-460).is_within,realpath, allowlist roots, and validation/sanitization patterns returns nothing; the siblingfail2banplugin's confinement was not propagated here.Bypass Evidence
The relevant guard is per-plugin socket-path confinement. The sibling
check-plugins/fail2ban/fail2banplugin 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 touchstrongswan-connections, which has noosimport, norealpathcall, no allowlist, and nois_withincall.Disproof attempts and outcomes:
lockfiles/py311/requirements.txt) confirms the unbounded receive path as well.Affected Versions
Ecosystem: other (GitHub release distribution)Package: Linuxfabrik/monitoring-pluginsConfirmed affected range: <= 8.0.0Fix status: fixed in v8.0.1The 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
--socketonly 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--timeoutseconds 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
fail2banplugin already uses: resolve--socketwithos.path.realpathand reject it unlesslib.disk.is_withinconfirms it lies under an allowlist such as('/run', '/var/run'). Additionally set an explicit receive timeout and enforce the VICI transport'sMAX_SEGMENTbound 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 onlystrongswan-connections --socket /run/strongswan/charon.vici) so an arbitrary path can no longer be supplied.Reported by zx (GitHub: @manus-pi).