Security improvements.
Ylian Saint-Hilaire committed
Sep 22, 2020 at 14:25 UTC
c65098c6fa2293621d83581d25f63f853b83382f
7 files changed
+40
-48
agents/meshcore.js
+1
-1
@@ -3067,7 +3067,7 @@ function createMeshCore(agent) {
3067
if (args['_'].length < 1) {
3068
response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
3069
} else {
3070
- response = JSON.stringify(mesh.eval(args['_'][0]));
3070
+ response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
3071
}
3072
break;
3073
}
agents/modules_meshcore/amt-manage.js
+5
-4
@@ -560,14 +560,15 @@ function AmtManager(agent, db, isdebug) {
560
// Activate Intel AMT to CCM
561
//
562
563
- function makePass(length) {
564
- var text = "", possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
565
- for (var i = 0; i < length; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); }
563
+ obj.makePass = function(length) {
564
+ var buf = Buffer.alloc(length), text = "", possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
565
+ buf.randomFill(); // Fills buffer with secure random from OpenSSL.
566
+ for (var i = 0; i < length; i++) { text += possible.charAt(buf[i] % possible.length); }
567
return text;
568
}
569
570
obj.activeToCCM = function (adminpass) {
570
- if ((adminpass == null) || (adminpass == '')) { adminpass = 'P@0s' + makePass(23); }
571
+ if ((adminpass == null) || (adminpass == '')) { adminpass = 'P@0s' + obj.makePass(23); }
572
intelAmtAdminPass = adminpass;
573
if (osamtstack != null) {
574
osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToCCMEx2, adminpass);
amt/amt-wsman-comm.js
+6
-6
@@ -17,12 +17,6 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
17
obj.noncecounter = 1;
18
obj.authcounter = 0;
19
20
- obj.Address = '/wsman';
21
- obj.challengeParams = null;
22
- obj.noncecounter = 1;
23
- obj.authcounter = 0;
24
- obj.cnonce = Math.random().toString(36).substring(7); // Generate a random client nonce
25
-
20
obj.net = require('net');
21
obj.tls = require('tls');
22
obj.crypto = require('crypto');
@@ -32,6 +26,12 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
26
obj.kerberosDone = 0;
27
obj.amtVersion = null;
28
29
+ obj.Address = '/wsman';
30
+ obj.challengeParams = null;
31
+ obj.noncecounter = 1;
32
+ obj.authcounter = 0;
33
+ obj.cnonce = obj.crypto.randomBytes(16).toString('hex'); // Generate a random client nonce
34
+
35
obj.host = host;
36
obj.port = port;
37
obj.user = user;
certoperations.js
+2
-2
@@ -307,7 +307,7 @@ module.exports.CertificateOperations = function (parent) {
307
var keys = obj.pki.rsa.generateKeyPair({ bits: (strong == true) ? 3072 : 2048, e: 0x10001 });
308
var cert = obj.pki.createCertificate();
309
cert.publicKey = keys.publicKey;
310
- cert.serialNumber = String(Math.floor((Math.random() * 100000) + 1));
310
+ cert.serialNumber = require('crypto').randomInt(1, 100000);
311
cert.validity.notBefore = new Date(2018, 0, 1);
312
cert.validity.notAfter = new Date(2049, 11, 31);
313
if (addThumbPrintToName === true) { commonName += '-' + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
@@ -329,7 +329,7 @@ module.exports.CertificateOperations = function (parent) {
329
var keys = obj.pki.rsa.generateKeyPair({ bits: (strong == true) ? 3072 : 2048, e: 0x10001 });
330
var cert = obj.pki.createCertificate();
331
cert.publicKey = keys.publicKey;
332
- cert.serialNumber = String(Math.floor((Math.random() * 100000) + 1));
332
+ cert.serialNumber = require('crypto').randomInt(1, 100000);
333
cert.validity.notBefore = new Date(2018, 0, 1);
334
cert.validity.notAfter = new Date(2049, 11, 31);
335
if (addThumbPrintToName === true) { commonName += "-" + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
common.js
+1
-8
@@ -96,7 +96,7 @@ module.exports.data2blob = function (data) {
96
};
97
98
// Generate random numbers
99
-module.exports.random = function (max) { return Math.floor(Math.random() * max); };
99
+module.exports.random = function (max) { require('crypto').randomInt(0, max); };
100
101
// Split a comma seperated string, ignoring commas in quotes.
102
module.exports.quoteSplit = function (str) {
@@ -187,13 +187,6 @@ module.exports.checkPasswordRequirements = function(password, requirements) {
187
// Limits the number of tasks running to a fixed limit placing the rest in a pending queue.
188
// This is useful to limit the number of agents upgrading at the same time, to not swamp
189
// the network with traffic.
190
-
191
-// taskLimiterQueue.launch(somethingToDo, argument, priority);
192
-//
193
-// function somethingToDo(argument, taskid, taskLimiterQueue) {
194
-// setTimeout(function () { taskLimiterQueue.completed(taskid); }, Math.random() * 2000);
195
-// }
196
-
190
module.exports.createTaskLimiterQueue = function (maxTasks, maxTaskTime, cleaningInterval) {
191
var obj = { maxTasks: maxTasks, maxTaskTime: (maxTaskTime * 1000), nextTaskId: 0, currentCount: 0, current: {}, pending: [[], [], []], timer: null };
192
multiserver.js
+1
-1
@@ -172,7 +172,7 @@ module.exports.CreateMultiServer = function (parent, args) {
172
173
// Get the next retry time in milliseconds
174
function getConnectRetryTime() {
175
- if (obj.retryBackoff < 30000) { obj.retryBackoff += Math.floor((Math.random() * 3000) + 1000); }
175
+ if (obj.retryBackoff < 30000) { obj.retryBackoff += require('crypto').randomInt(1000, 4000); }
176
return obj.retryBackoff;
177
}
178
views/default.handlebars
+24
-26
@@ -2301,27 +2301,23 @@
2301
if (message.trustedCert == true) {
2302
// Trusted certificate, use HTTPS port.
2303
var rdpurl = window.location.origin + domainUrl + 'clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F' + window.location.hostname + '%2Fmeshrelay.ashx%3Fauth=' + message.cookie + '&CH={{{webcerthash}}}&AP=' + message.protocol + ((debugmode == 1) ? '' : '&HOL=1');
2304
- var newWindow = window.open(rdpurl, '_blank');
2305
- newWindow.opener = null;
2304
+ safeNewWindow(rdpurl, '_blank');
2305
} else {
2306
// Not a trusted certificate, use HTTP port.
2307
var basicPort = ('{{{serverRedirPort}}}'.toLowerCase() == '') ? '{{{serverPublicPort}}}' : '{{{serverRedirPort}}}';
2308
var rdpurl = 'http://' + window.location.hostname + ':' + basicPort + domainUrl + 'clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F' + window.location.hostname + '%2Fmeshrelay.ashx%3Fauth=' + message.cookie + '&CH={{{webcerthash}}}&AP=' + message.protocol + ((debugmode == 1) ? '' : '&HOL=1');
2310
- var newWindow = window.open(rdpurl, '_blank');
2311
- newWindow.opener = null;
2309
+ safeNewWindow(rdpurl, '_blank');
2310
}
2311
} else if (message.tag == 'novnc') {
2312
var vncurl = window.location.origin + domainUrl + 'novnc/vnc.html?ws=wss%3A%2F%2F' + window.location.host + encodeURIComponentEx(domainUrl) + 'meshrelay.ashx%3Fauth%3D' + message.cookie + '&show_dot=1' + (urlargs.key?('&key=' + urlargs.key):'') + '&l={{{lang}}}';
2313
var node = getNodeFromId(message.nodeid);
2314
if (node != null) { vncurl += '&name=' + encodeURIComponentEx(node.name); }
2317
- var newWindow = window.open(vncurl, 'mcnovnc/' + message.nodeid);
2318
- newWindow.opener = null;
2315
+ safeNewWindow(vncurl, 'mcnovnc/' + message.nodeid);
2316
} else if (message.tag == 'mstsc') {
2317
var rdpurl = window.location.origin + domainUrl + 'mstsc.html?ws=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
2318
var node = getNodeFromId(message.nodeid);
2319
if (node != null) { rdpurl += '&name=' + encodeURIComponentEx(node.name); }
2323
- var newWindow = window.open(rdpurl, 'mcmstsc/' + message.nodeid);
2324
- newWindow.opener = null;
2320
+ safeNewWindow(rdpurl, 'mcmstsc/' + message.nodeid);
2321
}
2322
break;
2323
}
@@ -3014,7 +3010,7 @@
3010
if (message.consent & 64) { y.push("Privacy bar"); }
3011
if (y.length == 0) { y.push("None"); }
3012
x += addHtmlValue("User Consent", y.join(', '));
3017
- x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px"><a href="' + message.url + '" id=agentInvitationLink target="_blank" style=cursor:pointer>' + "Remote Desktop Link" + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
3013
+ x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px"><a href="' + message.url + '" id=agentInvitationLink rel="noreferrer noopener" target="_blank" style=cursor:pointer>' + "Remote Desktop Link" + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
3014
setDialogMode(2, "Share Device", 1, null, x);
3015
break;
3016
}
@@ -4133,7 +4129,7 @@
4129
x += '<div id=urlInviteDiv>' + format("Invite someone to install the mesh agent by sharing an invitation link. This link points the user to installation instructions for the \"{0}\" device group. The link is public and no account for this server is needed.", EscapeHtml(mesh.name)) + '<br /><br />';
4130
x += addHtmlValue("Link Expiration", '<select id=d2inviteExpire style=width:236px onchange=d2RequestInvitationLink()><option value=1>' + "1 hour" + '</option><option value=8>' + "8 hours" + '</option><option value=24>' + "1 day" + '</option><option value=168>' + "1 week" + '</option><option value=5040>' + "1 month" + '</option><option value=0>' + "Unlimited" + '</option></select>');
4131
x += addHtmlValue("Installation Type", '<select id=d2agentInviteType style=width:236px onchange=d2RequestInvitationLink()><option value=0>' + "Background and interactive" + '</option><option value=2>' + "Background only" + '</option><option value=1>' + "Interactive only" + '</option></select>');
4136
- x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px;display:none"><a href=# id=agentInvitationLink target="_blank" style=cursor:pointer></a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
4132
+ x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px;display:none"><a href=# id=agentInvitationLink rel="noreferrer noopener" target="_blank" style=cursor:pointer></a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
4133
setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid);
4134
if (features & 64) { Q('d2InviteType').focus(); d2ChangedInviteType(); } else { Q('d2inviteExpire').focus(); validateAgentInvite(); }
4135
d2RequestInvitationLink();
@@ -4710,7 +4706,7 @@
4706
var panel = [0, 10, 12, 11, 13, 16, 17, 15, 19][action]; // (invalid), General, Desktop, Terminal, Files, Events, Console, Plugin
4707
if (event && (event.shiftKey == true)) {
4708
// Open the device in a different tab
4713
- window.open(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=' + panel + '&hide=16', 'meshcentral:' + nodeid);
4709
+ safeNewWindow(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=' + panel + '&hide=16', 'meshcentral:' + nodeid);
4710
} else {
4711
// Go to the right panel
4712
gotoDevice(nodeid, panel);
@@ -4810,7 +4806,7 @@
4806
4807
function cmdeskplayeraction(action) {
4808
if (xxdialogMode) return;
4813
- window.open(window.location.origin + '{{{domainurl}}}player.htm', 'meshcentral-deskplayer');
4809
+ safeNewWindow(window.location.origin + '{{{domainurl}}}player.htm', 'meshcentral-deskplayer');
4810
}
4811
4812
function p13deletefileCm(b, file) {
@@ -5471,7 +5467,7 @@
5467
5468
if (event && (event.shiftKey == true)) {
5469
// Open the device in a different tab
5474
- window.open(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=10&hide=16', 'meshcentral:' + nodeid);
5470
+ safeNewWindow(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=10&hide=16', 'meshcentral:' + nodeid);
5471
return;
5472
}
5473
@@ -5972,9 +5968,9 @@
5968
var url = '/messenger?id=meshmessenger/' + encodeURIComponentEx(currentNode._id) + '/' + encodeURIComponentEx(userinfo._id) + '&title=' + currentNode.name;
5969
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
5970
if (e && (e.shiftKey == true)) {
5975
- window.open(url, 'meshmessenger:' + currentNode._id);
5971
+ safeNewWindow(url, 'meshmessenger:' + currentNode._id);
5972
} else {
5977
- window.open(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560');
5973
+ safeNewWindow(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560');
5974
}
5975
meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) });
5976
}
@@ -6386,7 +6382,7 @@
6382
var node = getNodeFromId(nodeid);
6383
if (node == null) return;
6384
if ([1, 2, 3, 4, 21, 22].indexOf(node.agent.id) >= 0) { url += '&os=win'; } else { url += '&os=linux'; }
6389
- window.open(url, 'xterm:' + nodeid);
6385
+ safeNewWindow(url, 'xterm:' + nodeid);
6386
return false;
6387
}
6388
@@ -10060,7 +10056,7 @@
10056
if (meshrights & 1) {
10057
// We can edit the mesh invite codes
10058
var x = "When enabled, invitation codes can be used by anyone to join devices to this device group using the following public link:" + '<br /><br />';
10063
- x += '<div style=width:100%;text-align:center><a target=_blank href="' + url + '">' + url + '</a></div><br />';
10059
+ x += '<div style=width:100%;text-align:center><a rel="noreferrer noopener" target=_blank href="' + url + '">' + url + '</a></div><br />';
10060
x += '<div style=margin-bottom:5px><label><input id=agentJoinCheck type=checkbox onclick=p20editmeshInviteCodeValidate() />' + "Enable Invite Codes" + '</label></div>';
10061
x += addHtmlValue("Invite Codes", '<input id=agentInviteCode style=width:236px onkeyup=p20editmeshInviteCodeValidate() placeholder="code1, code2, code3" />');
10062
x += addHtmlValue("Installation Type", '<select id=agentInviteType style=width:236px><option value=0>' + "Background and interactive" + '</option><option value=2>' + "Background only" + '</option><option value=1>' + "Interactive only" + '</option></select>');
@@ -10074,7 +10070,7 @@
10070
} else {
10071
// View codes only
10072
var x = "Invitation codes can be used by anyone to join devices to this device group using the following public link:" + '<br /><br />';
10077
- x += '<div style=width:100%;text-align:center><a target=_blank href="' + url + '">' + url + '</a></div><br />';
10073
+ x += '<div style=width:100%;text-align:center><a rel="noreferrer noopener" target=_blank href="' + url + '">' + url + '</a></div><br />';
10074
x += addHtmlValue("Invite Codes", currentMesh.invite.codes.join(', '));
10075
x += addHtmlValue("Installation Type", ["Background and interactive", "Background only", "Interactive only"][currentMesh.invite.flags & 3]);
10076
setDialogMode(2, "Invite Codes", 1, null, x);
@@ -11057,7 +11053,7 @@
11053
haltEvent(e);
11054
var url = '/messenger?id=meshmessenger/' + userid + '/' + encodeURIComponentEx(userinfo._id) + '&title=' + name;
11055
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
11060
- window.open(url, 'meshmessenger:' + userid);
11056
+ safeNewWindow(url, 'meshmessenger:' + userid);
11057
meshserver.send({ action: 'meshmessenger', userid: decodeURIComponent(userid) });
11058
return false;
11059
}
@@ -12408,7 +12404,7 @@
12404
}
12405
12406
function refreshRecodings() { meshserver.send({ action: 'recordings', limit: 1000 }); }
12411
- function openRecodringPlayer() { if (!xxdialogMode) window.open(window.location.origin + '{{{domainurl}}}player.htm', 'meshcentral-deskplayer'); }
12407
+ function openRecodringPlayer() { if (!xxdialogMode) safeNewWindow(window.location.origin + '{{{domainurl}}}player.htm', 'meshcentral-deskplayer'); }
12408
function p52updateInfo() {
12409
var elements = document.getElementsByClassName('RecordingCheckbox'), checkcount = 0;
12410
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) { checkcount++; } }
@@ -12582,7 +12578,7 @@
12578
else gotoDevice(n.nodeid, 10); // General
12579
} else {
12580
if ((n.tag != null) && n.tag.startsWith('meshmessenger/')) {
12585
- window.open('/messenger?id=' + n.tag + '&title=' + encodeURIComponentEx(n.username), n.tag.split('/')[2]);
12581
+ safeNewWindow('/messenger?id=' + n.tag + '&title=' + encodeURIComponentEx(n.username), n.tag.split('/')[2]);
12582
notificationDelete(id);
12583
}
12584
}
@@ -13007,15 +13003,15 @@
13003
if (event && (event.shiftKey == true) && (x != 15) && ('{{{currentNode}}}'.toLowerCase() == '')) {
13004
// Open the device in a different tab
13005
if ((x >= 10) && (x <= 19)) {
13010
- if (currentNode) { window.open(window.location.origin + '{{{domainurl}}}' + '?gotonode=' + currentNode._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentNode._id); }
13006
+ if (currentNode) { safeNewWindow(window.location.origin + '{{{domainurl}}}' + '?gotonode=' + currentNode._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentNode._id); }
13007
} else if ((x >= 20) && (x <= 29)) {
13012
- if (currentMesh) { window.open(window.location.origin + '{{{domainurl}}}' + '?gotomesh=' + currentMesh._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentMesh._id); }
13008
+ if (currentMesh) { safeNewWindow(window.location.origin + '{{{domainurl}}}' + '?gotomesh=' + currentMesh._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentMesh._id); }
13009
} else if ((x >= 30) && (x <= 39)) {
13014
- if (currentUser) { window.open(window.location.origin + '{{{domainurl}}}' + '?gotouser=' + ((serverinfo.crossDomain)?currentUser._id:currentUser._id.split('/')[2]) + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentUser._id); }
13010
+ if (currentUser) { safeNewWindow(window.location.origin + '{{{domainurl}}}' + '?gotouser=' + ((serverinfo.crossDomain)?currentUser._id:currentUser._id.split('/')[2]) + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentUser._id); }
13011
} else if ((x >= 50) && (x <= 59)) {
13016
- if (currentUserGroup) { window.open(window.location.origin + '{{{domainurl}}}' + '?gotougrp=' + ((serverinfo.crossDomain)?currentUserGroup._id:currentUserGroup._id.split('/')[2]) + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentUserGroup._id); }
13012
+ if (currentUserGroup) { safeNewWindow(window.location.origin + '{{{domainurl}}}' + '?gotougrp=' + ((serverinfo.crossDomain)?currentUserGroup._id:currentUserGroup._id.split('/')[2]) + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentUserGroup._id); }
13013
} else { // if (x < 10))
13018
- window.open(window.location.origin + '{{{domainurl}}}' + '?viewmode=' + x + '&hide=0', 'meshcentral:' + x);
13014
+ safeNewWindow(window.location.origin + '{{{domainurl}}}' + '?viewmode=' + x + '&hide=0', 'meshcentral:' + x);
13015
}
13016
return;
13017
}
@@ -13542,6 +13538,8 @@
13538
function encodeURIComponentEx(txt) { return encodeURIComponent(txt).replace(/'/g,'%27'); };
13539
function getUserName(userid) { if (users && users[userid] != null) return users[userid].name; return userid.split('/')[2]; }
13540
function round(value, precision) { var multiplier = Math.pow(10, precision || 0); return Math.round(value * multiplier) / multiplier; }
13541
+ function safeNewWindow(url, target) { var newWindow = window.open(url, target, 'noopener,noreferrer'); if (newWindow) { newWindow.opener = null; } }
13542
+
13543
</script>
13544
</body>
13545
</html>