VYPR
High severityNVD Advisory· Published Nov 26, 2020· Updated Sep 16, 2024

Prototype Pollution

CVE-2020-7778

Description

This affects the package systeminformation before 4.30.2. The attacker can overwrite the properties and functions of an object, which can lead to executing OS commands.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Prototype pollution in systeminformation before 4.30.2 allows an attacker to overwrite object properties and execute arbitrary OS commands.

Vulnerability

Overview

The vulnerability is a prototype pollution flaw in the npm package systeminformation prior to version 4.30.2 [1][2]. By polluting the prototype of a base object, an attacker can overwrite internal properties and functions used by the library. This type of attack leverages JavaScript's dynamic nature, where properties like __proto__, constructor, and prototype can be injected into the prototype chain, affecting all objects that inherit from it [2].

Exploitation

Mechanism

The exploitable code path resides in lib/internet.js, specifically within the inetChecksite() function [3][4]. The attacker can set obj.__proto__.replace to a malicious function that executes arbitrary OS commands via child_process.execSync(). The library's sanitization routine calls String.prototype.replace() (which is itself a function on the prototype) to clean user input, so by polluting that function, the attacker hijacks the sanitization step [4]. The attack requires the attacker to control an object passed to inetChecksite() — no authentication is needed if the application passes attacker-controlled data to this function [1][4].

Impact

Successful exploitation allows arbitrary OS command execution with the privileges of the Node.js process [1][4]. This can lead to full compromise of the server, including data exfiltration, lateral movement, or denial of service. The vulnerability is classified as critical, with a CVSS score reflecting remote code execution potential [1].

Mitigation

The issue is patched in systeminformation version 4.30.5 and later [4]. Users should update immediately. If an upgrade is not possible, avoid passing untrusted input to the inetChecksite() function or any function that calls it. No other workaround is available [2][4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
systeminformationnpm
< 4.30.24.30.2

Affected products

2

Patches

2
11103a447ab9

security update (prototype pollution prevention)

https://github.com/sebhildebrandt/systeminformationSebastian HildebrandtNov 25, 2020via ghsa
3 files changed · +13 13
  • lib/internet.js+2 2 modified
    @@ -35,14 +35,14 @@ function inetChecksite(url, callback) {
         process.nextTick(() => {
           let urlSanitized = '';
           const s = util.sanitizeShellString(url);
    -      for (i = 0; i <= 2000; i++) {
    +      for (let i = 0; i <= 2000; i++) {
             if (!(s[i] === undefined ||
               s[i] === ' ' ||
               s[i] === '{' ||
               s[i] === '}')) {
               const sl = s[i].toLowerCase();
               if (sl[0] && !sl[1]) {
    -            urlSanitized = urlSanitized + sl[i];
    +            urlSanitized = urlSanitized + sl[0];
               }
             }
           }
    
  • lib/network.js+7 7 modified
    @@ -1134,13 +1134,13 @@ function networkStatsSingle(iface) {
                       // skip header line
                       // use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address
                       stats = lines[1].replace(/ +/g, ' ').split(' ');
    -                  rx_bytes = parseInt(stats[6]);
    -                  rx_dropped = parseInt(stats[11]);
    -                  rx_errors = parseInt(stats[5]);
    -                  tx_bytes = parseInt(stats[9]);
    -                  tx_dropped = parseInt(stats[11]);
    -                  tx_errors = parseInt(stats[8]);
    -
    +                  const offset = stats.length > 11 ? 1 : 0;
    +                  rx_bytes = parseInt(stats[offset + 5]);
    +                  rx_dropped = parseInt(stats[offset + 10]);
    +                  rx_errors = parseInt(stats[offset + 4]);
    +                  tx_bytes = parseInt(stats[offset + 8]);
    +                  tx_dropped = parseInt(stats[offset + 10]);
    +                  tx_errors = parseInt(stats[offset + 7]);
                       result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, result.operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
                     }
                   }
    
  • lib/util.js+4 4 modified
    @@ -492,7 +492,7 @@ function countLines(lines, startingWith) {
     function sanitizeShellString(str) {
       const s = str || '';
       let result = '';
    -  for (i = 0; i <= 2000; i++) {
    +  for (let i = 0; i <= 2000; i++) {
         if (!(s[i] === undefined ||
           s[i] === '>' ||
           s[i] === '<' ||
    @@ -520,15 +520,15 @@ function sanitizeShellString(str) {
     }
     
     function isPrototypePolluted() {
    -  s = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    +  const s = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
       let notPolluted = true;
       let st = '';
       notPolluted = notPolluted || !(s.length === 62)
       const ms = Date.now();
       if (typeof ms === 'number' && ms > 1600000000000) {
         const l = ms % 100 + 15;
         let c = 0;
    -    for (i = 0; i < l; i++) {
    +    for (let i = 0; i < l; i++) {
           const r = Math.random() * 61.99999999 + 1;
           const rs = parseInt(Math.floor(r).toString(), 10)
           const rs2 = parseInt(r.toString().split('.')[0], 10);
    @@ -561,7 +561,7 @@ function isPrototypePolluted() {
         // lower
         const stl = st.toLowerCase();
         notPolluted = notPolluted && (stl.length === l) && stl[l - 1] && !(stl[l])
    -    for (i = 0; i < l; i++) {
    +    for (let i = 0; i < l; i++) {
           const s1 = st[i];
           const s2 = stl[i];
           const s1l = s1.toLowerCase();
    
73dce8d717ca

security update (prototype pollution prevention)

https://github.com/sebhildebrandt/systeminformationSebastian HildebrandtNov 25, 2020via ghsa
7 files changed · +112 33
  • CHANGELOG.md+2 0 modified
    @@ -30,6 +30,8 @@ For major (breaking) changes - version 3 and 2 see end of page.
     
     | Version        | Date           | Comment  |
     | -------------- | -------------- | -------- |
    +| 4.30.2         | 2020-11-25     | security update (prototype pollution prevention) |
    +| 4.30.1         | 2020-11-12     | updated docs |
     | 4.30.0         | 2020-11-12     | `get()` possibility to provide params |
     | 4.29.3         | 2020-11-09     | `blockdevices()` catch errors adapted for just one line |
     | 4.29.2         | 2020-11-09     | `blockdevices()` catch errors |
    
  • docs/history.html+10 0 modified
    @@ -83,6 +83,16 @@ <h3>Full version history</h3>
                       </tr>
                     </thead>
                     <tbody>
    +                  <tr>
    +                    <th scope="row">4.30.2</th>
    +                    <td>2020-11-25</td>
    +                    <td>security update (prototype pollution prevention)</td>
    +                  </tr>
    +                  <tr>
    +                    <th scope="row">4.30.1</th>
    +                    <td>2020-11-12</td>
    +                    <td>updated docs</td>
    +                  </tr>
                       <tr>
                         <th scope="row">4.30.0</th>
                         <td>2020-11-11</td>
    
  • docs/index.html+1 1 modified
    @@ -168,7 +168,7 @@
           <img class="logo" src="assets/logo.png">
           <div class="title">systeminformation</div>
           <div class="subtitle"><span id="typed"></span></div>
    -      <div class="version">Current Version: <span id="version">4.30.0</span></div>
    +      <div class="version">Current Version: <span id="version">4.30.2</span></div>
           <button class="btn btn-light" onclick="location.href='https://github.com/sebhildebrandt/systeminformation'">View on Github <i class=" fab fa-github"></i></button>
         </div>
         <div class="down">
    
  • lib/internet.js+15 10 modified
    @@ -33,21 +33,26 @@ function inetChecksite(url, callback) {
     
       return new Promise((resolve) => {
         process.nextTick(() => {
    -
    -      let urlSanitized = util.sanitizeShellString(url).toLowerCase();
    -      urlSanitized = urlSanitized.replace(/ /g, '');
    -      urlSanitized = urlSanitized.replace(/\$/g, '');
    -      urlSanitized = urlSanitized.replace(/\(/g, '');
    -      urlSanitized = urlSanitized.replace(/\)/g, '');
    -      urlSanitized = urlSanitized.replace(/{/g, '');
    -      urlSanitized = urlSanitized.replace(/}/g, '');
    +      let urlSanitized = '';
    +      const s = util.sanitizeShellString(url);
    +      for (i = 0; i <= 2000; i++) {
    +        if (!(s[i] === undefined ||
    +          s[i] === ' ' ||
    +          s[i] === '{' ||
    +          s[i] === '}')) {
    +          const sl = s[i].toLowerCase();
    +          if (sl[0] && !sl[1]) {
    +            urlSanitized = urlSanitized + sl[i];
    +          }
    +        }
    +      }
           let result = {
             url: urlSanitized,
             ok: false,
             status: 404,
             ms: -1
           };
    -      if (urlSanitized) {
    +      if (urlSanitized && !util.isPrototypePolluted()) {
             let t = Date.now();
             if (_linux || _freebsd || _openbsd || _netbsd || _darwin || _sunos) {
               let args = ' -I --connect-timeout 5 -m 5 ' + urlSanitized + ' 2>/dev/null | head -n 1 | cut -d " " -f2';
    @@ -114,7 +119,7 @@ function inetLatency(host, callback) {
       }
     
       host = host || '8.8.8.8';
    -  const hostSanitized = util.sanitizeShellString(host);
    +  const hostSanitized = util.isPrototypePolluted() ? '8.8.8.8' : util.sanitizeShellString(host);
     
       return new Promise((resolve) => {
         process.nextTick(() => {
    
  • lib/network.js+1 1 modified
    @@ -1041,7 +1041,7 @@ function networkStatsSingle(iface) {
       return new Promise((resolve) => {
         process.nextTick(() => {
     
    -      const ifaceSanitized = util.sanitizeShellString(iface);
    +      const ifaceSanitized = util.isPrototypePolluted() ? '---' : util.sanitizeShellString(iface);
     
           let result = {
             iface: ifaceSanitized,
    
  • lib/processes.js+4 1 modified
    @@ -103,6 +103,9 @@ function services(srv, callback) {
             if (srvString === '') {
               srvString = '*';
             }
    +        if (util.isPrototypePolluted() && srvString !== '*') {
    +          srvString = '------';
    +        }
             let srvs = srvString.split('|');
             let result = [];
             let dataSrv = [];
    @@ -837,7 +840,7 @@ function processLoad(proc, callback) {
     
       return new Promise((resolve) => {
         process.nextTick(() => {
    -      const procSanitized = util.sanitizeShellString(proc);
    +      const procSanitized = util.isPrototypePolluted() ? '' : util.sanitizeShellString(proc);
     
           let result = {
             'proc': procSanitized,
    
  • lib/util.js+79 20 modified
    @@ -490,29 +490,87 @@ function countLines(lines, startingWith) {
     }
     
     function sanitizeShellString(str) {
    -  let result = str || '';
    -  result = result.replace(/>/g, '');
    -  result = result.replace(/</g, '');
    -  result = result.replace(/\*/g, '');
    -  result = result.replace(/\?/g, '');
    -  result = result.replace(/\[/g, '');
    -  result = result.replace(/\]/g, '');
    -  result = result.replace(/\|/g, '');
    -  result = result.replace(/\`/g, '');
    -  result = result.replace(/\$/g, '');
    -  result = result.replace(/;/g, '');
    -  result = result.replace(/&/g, '');
    -  result = result.replace(/\)/g, '');
    -  result = result.replace(/\(/g, '');
    -  result = result.replace(/\$/g, '');
    -  result = result.replace(/#/g, '');
    -  result = result.replace(/\\/g, '');
    -  result = result.replace(/\t/g, '');
    -  result = result.replace(/\n/g, '');
    -  result = result.replace(/\"/g, '');
    +  const s = str || '';
    +  let result = '';
    +  for (i = 0; i <= 2000; i++) {
    +    if (!(s[i] === undefined ||
    +      s[i] === '>' ||
    +      s[i] === '<' ||
    +      s[i] === '*' ||
    +      s[i] === '?' ||
    +      s[i] === '[' ||
    +      s[i] === ']' ||
    +      s[i] === '|' ||
    +      s[i] === '˚' ||
    +      s[i] === '$' ||
    +      s[i] === ';' ||
    +      s[i] === '&' ||
    +      s[i] === '(' ||
    +      s[i] === ')' ||
    +      s[i] === ']' ||
    +      s[i] === '#' ||
    +      s[i] === '\\' ||
    +      s[i] === '\t' ||
    +      s[i] === '\n' ||
    +      s[i] === '"')) {
    +      result = result + s[i];
    +    }
    +  }
       return result;
     }
     
    +function isPrototypePolluted() {
    +  s = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    +  let notPolluted = true;
    +  let st = '';
    +  notPolluted = notPolluted || !(s.length === 62)
    +  const ms = Date.now();
    +  if (typeof ms === 'number' && ms > 1600000000000) {
    +    const l = ms % 100 + 15;
    +    let c = 0;
    +    for (i = 0; i < l; i++) {
    +      const r = Math.random() * 61.99999999 + 1;
    +      const rs = parseInt(Math.floor(r).toString(), 10)
    +      const rs2 = parseInt(r.toString().split('.')[0], 10);
    +      const q = Math.random() * 61.99999999 + 1;
    +      const qs = parseInt(Math.floor(q).toString(), 10)
    +      const qs2 = parseInt(q.toString().split('.')[0], 10);
    +      notPolluted = notPolluted && !(r === q);
    +      notPolluted = notPolluted && rs === rs2 && qs === qs2;
    +      st += s[rs - 1];
    +    }
    +    notPolluted = notPolluted && st.length === l;
    +    // string manipulation
    +    let p = Math.random() * l * 0.9999999999;
    +    let stm = st.substr(0, p) + ' ' + st.substr(p, 2000);
    +    let sto = stm.replace(/ /g, '');
    +    notPolluted = notPolluted && st === sto;
    +    p = Math.random() * l * 0.9999999999;
    +    stm = st.substr(0, p) + '{' + st.substr(p, 2000);
    +    sto = stm.replace(/{/g, '');
    +    notPolluted = notPolluted && st === sto;
    +    p = Math.random() * l * 0.9999999999;
    +    stm = st.substr(0, p) + '*' + st.substr(p, 2000);
    +    sto = stm.replace(/\*/g, '');
    +    notPolluted = notPolluted && st === sto;
    +    p = Math.random() * l * 0.9999999999;
    +    stm = st.substr(0, p) + '$' + st.substr(p, 2000);
    +    sto = stm.replace(/\$/g, '');
    +    notPolluted = notPolluted && st === sto;
    +
    +    // lower
    +    const stl = st.toLowerCase();
    +    notPolluted = notPolluted && (stl.length === l) && stl[l - 1] && !(stl[l])
    +    for (i = 0; i < l; i++) {
    +      const s1 = st[i];
    +      const s2 = stl[i];
    +      const s1l = s1.toLowerCase();
    +      notPolluted = notPolluted && s1l[0] === s2 && s1l[0] && !(s1l[1]);
    +    }
    +  }
    +  return !notPolluted;
    +}
    +
     function hex2bin(hex) {
       return ("00000000" + (parseInt(hex, 16)).toString(2)).substr(-8);
     }
    @@ -747,4 +805,5 @@ exports.noop = noop;
     exports.isRaspberry = isRaspberry;
     exports.isRaspbian = isRaspbian;
     exports.sanitizeShellString = sanitizeShellString;
    +exports.isPrototypePolluted = isPrototypePolluted;
     exports.decodePiCpuinfo = decodePiCpuinfo;
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.