diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e1f1c..94be3e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,7 +77,8 @@ For major (breaking) changes - **version 4, 3 and 2** - see end of page. | Version | Date | Comment | | -------------- | -------------- | -------- | -| 5.6.3 | 2021-03-10 | `sanitizeShellString()` improvement | +| 5.6.4 | 2021-03-15 | `sanitizeShellString()` and other security improvements | +| 5.6.3 | 2021-03-14 | `sanitizeShellString()` improvement | | 5.6.2 | 2021-03-10 | `networkInterfaces()` `cpu()` improvement (win) | | 5.6.1 | 2021-03-03 | `get()` fixed issue boolean parameters | | 5.6.0 | 2021-03-03 | `cpuTemperature()` added socket and chipset temp (linux) | diff --git a/docs/history.html b/docs/history.html index f91d534..3e66d56 100644 --- a/docs/history.html +++ b/docs/history.html @@ -56,6 +56,11 @@ + + 5.6.4 + 2021-03-15 + sanitizeShellString() and other security improvements + 5.6.3 2021-03-14 diff --git a/docs/index.html b/docs/index.html index dfaecaf..6262226 100644 --- a/docs/index.html +++ b/docs/index.html @@ -166,11 +166,11 @@
- Security advisory:
Update to v5.6.3
+ Security advisory:
Update to v5.6.4
systeminformation
 
-
New Version: 5.6.3
+
New Version: 5.6.4
diff --git a/docs/security.html b/docs/security.html index aff46fb..3eeb8a8 100644 --- a/docs/security.html +++ b/docs/security.html @@ -45,8 +45,8 @@

Command Injection Vulnerability

Affected versions: - < 5.6.3 and < 4.34.16
- Date: 2021-03-14
+ < 5.6.4 and < 4.34.17
+ Date: 2021-03-15
CVE indentifier -

@@ -54,7 +54,7 @@

We had an issue that there was a possibility to perform a potential command injection possibility by passing a manipulated string prototype as a parameter to the following functions. Affected commands: inetLatency(), inetChecksite(), services(), processLoad().

Patch

-

Problem was fixed with additional parameter checking. Please upgrade to version >= 5.6.3 (or >= 4.34.16 if you are using version 4).

+

Problem was fixed with additional parameter checking. Please upgrade to version >= 5.6.4 (or >= 4.34.17 if you are using version 4).

Workarround

If you cannot upgrade, be sure to check or sanitize parameter strings that are passed to inetLatency(), inetChecksite(), services(), processLoad() (string only)

diff --git a/docs/v4/history.html b/docs/v4/history.html index 8efd931..55aa575 100644 --- a/docs/v4/history.html +++ b/docs/v4/history.html @@ -83,6 +83,11 @@ + + 4.34.17 + 2021-03-14 + sanitizeShellString() and other security improvements + 4.34.16 2021-03-14 diff --git a/docs/v4/index.html b/docs/v4/index.html index b51e9d8..2b08555 100644 --- a/docs/v4/index.html +++ b/docs/v4/index.html @@ -165,12 +165,12 @@
- Security advisory:
Update to v4.34.16
+ Security advisory:
Update to v4.34.17
systeminformation
 
Version 4 documentation
-
Current Version: 4.34.16
+
Current Version: 4.34.17
diff --git a/docs/v4/security.html b/docs/v4/security.html index f478020..3907e78 100644 --- a/docs/v4/security.html +++ b/docs/v4/security.html @@ -44,8 +44,8 @@

Command Injection Vulnerability

Affected versions: - < 4.34.13
- Date: 2021-03-14
+ < 4.34.17
+ Date: 2021-03-15
CVE indentifier -

@@ -53,7 +53,7 @@

We had an issue that there was a possibility to perform a potential command injection possibility by passing a manipulated string prototype as a parameter to the following functions. Affected commands: inetLatency(), inetChecksite(), services(), processLoad().

Patch

-

Problem was fixed with additional parameter checking. Please upgrade to version >= 4.34.13 if you are using version 4.

+

Problem was fixed with additional parameter checking. Please upgrade to version >= 4.34.17 if you are using version 4.

Workarround

If you cannot upgrade, be sure to check or sanitize parameter strings that are passed to inetLatency(), inetChecksite(), services(), processLoad() (string only)

diff --git a/lib/internet.js b/lib/internet.js index 6c4e9af..9ac1e71 100644 --- a/lib/internet.js +++ b/lib/internet.js @@ -13,7 +13,7 @@ // 12. Internet // ---------------------------------------------------------------------------------- -const exec = require('child_process').exec; +// const exec = require('child_process').exec; const execFile = require('child_process').execFile; const util = require('./util'); @@ -46,11 +46,12 @@ function inetChecksite(url, callback) { } let urlSanitized = ''; const s = util.sanitizeShellString(url, true); - for (let i = 0; i <= 2000; i++) { + const mathMin = util.mathMin; + for (let i = 0; i <= mathMin(s.length, 2000); i++) { if (!(s[i] === undefined)) { s[i].__proto__.toLowerCase = util.stringToLower; const sl = s[i].toLowerCase(); - if (sl && sl[0] && !sl[1]) { + if (sl && sl[0] && !sl[1] && sl[0].length === 1) { urlSanitized = urlSanitized + sl[0]; } } @@ -65,12 +66,14 @@ function inetChecksite(url, callback) { } 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'; + let args = ['-I', '--connect-timeout', '5', '-m', '5']; + args.push(urlSanitized); let cmd = 'curl'; - exec(cmd + args, function (error, stdout) { - let statusCode = parseInt(stdout.toString()); + util.execSave(cmd, args).then((stdout) => { + const lines = stdout.split('\n'); + let statusCode = lines[0] && lines[0].indexOf(' ') >= 0 ? parseInt(lines[0].split(' ')[1], 10) : 404; result.status = statusCode || 404; - result.ok = !error && (statusCode === 200 || statusCode === 301 || statusCode === 302 || statusCode === 304); + result.ok = (statusCode === 200 || statusCode === 301 || statusCode === 302 || statusCode === 304); result.ms = (result.ok ? Date.now() - t : null); if (callback) { callback(result); } resolve(result); @@ -142,7 +145,8 @@ function inetLatency(host, callback) { } let hostSanitized = ''; const s = (util.isPrototypePolluted() ? '8.8.8.8' : util.sanitizeShellString(host, true)).trim(); - for (let i = 0; i <= 2000; i++) { + const mathMin = util.mathMin; + for (let i = 0; i <= mathMin(s.length, 2000); i++) { if (!(s[i] === undefined)) { s[i].__proto__.toLowerCase = util.stringToLower; const sl = s[i].toLowerCase(); @@ -171,10 +175,10 @@ function inetLatency(host, callback) { params = '-c2 -t3 ' + hostSanitized; filt = 'avg'; } - execFile('ping', params.split(' '), function (error, stdout) { + util.execSave('ping', params.split(' ')).then((stdout) => { let result = null; - if (!error) { - const lines = stdout.toString().split('\n').filter(line => line.indexOf(filt) >= 0).join('\n'); + if (stdout) { + const lines = stdout.split('\n').filter(line => line.indexOf(filt) >= 0).join('\n'); const line = lines.split('='); if (line.length > 1) { @@ -191,10 +195,10 @@ function inetLatency(host, callback) { if (_sunos) { const params = '-s -a ' + hostSanitized + ' 56 2'; const filt = 'avg'; - execFile('ping', params.split(' '), { timeout: 3000 }, function (error, stdout) { + util.execSave('ping', params.split(' '), { timeout: 3000 }).then((stdout) => { let result = null; - if (!error) { - const lines = stdout.toString().split('\n').filter(line => line.indexOf(filt) >= 0).join('\n'); + if (stdout) { + const lines = stdout.split('\n').filter(line => line.indexOf(filt) >= 0).join('\n'); const line = lines.split('='); if (line.length > 1) { const parts = line[1].split('/'); diff --git a/lib/network.js b/lib/network.js index 9e1087b..5398ece 100644 --- a/lib/network.js +++ b/lib/network.js @@ -1061,7 +1061,8 @@ function networkStatsSingle(iface) { process.nextTick(() => { let ifaceSanitized = ''; const s = util.isPrototypePolluted() ? '---' : util.sanitizeShellString(iface); - for (let i = 0; i <= 2000; i++) { + const mathMin = util.mathMin; + for (let i = 0; i <= mathMin(s.length, 2000); i++) { if (!(s[i] === undefined)) { ifaceSanitized = ifaceSanitized + s[i]; } diff --git a/lib/processes.js b/lib/processes.js index 400c01f..2f234c6 100644 --- a/lib/processes.js +++ b/lib/processes.js @@ -111,7 +111,8 @@ function services(srv, callback) { srvString.__proto__.trim = util.stringTrim; const s = util.sanitizeShellString(srv); - for (let i = 0; i <= 2000; i++) { + const mathMin = util.mathMin; + for (let i = 0; i <= mathMin(s.length, 2000); i++) { if (!(s[i] === undefined)) { srvString = srvString + s[i]; } @@ -164,15 +165,15 @@ function services(srv, callback) { } } } - if ((_darwin) && srvString === '*') { // service enumeration mnot yet suported on mac OS + if ((_darwin) && srvString === '*') { // service enumeration not yet suported on mac OS if (callback) { callback(result); } resolve(result); } - let comm = (_darwin) ? 'ps -caxo pcpu,pmem,pid,command' : 'ps -axo pcpu,pmem,pid,command'; + let args = (_darwin) ? ['-caxo', 'pcpu,pmem,pid,command'] : ['-axo', 'pcpu,pmem,pid,command']; if (srvString !== '' && srvs.length > 0) { - exec(comm + ' | grep -v grep | grep -iE "' + srvString + '"', { maxBuffer: 1024 * 20000 }, function (error, stdout) { // lgtm [js/shell-command-constructed-from-input] - if (!error) { - let lines = stdout.toString().replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); + util.execSave('ps', args).then((stdout) => { + if (stdout) { + let lines = stdout.replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); srvs.forEach(function (srv) { let ps; if (_darwin) { @@ -267,9 +268,10 @@ function services(srv, callback) { resolve(result); } } else { - exec('ps -o comm | grep -v grep | egrep "' + srvString + '"', { maxBuffer: 1024 * 20000 }, function (error, stdout) { // lgtm [js/shell-command-constructed-from-input] - if (!error) { - let lines = stdout.toString().replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); + args = ['-o', 'comm']; + util.execSave('ps', args).then((stdout) => { + if (stdout) { + let lines = stdout.replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); srvs.forEach(function (srv) { let ps = lines.filter(function (e) { return e.indexOf(srv) !== -1; @@ -909,7 +911,8 @@ function processLoad(proc, callback) { processesString.__proto__.trim = util.stringTrim; const s = util.sanitizeShellString(proc); - for (let i = 0; i <= 2000; i++) { + const mathMin = util.mathMin; + for (let i = 0; i <= mathMin(s.length, 2000); i++) { if (!(s[i] === undefined)) { processesString = processesString + s[i]; } diff --git a/lib/util.js b/lib/util.js index 04e5a89..d5c689f 100644 --- a/lib/util.js +++ b/lib/util.js @@ -58,6 +58,7 @@ const stringToString = new String().toString; const stringSubstr = new String().substr; const stringTrim = new String().trim; const stringStartWith = new String().startsWith; +const mathMin = Math.min; function isFunction(functionToCheck) { let getType = {}; @@ -389,6 +390,42 @@ function powerShell(cmd) { }); } +function execSave(cmd, args, options) { + let result = ''; + options = options || {}; + + return new Promise((resolve) => { + process.nextTick(() => { + try { + const child = spawn(cmd, args, options); + + if (child && !child.pid) { + child.on('error', function () { + resolve(result); + }); + } + if (child && child.pid) { + child.stdout.on('data', function (data) { + result += data.toString(); + }); + child.on('close', function () { + child.kill(); + resolve(result); + }); + child.on('error', function () { + child.kill(); + resolve(result); + }); + } else { + resolve(result); + } + } catch (e) { + resolve(result); + } + }); + }); +} + function getCodepage() { if (_windows) { if (!codepage) { @@ -506,7 +543,7 @@ function countLines(lines, startingWith) { function sanitizeShellString(str, strict = false) { const s = str || ''; let result = ''; - for (let i = 0; i <= 2000; i++) { + for (let i = 0; i <= mathMin(s.length, 2000); i++) { if (!(s[i] === undefined || s[i] === '>' || s[i] === '<' || @@ -925,6 +962,7 @@ exports.wmic = wmic; exports.darwinXcodeExists = darwinXcodeExists; exports.getVboxmanage = getVboxmanage; exports.powerShell = powerShell; +exports.execSave = execSave; exports.nanoSeconds = nanoSeconds; exports.countUniqueLines = countUniqueLines; exports.countLines = countLines; @@ -943,5 +981,6 @@ exports.stringToString = stringToString; exports.stringSubstr = stringSubstr; exports.stringTrim = stringTrim; exports.stringStartWith = stringStartWith; +exports.mathMin = mathMin; exports.WINDIR = WINDIR; exports.getFilesInPath = getFilesInPath;