Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
MEDIUM severity

GHSA-rh9c-rqvg-f7pr linuxfabrik-lib

MEDIUMFix: Linuxfabrik/lib@d665042

GHSA-rh9c-rqvg-f7pr is a medium-severity (CVSS 5.5) Path Traversal vulnerability in linuxfabrik-lib. A fix is available for linuxfabrik-lib — see the affected versions and patch details below.

linuxfabrik-lib: Arbitrary root file read via live --test argument (lib.lftest) across sudoers-whitelisted plugins (LPE)

Also known asCVE-2026-73974PYSEC-2026-3682
Published
Aug 18, 2026
Updated
Aug 19, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 20, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

EPSS Exploitation Probability

via FIRST.org ↗
0.2%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs10th percentile — riskier than 10% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-rh9c-rqvg-f7pr plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,333 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐍linuxfabrik-lib

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

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.read_file(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 CLIENT_LIST 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.read_file(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.db_sqlite.insert(conn, {'package': item}, ...); default QUERY='1'SELECT * FROM deb_updates WHERE 1.
    • Guard: --only-critical or a restrictive --query would filter, but both default to permissive (ONLY_CRITICAL=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.read_file(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.read_file(). 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 Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIlinuxfabrik-liball versions6.1.0pip install --upgrade 'linuxfabrik-lib==6.1.0'

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for linuxfabrik-lib, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update linuxfabrik-lib to 6.1.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-rh9c-rqvg-f7pr is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-rh9c-rqvg-f7pr can be triaged on real exposure rather than presence alone.

Tailored to GHSA-rh9c-rqvg-f7pr. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## 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
O3 Security · Impact-Aware SCA

Is GHSA-rh9c-rqvg-f7pr in your dependencies?

O3 Security finds GHSA-rh9c-rqvg-f7pr across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-rh9c-rqvg-f7pr: linuxfabrik (Medium 5.5) | O3 Security