From f89a2ec63fe4dba889612a086ea243a2e7d57f58 Mon Sep 17 00:00:00 2001 From: Sebastian Hildebrandt Date: Tue, 19 May 2020 14:56:58 +0200 Subject: [PATCH] security fix exploits, memory leak fix --- lib/internet.js | 23 ++++++------ lib/network.js | 70 ++++++++++++++++++----------------- lib/processes.js | 96 ++++++++++++++++++++++++++++-------------------- lib/util.js | 22 +++++++++++ 4 files changed, 127 insertions(+), 84 deletions(-) diff --git a/lib/internet.js b/lib/internet.js index 1ba575d..414ef75 100644 --- a/lib/internet.js +++ b/lib/internet.js @@ -34,17 +34,17 @@ function inetChecksite(url, callback) { return new Promise((resolve) => { process.nextTick(() => { + const urlSanitized = util.sanitizeShellString(url).toLowerCase(); let result = { - url: url, + url: urlSanitized, ok: false, status: 404, ms: -1 }; - if (url) { - url = url.toLowerCase(); + if (urlSanitized) { let t = Date.now(); if (_linux || _freebsd || _openbsd || _netbsd || _darwin || _sunos) { - let args = ' -I --connect-timeout 5 -m 5 ' + url + ' 2>/dev/null | head -n 1 | cut -d " " -f2'; + let args = ' -I --connect-timeout 5 -m 5 ' + urlSanitized + ' 2>/dev/null | head -n 1 | cut -d " " -f2'; let cmd = 'curl'; exec(cmd + args, function (error, stdout) { let statusCode = parseInt(stdout.toString()); @@ -56,9 +56,9 @@ function inetChecksite(url, callback) { }); } if (_windows) { // if this is stable, this can be used for all OS types - const http = (url.startsWith('https:') ? require('https') : require('http')); + const http = (urlSanitized.startsWith('https:') ? require('https') : require('http')); try { - http.get(url, (res) => { + http.get(urlSanitized, (res) => { const statusCode = res.statusCode; result.status = statusCode || 404; @@ -108,19 +108,20 @@ function inetLatency(host, callback) { } host = host || '8.8.8.8'; + const hostSanitized = util.sanitizeShellString(host); return new Promise((resolve) => { process.nextTick(() => { let cmd; if (_linux || _freebsd || _openbsd || _netbsd || _darwin) { if (_linux) { - cmd = 'ping -c 2 -w 3 ' + host + ' | grep rtt'; + cmd = 'ping -c 2 -w 3 ' + hostSanitized + ' | grep rtt'; } if (_freebsd || _openbsd || _netbsd) { - cmd = 'ping -c 2 -t 3 ' + host + ' | grep round-trip'; + cmd = 'ping -c 2 -t 3 ' + hostSanitized + ' | grep round-trip'; } if (_darwin) { - cmd = 'ping -c 2 -t 3 ' + host + ' | grep avg'; + cmd = 'ping -c 2 -t 3 ' + hostSanitized + ' | grep avg'; } exec(cmd, function (error, stdout) { @@ -139,7 +140,7 @@ function inetLatency(host, callback) { }); } if (_sunos) { - exec('ping -s -a ' + host + ' 56 2 | grep avg', { timeout: 3000 }, function (error, stdout) { + exec('ping -s -a ' + hostSanitized + ' 56 2 | grep avg', { timeout: 3000 }, function (error, stdout) { let result = -1; if (!error) { const line = stdout.toString().split('='); @@ -157,7 +158,7 @@ function inetLatency(host, callback) { if (_windows) { let result = -1; try { - exec('ping ' + host + ' -n 1', util.execOptsWin, function (error, stdout) { + exec('ping ' + hostSanitized + ' -n 1', util.execOptsWin, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\r\n'); lines.shift(); diff --git a/lib/network.js b/lib/network.js index d379d41..65fc392 100644 --- a/lib/network.js +++ b/lib/network.js @@ -572,13 +572,13 @@ function getLinuxIfaceDHCPstatus(iface, connectionName, DHCPNics) { let dhcStatus = resultFormat.split(' ').slice(1).toString(); switch (dhcStatus) { - case 'auto': - result = true; - break; + case 'auto': + result = true; + break; - default: - result = false; - break; + default: + result = false; + break; } return result; } catch (e) { @@ -1018,8 +1018,10 @@ function networkStatsSingle(iface) { return new Promise((resolve) => { process.nextTick(() => { + const ifaceSanitized = util.sanitizeShellString(iface); + let result = { - iface: iface, + iface: ifaceSanitized, operstate: 'unknown', rx_bytes: 0, rx_dropped: 0, @@ -1041,17 +1043,17 @@ function networkStatsSingle(iface) { let tx_errors = 0; let cmd, lines, stats; - if (!_network[iface] || (_network[iface] && !_network[iface].ms) || (_network[iface] && _network[iface].ms && Date.now() - _network[iface].ms >= 500)) { + if (!_network[ifaceSanitized] || (_network[ifaceSanitized] && !_network[ifaceSanitized].ms) || (_network[ifaceSanitized] && _network[ifaceSanitized].ms && Date.now() - _network[ifaceSanitized].ms >= 500)) { if (_linux) { - if (fs.existsSync('/sys/class/net/' + iface)) { + if (fs.existsSync('/sys/class/net/' + ifaceSanitized)) { cmd = - 'cat /sys/class/net/' + iface + '/operstate; ' + - 'cat /sys/class/net/' + iface + '/statistics/rx_bytes; ' + - 'cat /sys/class/net/' + iface + '/statistics/tx_bytes; ' + - 'cat /sys/class/net/' + iface + '/statistics/rx_dropped; ' + - 'cat /sys/class/net/' + iface + '/statistics/rx_errors; ' + - 'cat /sys/class/net/' + iface + '/statistics/rx_dropped; ' + - 'cat /sys/class/net/' + iface + '/statistics/tx_errors; '; + 'cat /sys/class/net/' + ifaceSanitized + '/operstate; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_bytes; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/tx_bytes; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_dropped; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_errors; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_dropped; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/tx_errors; '; exec(cmd, function (error, stdout) { if (!error) { lines = stdout.toString().split('\n'); @@ -1063,7 +1065,7 @@ function networkStatsSingle(iface) { tx_dropped = parseInt(lines[5], 10); tx_errors = parseInt(lines[6], 10); - result = calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); + result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); } resolve(result); @@ -1073,7 +1075,7 @@ function networkStatsSingle(iface) { } } if (_freebsd || _openbsd || _netbsd) { - cmd = 'netstat -ibndI ' + iface; + cmd = 'netstat -ibndI ' + ifaceSanitized; exec(cmd, function (error, stdout) { if (!error) { lines = stdout.toString().split('\n'); @@ -1089,18 +1091,18 @@ function networkStatsSingle(iface) { operstate = 'up'; } } - result = calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); + result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); } resolve(result); }); } if (_darwin) { - cmd = 'ifconfig ' + iface + ' | grep "status"'; + cmd = 'ifconfig ' + ifaceSanitized + ' | grep "status"'; exec(cmd, function (error, stdout) { result.operstate = (stdout.toString().split(':')[1] || '').trim(); result.operstate = (result.operstate || '').toLowerCase(); result.operstate = (result.operstate === 'active' ? 'up' : (result.operstate === 'inactive' ? 'down' : 'unknown')); - cmd = 'netstat -bdI ' + iface; + cmd = 'netstat -bdI ' + ifaceSanitized; exec(cmd, function (error, stdout) { if (!error) { lines = stdout.toString().split('\n'); @@ -1116,7 +1118,7 @@ function networkStatsSingle(iface) { tx_dropped = parseInt(stats[11]); tx_errors = parseInt(stats[8]); - result = calcNetworkSpeed(iface, rx_bytes, tx_bytes, result.operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); + result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, result.operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); } } resolve(result); @@ -1125,7 +1127,7 @@ function networkStatsSingle(iface) { } if (_windows) { let perfData = []; - let ifaceName = iface; + let ifaceName = ifaceSanitized; // Performance Data util.wmic('path Win32_PerfRawData_Tcpip_NetworkInterface Get name,BytesReceivedPersec,BytesSentPersec,BytesTotalPersec,PacketsOutboundDiscarded,PacketsOutboundErrors,PacketsReceivedDiscarded,PacketsReceivedErrors /value').then((stdout, error) => { @@ -1141,11 +1143,11 @@ function networkStatsSingle(iface) { tx_bytes = 0; perfData.forEach(detail => { interfaces.forEach(det => { - if ((det.iface.toLowerCase() === iface.toLowerCase() || - det.mac.toLowerCase() === iface.toLowerCase() || - det.ip4.toLowerCase() === iface.toLowerCase() || - det.ip6.toLowerCase() === iface.toLowerCase() || - (det.ifaceName.replace(/[()\[\] ]+/g, '').toLowerCase() === iface.replace(/[()\[\] ]+/g, '').toLowerCase()) && + if ((det.iface.toLowerCase() === ifaceSanitized.toLowerCase() || + det.mac.toLowerCase() === ifaceSanitized.toLowerCase() || + det.ip4.toLowerCase() === ifaceSanitized.toLowerCase() || + det.ip6.toLowerCase() === ifaceSanitized.toLowerCase() || + (det.ifaceName.replace(/[()\[\] ]+/g, '').toLowerCase() === ifaceSanitized.replace(/[()\[\] ]+/g, '').toLowerCase()) && det.ifaceName.replace(/[()\[\] ]+/g, '').toLowerCase() === detail.name)) { ifaceName = det.iface; rx_bytes = detail.rx_bytes; @@ -1167,12 +1169,12 @@ function networkStatsSingle(iface) { }); } } else { - result.rx_bytes = _network[iface].rx_bytes; - result.tx_bytes = _network[iface].tx_bytes; - result.rx_sec = _network[iface].rx_sec; - result.tx_sec = _network[iface].tx_sec; - result.ms = _network[iface].last_ms; - result.operstate = _network[iface].operstate; + result.rx_bytes = _network[ifaceSanitized].rx_bytes; + result.tx_bytes = _network[ifaceSanitized].tx_bytes; + result.rx_sec = _network[ifaceSanitized].rx_sec; + result.tx_sec = _network[ifaceSanitized].tx_sec; + result.ms = _network[ifaceSanitized].last_ms; + result.operstate = _network[ifaceSanitized].operstate; resolve(result); } }); diff --git a/lib/processes.js b/lib/processes.js index 8f883a9..8aa112c 100644 --- a/lib/processes.js +++ b/lib/processes.js @@ -31,26 +31,26 @@ const _openbsd = (_platform === 'openbsd'); const _netbsd = (_platform === 'netbsd'); const _sunos = (_platform === 'sunos'); -let _processes_cpu = { +const _processes_cpu = { all: 0, - list: {}, + list: [], ms: 0, result: {} }; -let _services_cpu = { +const _services_cpu = { all: 0, - list: {}, + list: [], ms: 0, result: {} }; -let _process_cpu = { +const _process_cpu = { all: 0, - list: {}, + list: [], ms: 0, result: {} }; -let _winStatusValues = { +const _winStatusValues = { '0': 'unknown', '1': 'other', '2': 'ready', @@ -98,28 +98,32 @@ function services(srv, callback) { return new Promise((resolve) => { process.nextTick(() => { if (srv) { - srv = srv.trim().toLowerCase().replace(/,+/g, ' ').replace(/ +/g, ' ').replace(/ +/g, '|'); - let srvs = srv.split('|'); + let srvString = util.sanitizeShellString(srv); + srvString = srvString.trim().toLowerCase().replace(/,+/g, ' ').replace(/ +/g, ' ').replace(/ +/g, '|'); + if (srvString === '') { + srvString = '*'; + } + let srvs = srvString.split('|'); let result = []; let dataSrv = []; let allSrv = []; if (_linux || _freebsd || _openbsd || _netbsd || _darwin) { - if ((_linux || _freebsd || _openbsd || _netbsd) && srv === '*') { - srv = ''; + if ((_linux || _freebsd || _openbsd || _netbsd) && srvString === '*') { + srvString = ''; let tmpsrv = execSync('service --status-all 2> /dev/null').toString().split('\n'); for (const s of tmpsrv) { const parts = s.split(']'); if (parts.length === 2) { - srv += (srv !== '' ? '|' : '') + parts[1].trim(); + srvString += (srvString !== '' ? '|' : '') + parts[1].trim(); allSrv.push({ name: parts[1].trim(), running: parts[0].indexOf('+') > 0 }); } } - srvs = srv.split('|'); + srvs = srvString.split('|'); } let comm = (_darwin) ? 'ps -caxo pcpu,pmem,pid,command' : 'ps -axo pcpu,pmem,pid,command'; - if (srv !== '' && srvs.length > 0) { - exec(comm + ' | grep -v grep | grep -iE "' + srv + '"', { maxBuffer: 1024 * 20000 }, function (error, stdout) { + if (srvString !== '' && srvs.length > 0) { + exec(comm + ' | grep -v grep | grep -iE "' + srvString + '"', { maxBuffer: 1024 * 20000 }, function (error, stdout) { if (!error) { let lines = stdout.toString().replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); srvs.forEach(function (srv) { @@ -167,7 +171,7 @@ function services(srv, callback) { let curr_processes = stdout.toString().split('\n'); // first line (all - /proc/stat) - let all = parseProcStat(curr_processes.shift()); + let all = parseProcStat(curr_processes.shift()).slice(); // process let list_new = {}; @@ -202,9 +206,11 @@ function services(srv, callback) { // store old values _services_cpu.all = all; - _services_cpu.list = list_new; + // _services_cpu.list = list_new; + _services_cpu.list = list_new.slice(); _services_cpu.ms = Date.now() - _services_cpu.ms; - _services_cpu.result = result; + // _services_cpu.result = result; + _services_cpu.result = Object.assign({}, result); if (callback) { callback(result); } resolve(result); }); @@ -213,7 +219,7 @@ function services(srv, callback) { resolve(result); } } else { - exec('ps -o comm | grep -v grep | egrep "' + srv + '"', { maxBuffer: 1024 * 20000 }, function (error, stdout) { + exec('ps -o comm | grep -v grep | egrep "' + srvString + '"', { maxBuffer: 1024 * 20000 }, function (error, stdout) { if (!error) { let lines = stdout.toString().replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); srvs.forEach(function (srv) { @@ -263,12 +269,12 @@ function services(srv, callback) { let started = util.getValue(lines, 'Started', '=', true); let startMode = util.getValue(lines, 'StartMode', '=', true); let pid = util.getValue(lines, 'ProcessId', '=', true); - if (srv === '*' || srvs.indexOf(srvName) >= 0) { + if (srvString === '*' || srvs.indexOf(srvName) >= 0) { result.push({ name: srvName, running: (started === 'TRUE'), startmode: startMode, - pids: [ pid], + pids: [pid], pcpu: 0, pmem: 0 }); @@ -276,7 +282,7 @@ function services(srv, callback) { } } } - if (srv !== '*') { + if (srvString !== '*') { let srvsMissing = srvs.filter(function (e) { return dataSrv.indexOf(e) === -1; }); @@ -609,7 +615,7 @@ function processes(callback) { if (_sunos) cmd = 'ps -Ao pid,ppid,pcpu,pmem,pri,vsz,rss,nice,stime,s,tty,user,comm'; exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { if (!error) { - result.list = parseProcesses(stdout.toString().split('\n')); + result.list = (parseProcesses(stdout.toString().split('\n'))).slice(); result.all = result.list.length; result.running = result.list.filter(function (e) { return e.state === 'running'; @@ -663,9 +669,11 @@ function processes(callback) { // store old values _processes_cpu.all = all; - _processes_cpu.list = list_new; + // _processes_cpu.list = list_new; + _processes_cpu.list = list_new.slice(); _processes_cpu.ms = Date.now() - _processes_cpu.ms; - _processes_cpu.result = result; + // _processes_cpu.result = result; + _processes_cpu.result = Object.assign({}, result); if (callback) { callback(result); } resolve(result); }); @@ -683,7 +691,7 @@ function processes(callback) { let lines = stdout.toString().split('\n'); lines.shift(); - result.list = parseProcesses2(lines); + result.list = parseProcesses2(lines).slice(); result.all = result.list.length; result.running = result.list.filter(function (e) { return e.state === 'running'; @@ -703,8 +711,7 @@ function processes(callback) { }); } }); - } - if (_windows) { + } else if (_windows) { try { util.wmic('process get /value').then((stdout, error) => { if (!error) { @@ -786,9 +793,11 @@ function processes(callback) { } // store old values _processes_cpu.all = allcpuu + allcpus; - _processes_cpu.list = list_new; + // _processes_cpu.list = list_new; + _processes_cpu.list = list_new.slice(); _processes_cpu.ms = Date.now() - _processes_cpu.ms; - _processes_cpu.result = result; + // _processes_cpu.result = result; + _processes_cpu.result = Object.assign({}, result); } if (callback) { callback(result); @@ -799,6 +808,9 @@ function processes(callback) { if (callback) { callback(result); } resolve(result); } + } else { + if (callback) { callback(result); } + resolve(result); } } else { if (callback) { callback(_processes_cpu.result); } @@ -825,14 +837,16 @@ function processLoad(proc, callback) { return new Promise((resolve) => { process.nextTick(() => { + const procSanitized = util.sanitizeShellString(proc); + let result = { - 'proc': proc, + 'proc': procSanitized, 'pid': -1, 'cpu': 0, 'mem': 0 }; - if (proc) { + if (procSanitized) { if (_windows) { try { util.wmic('process get /value').then((stdout, error) => { @@ -861,7 +875,7 @@ function processLoad(proc, callback) { pcpuu: 0, pcpus: 0, }); - if (name.toLowerCase().indexOf(proc.toLowerCase()) >= 0) { + if (name.toLowerCase().indexOf(procSanitized.toLowerCase()) >= 0) { if (result.pid === -1) { result = { proc: name, @@ -898,9 +912,11 @@ function processLoad(proc, callback) { } // store old values _process_cpu.all = allcpuu + allcpus; - _process_cpu.list = list_new; + // _process_cpu.list = list_new; + _process_cpu.list = list_new.slice(); _process_cpu.ms = Date.now() - _process_cpu.ms; - _process_cpu.result = result; + // _process_cpu.result = result; + _process_cpu.result = Object.assign({}, result); if (callback) { callback(result); } @@ -914,7 +930,7 @@ function processLoad(proc, callback) { } if (_darwin || _linux) { - exec('ps -axo pid,pcpu,pmem,comm | grep -i ' + proc + ' | grep -v grep', { maxBuffer: 1024 * 20000 }, function (error, stdout) { + exec('ps -axo pid,pcpu,pmem,comm | grep -i ' + procSanitized + ' | grep -v grep', { maxBuffer: 1024 * 20000 }, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); @@ -934,7 +950,7 @@ function processLoad(proc, callback) { }); result = { - 'proc': proc, + 'proc': procSanitized, 'pid': pid, 'pids': pids, 'cpu': parseFloat((cpu / lines.length).toFixed(2)), @@ -980,9 +996,11 @@ function processLoad(proc, callback) { result.cpu = Math.round(result.cpu * 100) / 100; _process_cpu.all = all; - _process_cpu.list = list_new; + // _process_cpu.list = list_new; + _process_cpu.list = list_new.slice(); _process_cpu.ms = Date.now() - _process_cpu.ms; - _process_cpu.result = result; + // _process_cpu.result = result; + _process_cpu.result = Object.assign({}, result); if (callback) { callback(result); } resolve(result); }); diff --git a/lib/util.js b/lib/util.js index 9e19701..19df5e7 100644 --- a/lib/util.js +++ b/lib/util.js @@ -485,6 +485,27 @@ function countLines(lines, startingWith) { return uniqueLines.length; } +function sanitizeShellString(str) { + let result = str; + result = result.replace(/>/g, ""); + result = result.replace(/