{"id":"GHSA-7648-jmwf-vw7j","revision":null,"records":[{"revision":"2755cd69924b48a0818af15bd8a9d1f0","observedAt":"2026-10-27T17:43:43.295+00:00","statement":{"source":"cve-program","id":"CVE-2026-116674","aliases":[],"title":"systeminformation: Prototype pollution in `networkStats()` via the interface name","description":"systeminformation is a System and OS information library for node.js. Prior to 5.33.13, networkStats(ifaces) in lib/network.js on FreeBSD, OpenBSD, and NetBSD stores per-interface data in the plain-object _network cache and indexes it with the sanitized caller-controlled interface name. Passing proto causes the inherited Object.prototype value to be used instead of creating an own cache entry, so calcNetworkSpeed() writes rx_bytes, tx_bytes, rx_sec, tx_sec, ms, last_ms, and operstate onto Object.prototype. The polluted property names are fixed and their values come from interface statistics, but inherited-name collisions and enumerable keys can corrupt application branching, serialization, configuration merging, validation, or lookup behavior. This issue is fixed in version 5.33.13.","state":0,"publishedAt":"2026-10-27T16:40:46.246+00:00","modifiedAt":"2026-10-27T16:40:46.246+00:00","affected":[{"ecosystem":"Vendor product","name":"systeminformation","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"sebhildebrandt\",\n                    \"product\": \"systeminformation\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 5.33.13\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:L","score":6.5,"severity":"MEDIUM"}],"references":["https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-7648-jmwf-vw7j","https://github.com/sebhildebrandt/systeminformation/commit/6ff25a1ebc0b1fdc7a8aa3507e46f22007da2969","https://github.com/sebhildebrandt/systeminformation/releases/tag/v5.33.13"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-116674","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-116674","identifiers":["CVE-2026-116674"]},"retained":false,"historicalAffected":[]},{"revision":"4ac76415144242589e0d40a5bd25957b","observedAt":"2026-10-28T21:57:44.964+00:00","statement":{"source":"euvd","id":"EUVD-2026-111104","aliases":["CVE-2026-116674"],"title":"systeminformation is a System and OS information library for node.js. Prior to 5.33.13, networkStats(ifaces) in lib/network.js on FreeBSD, OpenBSD, and NetBSD stores per-interface data in the plain-object _network cache and indexes it with …","description":"systeminformation is a System and OS information library for node.js. Prior to 5.33.13, networkStats(ifaces) in lib/network.js on FreeBSD, OpenBSD, and NetBSD stores per-interface data in the plain-object _network cache and indexes it with the sanitized caller-controlled interface name. Passing proto causes the inherited Object.prototype value to be used instead of creating an own cache entry, so calcNetworkSpeed() writes rx_bytes, tx_bytes, rx_sec, tx_sec, ms, last_ms, and operstate onto Object.prototype. The polluted property names are fixed and their values come from interface statistics, but inherited-name collisions and enumerable keys can corrupt application branching, serialization, configuration merging, validation, or lookup behavior. This issue is fixed in version 5.33.13.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:L","score":6.5,"severity":null}],"references":["https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-7648-jmwf-vw7j","https://github.com/sebhildebrandt/systeminformation/commit/6ff25a1ebc0b1fdc7a8aa3507e46f22007da2969","https://github.com/sebhildebrandt/systeminformation/releases/tag/v5.33.13"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-111104","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 27, 2026, 4:40:46 PM","dateUpdated":"Oct 27, 2026, 4:40:46 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-111104","identifiers":["EUVD-2026-111104","CVE-2026-116674"]},"retained":false,"historicalAffected":[]},{"revision":"f3a8d56bec164ebc9052cdb2248d70ed","observedAt":"2026-10-28T21:57:06.789+00:00","statement":{"source":"nvd","id":"CVE-2026-116674","aliases":[],"title":"systeminformation is a System and OS information library for node.js. Prior to 5.33.13, networkStats(ifaces) in lib/network.js on FreeBSD, OpenBSD, and NetBSD stores per-interface data in the plain-object _network cache and indexes it with …","description":"systeminformation is a System and OS information library for node.js. Prior to 5.33.13, networkStats(ifaces) in lib/network.js on FreeBSD, OpenBSD, and NetBSD stores per-interface data in the plain-object _network cache and indexes it with the sanitized caller-controlled interface name. Passing proto causes the inherited Object.prototype value to be used instead of creating an own cache entry, so calcNetworkSpeed() writes rx_bytes, tx_bytes, rx_sec, tx_sec, ms, last_ms, and operstate onto Object.prototype. The polluted property names are fixed and their values come from interface statistics, but inherited-name collisions and enumerable keys can corrupt application branching, serialization, configuration merging, validation, or lookup behavior. This issue is fixed in version 5.33.13.","state":0,"publishedAt":"2026-10-27T17:16:59.453+00:00","modifiedAt":"2026-10-27T17:16:59.583+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:L","score":6.5,"severity":"MEDIUM"}],"references":["https://github.com/sebhildebrandt/systeminformation/commit/6ff25a1ebc0b1fdc7a8aa3507e46f22007da2969","https://github.com/sebhildebrandt/systeminformation/releases/tag/v5.33.13","https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-7648-jmwf-vw7j"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-116674","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-1321"],"productCriteria":[],"sourceDates":{"published":"2026-10-27T17:16:59.453","lastModified":"2026-10-27T17:16:59.583"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-116674","identifiers":["CVE-2026-116674"]},"retained":false,"historicalAffected":[]},{"revision":"ba22ae4d27d54631b5bdd8e96604dd30","observedAt":"2026-10-28T21:55:29.474+00:00","statement":{"source":"osv","id":"GHSA-7648-jmwf-vw7j","aliases":["CVE-2026-116674"],"title":"systeminformation has prototype pollution in `networkStats()` via the interface name","description":"## Summary\n\n`networkStats()` keeps a per interface cache for computing `rx_sec` / `tx_sec` between calls.\nThe cache is a plain JavaScript object and is indexed with the interface name the caller\nsupplied. Passing the name `__proto__` makes every cache write land on `Object.prototype`\ninstead of the cache, polluting every object in the Node.js process.\n\nAn application is affected if it passes untrusted input as the `ifaces` argument of\n`si.networkStats()` and runs on FreeBSD, OpenBSD or NetBSD.\n\n## Details\n\n### The vulnerable code\n\n`lib/network.js` declares the cache as a plain object:\n\n```js\nconst _network = {};\n```\n\n`calcNetworkSpeed()` writes into it, keyed by the interface name:\n\n```js\n} else {\n  if (!_network[iface]) {\n    _network[iface] = {};\n  }\n  _network[iface].rx_bytes  = rx_bytes;\n  _network[iface].tx_bytes  = tx_bytes;\n  _network[iface].rx_sec    = null;\n  _network[iface].tx_sec    = null;\n  _network[iface].ms        = Date.now();\n  _network[iface].last_ms   = 0;\n  _network[iface].operstate = operstate;\n}\n```\n\nWith `iface === '__proto__'` this misbehaves twice:\n\n1. `_network['__proto__']` does not read a missing own property — it invokes the inherited\n   `__proto__` getter and returns `Object.prototype`, which is truthy. The\n   `if (!_network[iface])` guard therefore never creates the `{}` placeholder.\n2. Each following assignment writes onto `Object.prototype`.\n\nAfter one call, `rx_bytes`, `tx_bytes`, `rx_sec`, `tx_sec`, `ms`, `last_ms` and `operstate`\nare visible on **every** object in the process. A second call takes the other branch\n(`_network['__proto__'].ms` is now truthy) and keeps them updated.\n\n### Why the existing sanitizing does not help\n\nThe interface name does pass through `util.sanitizeString(iface, true)` before it is used as\nthe cache key. That function is a **shell metacharacter** filter, built to stop command\ninjection in the `netstat` / `ifconfig` invocations — a different threat. Property names made\nof letters and underscores are legitimate interface names and pass through untouched:\n\n```\n\"a;id\"        -> \"aid\"\n\"a$(id)\"      -> \"aid\"\n\"a|id\"        -> \"aid\"\n\"a`id`\"       -> \"aid\"\n\"__proto__\"   -> \"__proto__\"     <-- unchanged\n\"constructor\" -> \"constructor\"   <-- unchanged\n```\n\nThe sanitizer is doing its job; the gap is that no layer treats the value as an *object key*.\n\n### Reachability\n\nThe cache write is guarded incidentally on three of four platform branches. These guards exist\nfor unrelated reasons and are not a deliberate defence:\n\n| Platform | Code path | Reachable with an arbitrary name? |\n| --- | --- | --- |\n| Linux / Android | `fs.existsSync('/sys/class/net/' + ifaceSanitized)` | **No** — a non-existent interface returns early |\n| macOS | requires `netstat` to emit a data row beyond the header | **No** — verified on macOS 27, `Object.prototype` stays clean |\n| Windows | `if (rx_bytes && tx_bytes)` — requires a real adapter match | **No** |\n| **FreeBSD / OpenBSD / NetBSD** | `if (!error) { …; result = calcNetworkSpeed(ifaceSanitized, …) }` | **Yes** — the call sits outside the parsing loop, so any successful `netstat` exit reaches it, and `netstat -ibndI <unknown>` prints only its header and exits `0` |\n\nThe important part is not that three platforms happen to be safe, but that nothing in the\ndesign prevents the write. Any future platform branch without such a guard reopens the issue,\nwhich is why the fix is applied at the cache rather than at the call sites.\n\n### Age\n\nThe pattern predates the v5 line — `git log -S \"_network[iface] = {}\"` traces it back to a\nrefactoring before `v3.42.5`. The v3 and v4 lines are end of life; the advisory covers v5.\n\n## Impact\n\nThe attacker controls which attributes appear on `Object.prototype` but not their values —\nthose come from the interface statistics (`rx_bytes`, `ms`, `operstate` …). The attribute\n*names* are fixed by the library, so this is not an arbitrary-key pollution primitive.\n\nThe consequences are therefore application specific and arise from name collisions:\n\n- Any application code that tests `if (obj.operstate)`, `if (obj.ms)` or similar on an object\n  that should not have those properties now takes the wrong branch.\n- `for…in` loops over plain objects gain seven extra enumerable keys, which can corrupt\n  serialisation, configuration merging or validation logic.\n- Libraries that use `{}` as a lookup map and check membership with `in` or truthiness can\n  return values for keys that were never inserted.\n\nNo path to remote code execution is known, and the values cannot be chosen by the attacker.\n\n## Proof of concept\n\n### The underlying primitive (any platform)\n\n```js\nconst { calcNetworkSpeed } = require('systeminformation/lib/network.js'); // internal\nconst cache = {};                       // the declaration used by the library\ncalcNetworkSpeed('__proto__', 1, 2, 'up', 0, 0, 0, 0);\n\nconsole.log({}.rx_bytes);               // 1\nconsole.log({}.operstate);              // 'up'\nconsole.log(Object.prototype.hasOwnProperty('rx_bytes')); // true\n```\n\n### End to end through the public API (FreeBSD / OpenBSD / NetBSD)\n\n```js\nconst si = require('systeminformation');\n\n(async () => {\n  console.log('before:', {}.operstate);   // undefined\n  await si.networkStats('__proto__');     // an attacker supplied interface name\n  console.log('after :', {}.operstate);   // 'up'  -> Object.prototype is polluted\n})();\n```\n\nOn Linux, macOS and Windows the same call returns an empty result set and leaves\n`Object.prototype` untouched, for the reasons in the *Reachability* table.\n\n## Patch\n\n`lib/network.js` — give the cache a null prototype, so `__proto__` becomes an ordinary own\nproperty instead of a setter into the prototype chain:\n\n```diff\n-const _network = {};\n+const _network = Object.create(null);\n```\n\nThis is the smallest change that fixes the class of problem rather than one reachable path:\n\n- It is independent of the platform branches, so no guard has to be added or maintained.\n- It keeps behaviour identical for every legitimate interface name — `Object.keys()`,\n  member reads and writes all work unchanged on a null prototype object.\n- It matches the pattern already used elsewhere in the codebase\n  (`lib/filesystem.js:114-115`, `lib/graphics.js:1060`, `lib/graphics.js:1087`).\n- `Object.create` is ES5.1, well below the declared `engines.node >= 10.0.0`.\n\nAfter the patch the hostile name is stored as a normal cache entry:\n\n```js\nconst cache = Object.create(null);\n// ... cache['__proto__'] holds the stats object, Object.prototype is untouched\n```\n\n## Workarounds\n\nFor users who cannot upgrade immediately, either of these is sufficient:\n\n1. **Validate the interface name before calling** — accept only names returned by\n   `si.networkInterfaces()`, or reject the names `__proto__`, `constructor` and `prototype`.\n2. **Never pass untrusted input** as the `ifaces` argument. Passing `'*'` or omitting the\n   argument is unaffected, because those names come from the system rather than the caller.\n\nFreezing `Object.prototype` (`Object.freeze(Object.prototype)`) also prevents the write, but it\nhas wide-reaching side effects on other dependencies and is not recommended as a general\nmitigation.\n\n## Affected versions\n\nAll `5.x` releases up to and including `5.33.12`. Earlier major lines contain the same pattern\nbut are end of life.\n\n### What was verified, and what was not\n\nVerified on this machine (macOS 27, Node 24):\n\n- the pollution primitive, by calling the cache write with `iface = '__proto__'`;\n- that `util.sanitizeString('__proto__', true)` returns the string unchanged, while shell\n  metacharacters are stripped;\n- that unpatched v5 on macOS does **not** pollute through the public API;\n- that `netstat -bdnI <unknown-interface>` prints only its header and exits `0`;\n- that the patch stores the hostile key as an own property and leaves `Object.prototype` clean.\n\nNot verified, because no such host was available:\n\n- end to end exploitation on FreeBSD, OpenBSD or NetBSD. That conclusion is derived from\n  reading the BSD branch of `networkStatsSingle()` — where the `calcNetworkSpeed()` call sits\n  outside the row parsing loop and is reached whenever `netstat` exits successfully — combined\n  with the observed `netstat` exit behaviour above. Worth confirming on a BSD host before\n  publishing, since the affected-platform list depends on it.","state":0,"publishedAt":"2026-10-28T21:13:32+00:00","modifiedAt":"2026-10-28T21:30:05.7392662+00:00","affected":[{"ecosystem":"npm","name":"systeminformation","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"5.33.13"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:L","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-7648-jmwf-vw7j","https://nvd.nist.gov/vuln/detail/CVE-2026-116674","https://github.com/sebhildebrandt/systeminformation/commit/6ff25a1ebc0b1fdc7a8aa3507e46f22007da2969","https://github.com/sebhildebrandt/systeminformation","https://github.com/sebhildebrandt/systeminformation/releases/tag/v5.33.13"],"sourceUrl":"https://github.com/advisories/GHSA-7648-jmwf-vw7j","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-7648-jmwf-vw7j","identifiers":["GHSA-7648-jmwf-vw7j","CVE-2026-116674"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-10-30T19:45:46.8102278+00:00","complete":true,"records":1766,"lastAttemptAt":"2026-10-30T19:45:46.8102278+00:00","lastSuccessAt":"2026-10-30T19:45:46.9736724+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:45:46.9736729+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-7e2c4e7288f2c464ad8c61f3bb6017374714faedcc781283ac06a706ee1a820b-fe2415543f6176e43180f3324a663ce61f6ebd2e6917542e40176ff2e9495143","complete":true,"records":417364,"lastAttemptAt":"2026-10-30T19:45:36.586101+00:00","lastSuccessAt":"2026-10-30T19:45:36.3062344+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:45:36.586101+00:00","snapshotPublishedAt":"2026-10-30T19:18:05+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-81fe3422e2574698879c073ecf5b9e6e","complete":false,"records":13527,"lastAttemptAt":"2026-10-30T19:47:13.493727+00:00","lastSuccessAt":"2026-10-30T19:47:13.5711814+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:47:13.5711814+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-10-30T19:45:46.8102278+00:00","complete":true,"records":1094,"lastAttemptAt":"2026-10-30T19:45:46.8102278+00:00","lastSuccessAt":"2026-10-30T19:45:46.974479+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:45:46.974479+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-6bce51b063124715a481c34785f7e75c","complete":true,"records":13545,"lastAttemptAt":"2026-10-30T19:46:12.1759308+00:00","lastSuccessAt":"2026-10-30T19:46:12.173+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:46:12.173+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13605,"expectedIdentifiers":13605,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-311dd376326605484703c43905ee6a0126d56b980059b1c691182fb1a6e71c84","complete":true,"records":9808,"lastAttemptAt":"2026-10-30T19:45:46.756792+00:00","lastSuccessAt":"2026-10-30T19:45:46.4838995+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:45:46.756792+00:00","snapshotPublishedAt":"2026-10-30T18:48:16+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-2de9ccc5290f060c7aa63f89c25dd76f655a64ba1aba7c05c97fa3bee4f09899","complete":true,"records":1967,"lastAttemptAt":"2026-10-30T19:45:36.74539+00:00","lastSuccessAt":"2026-10-28T20:55:34.2982937+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:45:36.74539+00:00","snapshotPublishedAt":"2026-10-28T20:03:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-f7956b0b80c3f12706a4454a1e0aea0c83c0a5730f2cff4597eb5c4eedd05097","complete":true,"records":3023,"lastAttemptAt":"2026-10-30T19:45:46.7721443+00:00","lastSuccessAt":"2026-10-30T17:47:18.9941876+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:45:46.7721443+00:00","snapshotPublishedAt":"2026-10-30T15:48:06+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-a12b17cc91216d5a280d5a5e257a4bc61a7e8824cd7f389e730ce6d1147fa887","complete":true,"records":230969,"lastAttemptAt":"2026-10-30T19:45:36.8513477+00:00","lastSuccessAt":"2026-10-30T18:47:14.1288263+00:00","error":null,"lastVerifiedAt":"2026-10-30T19:45:36.8513477+00:00","snapshotPublishedAt":"2026-10-30T18:33:11+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}