Moved attack matrix to attack report page

This commit is contained in:
VakarisZ 2019-11-22 18:30:08 +02:00
parent 222644a3fb
commit cc1f46af32
6 changed files with 138 additions and 20 deletions

View File

@ -1,7 +1,8 @@
import flask_restful import flask_restful
from flask import jsonify
from monkey_island.cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from monkey_island.cc.services.attack.attack_report import AttackReportService from monkey_island.cc.services.attack.attack_report import AttackReportService
from monkey_island.cc.services.attack.attack_schema import SCHEMA
from flask import json, current_app
__author__ = "VakarisZ" __author__ = "VakarisZ"
@ -10,4 +11,9 @@ class AttackReport(flask_restful.Resource):
@jwt_required() @jwt_required()
def get(self): def get(self):
return jsonify(AttackReportService.get_latest_report()['techniques']) response_content = {'techniques': AttackReportService.get_latest_report()['techniques'], 'schema': SCHEMA}
return current_app.response_class(json.dumps(response_content,
indent=None,
separators=(",", ":"),
sort_keys=False) + "\n",
mimetype=current_app.config['JSONIFY_MIMETYPE'])

View File

@ -173,3 +173,15 @@ class AttackConfig(object):
for key, technique in list(attack_type['properties'].items()): for key, technique in list(attack_type['properties'].items()):
techniques[key] = technique['value'] techniques[key] = technique['value']
return techniques return techniques
@staticmethod
def get_techniques_for_report():
"""
:return: Format: {"T1110": {"selected": True, "type": "Credential Access", "T1075": ...}
"""
attack_config = AttackConfig.get_config()
techniques = {}
for type_name, attack_type in list(attack_config.items()):
for key, technique in list(attack_type['properties'].items()):
techniques[key] = {'selected': technique['value'], 'type': SCHEMA['properties'][type_name]['title']}
return techniques

View File

@ -58,13 +58,14 @@ class AttackReportService:
'name': REPORT_NAME 'name': REPORT_NAME
} }
for tech_id, value in list(AttackConfig.get_technique_values().items()): for tech_id, tech_info in list(AttackConfig.get_techniques_for_report().items()):
if value: try:
try: technique_report_data = TECHNIQUES[tech_id].get_report_data()
report['techniques'].update({tech_id: TECHNIQUES[tech_id].get_report_data()}) technique_report_data.update(tech_info)
except KeyError as e: report['techniques'].update({tech_id: technique_report_data})
LOG.error("Attack technique does not have it's report component added " except KeyError as e:
"to attack report service. %s" % e) LOG.error("Attack technique does not have it's report component added "
"to attack report service. %s" % e)
mongo.db.attack_report.replace_one({'name': REPORT_NAME}, report, upsert=True) mongo.db.attack_report.replace_one({'name': REPORT_NAME}, report, upsert=True)
return report return report

View File

@ -6,6 +6,7 @@ import '../../styles/Collapse.scss';
import AuthComponent from '../AuthComponent'; import AuthComponent from '../AuthComponent';
import {ScanStatus} from '../attack/techniques/Helpers'; import {ScanStatus} from '../attack/techniques/Helpers';
import Collapse from '@kunukn/react-collapse'; import Collapse from '@kunukn/react-collapse';
import Matrix from './attack/Matrix';
import T1210 from '../attack/techniques/T1210'; import T1210 from '../attack/techniques/T1210';
import T1197 from '../attack/techniques/T1197'; import T1197 from '../attack/techniques/T1197';
@ -66,7 +67,8 @@ class AttackReportPageComponent extends AuthComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
report: this.props.report, techniques: this.props.report['techniques'],
schema: this.props.report['schema'],
collapseOpen: '' collapseOpen: ''
}; };
} }
@ -81,7 +83,7 @@ class AttackReportPageComponent extends AuthComponent {
this.setState(state => ({collapseOpen: state.collapseOpen === technique ? null : technique})); this.setState(state => ({collapseOpen: state.collapseOpen === technique ? null : technique}));
getComponentClass(tech_id) { getComponentClass(tech_id) {
switch (this.state.report[tech_id].status) { switch (this.state.techniques[tech_id].status) {
case ScanStatus.SCANNED: case ScanStatus.SCANNED:
return 'collapse-info'; return 'collapse-info';
case ScanStatus.USED: case ScanStatus.USED:
@ -95,7 +97,7 @@ class AttackReportPageComponent extends AuthComponent {
return ( return (
<div key={tech_id} className={classNames('collapse-item', {'item--active': this.state.collapseOpen === tech_id})}> <div key={tech_id} className={classNames('collapse-item', {'item--active': this.state.collapseOpen === tech_id})}>
<button className={classNames('btn-collapse', this.getComponentClass(tech_id))} onClick={() => this.onToggle(tech_id)}> <button className={classNames('btn-collapse', this.getComponentClass(tech_id))} onClick={() => this.onToggle(tech_id)}>
<span>{this.state.report[tech_id].title}</span> <span>{this.state.techniques[tech_id].title}</span>
<span> <span>
<i className={classNames('fa', this.state.collapseOpen === tech_id ? 'fa-chevron-down' : 'fa-chevron-up')}></i> <i className={classNames('fa', this.state.collapseOpen === tech_id ? 'fa-chevron-down' : 'fa-chevron-up')}></i>
</span> </span>
@ -118,22 +120,26 @@ class AttackReportPageComponent extends AuthComponent {
const TechniqueComponent = tech_components[technique]; const TechniqueComponent = tech_components[technique];
return ( return (
<div className={`content ${collapseState}`}> <div className={`content ${collapseState}`}>
<TechniqueComponent data={this.state.report[technique]} reportData={this.props.reportData}/> <TechniqueComponent data={this.state.techniques[technique]}/>
</div> </div>
); );
} }
renderLegend() { renderLegend() {
return (<div id="header" className="row justify-content-between attack-legend"> return (<div id="header" className="row justify-content-between attack-legend">
<Col xs={4}> <Col xs={3}>
<i className="fa fa-circle-thin icon-unchecked"></i>
<span> - Dissabled</span>
</Col>
<Col xs={3}>
<i className="fa fa-circle icon-default"></i> <i className="fa fa-circle icon-default"></i>
<span> - Unscanned</span> <span> - Unscanned</span>
</Col> </Col>
<Col xs={4}> <Col xs={3}>
<i className="fa fa-circle icon-info"></i> <i className="fa fa-circle icon-info"></i>
<span> - Scanned</span> <span> - Scanned</span>
</Col> </Col>
<Col xs={4}> <Col xs={3}>
<i className="fa fa-circle icon-danger"></i> <i className="fa fa-circle icon-danger"></i>
<span> - Used</span> <span> - Used</span>
</Col> </Col>
@ -142,7 +148,7 @@ class AttackReportPageComponent extends AuthComponent {
generateReportContent() { generateReportContent() {
let content = []; let content = [];
Object.keys(this.state.report).forEach((tech_id) => { Object.keys(this.state.techniques).forEach((tech_id) => {
content.push(this.getTechniqueCollapse(tech_id)) content.push(this.getTechniqueCollapse(tech_id))
}); });
return ( return (
@ -153,8 +159,9 @@ class AttackReportPageComponent extends AuthComponent {
<p> <p>
This report shows information about ATT&CK techniques used by Infection Monkey. This report shows information about ATT&CK techniques used by Infection Monkey.
</p> </p>
{this.renderLegend()}
<Matrix techniques={this.state.techniques} schema={this.state.schema}/>
<div> <div>
{this.renderLegend()}
<section className="attack-report">{content}</section> <section className="attack-report">{content}</section>
</div> </div>
<br/> <br/>
@ -163,7 +170,7 @@ class AttackReportPageComponent extends AuthComponent {
} }
render() { render() {
if (Object.keys(this.state.report).length === 0 && this.state.runStarted) { if (Object.keys(this.state.techniques).length === 0 && this.state.runStarted) {
return (<h1>No techniques were scanned</h1>); return (<h1>No techniques were scanned</h1>);
} else { } else {
return (<div> {this.generateReportContent()}</div>); return (<div> {this.generateReportContent()}</div>);

View File

@ -0,0 +1,82 @@
import React from 'react';
import Checkbox from '../../ui-components/Checkbox';
import ReactTable from 'react-table';
import 'filepond/dist/filepond.min.css';
class MatrixComponent extends React.Component {
constructor(props) {
super(props);
this.state = {techniques: this.props.techniques,
schema: this.props.schema}
}
getColumns() {
let columns = [];
for(let type_key in this.state.schema.properties){
if (! this.state.schema.properties.hasOwnProperty(type_key)){
continue;
}
let tech_type = this.state.schema.properties[type_key];
columns.push({
Header: tech_type.title,
id: type_key,
accessor: x => MatrixComponent.renderTechnique(x[tech_type.title]),
style: {'whiteSpace': 'unset'}
});
}
return columns;
}
getTableRows() {
let rows = [];
for (let tech_id in this.state.techniques) {
if (this.state.techniques.hasOwnProperty(tech_id)){
let technique_added = false;
let technique = this.state.techniques[tech_id];
for(let row of rows){
if (! row.hasOwnProperty(technique.type)){
row[technique.type] = technique;
technique_added = true;
break;
}
}
if (! technique_added){
let newRow = {};
newRow[technique.type] = technique;
rows.push(newRow)
}
}
}
return rows;
}
static renderTechnique(technique) {
if (technique == null || typeof technique === undefined) {
return (<div/>)
} else {
return (
<Checkbox checked={technique.selected}
necessary={true}
name={technique.title}
changeHandler={function(){}}
status={technique.status}>
{technique.title}
</Checkbox>)
}
}
render() {
let tableRows = this.getTableRows();
return (
<div>
<div className={'attack-matrix'}>
<ReactTable columns={this.getColumns()}
data={tableRows}
showPagination={false}
defaultPageSize={tableRows.length}/>
</div>
</div>);
}
}
export default MatrixComponent;

View File

@ -14,10 +14,18 @@ class CheckboxComponent extends React.PureComponent {
changeHandler(name, checked) function will be called with these parameters: changeHandler(name, checked) function will be called with these parameters:
this.props.name (the name of this component) and this.props.name (the name of this component) and
this.state.checked (boolean indicating if this component is checked or not) this.state.checked (boolean indicating if this component is checked or not)
this.props.status (int) adds a class "status-x" to this checkbox. Used for styling.
*/ */
constructor(props) { constructor(props) {
super(props); super(props);
if (this.props.hasOwnProperty("status")){
this.status = this.props.status;
} else {
this.status = false
}
console.log(this.props);
this.state = { this.state = {
status: this.status,
checked: this.props.checked, checked: this.props.checked,
necessary: this.props.necessary, necessary: this.props.necessary,
isAnimating: false isAnimating: false
@ -48,6 +56,9 @@ class CheckboxComponent extends React.PureComponent {
// Creates class string for component // Creates class string for component
composeStateClasses(core) { composeStateClasses(core) {
let result = core; let result = core;
if (this.state.status) {
result += ' status-'+this.state.status;
}
if (this.state.necessary) { if (this.state.necessary) {
return result + ' blocked' return result + ' blocked'
} }
@ -56,7 +67,6 @@ class CheckboxComponent extends React.PureComponent {
} else { } else {
result += ' is-unchecked' result += ' is-unchecked'
} }
if (this.state.isAnimating) { if (this.state.isAnimating) {
result += ' do-ping'; result += ' do-ping';
} }