GHSA-rh9c-rqvg-f7pr: Path Traversal

Published Aug 18, 2026
·
Updated

Summary Every Linuxfabrik check plugin that supports the shared --test argument (routed through lib.lftest.test()) will, when --test is supplied, treat the first CSV element as a filesystem path and read its full contents as the plugin's simulated STDOUT — running as root when the plugin is invoked through the shipped nagios/icinga sudoers allowlist. --test is a live production argument (centrally mapped to argparse.SUPPRESS, so it is hidden from --help but still accepted on the command line), not a build-time-only gate. This yields an arbitrary root file-read primitive (full disclosure on deb-updates; filtered disclosure / existence-and-readability oracle on ~22 other whitelisted plugins), i.e. local privilege escalation from the nagios account to root.

Root Cause - lib.lftest.test(args) (lftest.py lines 659-664): stdout = args[0]; if stdout and os.path.isfile(stdout): , stdout = disk.readfile(stdout). Element[1] (stderr channel) is read the same way. There is no path confinement on the supplied path. - check-plugins/deb-updates/deb-updates: --test is registered with type=lib.args.csv (lines 78-82). When supplied, control flows to stdout, , retc = lib.lftest.test(args.TEST) (line 143), bypassing the apt path (if args.TEST is None: at 121). Each returned line is stored as a package row and, under the default --query='1' (WHERE 1, matches all rows), every row is printed via '\n '.join([row['package'] ...]) → lib.base.oao(...). - The same --test/lib.lftest.test() mechanism exists identically on ~22 whitelisted plugins (e.g. docker-info), each performing a root open()/read of the attacker-named path. Disclosure degree varies by each plugin's downstream parser: full (deb-updates), filtered (docker-info echoes lines containing warning:/error:; openvpn-client-list echoes CLIENTLIST lines), or existence/readability oracle (JSON parsers).

Impact An attacker controlling the low-privilege nagios/icinga account (the documented threat model for the shipped sudoers file — same precondition as CVE-2026-52817) obtains the full contents of any root-readable file via deb-updates (e.g. /etc/shadow, /root/.ssh/id, TLS keys, cloud credentials), plus a fleet-wide root file existence/readability oracle and filtered content leak via the other plugins → local privilege escalation to root.

Proof of Concept Full disclosure (deb-updates): sudo /usr/lib64/nagios/plugins/deb-updates --test=/etc/shadow,,0 Filtered disclosure / oracle (docker-info, target routed to the stderr channel that gets echoed): sudo /usr/lib64/nagios/plugins/docker-info --test="dummy,/etc/shadow,0"

Attack Chain 1. Entry: sudo /usr/lib64/nagios/plugins/deb-updates --test=/etc/shadow,,0 - Action: the nagios user invokes the whitelisted plugin as root with a --test CSV whose element[0] is the target path and retc=0. - Guard: sudoers (Debian.sudoers:3) lists the binary only; --test is not gated to test builds. - Bypass proof: CONTRIBUTING.md documents --test as centrally mapped to argparse.SUPPRESS — hidden from --help but still accepted on the command line; lib.args.csv splits /etc/shadow,,0 into ['/etc/shadow','','0']. 2. Sink: lib.lftest.test(args.TEST) (deb-updates:143) reads element[0] as a file, as root. - Guard: none — no path confinement on element[0]. - Bypass proof (from lib source): lftest.py:661-664: stdout = args[0]; if stdout and os.path.isfile(stdout): , stdout = disk.readfile(stdout) — element[0], if it exists on disk, is opened and its contents returned as stdout. retc=0 (element[2]) so there is no early cu() abort. 3. Store + query: each line → lib.dbsqlite.insert(conn, {'package': item}, ...); default QUERY='1' → SELECT FROM debupdates WHERE 1. - Guard: --only-critical or a restrictive --query would filter, but both default to permissive (ONLYCRITICAL=False, QUERY='1'). - Bypass proof: attacker passes neither → all rows selected. 4. Disclosure: msg += '\n '.join([row['package'] for row in result]) → lib.base.oao(...) → stdout. - Guard: none. - Bypass proof: with len(result) > 0 the branch prints every row (every file line). 5. Impact: full contents of any root-readable file disclosed to the nagios user → root. On the ~22 other --test plugins the same primitive yields a filtered leak / universal root file existence-and-readability oracle.

Bypass Evidence - lib.lftest.test() file-read behavior verified directly from linuxfabrik-lib source (lftest.py:659-664, disk.readfile(stdout) when os.path.isfile(stdout)). - --test registration (type=lib.args.csv) and the stdout, , retc = lib.lftest.test(args.TEST) call verified on the latest release tag v6.0.0 at check-plugins/deb-updates/deb-updates:143 (GitHub contents API); default QUERY='1' confirmed. - No path-confinement guard exists on the --test path element in either the plugin or lib.lftest.

Affected Versions <= 6.0.0 (latest release; --test/lib.lftest.test() flow present on tag v6.0.0). Not covered by any existing advisory (none reference --test or arbitrary file read).

Suggested Fix Compile --test out of production builds (or gate it behind an explicit build/dev flag so it is not accepted at runtime), OR confine the --test path element(s) to a dedicated fixtures directory via realpath() + containment check before disk.readfile(). As defense-in-depth, constrain the sudoers entries to specific argument values so --test cannot be supplied to a root-run plugin.

--- Reported by zx (Jace)

Affected Software

1 affected componentFixes available
pip/linuxfabrik-lib<6.1.0
6.1.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/linuxfabrik-lib to a version that resolves this vulnerability.

    Fixed in 6.1.0
  2. Upgrade

    Upgrade check-plugins/deb-updates/deb-updates to a version that resolves this vulnerability.

    Fixed in <= 6.0.0
  3. Configuration

    Constrain the shipped sudoers entries so the whitelisted Nagios/Icinga plugins cannot be executed with the runtime "--test" argument (deny or restrict allowed argument values so "--test" cannot be supplied to a root-run plugin).

    sudoers (nagios/icinga allowlist) --test argument gating = not gated (currently)
  4. Configuration

    Add path confinement to the --test path element used by lib.lftest.test(): before disk.read_file() opens args[0], resolve it (e.g., realpath) and enforce it must be inside a dedicated fixtures directory; otherwise reject/abort the test execution.

    lib.lftest.test() (lftest.py) path handling for args[0] = no confinement (currently uses os.path.isfile and disk.read_file(stdout) directly)

Event History

Aug 18, 2026
Advisory Published
via GitHub·09:18 PM
Data Sourced
via GitHub·09:18 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Who is realistically exposed to this issue?

A local attacker needs access to the nagios account and the ability to invoke an affected plugin through the shipped nagios/icinga sudoers allowlist. The issue is local-only; the supplied vector has AV:L and PR:L.

2

What attacker-controlled input enables the file read?

The --test argument remains accepted in production even though it is hidden from --help. On affected plugins, its first CSV element is handled as a filesystem path, and the file contents are used as simulated STDOUT without path confinement.

3

Does the impact differ between affected plugins?

When deb-updates is run through the allowlist, the issue permits full disclosure of root-readable files. For approximately 22 other whitelisted plugins, disclosure is filtered, but the behavior can still reveal whether a file exists and is readable.

4

How can I determine whether a deployment is exposed?

Systems are affected where a plugin supporting the shared --test argument is callable as root through the shipped nagios/icinga sudoers allowlist. Check whether the local nagios account can invoke such a whitelisted plugin and supply --test, including deb-updates.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203