blob: 658b3c706feb04572450debfb320c4a328985dd9 [file]
<div x-data="alpineExfilled()" x-init="initPage()">
<div x-ref="headerFiles">
<h2>Exfilled Files</h2>
<p>
Exfilled files are uploaded from an agent on a host to the server during an operation are by default stored on the server at <span x-text="exfilDir"></span>.
<br>
If an operation has exfilled files, you can view and download the files here.
</p>
</div>
<hr>
<div>
<div class="buttons" x-show="numFiles !== 0">
<button class="button is-primary is-small" @click="toggleAllFiles(selectedFiles.length < numFiles)">
<span x-text="selectedFiles.length < numFiles ? 'Select' : 'Unselect'"></span>&nbsp;All Files
</button>
<button class="button is-primary is-small" x-bind:disabled="!selectedFiles.length" @click="downloadFiles">
<span class="icon"><i class="fas fa-save"></i></span>
<span>Download</span>
</button>
</div>
<p x-show="numFiles === 0">
The exfill directory (<span x-text="exfilDir"></span>) is empty.
</p>
<ul class="tree" x-show="numFiles !== 0">
<li class="root">
<span class="icon is-small"><i class="far fa-folder-open"></i></span>
<span x-text="exfilDir"></span>
</li>
<template x-for="agentName of Object.keys(files)" :key="agentName">
<li class="root-child">
<span class="icon is-small"><i class="far fa-folder-open"></i></span>
<span x-text="agentName"></span>
<template x-for="operation of Object.keys(files[agentName])" :key="operation">
<ul class="tree">
<li class="agentName-child">
<span class="icon is-small"><i class="fas fa-dragon"></i></span>
<span x-text="operation"></span>
<ul class="tree">
<template x-for="filename of Object.keys(files[agentName][operation])" :key="filename">
<li class="agent-child">
<label class="checkbox">
<input type="checkbox" class="file-checkbox" x-bind:value="files[agentName][operation][filename]" @click="toggleFile($el, files[agentName][operation][filename])">
<span class="icon is-small"><i class="far fa-file-alt"></i></span>
<span x-text="filename"></span>
</label>
</li>
</template>
<li class="agent-child" x-show="!Object.keys(files[agentName][operation]).length">
(none)
</li>
</ul>
</li>
</ul>
</template>
</li>
</template>
</ul>
</div>
</div>
<script>
function alpineExfilled() {
return {
exfilDir: '',
operations: [],
selectedOperationId: '',
files: {},
numFiles: 0,
selectedFiles: [],
initPage() {
apiV2('GET', '/api/v2/config/main').then((config) => {
this.exfilDir = config.exfil_dir;
return apiV2('GET', '/api/v2/operations');
}).then(async (operations) => {
this.operations = operations;
this.loadFiles();
// While the agents tab is open, keep checking for new/killed agents
while (this.$refs.headerFiles) {
await sleep(3000);
this.loadFiles();
}
}).catch((error) => {
toast('Error loading page', false);
console.error(error);
});
},
loadFiles() {
restRequest('POST', { index: 'exfil_files', operation_id: this.selectedOperationId }, (files) => {
this.files = JSON.parse(files);
this.numFiles = 0;
if (this.selectedOperationId) {
let validHosts = [];
this.operations.filter((operation) => operation.id === this.selectedOperationId).forEach((operation) => {
operation.host_group.forEach((host) => {
validHosts.push(`${host.host}-${host.paw}`);
});
});
Object.keys(this.files).forEach((host) => {
if (!validHosts.includes(host)) {
this.files[host] = {};
}
});
}
Object.keys(this.files).forEach((agent) => {
Object.keys(this.files[agent]).forEach((filename) => this.numFiles++);
});
});
},
toggleFile(el, filePath) {
if (el.checked) {
this.selectedFiles.push(filePath);
} else {
let index = this.selectedFiles.indexOf(filePath);
this.selectedFiles.splice(index, 1);
}
},
toggleAllFiles(selectAll) {
let inputs = Array.from(document.querySelectorAll('.file-checkbox'));
if (inputs.every((input) => input.checked)) {
inputs.forEach((input) => {
input.checked = false;
});
this.selectedFiles = [];
} else {
inputs.forEach((input) => {
input.checked = true;
});
this.selectedFiles = inputs.map((input) => input.value);
}
},
downloadFiles() {
this.selectedFiles.forEach((filePath) => {
let filename = filePath.split(/[\/\\]/);
filename = filename[filename.length - 1];
let uri = `/file/download_exfil?file=${btoa(filePath)}`;
let downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute('href', uri);
downloadAnchorNode.setAttribute('download', filename);
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
});
}
};
}
// # sourceURL=exfilled_files.js
</script>
<style scoped>
#select-operation {
max-width: 800px;
margin: 0 auto;
}
ul.tree {
margin: 0px 0px 0px 20px;
list-style: none !important;
line-height: 2em;
font-family: Arial;
}
ul.tree li {
font-size: 16px;
position: relative;
}
ul.tree li:before {
position: absolute;
left: -15px;
top: -10px;
content: '';
display: block;
border-left: 1px solid #ddd;
height: 1.7em;
border-bottom: 1px solid #ddd;
width: 10px;
}
ul.tree li:after {
position: absolute;
left: -15px;
bottom: -7px;
content: '';
display: block;
border-left: 1px solid #ddd;
height: 100%;
}
ul.tree li.root {
margin: 0px 0px 0px -20px;
}
ul.tree li.root:before {
display: none;
}
ul.tree li.root:after {
display: none;
}
ul.tree li.root-child:last-child:after {
display: none;
}
ul.tree li.agentName-child:after{
display: none;
}
ul.tree li.agent-child:nth-last-child(2):after {
display: none;
}
ul.tree li:last-child:after {
display: none;
}
</style>