1. Executive summary
A command injection flaw in the npm package systeminformation version 5.33.15 lets a crafted string passed to services() run arbitrary PowerShell on Windows under the Node.js process account. The default sanitizer strips ASCII double quotes but leaves U+201C and U+201D (curly double quotation marks), spaces and parentheses, and PowerShell treats the curly marks as string delimiters, so the input escapes the Get-CimInstance Win32_Service -Filter command the library builds. The flaw is only reachable where a consuming application forwards attacker-controlled input to services() or getDynamicData(); the package itself ships no HTTP server or route handler, so remote exploitability depends entirely on the host application. No CVE has been assigned in the advisory and no CVSS score, severity rating or CISA KEV entry is available, so this note does not assign one. For EMEA financial services the exposure is confined to Windows hosts running Node.js services that pass user or upstream data into this API, typically inventory agents, monitoring backends and admin dashboards, and the impact is bounded by the privileges of that Node.js account.
2. Regulatory framing
No specific DORA/NIS2 article is directly engaged by this item.
3. Technical analysis & attack chain
The flaw is a sanitizer gap in lib/util.js combined with string interpolation in lib/processes.js. services() calls sanitizeShellString() on the caller's string, then inserts each resulting name into a Get-CimInstance Win32_Service -Filter command that the Windows branch passes to util.powerShell(). The sanitizer removes the ASCII double quote but not U+201C or U+201D, and in its default non-strict mode it permits spaces and parentheses. PowerShell accepts the curly marks as string delimiters, so an input can close the filter string and append a command expression.
Confirmed steps, reproduced by the reporter on Windows against the checked-out 5.33.15 source:
- The consuming application passes an attacker-influenced string to
si.services(). sanitizeShellString()strips ASCII quotes but passes the curly quotes, spaces and parentheses through.- The value is interpolated into the
Get-CimInstance Win32_Service -Filtercommand. util.powerShell()executes the command; the curly quote terminates the filter string and the parenthesised expression runs as PowerShell.- The injected expression executes with the permissions of the Node.js process account.
The reporter's proof of concept passes the string not-a-service” (New-Item -Name <marker> -ItemType File -Force) “ to si.services(). On Windows the call returned an array and created the marker file (markerCreated=true); a benign input left the marker absent. The marker was created with the Node.js process account. The same primitive would run any PowerShell expression the account is permitted to execute, so the ceiling is file read and write, alteration of application data, or disruption of the service, bounded by that account's rights.
Reachability is the limiting factor. The reporter searched the 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 makes the input network-reachable only if it forwards request data into either. A separate loopback HTTP harness in test code did trigger the command through a query parameter, but that server is not part of the 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.
Only version 5.33.15 was checked. The advisory states that other package versions have not been tested, so the affected range is unknown and no fixed version is named. Treat any version whose lib/util.js sanitizer omits U+201C and U+201D as potentially affected until the maintainer publishes a range.
A related advisory (GHSA-hvx9-hwr7-wjj9) describes a separate Linux command injection in networkInterfaces(), where an active NetworkManager connection profile name containing shell metacharacters is interpolated unsanitized into execSync() command strings in lib/network.js. That is a different code path and trigger from this item; it is noted because the same package is in scope for inventory and monitoring agents, and a host running both code paths carries both exposures. The Linux advisory makes no remote exploitation claim and no AV:N or AV:A claim.
4. Mitigation & containment
P1 (within 24 hours)
- Inventory every Windows host and container image running a Node.js application that depends on
systeminformation. Runnpm ls systeminformationin each application root and record the resolved version. Any tree resolving to 5.33.15 is in scope. - For each such application, trace every call to
services()andgetDynamicData(). If request data, query parameters, headers, message-queue payloads or upstream API fields reach either call, treat the path as exploitable and disable or gate that feature until the code is changed. - Where the input path cannot be removed immediately, reject any argument to
services()containing U+201C (\u201C), U+201D (\u201D),(,)or whitespace before the call, and allowlist expected service names. This is a stopgap at the call site, not a fix in the package. - Restrict the privileges of the Node.js service account on Windows hosts running these applications so an injected PowerShell expression cannot write outside the application directory or modify service configuration.
P2 (within 72 hours)
- Pin
systeminformationto a version the maintainer confirms fixes the sanitizer, once a fixed version is published. No fixed version is named in the advisory, so track the GitHub advisory GHSA-3f7j-7jwc-fxfj for the patched release and do not assume 5.33.15 is the only affected build. - Where the package cannot be upgraded, wrap
services()in an application-level function that validates input against a strict allowlist of characters before calling the library. - Add EDR or PowerShell script-block logging coverage for
powershell.exeprocesses spawned as children ofnode.exeon application servers, and alert onGet-CimInstance Win32_Service -Filtercommand lines that contain U+201C or U+201D.
P3 (within 7 days)
- Review the Linux exposure in the same package: audit hosts running
networkInterfaces()under NetworkManager and confirm no untrusted party can create or rename an active connection profile. Apply the same version-pinning discipline. - Add
systeminformationto the dependency-review gate so future versions are checked against the advisory before merge.
5. Indicators of compromise
No indicators of compromise available in the source material.
The proof of concept creates a marker file named si-injection-<pid>.txt in the working directory, but that is test-harness output from the reporter's script, not an artefact of an intrusion, and the script deletes it. It is not listed as an indicator.
6. Detection
The sources give usable command-line artefacts: the Get-CimInstance Win32_Service -Filter command the library builds, and the curly quote characters that distinguish a malicious argument from a benign one. A Sigma rule on process creation is the practical detection, since the injection manifests as a PowerShell child process of node.exe carrying a filter string with U+201C or U+201D.
title: systeminformation services() PowerShell command injection via Unicode curly quotes
id: 8f2c1a4e-6b7d-4c9e-9a1f-3d5e7b2c4a10
status: experimental
description: Detects PowerShell spawned by a Node.js process running a Get-CimInstance Win32_Service filter that contains U+201C or U+201D, consistent with command injection through systeminformation services().
author: Adverse Trace
date: 2026-10-29
references:
- https://github.com/advisories/GHSA-3f7j-7jwc-fxfj
logsource:
category: process_creation
product: windows
detection:
parent_node:
ParentImage|endswith:
- '\node.exe'
powershell_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
cim_filter:
CommandLine|contains:
- 'Get-CimInstance Win32_Service -Filter'
curly_quote:
CommandLine|contains:
- '\u201C'
- '\u201D'
condition: parent_node and powershell_child and cim_filter and curly_quote
falsepositives:
- Legitimate service-name searches that include typographic quotes copied from documentation or email
level: high
A YARA rule is not emitted. The only file-level artefacts in the sources are the reporter's proof-of-concept script and marker file, which are test code and not present in a real intrusion.
7. Sources
- GitHub Security Advisories (npm), "systeminformation vulnerable to Windows command injection in services() through Unicode quotation marks", GHSA-3f7j-7jwc-fxfj, published 2026-10-28. https://github.com/advisories/GHSA-3f7j-7jwc-fxfj
- GitHub Security Advisories (high+), "Systeminformation vulnerable to Linux command injection in networkInterfaces() via unsanitized NetworkManager connection profile name", GHSA-hvx9-hwr7-wjj9. https://github.com/advisories/GHSA-hvx9-hwr7-wjj9
- GitHub Advisory Database, CVE-2026-116576, GHSA-3f7j-7jwc-fxfj. https://github.com/advisories/GHSA-3f7j-7jwc-fxfj
8. Adverse Trace position
Adverse Trace assesses this as a moderate-impact local code-execution flaw whose real-world severity depends on the host application, not on the package alone. No CVE has been assigned, no CVSS score or severity rating is available, and there is no CISA KEV entry, so we do not assign a severity band here; the exploitation state is unconfirmed and the affected version range is unverified beyond 5.33.15. The technical detail rests on a single advisory and its reporter's reproduction, so treat the proof of concept and the reachability assessment as single-sourced and verify against your own dependency tree before acting. No named actor is involved and no attribution applies. We will track GHSA-3f7j-7jwc-fxfj for a fixed version and an affected range, and we will reissue this note when either is published. Clients running Node.js inventory, monitoring or dashboard services on Windows should complete the P1 dependency inventory and call-path trace now, because the exposure is decided by whether their own code forwards untrusted input into services().
Published via PulseTrace — Adverse Trace threat intelligence.