UI: Display encrypted file paths in ransomware report table
This commit is contained in:
parent
d4d055ed95
commit
da3a2c1a02
|
@ -22,6 +22,7 @@ class ReportPageComponent extends AuthComponent {
|
||||||
attackReport: {},
|
attackReport: {},
|
||||||
zeroTrustReport: {},
|
zeroTrustReport: {},
|
||||||
ransomwareReport: {},
|
ransomwareReport: {},
|
||||||
|
ransomwareTelemetry: {},
|
||||||
allMonkeysAreDead: false,
|
allMonkeysAreDead: false,
|
||||||
runStarted: true,
|
runStarted: true,
|
||||||
selectedSection: ReportPageComponent.selectReport(this.sections),
|
selectedSection: ReportPageComponent.selectReport(this.sections),
|
||||||
|
@ -67,6 +68,13 @@ class ReportPageComponent extends AuthComponent {
|
||||||
ransomwareReport: res
|
ransomwareReport: res
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
this.authFetch('/api/telemetry?telem_category=file_encryption')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => {
|
||||||
|
this.setState({
|
||||||
|
ransomwareTelemetry: res
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +164,12 @@ class ReportPageComponent extends AuthComponent {
|
||||||
case 'zeroTrust':
|
case 'zeroTrust':
|
||||||
return (<ZeroTrustReport report={this.state.zeroTrustReport}/>);
|
return (<ZeroTrustReport report={this.state.zeroTrustReport}/>);
|
||||||
case 'ransomware':
|
case 'ransomware':
|
||||||
return (<RansomwareReport report={this.state.ransomwareReport}/>);
|
return (
|
||||||
|
<RansomwareReport
|
||||||
|
report={this.state.ransomwareReport}
|
||||||
|
telemetry={this.state.ransomwareTelemetry}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ class RansomwareReport extends React.Component {
|
||||||
<div>
|
<div>
|
||||||
<BreachSection/>
|
<BreachSection/>
|
||||||
<LateralMovement propagationStats={this.props.report.propagation_stats} />
|
<LateralMovement propagationStats={this.props.report.propagation_stats} />
|
||||||
<FileEncryptionTable tableData={this.props.report.encrypted_files_table} />
|
<FileEncryptionTable telemetry={this.props.telemetry} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import {renderArray} from '../common/RenderArrays';
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tableData: [TableRow]
|
telemetry: object,
|
||||||
}
|
}
|
||||||
|
|
||||||
type TableRow = {
|
type TableRow = {
|
||||||
exploits: [string],
|
hostname: string,
|
||||||
total_attempts: number,
|
file_path: number,
|
||||||
successful_encryptions: number,
|
|
||||||
hostname: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageSize = 10;
|
const PAGE_SIZE = 10;
|
||||||
|
const HOSTNAME_REGEX = /^(.* - )?(\S+) :.*$/
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
Header: 'Encrypted Files',
|
||||||
|
columns: [
|
||||||
|
{Header: 'Host', id: 'host', accessor: x => x.hostname},
|
||||||
|
{Header: 'File Path', id: 'file_path', accessor: x => x.file_path},
|
||||||
|
{Header: 'Encryption Algorithm',
|
||||||
|
id: 'encryption_algorithm',
|
||||||
|
accessor: () => {return 'Bit Flip'}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const FileEncryptionTable = (props: Props) => {
|
const FileEncryptionTable = (props: Props) => {
|
||||||
let defaultPageSize = props.tableData.length > pageSize ? pageSize : props.tableData.length;
|
let tableData = processTelemetry(props.telemetry);
|
||||||
let showPagination = props.tableData.length > pageSize;
|
let defaultPageSize = tableData.length > PAGE_SIZE ? PAGE_SIZE : tableData.length;
|
||||||
|
let showPagination = tableData.length > PAGE_SIZE;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3 className={'report-section-header'}>
|
<h3 className={'report-section-header'}>
|
||||||
|
@ -28,7 +39,7 @@ const FileEncryptionTable = (props: Props) => {
|
||||||
<div className="data-table-container">
|
<div className="data-table-container">
|
||||||
<ReactTable
|
<ReactTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={props.tableData}
|
data={tableData}
|
||||||
showPagination={showPagination}
|
showPagination={showPagination}
|
||||||
defaultPageSize={defaultPageSize}
|
defaultPageSize={defaultPageSize}
|
||||||
/>
|
/>
|
||||||
|
@ -37,30 +48,61 @@ const FileEncryptionTable = (props: Props) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = [
|
function processTelemetry(telemetry): Array<TableRow> {
|
||||||
{
|
// Sort ascending so that newer telemetry records overwrite older ones.
|
||||||
Header: 'Ransomware info',
|
sortTelemetry(telemetry);
|
||||||
columns: [
|
|
||||||
{Header: 'Machine', id: 'machine', accessor: x => x.hostname},
|
|
||||||
{Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)},
|
|
||||||
{Header: 'Files encrypted',
|
|
||||||
id: 'files_encrypted',
|
|
||||||
accessor: x => renderFileEncryptionStats(x.successful_encryptions, x.total_attempts)}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function renderFileEncryptionStats(successful: number, total: number) {
|
let latestTelemetry = getLatestTelemetry(telemetry);
|
||||||
let textClassName = ''
|
let tableData = getDataForTable(latestTelemetry);
|
||||||
|
|
||||||
if(successful > 0) {
|
return tableData;
|
||||||
textClassName = 'text-danger'
|
|
||||||
} else {
|
|
||||||
textClassName = 'text-dark'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<p className={textClassName}>{successful} out of {total}</p>);
|
function sortTelemetry(telemetry): void {
|
||||||
|
telemetry.objects.sort((a, b) => {
|
||||||
|
if (a.timestamp > b.timestamp) {
|
||||||
|
return 1;
|
||||||
|
} else if (a.timestamp > b.timestamp) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLatestTelemetry(telemetry) {
|
||||||
|
let latestTelemetry = {};
|
||||||
|
for (let i = 0; i < telemetry.objects.length; i++) {
|
||||||
|
let monkey = telemetry.objects[i].monkey
|
||||||
|
|
||||||
|
if (! (monkey in latestTelemetry)) {
|
||||||
|
latestTelemetry[monkey] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
telemetry.objects[i].data.files.forEach((file_encryption_telemetry) => {
|
||||||
|
latestTelemetry[monkey][file_encryption_telemetry.path] = file_encryption_telemetry.success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestTelemetry
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDataForTable(telemetry): Array<TableRow> {
|
||||||
|
let tableData = [];
|
||||||
|
|
||||||
|
for (const monkey in telemetry) {
|
||||||
|
for (const path in telemetry[monkey]) {
|
||||||
|
if (telemetry[monkey][path]) {
|
||||||
|
tableData.push({'hostname': parseHostname(monkey), 'file_path': path});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tableData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHostname(monkey) {
|
||||||
|
return monkey.match(HOSTNAME_REGEX)[2]
|
||||||
|
}
|
||||||
|
|
||||||
export default FileEncryptionTable;
|
export default FileEncryptionTable;
|
||||||
|
|
Loading…
Reference in New Issue