mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-26 16:06:33 +03:00
Added Shell command to MeshCtrl.js
This commit is contained in:
parent
143a39fca7
commit
c8b75d70d6
82
meshctrl.js
82
meshctrl.js
@ -7,7 +7,7 @@ try { require('ws'); } catch (ex) { console.log('Missing module "ws", type "npm
|
|||||||
var settings = {};
|
var settings = {};
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const args = require('minimist')(process.argv.slice(2));
|
const args = require('minimist')(process.argv.slice(2));
|
||||||
const possibleCommands = ['listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand'];
|
const possibleCommands = ['listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell'];
|
||||||
if (args.proxy != null) { try { require('https-proxy-agent'); } catch (ex) { console.log('Missing module "https-proxy-agent", type "npm install https-proxy-agent" to install it.'); return; } }
|
if (args.proxy != null) { try { require('https-proxy-agent'); } catch (ex) { console.log('Missing module "https-proxy-agent", type "npm install https-proxy-agent" to install it.'); return; } }
|
||||||
|
|
||||||
if (args['_'].length == 0) {
|
if (args['_'].length == 0) {
|
||||||
@ -42,6 +42,7 @@ if (args['_'].length == 0) {
|
|||||||
console.log(" Broadcast - Display a message to all online users.");
|
console.log(" Broadcast - Display a message to all online users.");
|
||||||
console.log(" ShowEvents - Display real-time server events in JSON format.");
|
console.log(" ShowEvents - Display real-time server events in JSON format.");
|
||||||
console.log(" RunCommand - Run a shell command on a remote device.");
|
console.log(" RunCommand - Run a shell command on a remote device.");
|
||||||
|
console.log(" Shell - Access command shell of a remote device.");
|
||||||
console.log("\r\nSupported login arguments:");
|
console.log("\r\nSupported login arguments:");
|
||||||
console.log(" --url [wss://server] - Server url, wss://localhost:443 is default.");
|
console.log(" --url [wss://server] - Server url, wss://localhost:443 is default.");
|
||||||
console.log(" --loginuser [username] - Login username, admin is default.");
|
console.log(" --loginuser [username] - Login username, admin is default.");
|
||||||
@ -165,6 +166,11 @@ if (args['_'].length == 0) {
|
|||||||
else { ok = true; }
|
else { ok = true; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'shell': {
|
||||||
|
if (args.id == null) { console.log("Missing device id, use --id [deviceid]"); }
|
||||||
|
else { ok = true; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'help': {
|
case 'help': {
|
||||||
if (args['_'].length < 2) {
|
if (args['_'].length < 2) {
|
||||||
console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.');
|
console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.');
|
||||||
@ -428,6 +434,16 @@ if (args['_'].length == 0) {
|
|||||||
console.log(" --powershell - Run in Windows PowerShell.");
|
console.log(" --powershell - Run in Windows PowerShell.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'shell': {
|
||||||
|
console.log("Access a command shell on a remote device, Example usages:\r\n");
|
||||||
|
console.log(" MeshCtrl Shell --id deviceid");
|
||||||
|
console.log(" MeshCtrl Shell --id deviceid --powershell");
|
||||||
|
console.log("\r\nRequired arguments:\r\n");
|
||||||
|
console.log(" --id [deviceid] - The device identifier.");
|
||||||
|
console.log("\r\nOptional arguments:\r\n");
|
||||||
|
console.log(" --powershell - Run a Windows PowerShell.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.');
|
console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.');
|
||||||
}
|
}
|
||||||
@ -790,6 +806,10 @@ function serverConnect() {
|
|||||||
ws.send(JSON.stringify({ action: 'runcommands', nodeids: [args.id], type: ((args.powershell) ? 2 : 0), cmds: args.run, responseid: 'meshctrl' }));
|
ws.send(JSON.stringify({ action: 'runcommands', nodeids: [args.id], type: ((args.powershell) ? 2 : 0), cmds: args.run, responseid: 'meshctrl' }));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'shell': {
|
||||||
|
ws.send("{\"action\":\"authcookie\"}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -811,6 +831,7 @@ function serverConnect() {
|
|||||||
}
|
}
|
||||||
switch (data.action) {
|
switch (data.action) {
|
||||||
case 'serverinfo': { // SERVERINFO
|
case 'serverinfo': { // SERVERINFO
|
||||||
|
settings.currentDomain = data.serverinfo.domain;
|
||||||
if (settings.cmd == 'serverinfo') {
|
if (settings.cmd == 'serverinfo') {
|
||||||
if (args.json) {
|
if (args.json) {
|
||||||
console.log(JSON.stringify(data.serverinfo, ' ', 2));
|
console.log(JSON.stringify(data.serverinfo, ' ', 2));
|
||||||
@ -821,6 +842,15 @@ function serverConnect() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'authcookie': { // SHELL
|
||||||
|
if (settings.cmd == 'shell') {
|
||||||
|
if ((args.id.split('/') != 3) && (settings.currentDomain != null)) { args.id = 'node/' + settings.currentDomain + '/' + args.id; }
|
||||||
|
var id = getRandomHex(6);
|
||||||
|
ws.send(JSON.stringify({ action: 'msg', nodeid: args.id, type: 'tunnel', usage: 1, value: '*/meshrelay.ashx?p=1&nodeid=' + args.id + '&id=' + id + '&rauth=' + data.rcookie, responseid: 'meshctrl' }));
|
||||||
|
connectShell(url.replace('/control.ashx', '/meshrelay.ashx?browser=1&p=1&nodeid=' + args.id + '&id=' + id + '&rauth=' + data.cookie));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'userinfo': { // USERINFO
|
case 'userinfo': { // USERINFO
|
||||||
if (settings.cmd == 'userinfo') {
|
if (settings.cmd == 'userinfo') {
|
||||||
if (args.json) {
|
if (args.json) {
|
||||||
@ -858,6 +888,7 @@ function serverConnect() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'msg': // SHELL
|
||||||
case 'adduser': // ADDUSER
|
case 'adduser': // ADDUSER
|
||||||
case 'deleteuser': // REMOVEUSER
|
case 'deleteuser': // REMOVEUSER
|
||||||
case 'createmesh': // ADDDEVICEGROUP
|
case 'createmesh': // ADDDEVICEGROUP
|
||||||
@ -1055,6 +1086,54 @@ function serverConnect() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect tunnel to a remote agent shell
|
||||||
|
function connectShell(url) {
|
||||||
|
// Setup WebSocket options
|
||||||
|
var options = { rejectUnauthorized: false, checkServerIdentity: onVerifyServer }
|
||||||
|
|
||||||
|
// Setup the HTTP proxy if needed
|
||||||
|
if (args.proxy != null) { const HttpsProxyAgent = require('https-proxy-agent'); options.agent = new HttpsProxyAgent(require('url').parse(args.proxy)); }
|
||||||
|
|
||||||
|
// Connect the WebSocket
|
||||||
|
console.log('Connecting...');
|
||||||
|
const WebSocket = require('ws');
|
||||||
|
settings.tunnelwsstate = 0;
|
||||||
|
settings.tunnelws = new WebSocket(url, options);
|
||||||
|
settings.tunnelws.on('open', function () { console.log('Waiting for Agent...'); }); // Wait for agent connection
|
||||||
|
settings.tunnelws.on('close', function () { console.log('Connection Closed.'); process.exit(); });
|
||||||
|
settings.tunnelws.on('error', function (err) { console.log(err); process.exit(); });
|
||||||
|
settings.tunnelws.on('message', function (rawdata) {
|
||||||
|
var data = rawdata.toString();
|
||||||
|
if (settings.tunnelwsstate == 1) {
|
||||||
|
process.stdout.write(data);
|
||||||
|
} else if (settings.tunnelwsstate == 0) {
|
||||||
|
if (data == 'c') {
|
||||||
|
// Send terminal size
|
||||||
|
var termSize = null;
|
||||||
|
if (typeof process.stdout.getWindowSize == 'function') { termSize = process.stdout.getWindowSize(); }
|
||||||
|
if (termSize != null) { settings.tunnelws.send(JSON.stringify({ ctrlChannel: '102938', type: 'options', cols: termSize[0], rows: termSize[1] })); }
|
||||||
|
console.log('Connected.');
|
||||||
|
settings.tunnelwsstate = 1;
|
||||||
|
settings.tunnelws.send('1');
|
||||||
|
}
|
||||||
|
else if (data == 'cr') { console.log('Connected, session is being recorded.'); settings.tunnelwsstate = 1; settings.tunnelws.send('1'); }
|
||||||
|
process.stdin.setEncoding('utf8');
|
||||||
|
process.stdin.setRawMode(true);
|
||||||
|
process.stdout.setEncoding('utf8');
|
||||||
|
process.stdin.unpipe(process.stdout);
|
||||||
|
process.stdout.unpipe(process.stdin);
|
||||||
|
process.stdin.on('data', function (data) { settings.tunnelws.send(Buffer.from(data)); });
|
||||||
|
//process.stdin.on('readable', function () { var chunk; while ((chunk = process.stdin.read()) !== null) { settings.tunnelws.send(Buffer.from(chunk)); } });
|
||||||
|
process.stdin.on('end', function () { process.exit(); });
|
||||||
|
process.stdout.on('resize', function() {
|
||||||
|
var termSize = null;
|
||||||
|
if (typeof process.stdout.getWindowSize == 'function') { termSize = process.stdout.getWindowSize(); }
|
||||||
|
if (termSize != null) { settings.tunnelws.send(JSON.stringify({ ctrlChannel: '102938', type: 'termsize', cols: termSize[0], rows: termSize[1] })); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Encode an object as a cookie using a key using AES-GCM. (key must be 32 bytes or more)
|
// Encode an object as a cookie using a key using AES-GCM. (key must be 32 bytes or more)
|
||||||
function encodeCookie(o, key) {
|
function encodeCookie(o, key) {
|
||||||
try {
|
try {
|
||||||
@ -1069,6 +1148,7 @@ function encodeCookie(o, key) {
|
|||||||
// Generate a random Intel AMT password
|
// Generate a random Intel AMT password
|
||||||
function checkAmtPassword(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
|
function checkAmtPassword(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
|
||||||
function getRandomAmtPassword() { var p; do { p = Buffer.from(crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } while (checkAmtPassword(p) == false); return p; }
|
function getRandomAmtPassword() { var p; do { p = Buffer.from(crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } while (checkAmtPassword(p) == false); return p; }
|
||||||
|
function getRandomHex(count) { return Buffer.from(crypto.randomBytes(count), 'binary').toString('hex'); }
|
||||||
function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
|
function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
|
||||||
|
|
||||||
function displayDeviceInfo(sysinfo, lastconnect, network) {
|
function displayDeviceInfo(sysinfo, lastconnect, network) {
|
||||||
|
20
meshuser.js
20
meshuser.js
@ -175,8 +175,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Route a command to a target node
|
// Route a command to a target node
|
||||||
function routeCommandToNode(command) {
|
function routeCommandToNode(command, func) {
|
||||||
if (common.validateString(command.nodeid, 8, 128) == false) return false;
|
if (common.validateString(command.nodeid, 8, 128) == false) { if (func) { func(false); } return false; }
|
||||||
var splitnodeid = command.nodeid.split('/');
|
var splitnodeid = command.nodeid.split('/');
|
||||||
// Check that we are in the same domain and the user has rights over this node.
|
// Check that we are in the same domain and the user has rights over this node.
|
||||||
if ((splitnodeid[0] == 'node') && (splitnodeid[1] == domain.id)) {
|
if ((splitnodeid[0] == 'node') && (splitnodeid[1] == domain.id)) {
|
||||||
@ -201,7 +201,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
|
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
|
||||||
delete command.nodeid; // Remove the nodeid since it's implied
|
delete command.nodeid; // Remove the nodeid since it's implied
|
||||||
try { agent.send(JSON.stringify(command)); } catch (ex) { }
|
try { agent.send(JSON.stringify(command)); } catch (ex) { }
|
||||||
}
|
} else { if (func) { func(false); } }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Check if a peer server is connected to this agent
|
// Check if a peer server is connected to this agent
|
||||||
@ -224,11 +224,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
command.remoteaddr = req.clientIp; // User's IP address
|
command.remoteaddr = req.clientIp; // User's IP address
|
||||||
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
|
if (typeof domain.desktopprivacybartext == 'string') { command.privacybartext = domain.desktopprivacybartext; } // Privacy bar text
|
||||||
parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
|
parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
|
||||||
}
|
} else { if (func) { func(false); } }
|
||||||
});
|
});
|
||||||
|
} else { if (func) { func(false); } return false; }
|
||||||
}
|
}
|
||||||
}
|
} else { if (func) { func(false); } return false; }
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +391,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
||||||
|
|
||||||
// Build server information object
|
// Build server information object
|
||||||
var serverinfo = { name: domain.dns ? domain.dns : parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (args.lanonly != true) && (parent.certificates.CommonName != null) && (parent.certificates.CommonName.indexOf('.') != -1)), domainauth: (domain.auth == 'sspi'), serverTime: Date.now() };
|
var serverinfo = { domain: domain.id, name: domain.dns ? domain.dns : parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (args.lanonly != true) && (parent.certificates.CommonName != null) && (parent.certificates.CommonName.indexOf('.') != -1)), domainauth: (domain.auth == 'sspi'), serverTime: Date.now() };
|
||||||
serverinfo.languages = parent.renderLanguages;
|
serverinfo.languages = parent.renderLanguages;
|
||||||
serverinfo.tlshash = Buffer.from(parent.webCertificateHashs[domain.id], 'binary').toString('hex').toUpperCase(); // SHA384 of server HTTPS certificate
|
serverinfo.tlshash = Buffer.from(parent.webCertificateHashs[domain.id], 'binary').toString('hex').toUpperCase(); // SHA384 of server HTTPS certificate
|
||||||
if ((parent.parent.config.domains[domain.id].amtacmactivation != null) && (parent.parent.config.domains[domain.id].amtacmactivation.acmmatch != null)) {
|
if ((parent.parent.config.domains[domain.id].amtacmactivation != null) && (parent.parent.config.domains[domain.id].amtacmactivation.acmmatch != null)) {
|
||||||
@ -1207,8 +1207,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a response is needed, set a callback function
|
||||||
|
var func = null;
|
||||||
|
if (command.responseid != null) { func = function (r) { try { ws.send(JSON.stringify({ action: 'msg', result: r ? 'OK' : 'Unable to route', tag: command.tag, responseid: command.responseid })); } catch (ex) { } } }
|
||||||
|
|
||||||
// Route this command to a target node
|
// Route this command to a target node
|
||||||
routeCommandToNode(command);
|
routeCommandToNode(command, func);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'events':
|
case 'events':
|
||||||
|
2
public/scripts/amt-wsman-0.2.0-min.js
vendored
2
public/scripts/amt-wsman-0.2.0-min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user