From 287115dded5adda7ac3645500c2d716b3ed18070 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 1 Aug 2019 14:07:05 +0300 Subject: [PATCH 001/186] Moved services which are related to the report to a dedicated directory --- monkey/monkey_island/cc/resources/report.py | 2 +- monkey/monkey_island/cc/resources/root.py | 2 +- monkey/monkey_island/cc/services/reporting/__init__.py | 0 monkey/monkey_island/cc/services/{ => reporting}/pth_report.py | 0 monkey/monkey_island/cc/services/{ => reporting}/report.py | 2 +- .../cc/services/{ => reporting}/test_PTHReportService.py | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 monkey/monkey_island/cc/services/reporting/__init__.py rename monkey/monkey_island/cc/services/{ => reporting}/pth_report.py (100%) rename monkey/monkey_island/cc/services/{ => reporting}/report.py (99%) rename monkey/monkey_island/cc/services/{ => reporting}/test_PTHReportService.py (96%) diff --git a/monkey/monkey_island/cc/resources/report.py b/monkey/monkey_island/cc/resources/report.py index 62a014fef..d75f91b0f 100644 --- a/monkey/monkey_island/cc/resources/report.py +++ b/monkey/monkey_island/cc/resources/report.py @@ -1,7 +1,7 @@ import flask_restful from monkey_island.cc.auth import jwt_required -from monkey_island.cc.services.report import ReportService +from monkey_island.cc.services.reporting.report import ReportService __author__ = "itay.mizeretz" diff --git a/monkey/monkey_island/cc/resources/root.py b/monkey/monkey_island/cc/resources/root.py index 2af73a45e..e3b3e9854 100644 --- a/monkey/monkey_island/cc/resources/root.py +++ b/monkey/monkey_island/cc/resources/root.py @@ -7,7 +7,7 @@ from flask import request, make_response, jsonify from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo from monkey_island.cc.services.node import NodeService -from monkey_island.cc.services.report import ReportService +from monkey_island.cc.services.reporting.report import ReportService from monkey_island.cc.services.attack.attack_report import AttackReportService from monkey_island.cc.utils import local_ip_addresses from monkey_island.cc.services.database import Database diff --git a/monkey/monkey_island/cc/services/reporting/__init__.py b/monkey/monkey_island/cc/services/reporting/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/pth_report.py b/monkey/monkey_island/cc/services/reporting/pth_report.py similarity index 100% rename from monkey/monkey_island/cc/services/pth_report.py rename to monkey/monkey_island/cc/services/reporting/pth_report.py diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/reporting/report.py similarity index 99% rename from monkey/monkey_island/cc/services/report.py rename to monkey/monkey_island/cc/services/reporting/report.py index 593bbfdaf..1d7ac162d 100644 --- a/monkey/monkey_island/cc/services/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -16,7 +16,7 @@ from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge import EdgeService from monkey_island.cc.services.node import NodeService from monkey_island.cc.utils import local_ip_addresses, get_subnets -from pth_report import PTHReportService +from monkey_island.cc.services.reporting.pth_report import PTHReportService from common.network.network_range import NetworkRange __author__ = "itay.mizeretz" diff --git a/monkey/monkey_island/cc/services/test_PTHReportService.py b/monkey/monkey_island/cc/services/reporting/test_PTHReportService.py similarity index 96% rename from monkey/monkey_island/cc/services/test_PTHReportService.py rename to monkey/monkey_island/cc/services/reporting/test_PTHReportService.py index 24e560ff0..f934f50ab 100644 --- a/monkey/monkey_island/cc/services/test_PTHReportService.py +++ b/monkey/monkey_island/cc/services/reporting/test_PTHReportService.py @@ -1,7 +1,7 @@ import uuid from monkey_island.cc.models import Monkey -from monkey_island.cc.services.pth_report import PTHReportService +from monkey_island.cc.services.reporting.pth_report import PTHReportService from monkey_island.cc.testing.IslandTestCase import IslandTestCase From 444144aecceca05a86cc03b94e8d19cf124d3d2d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 1 Aug 2019 14:58:55 +0300 Subject: [PATCH 002/186] Added report types: zero_trust and general Also moved the report resources to their own folder --- monkey/monkey_island/cc/app.py | 6 ++- monkey/monkey_island/cc/resources/report.py | 13 ----- .../cc/resources/reporting/__init__.py | 0 .../cc/resources/reporting/report.py | 51 +++++++++++++++++++ .../cc/ui/src/components/pages/ReportPage.js | 2 +- 5 files changed, 56 insertions(+), 16 deletions(-) delete mode 100644 monkey/monkey_island/cc/resources/report.py create mode 100644 monkey/monkey_island/cc/resources/reporting/__init__.py create mode 100644 monkey/monkey_island/cc/resources/reporting/report.py diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 2e04ef0be..2c778e61b 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -23,7 +23,7 @@ from monkey_island.cc.resources.monkey_download import MonkeyDownload from monkey_island.cc.resources.netmap import NetMap from monkey_island.cc.resources.node import Node from monkey_island.cc.resources.remote_run import RemoteRun -from monkey_island.cc.resources.report import Report +from monkey_island.cc.resources.reporting.report import Report from monkey_island.cc.resources.root import Root from monkey_island.cc.resources.telemetry import Telemetry from monkey_island.cc.resources.telemetry_feed import TelemetryFeed @@ -122,7 +122,9 @@ def init_api_resources(api): api.add_resource(NetMap, '/api/netmap', '/api/netmap/') api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/') api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') - api.add_resource(Report, '/api/report', '/api/report/') + + # report_type: zero_trust or general + api.add_resource(Report, '/api/report/') api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') api.add_resource(Log, '/api/log', '/api/log/') api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/') diff --git a/monkey/monkey_island/cc/resources/report.py b/monkey/monkey_island/cc/resources/report.py deleted file mode 100644 index d75f91b0f..000000000 --- a/monkey/monkey_island/cc/resources/report.py +++ /dev/null @@ -1,13 +0,0 @@ -import flask_restful - -from monkey_island.cc.auth import jwt_required -from monkey_island.cc.services.reporting.report import ReportService - -__author__ = "itay.mizeretz" - - -class Report(flask_restful.Resource): - - @jwt_required() - def get(self): - return ReportService.get_report() diff --git a/monkey/monkey_island/cc/resources/reporting/__init__.py b/monkey/monkey_island/cc/resources/reporting/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py new file mode 100644 index 000000000..9c6cf88da --- /dev/null +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -0,0 +1,51 @@ +import httplib + +import flask_restful +from flask import jsonify + +from monkey_island.cc.auth import jwt_required +from monkey_island.cc.services.reporting.report import ReportService + +ZERO_TRUST_REPORT_TYPE = "zero_trust" +GENERAL_REPORT_TYPE = "general" +REPORT_TYPES = [GENERAL_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] + +__author__ = "itay.mizeretz" + + +class Report(flask_restful.Resource): + + @jwt_required() + def get(self, report_type): + if report_type == GENERAL_REPORT_TYPE: + return ReportService.get_report() + elif report_type == ZERO_TRUST_REPORT_TYPE: + fakedict = { + "are_all_monkeys_done": False, + "findings": [ + { + "test": "Monkey 8 found a machine with no AV software active.", + "pillars": ["Devices"], + "events": [ + { + "timestamp": "2019-08-01 14:48:46.112000", + "message": "log1" + }, { + "timestamp": "2019-08-01 14:48:42.112000", + "message": "log2" + }] + }, + { + "test": "Monkey 6 successfully exploited machine XXX with shellshock.", + "pillars": ["Devices", "Networks"], + "events": [ + { + "timestamp": "2019-08-01 14:48:46.112000", + "message": "log3" + }] + } + ] + } + return jsonify(fakedict) + + flask_restful.abort(httplib.NOT_FOUND) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 4d1f6d4dd..b48d95a20 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -117,7 +117,7 @@ class ReportPageComponent extends AuthComponent { getReportFromServer(res) { if (res['completed_steps']['run_monkey']) { - this.authFetch('/api/report') + this.authFetch('/api/report/general') .then(res => res.json()) .then(res => { this.setState({ From 3dd7b9a15e4575a02dcc80b320f90486e3d69500 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 1 Aug 2019 19:39:09 +0300 Subject: [PATCH 003/186] Added a temporary Zero Trust report page. --- .../cc/ui/src/components/Main.js | 16 ++++- .../components/pages/ZeroTrustReportPage.js | 60 +++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 619d5e922..5dd17659b 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -7,10 +7,10 @@ import RunServerPage from 'components/pages/RunServerPage'; import ConfigurePage from 'components/pages/ConfigurePage'; import RunMonkeyPage from 'components/pages/RunMonkeyPage'; import MapPage from 'components/pages/MapPage'; -import PassTheHashMapPage from 'components/pages/PassTheHashMapPage'; import TelemetryPage from 'components/pages/TelemetryPage'; import StartOverPage from 'components/pages/StartOverPage'; import ReportPage from 'components/pages/ReportPage'; +import ZeroTrustReportPage from 'components/pages/ZeroTrustReportPage'; import LicensePage from 'components/pages/LicensePage'; import AuthComponent from 'components/AuthComponent'; import LoginPageComponent from 'components/pages/LoginPage'; @@ -148,7 +148,7 @@ class AppComponent extends AuthComponent {
  • - + 4. Security Report {this.state.completedSteps.report_done ? @@ -156,6 +156,15 @@ class AppComponent extends AuthComponent { : ''}
  • +
  • + + 5. + Zero Trust Report + {this.state.completedSteps.report_done ? + + : ''} + +
  • @@ -190,7 +199,8 @@ class AppComponent extends AuthComponent { {this.renderRoute('/infection/map', )} {this.renderRoute('/infection/telemetry', )} {this.renderRoute('/start-over', )} - {this.renderRoute('/report', )} + {this.renderRoute('/report/general', )} + {this.renderRoute('/report/zero_trust', )} {this.renderRoute('/license', )} diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js new file mode 100644 index 000000000..69aaf198d --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -0,0 +1,60 @@ +import React from 'react'; +import {Button, Col} from 'react-bootstrap'; +import BreachedServers from 'components/report-components/BreachedServers'; +import ScannedServers from 'components/report-components/ScannedServers'; +import PostBreach from 'components/report-components/PostBreach'; +import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; +import {edgeGroupToColor, options} from 'components/map/MapOptions'; +import StolenPasswords from 'components/report-components/StolenPasswords'; +import CollapsibleWellComponent from 'components/report-components/CollapsibleWell'; +import {Line} from 'rc-progress'; +import AuthComponent from '../AuthComponent'; +import PassTheHashMapPageComponent from "./PassTheHashMapPage"; +import StrongUsers from "components/report-components/StrongUsers"; +import AttackReport from "components/report-components/AttackReport"; + +let guardicoreLogoImage = require('../../images/guardicore-logo.png'); +let monkeyLogoImage = require('../../images/monkey-icon.svg'); + +class ZeroTrustReportPageComponent extends AuthComponent { + + constructor(props) { + super(props); + this.state = { + report: {}, + allMonkeysAreDead: false, + runStarted: true + }; + } + + render() { + let content; + let res; + this.getZeroTrustReportFromServer(res); + content = JSON.stringify(this.state.report); + + return ( + +

    4. Security Report

    +
    + {content} +
    + + ); + } + + // This dups the regular report + getZeroTrustReportFromServer(res) { + //if (res['completed_steps']['run_monkey']) { + this.authFetch('/api/report/zero_trust') + .then(res => res.json()) + .then(res => { + this.setState({ + report: res + }); + }); + //} + } +} + +export default ZeroTrustReportPageComponent; From 197ac585e80c7614a6be2e9f7e5d85732cb44a37 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 5 Aug 2019 14:27:47 +0300 Subject: [PATCH 004/186] Created basic zero trust report page mockup Extracted the reportHeader from the regular report --- .../cc/ui/src/components/pages/ReportPage.js | 28 ++----- .../components/pages/ZeroTrustReportPage.js | 84 ++++++++++++------- .../report-components/ReportHeader.js | 40 +++++++++ .../zerotrust/ZeroTrustPillars.js | 11 +++ .../zerotrust/ZeroTrustReportPillarGrades.js | 40 +++++++++ 5 files changed, 152 insertions(+), 51 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/ReportHeader.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index b48d95a20..936171aad 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -12,9 +12,10 @@ import AuthComponent from '../AuthComponent'; import PassTheHashMapPageComponent from "./PassTheHashMapPage"; import StrongUsers from "components/report-components/StrongUsers"; import AttackReport from "components/report-components/AttackReport"; +import ReportHeader, { ReportTypes } from "../report-components/ReportHeader"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); -let monkeyLogoImage = require('../../images/monkey-icon.svg'); + class ReportPageComponent extends AuthComponent { @@ -130,13 +131,16 @@ class ReportPageComponent extends AuthComponent { generateReportContent() { return (
    + { + // extract to print component. + }
    - {this.generateReportHeader()} +
    {this.generateReportOverviewSection()} {this.generateReportFindingsSection()} @@ -154,26 +158,6 @@ class ReportPageComponent extends AuthComponent { ); } - generateReportHeader() { - return ( - - ); - } - generateReportOverviewSection() { return (
    diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 69aaf198d..35e348e15 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,41 +1,32 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; -import BreachedServers from 'components/report-components/BreachedServers'; -import ScannedServers from 'components/report-components/ScannedServers'; -import PostBreach from 'components/report-components/PostBreach'; -import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; -import {edgeGroupToColor, options} from 'components/map/MapOptions'; -import StolenPasswords from 'components/report-components/StolenPasswords'; -import CollapsibleWellComponent from 'components/report-components/CollapsibleWell'; -import {Line} from 'rc-progress'; import AuthComponent from '../AuthComponent'; -import PassTheHashMapPageComponent from "./PassTheHashMapPage"; -import StrongUsers from "components/report-components/StrongUsers"; -import AttackReport from "components/report-components/AttackReport"; - -let guardicoreLogoImage = require('../../images/guardicore-logo.png'); -let monkeyLogoImage = require('../../images/monkey-icon.svg'); +import ReportHeader, { ReportTypes } from "../report-components/ReportHeader"; +import ZeroTrustReportPillarGrades from "../report-components/zerotrust/ZeroTrustReportPillarGrades"; class ZeroTrustReportPageComponent extends AuthComponent { constructor(props) { super(props); + this.state = { - report: {}, + report: { + findings: undefined + }, allMonkeysAreDead: false, - runStarted: true + runStarted: false }; } render() { - let content; let res; this.getZeroTrustReportFromServer(res); - content = JSON.stringify(this.state.report); + + const content = this.generateReportContent(); return ( -

    4. Security Report

    +

    5. Zero Trust Report

    {content}
    @@ -43,17 +34,52 @@ class ZeroTrustReportPageComponent extends AuthComponent { ); } - // This dups the regular report - getZeroTrustReportFromServer(res) { - //if (res['completed_steps']['run_monkey']) { - this.authFetch('/api/report/zero_trust') - .then(res => res.json()) - .then(res => { - this.setState({ - report: res - }); + generateReportContent() { + let content; + + console.log(this.state.report.findings); + + if (typeof this.state.report.findings === "undefined") { + content = "Still empty"; + } else { + content =
    + +
    ; + } + + return ( +
    +
    + +
    + +
    + +
    + {content} + THIS IS THE RAW SERVER DATA +
    +            {JSON.stringify(this.state.report, undefined, 2)}
    +          
    +
    +
    + ) + } + + print() { + alert("unimplemented"); + } + + getZeroTrustReportFromServer() { + this.authFetch('/api/report/zero_trust') + .then(res => res.json()) + .then(res => { + this.setState({ + report: res }); - //} + }); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ReportHeader.js b/monkey/monkey_island/cc/ui/src/components/report-components/ReportHeader.js new file mode 100644 index 000000000..adccb7371 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ReportHeader.js @@ -0,0 +1,40 @@ +import React, {Component} from "react"; +import {Col} from "react-bootstrap"; + +let monkeyLogoImage = require('../../images/monkey-icon.svg'); + +export const ReportTypes = { + zeroTrust: "Zero Trust", + security: "Security", + null: "" +}; + +export class ReportHeader extends Component { + report_type; + + render() { + return + } +} + +export default ReportHeader; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js new file mode 100644 index 000000000..bd8898205 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js @@ -0,0 +1,11 @@ +export const ZeroTrustPillars = { + data: "Data", + people: "People", + network: "Networks", + workload: "Workload", + devices: "Devices", + visibility: "Visibility & Analytics", + automation: "Automation & Orchestration" +}; + +export default ZeroTrustPillars; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js new file mode 100644 index 000000000..125151ea7 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js @@ -0,0 +1,40 @@ +import React, {Component} from "react"; +import ZeroTrustPillars from "./ZeroTrustPillars"; + +export class ZeroTrustReportPillarGrades extends Component { + render() { + let pillarsCounters = {}; + for(const pillar in ZeroTrustPillars){ + pillarsCounters[ZeroTrustPillars[pillar]] = 0; + } + + if (this.props.findings !== null) { + for (const finding of this.props.findings) { + console.log("finding: " + JSON.stringify(finding)); + if (typeof finding === 'object' && finding !== null) { + if (finding.hasOwnProperty("pillars")) { + for (const pillar of finding["pillars"]) { + pillarsCounters[pillar] = pillarsCounters[pillar] + 1; + } + } + } + } + } + + return ( +
    +

    + Pillars Overview +

    +

    + TODO: table with conditional colouring. +

    +
    +          {JSON.stringify(pillarsCounters, undefined, 2)}
    +        
    +
    + ) + } +} + +export default ZeroTrustReportPillarGrades; From 918d86c4d951012184eb8be5a4677ec40c72cbf9 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 5 Aug 2019 14:36:16 +0300 Subject: [PATCH 005/186] Moved report header to common components in folder --- monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js | 2 +- .../cc/ui/src/components/pages/ZeroTrustReportPage.js | 2 +- .../components/report-components/{ => common}/ReportHeader.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => common}/ReportHeader.js (93%) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 936171aad..132b68651 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -12,7 +12,7 @@ import AuthComponent from '../AuthComponent'; import PassTheHashMapPageComponent from "./PassTheHashMapPage"; import StrongUsers from "components/report-components/StrongUsers"; import AttackReport from "components/report-components/AttackReport"; -import ReportHeader, { ReportTypes } from "../report-components/ReportHeader"; +import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 35e348e15..f9702bf8d 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,7 +1,7 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; -import ReportHeader, { ReportTypes } from "../report-components/ReportHeader"; +import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; import ZeroTrustReportPillarGrades from "../report-components/zerotrust/ZeroTrustReportPillarGrades"; class ZeroTrustReportPageComponent extends AuthComponent { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ReportHeader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js similarity index 93% rename from monkey/monkey_island/cc/ui/src/components/report-components/ReportHeader.js rename to monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js index adccb7371..0db9cb93e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ReportHeader.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js @@ -1,7 +1,7 @@ import React, {Component} from "react"; import {Col} from "react-bootstrap"; -let monkeyLogoImage = require('../../images/monkey-icon.svg'); +let monkeyLogoImage = require('../../../images/monkey-icon.svg'); export const ReportTypes = { zeroTrust: "Zero Trust", From eaf923a0e44dbed452fc086e9d70e6d8d66d030b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 5 Aug 2019 14:39:17 +0300 Subject: [PATCH 006/186] Moved all the security report components into their own folder, finished folder structure --- .../src/components/attack/techniques/T1003.js | 4 +-- .../cc/ui/src/components/pages/ReportPage.js | 14 +++++----- .../{ => security}/AttackReport.js | 28 +++++++++---------- .../{ => security}/BreachedServers.js | 0 .../{ => security}/CollapsibleWell.js | 0 .../{ => security}/PostBreach.js | 0 .../{ => security}/ScannedServers.js | 0 .../{ => security}/StolenPasswords.js | 0 .../{ => security}/StrongUsers.js | 0 9 files changed, 23 insertions(+), 23 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => security}/AttackReport.js (87%) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => security}/BreachedServers.js (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => security}/CollapsibleWell.js (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => security}/PostBreach.js (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => security}/ScannedServers.js (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => security}/StolenPasswords.js (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/{ => security}/StrongUsers.js (100%) diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js index 07fd4a400..24d742c14 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js @@ -1,7 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' -import '../../report-components/StolenPasswords' -import StolenPasswordsComponent from "../../report-components/StolenPasswords"; +import '../../report-components/security/StolenPasswords' +import StolenPasswordsComponent from "../../report-components/security/StolenPasswords"; import {ScanStatus} from "./Helpers" diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 132b68651..d68a7b3f6 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -1,17 +1,17 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; -import BreachedServers from 'components/report-components/BreachedServers'; -import ScannedServers from 'components/report-components/ScannedServers'; -import PostBreach from 'components/report-components/PostBreach'; +import BreachedServers from 'components/report-components/security/BreachedServers'; +import ScannedServers from 'components/report-components/security/ScannedServers'; +import PostBreach from 'components/report-components/security/PostBreach'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; -import StolenPasswords from 'components/report-components/StolenPasswords'; -import CollapsibleWellComponent from 'components/report-components/CollapsibleWell'; +import StolenPasswords from 'components/report-components/security/StolenPasswords'; +import CollapsibleWellComponent from 'components/report-components/security/CollapsibleWell'; import {Line} from 'rc-progress'; import AuthComponent from '../AuthComponent'; import PassTheHashMapPageComponent from "./PassTheHashMapPage"; -import StrongUsers from "components/report-components/StrongUsers"; -import AttackReport from "components/report-components/AttackReport"; +import StrongUsers from "components/report-components/security/StrongUsers"; +import AttackReport from "components/report-components/security/AttackReport"; import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js similarity index 87% rename from monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js index f3bf7d9ce..c5c58c92f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js @@ -2,21 +2,21 @@ import React from 'react'; import {Col} from 'react-bootstrap'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; -import '../../styles/Collapse.scss'; -import AuthComponent from '../AuthComponent'; -import {ScanStatus} from "../attack/techniques/Helpers"; +import '../../../styles/Collapse.scss'; +import AuthComponent from '../../AuthComponent'; +import {ScanStatus} from "../../attack/techniques/Helpers"; import Collapse from '@kunukn/react-collapse'; -import T1210 from '../attack/techniques/T1210'; -import T1197 from '../attack/techniques/T1197'; -import T1110 from '../attack/techniques/T1110'; -import T1075 from "../attack/techniques/T1075"; -import T1003 from "../attack/techniques/T1003"; -import T1059 from "../attack/techniques/T1059"; -import T1086 from "../attack/techniques/T1086"; -import T1082 from "../attack/techniques/T1082"; -import T1145 from "../attack/techniques/T1145"; -import T1107 from "../attack/techniques/T1107"; -import T1065 from "../attack/techniques/T1065"; +import T1210 from '../../attack/techniques/T1210'; +import T1197 from '../../attack/techniques/T1197'; +import T1110 from '../../attack/techniques/T1110'; +import T1075 from "../../attack/techniques/T1075"; +import T1003 from "../../attack/techniques/T1003"; +import T1059 from "../../attack/techniques/T1059"; +import T1086 from "../../attack/techniques/T1086"; +import T1082 from "../../attack/techniques/T1082"; +import T1145 from "../../attack/techniques/T1145"; +import T1107 from "../../attack/techniques/T1107"; +import T1065 from "../../attack/techniques/T1065"; const tech_components = { 'T1210': T1210, diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/CollapsibleWell.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/CollapsibleWell.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/StrongUsers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/StrongUsers.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js From 50e020403bd642eba96768cd0564e97f92431af1 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 5 Aug 2019 15:13:55 +0300 Subject: [PATCH 007/186] Added basic findings table, no access to events yet --- .../components/pages/ZeroTrustReportPage.js | 5 ++ .../zerotrust/ZeroTrustReportFindingsTable.js | 47 +++++++++++++++++++ .../zerotrust/ZeroTrustReportPillarGrades.js | 3 -- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index f9702bf8d..82d90bb7c 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -3,6 +3,7 @@ import {Button, Col} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; import ZeroTrustReportPillarGrades from "../report-components/zerotrust/ZeroTrustReportPillarGrades"; +import ZeroTrustReportFindingsTable from "../report-components/zerotrust/ZeroTrustReportFindingsTable"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -43,7 +44,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { content = "Still empty"; } else { content =
    +

    Pillars Overview

    +

    Findings

    +
    ; } @@ -59,6 +63,7 @@ class ZeroTrustReportPageComponent extends AuthComponent {
    {content} +
    THIS IS THE RAW SERVER DATA
                 {JSON.stringify(this.state.report, undefined, 2)}
    diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js
    new file mode 100644
    index 000000000..d2f782213
    --- /dev/null
    +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js
    @@ -0,0 +1,47 @@
    +import React, {Component} from "react";
    +import ReactTable from "react-table";
    +
    +const columns = [
    +  {
    +    Header: 'Findings',
    +    columns: [
    +      { Header: 'Test', accessor: 'test'},
    +      { Header: 'Pillars', id: "pillars",
    +        accessor: x => {
    +          const pillars = x.pillars;
    +          const listItems = pillars.map((pillar) =>
    +            
  • {pillar}
  • + ); + return
      {listItems}
    + } + }, + { Header: 'Events', id:"events", accessor: x => ZeroTrustReportFindingsTable.buildEventsComponent(x)} + ] + } +]; + +const pageSize = 10; + +class ZeroTrustReportFindingsTable extends Component { + render() { + let defaultPageSize = this.props.findings.length > pageSize ? pageSize : this.props.findings.length; + let showPagination = this.props.findings.length > pageSize; + + return ( +
    + +
    + ); + } + + static buildEventsComponent(events) { + return ; + } +} + +export default ZeroTrustReportFindingsTable; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js index 125151ea7..e8638d6b1 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js @@ -23,9 +23,6 @@ export class ZeroTrustReportPillarGrades extends Component { return (
    -

    - Pillars Overview -

    TODO: table with conditional colouring.

    From 1b958ed300907ecec7cb68de7b54f3590611c767 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 5 Aug 2019 15:23:52 +0300 Subject: [PATCH 008/186] Added label component for pillar and added word wrap --- .../zerotrust/ZeroTrustReportFindingsTable.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js index d2f782213..aa93a8d41 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js @@ -1,16 +1,25 @@ import React, {Component} from "react"; import ReactTable from "react-table"; +import ZeroTrustPillars from "./ZeroTrustPillars"; + +class PillarLabel extends Component { + render() { + return {this.props.pillar} + } +} const columns = [ { Header: 'Findings', columns: [ - { Header: 'Test', accessor: 'test'}, + { Header: 'Test', accessor: 'test', + style: {'white-space': 'unset'} // This enables word wrap + }, { Header: 'Pillars', id: "pillars", accessor: x => { const pillars = x.pillars; const listItems = pillars.map((pillar) => -
  • {pillar}
  • +
  • ); return
      {listItems}
    } From ec15561bcb9147c261aa50bf93f8dd2dc2dd5a96 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 5 Aug 2019 16:16:53 +0300 Subject: [PATCH 009/186] Changes JSON to say if tests are conclusive --- .../cc/resources/reporting/report.py | 2 ++ .../zerotrust/ZeroTrustReportFindingsTable.js | 1 - .../zerotrust/ZeroTrustReportPillarGrades.js | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 9c6cf88da..1b2895f8f 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -25,6 +25,7 @@ class Report(flask_restful.Resource): "findings": [ { "test": "Monkey 8 found a machine with no AV software active.", + "conclusive": False, "pillars": ["Devices"], "events": [ { @@ -37,6 +38,7 @@ class Report(flask_restful.Resource): }, { "test": "Monkey 6 successfully exploited machine XXX with shellshock.", + "conclusive": True, "pillars": ["Devices", "Networks"], "events": [ { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js index aa93a8d41..040bac420 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js @@ -1,6 +1,5 @@ import React, {Component} from "react"; import ReactTable from "react-table"; -import ZeroTrustPillars from "./ZeroTrustPillars"; class PillarLabel extends Component { render() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js index e8638d6b1..9a551e0a2 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js @@ -1,20 +1,28 @@ import React, {Component} from "react"; import ZeroTrustPillars from "./ZeroTrustPillars"; +import ZeroTrustReportFindingsTable from "./ZeroTrustReportFindingsTable"; export class ZeroTrustReportPillarGrades extends Component { render() { let pillarsCounters = {}; - for(const pillar in ZeroTrustPillars){ - pillarsCounters[ZeroTrustPillars[pillar]] = 0; + for(const pillar in ZeroTrustPillars) { + pillarsCounters[ZeroTrustPillars[pillar]] = { + "conclusive": 0, + "possible": 0 + }; } if (this.props.findings !== null) { for (const finding of this.props.findings) { console.log("finding: " + JSON.stringify(finding)); if (typeof finding === 'object' && finding !== null) { - if (finding.hasOwnProperty("pillars")) { + if (finding.hasOwnProperty("pillars") && finding.hasOwnProperty("conclusive")) { for (const pillar of finding["pillars"]) { - pillarsCounters[pillar] = pillarsCounters[pillar] + 1; + if (finding.conclusive) { + pillarsCounters[pillar]["conclusive"] += 1; + } else { + pillarsCounters[pillar]["possible"] += 1; + } } } } From b17d0a841b31db28fc5dcb258c1d1f885b062f6e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 5 Aug 2019 17:16:02 +0300 Subject: [PATCH 010/186] Added "Show events" button and modal --- .../zerotrust/ZeroTrustReportFindingsTable.js | 74 +++++++++++++++++-- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js index 040bac420..aa2e325c4 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js @@ -1,5 +1,6 @@ import React, {Component} from "react"; import ReactTable from "react-table"; +import {Button, Modal} from "react-bootstrap"; class PillarLabel extends Component { render() { @@ -7,12 +8,72 @@ class PillarLabel extends Component { } } +class EventsModal extends Component { + constructor(props) { + super(props); + } + + render() { + return ( +
    + this.props.hideCallback()}> + +

    Events

    + +
    {JSON.stringify(this.props.events)}
    + +
    + +
    +
    +
    +
    + ); + } +} + + +class EventsAndButtonComponent extends Component { + constructor(props) { + super(props); + this.state = { + show: false + } + } + + hide = () => { + this.setState({show: false}); + }; + + show = () => { + this.setState({show: true}); + }; + + render() { + return ( +
    + +

    + +

    +
    + ); + } + +} + const columns = [ { Header: 'Findings', columns: [ { Header: 'Test', accessor: 'test', - style: {'white-space': 'unset'} // This enables word wrap + style: {'whiteSpace': 'unset'} // This enables word wrap }, { Header: 'Pillars', id: "pillars", accessor: x => { @@ -23,7 +84,11 @@ const columns = [ return
      {listItems}
    } }, - { Header: 'Events', id:"events", accessor: x => ZeroTrustReportFindingsTable.buildEventsComponent(x)} + { Header: 'Events', id:"events", + accessor: x => { + return ; + } + } ] } ]; @@ -46,10 +111,7 @@ class ZeroTrustReportFindingsTable extends Component {
    ); } - - static buildEventsComponent(events) { - return ; - } } + export default ZeroTrustReportFindingsTable; From ad5929b4e507aa96201f4746bcc88caaa84bd443 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 6 Aug 2019 18:19:39 +0300 Subject: [PATCH 011/186] Added event timeline (not done yet, need to add title to data) and deleted console.logs --- monkey/monkey_island/cc/ui/package-lock.json | 325 ++++++++---------- monkey/monkey_island/cc/ui/package.json | 1 + .../components/pages/ZeroTrustReportPage.js | 2 - .../zerotrust/EventsModal.js | 32 ++ .../zerotrust/EventsTimeline.js | 18 + .../zerotrust/ZeroTrustReportFindingsTable.js | 38 +- .../zerotrust/ZeroTrustReportPillarGrades.js | 1 - 7 files changed, 192 insertions(+), 225 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 934b567e7..324112f9d 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -56,7 +56,7 @@ "@babel/helper-module-imports": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha1-lggbcRHkhtpNLNlxrRpP4hbMLj0=", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", "requires": { "@babel/types": "^7.0.0" }, @@ -74,7 +74,7 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "to-fast-properties": { "version": "2.0.0", @@ -259,7 +259,7 @@ "@emotion/cache": { "version": "10.0.9", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.9.tgz", - "integrity": "sha1-4Me3oon3Uw7c+tTc84WL0uVwCm8=", + "integrity": "sha512-f7MblpE2xoimC4fCMZ9pivmsIn7hyWRIvY75owMDi8pdOSeh+w5tH3r4hBJv/LLrwiMM7cTQURqTPcYoL5pWnw==", "requires": { "@emotion/sheet": "0.9.2", "@emotion/stylis": "0.8.3", @@ -270,7 +270,7 @@ "@emotion/core": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.10.tgz", - "integrity": "sha1-jTEU5aL4sXinBnxgOik3UW8YCwg=", + "integrity": "sha512-U1aE2cOWUscjc8ZJ3Cx32udOzLeRoJwGxBH93xQD850oQFpwPKZARzdUtdc9SByUOwzSFYxhDhrpXnV34FJmWg==", "requires": { "@emotion/cache": "^10.0.9", "@emotion/css": "^10.0.9", @@ -292,12 +292,12 @@ "@emotion/hash": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz", - "integrity": "sha1-mDNyI0E3n7fWfwaksAqzw3kT2lM=" + "integrity": "sha512-OYpa/Sg+2GDX+jibUfpZVn1YqSVRpYmTLF2eyAfrFTIJSbwyIrc+YscayoykvaOME/wV4BV0Sa0yqdMrgse6mA==" }, "@emotion/memoize": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz", - "integrity": "sha1-6TwTlCWSz17wGqgpdETcGSvu5S8=" + "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==" }, "@emotion/serialize": { "version": "0.11.6", @@ -314,27 +314,27 @@ "@emotion/sheet": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz", - "integrity": "sha1-dOXGteSJobowqyRqte7dlpFkh8Q=" + "integrity": "sha512-pVBLzIbC/QCHDKJF2E82V2H/W/B004mDFQZiyo/MSR+VC4pV5JLG0TF/zgQDFvP3fZL/5RTPGEmXlYJBMUuJ+A==" }, "@emotion/stylis": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.3.tgz", - "integrity": "sha1-PKfpvLMbPLSvuutmFW2G7oXiMkY=" + "integrity": "sha512-M3nMfJ6ndJMYloSIbYEBq6G3eqoYD41BpDOxreE8j0cb4fzz/5qvmqU9Mb2hzsXcCnIlGlWhS03PCzVGvTAe0Q==" }, "@emotion/unitless": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz", - "integrity": "sha1-YxCgR/EtIaEDb7AxMXIZiSRAQW8=" + "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg==" }, "@emotion/utils": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz", - "integrity": "sha1-hSm3QSputLSL325yDMG45uHhdig=" + "integrity": "sha512-8M3VN0hetwhsJ8dH8VkVy7xo5/1VoBsDOk/T4SJOeXwTO1c4uIqVNx2qyecLFnnUWD5vvUqHQ1gASSeUN6zcTg==" }, "@emotion/weak-memoize": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz", - "integrity": "sha1-Y5hdPYsCUw4IaZYvTaCRQu6OIA4=" + "integrity": "sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA==" }, "@kunukn/react-collapse": { "version": "1.0.5", @@ -630,7 +630,6 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -689,7 +688,7 @@ "are-we-there-yet": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha1-SzXClE8GKov82mZBB2A1D+nd/CE=", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -703,12 +702,12 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=" + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -722,7 +721,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" } @@ -3004,7 +3003,7 @@ "clone-deep": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", - "integrity": "sha1-ANs6Hhc2VnMNEYjD1qztbX6pdxM=", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", "requires": { "for-own": "^1.0.0", "is-plain-object": "^2.0.4", @@ -3023,7 +3022,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -3313,7 +3312,7 @@ "copy-to-clipboard": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz", - "integrity": "sha1-9OgvSogw3ORma3643tDJvMMTq6k=", + "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==", "requires": { "toggle-selection": "^1.0.3" } @@ -3369,7 +3368,7 @@ }, "yargs": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { @@ -3420,8 +3419,8 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "requires": { - "argparse": "1.0.9", - "esprima": "4.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "parse-json": { @@ -3429,8 +3428,8 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "requires": { - "error-ex": "1.3.2", - "json-parse-better-errors": "1.0.2" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } } } @@ -5271,7 +5270,7 @@ "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha1-q8/Iunb3CMQql7PWhbfpRQv7nOQ=" + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, "find-up": { "version": "1.1.2", @@ -5523,15 +5522,14 @@ "dev": true, "optional": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.1.1", @@ -5582,8 +5580,7 @@ "balanced-match": { "version": "0.4.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.1", @@ -5598,7 +5595,6 @@ "version": "0.0.9", "bundled": true, "dev": true, - "optional": true, "requires": { "inherits": "~2.0.0" } @@ -5607,7 +5603,6 @@ "version": "2.10.1", "bundled": true, "dev": true, - "optional": true, "requires": { "hoek": "2.x.x" } @@ -5616,7 +5611,6 @@ "version": "1.1.7", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^0.4.1", "concat-map": "0.0.1" @@ -5625,8 +5619,7 @@ "buffer-shims": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "caseless": { "version": "0.12.0", @@ -5643,14 +5636,12 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "combined-stream": { "version": "1.0.5", "bundled": true, "dev": true, - "optional": true, "requires": { "delayed-stream": "~1.0.0" } @@ -5658,20 +5649,17 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "cryptiles": { "version": "2.0.5", @@ -5717,8 +5705,7 @@ "delayed-stream": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "delegates": { "version": "1.0.0", @@ -5732,7 +5719,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "extend": { @@ -5744,8 +5731,7 @@ "extsprintf": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "forever-agent": { "version": "0.6.1", @@ -5767,14 +5753,12 @@ "fs.realpath": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "fstream": { "version": "1.0.11", "bundled": true, "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -5830,7 +5814,6 @@ "version": "7.1.2", "bundled": true, "dev": true, - "optional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5843,8 +5826,7 @@ "graceful-fs": { "version": "4.1.11", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "har-schema": { "version": "1.0.5", @@ -5858,8 +5840,8 @@ "dev": true, "optional": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "^4.9.1", + "har-schema": "^1.0.5" } }, "has-unicode": { @@ -5883,8 +5865,7 @@ "hoek": { "version": "2.16.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "http-signature": { "version": "1.1.1", @@ -5901,7 +5882,6 @@ "version": "1.0.6", "bundled": true, "dev": true, - "optional": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5910,8 +5890,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.4", @@ -5923,7 +5902,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5937,8 +5915,7 @@ "isarray": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "isstream": { "version": "0.1.2", @@ -5952,7 +5929,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "jsbn": { @@ -5973,7 +5950,7 @@ "dev": true, "optional": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stringify-safe": { @@ -6011,14 +5988,12 @@ "mime-db": { "version": "1.27.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "mime-types": { "version": "2.1.15", "bundled": true, "dev": true, - "optional": true, "requires": { "mime-db": "~1.27.0" } @@ -6027,7 +6002,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6035,14 +6009,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6095,8 +6067,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "oauth-sign": { "version": "0.8.2", @@ -6114,7 +6085,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6144,8 +6114,7 @@ "path-is-absolute": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "performance-now": { "version": "0.2.0", @@ -6156,8 +6125,7 @@ "process-nextick-args": { "version": "1.0.7", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "punycode": { "version": "1.4.1", @@ -6195,7 +6163,6 @@ "version": "2.2.9", "bundled": true, "dev": true, - "optional": true, "requires": { "buffer-shims": "~1.0.0", "core-util-is": "~1.0.0", @@ -6240,7 +6207,6 @@ "version": "2.6.1", "bundled": true, "dev": true, - "optional": true, "requires": { "glob": "^7.0.5" } @@ -6248,8 +6214,7 @@ "safe-buffer": { "version": "5.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "semver": { "version": "5.3.0", @@ -6307,7 +6272,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6318,7 +6282,6 @@ "version": "1.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -6333,7 +6296,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6348,7 +6310,6 @@ "version": "2.2.1", "bundled": true, "dev": true, - "optional": true, "requires": { "block-stream": "*", "fstream": "^1.0.2", @@ -6404,8 +6365,7 @@ "util-deprecate": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "uuid": { "version": "3.0.1", @@ -6434,15 +6394,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, "fstream": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha1-Touo7i1Ivk99DeUFRVVI6uWTIEU=", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -6500,7 +6459,7 @@ "gaze": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha1-xEFzPhO5J6yMD/C0w7Az8ogSkko=", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "requires": { "globule": "^1.0.0" } @@ -6570,7 +6529,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, - "optional": true, "requires": { "is-glob": "^2.0.0" } @@ -6614,7 +6572,7 @@ "globule": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha1-Xf+xsZHyLSB5epNptJ6rTpg5aW0=", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", "requires": { "glob": "~7.1.1", "lodash": "~4.17.10", @@ -7820,8 +7778,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "optional": true + "dev": true }, "is-finite": { "version": "1.0.2", @@ -7841,7 +7798,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -7906,8 +7862,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true, - "optional": true + "dev": true }, "is-promise": { "version": "2.1.0", @@ -8070,7 +8025,7 @@ "js-base64": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", - "integrity": "sha1-Hvo57yxfeYC7F4St5KivLeMpESE=" + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" }, "js-file-download": { "version": "0.4.4", @@ -8617,7 +8572,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -8648,7 +8603,7 @@ "dev": true, "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -8757,8 +8712,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -9554,8 +9509,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "optional": true + "dev": true }, "loose-envify": { "version": "1.3.1", @@ -10100,7 +10054,7 @@ "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha1-mA9vcthSEaU0fGsrwYxbhMPrR+8=", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", "requires": { "encoding": "^0.1.11", "is-stream": "^1.0.1" @@ -10115,7 +10069,7 @@ "node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha1-VAMEJhwzDoDQ1e3OJTpoyzlkIYw=", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", "requires": { "fstream": "^1.0.0", "glob": "^7.0.3", @@ -10134,7 +10088,7 @@ "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha1-kNDVRDnaWHzX6EO/twRfUL0ivfE=", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -10145,12 +10099,12 @@ "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=" + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "fast-deep-equal": { "version": "2.0.1", @@ -10160,7 +10114,7 @@ "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -10169,17 +10123,17 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI=" + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" }, "mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha1-tvjQs+lR77d97eyhlM/20W9nb4E=", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "requires": { "mime-db": "1.40.0" } @@ -10187,17 +10141,17 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "psl": { "version": "1.1.32", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", - "integrity": "sha1-PxMnF88vnBaXJLK2yvNzz2lBmNs=" + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -10224,7 +10178,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "semver": { "version": "5.3.0", @@ -10234,7 +10188,7 @@ "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -10243,7 +10197,7 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" } } }, @@ -10325,7 +10279,7 @@ "node-sass": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", - "integrity": "sha1-CRT1MZMjgBFKMMxfpPpjIzol8Bc=", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -10349,7 +10303,7 @@ "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha1-kNDVRDnaWHzX6EO/twRfUL0ivfE=", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -10360,7 +10314,7 @@ "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=" + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "cross-spawn": { "version": "3.0.1", @@ -10374,7 +10328,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "fast-deep-equal": { "version": "2.0.1", @@ -10384,7 +10338,7 @@ "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -10393,22 +10347,22 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI=" + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" }, "mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha1-tvjQs+lR77d97eyhlM/20W9nb4E=", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "requires": { "mime-db": "1.40.0" } @@ -10421,17 +10375,17 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "psl": { "version": "1.1.32", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", - "integrity": "sha1-PxMnF88vnBaXJLK2yvNzz2lBmNs=" + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -10458,12 +10412,12 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -10472,7 +10426,7 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" } } }, @@ -13227,7 +13181,7 @@ "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -13497,7 +13451,7 @@ "osenv": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha1-hc36+uso6Gd/QW4odZK18/SepBA=", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -13730,7 +13684,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "0.1.7", @@ -14005,7 +13959,7 @@ "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { "asap": "~2.0.3" } @@ -14360,6 +14314,15 @@ "scheduler": "^0.11.2" } }, + "react-event-timeline": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/react-event-timeline/-/react-event-timeline-1.6.3.tgz", + "integrity": "sha512-hMGhC9/Xx3sPF/TSlMCA13hZm/2c5CvBxbkDM7bQ4yq6VJ6AmhjqKPnU6/3nVmWUGpK3YqhHb95OiqulxVD3Eg==", + "dev": true, + "requires": { + "prop-types": "^15.6.0" + } + }, "react-fa": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/react-fa/-/react-fa-5.0.0.tgz", @@ -14600,7 +14563,7 @@ "react-spinners": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.5.4.tgz", - "integrity": "sha1-WBZvi/hMvwbdesytS5SleXz+qbk=", + "integrity": "sha512-jo7BE8prvnZNL7xNrQL16geVXH6LmaWrhvvXnmUwz2MhFO14bhlAdCLuKCwqmUJ37kGphH0C2CJRThwkjfpVzw==", "requires": { "@emotion/core": "^10.0.4", "prop-types": "^15.5.10", @@ -14648,7 +14611,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0 || ^4.0.0" } } } @@ -14751,7 +14714,7 @@ "recompose": { "version": "0.30.0", "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz", - "integrity": "sha1-gnc2QbOSfox9JKDYfWWu66GKq9A=", + "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==", "requires": { "@babel/runtime": "^7.0.0", "change-emitter": "^0.1.2", @@ -15047,7 +15010,7 @@ "resolve-pathname": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha1-fpriHtgV/WOrGJre7mTcgx7vqHk=" + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" }, "resolve-url": { "version": "0.2.1", @@ -15242,7 +15205,7 @@ "sass-loader": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", - "integrity": "sha1-Fv1ROMuLQkv4p1lSihly1yqtBp0=", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", "requires": { "clone-deep": "^2.0.1", "loader-utils": "^1.0.1", @@ -15255,12 +15218,12 @@ "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=" + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "requires": { "minimist": "^1.2.0" } @@ -15268,7 +15231,7 @@ "loader-utils": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "requires": { "big.js": "^5.2.2", "emojis-list": "^2.0.0", @@ -15283,7 +15246,7 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha1-eQp89v6lRZuslhELKbYEEtyP+Ws=" + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" } } }, @@ -15525,7 +15488,7 @@ "shallow-clone": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", - "integrity": "sha1-RIDNBuiC72iyrYij6lSDLixItXE=", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", "requires": { "is-extendable": "^0.1.1", "kind-of": "^5.0.0", @@ -15535,7 +15498,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -16027,7 +15990,7 @@ "stdout-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha1-WsF0zdXNcmEEqgwLK9g4FdjVNd4=", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "requires": { "readable-stream": "^2.0.1" }, @@ -16040,12 +16003,12 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=" + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -16059,7 +16022,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" } @@ -16397,7 +16360,7 @@ "tar": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha1-DKiEhWLHKZuLRG/2pNYM27I+3EA=", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", "requires": { "block-stream": "*", "fstream": "^1.0.12", @@ -16588,7 +16551,7 @@ "true-case-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha1-+BO1qMhrQNpZYGcisUTjIleZ9H0=", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "requires": { "glob": "^7.1.2" } @@ -17069,7 +17032,7 @@ "value-equal": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha1-xb3S9U7gk8BIOdcc4uR1imiQq8c=" + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" }, "vary": { "version": "1.1.2", @@ -19045,8 +19008,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -19089,8 +19051,7 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", @@ -19101,8 +19062,7 @@ "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -19219,8 +19179,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -19232,7 +19191,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -19247,7 +19205,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "1.1.11" } @@ -19255,14 +19212,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -19281,7 +19236,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -19362,8 +19316,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -19375,7 +19328,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -19461,8 +19413,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -19498,7 +19449,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -19518,7 +19468,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -19562,14 +19511,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -19979,7 +19926,7 @@ "whatwg-fetch": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha1-/IBORYzEYACbGiuWa8iBfSV4rvs=" + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" }, "which": { "version": "1.3.0", @@ -19998,7 +19945,7 @@ "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha1-rgdOa9wMFKQx6ATmJFScYzsABFc=", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "requires": { "string-width": "^1.0.2 || 2" } diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index a76541e36..48bc1be31 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -55,6 +55,7 @@ "null-loader": "^0.1.1", "phantomjs-prebuilt": "^2.1.16", "react-addons-test-utils": "^15.6.2", + "react-event-timeline": "^1.6.3", "react-hot-loader": "^4.3.11", "rimraf": "^2.6.2", "style-loader": "^0.22.1", diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 82d90bb7c..40c1b33cd 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -38,8 +38,6 @@ class ZeroTrustReportPageComponent extends AuthComponent { generateReportContent() { let content; - console.log(this.state.report.findings); - if (typeof this.state.report.findings === "undefined") { content = "Still empty"; } else { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js new file mode 100644 index 000000000..19dd7761a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js @@ -0,0 +1,32 @@ +import React, {Component} from "react"; +import {Modal} from "react-bootstrap"; +import {EventsTimeline} from "./EventsTimeline"; + +export class EventsModal extends Component { + constructor(props) { + super(props); + } + + render() { + return ( +
    + this.props.hideCallback()}> + +

    +
    Events
    +

    + + + +
    + +
    +
    +
    +
    + ); + } +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js new file mode 100644 index 000000000..821b6bd00 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -0,0 +1,18 @@ +import React, {Component} from "react"; +import {Timeline, TimelineEvent} from "react-event-timeline"; + +export class EventsTimeline extends Component { + render() { + return ( +
    + + { + this.props["events"].map(event => ( + {event.message} + )) + } + +
    + ); + } +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js index aa2e325c4..9088c5571 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js @@ -1,38 +1,10 @@ import React, {Component} from "react"; import ReactTable from "react-table"; -import {Button, Modal} from "react-bootstrap"; +import {Button} from "react-bootstrap"; +import {EventsModal} from "./EventsModal"; -class PillarLabel extends Component { - render() { - return {this.props.pillar} - } -} - -class EventsModal extends Component { - constructor(props) { - super(props); - } - - render() { - return ( -
    - this.props.hideCallback()}> - -

    Events

    - -
    {JSON.stringify(this.props.events)}
    - -
    - -
    -
    -
    -
    - ); - } +function PillarLabel(props) { + return {props.pillar} } @@ -86,7 +58,7 @@ const columns = [ }, { Header: 'Events', id:"events", accessor: x => { - return ; + return ; } } ] diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js index 9a551e0a2..20d97a90d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js @@ -14,7 +14,6 @@ export class ZeroTrustReportPillarGrades extends Component { if (this.props.findings !== null) { for (const finding of this.props.findings) { - console.log("finding: " + JSON.stringify(finding)); if (typeof finding === 'object' && finding !== null) { if (finding.hasOwnProperty("pillars") && finding.hasOwnProperty("conclusive")) { for (const pillar of finding["pillars"]) { From 47d37dcdd06812e8941e015408eeed74abe7249f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 6 Aug 2019 19:19:00 +0300 Subject: [PATCH 012/186] Added title (should be event type) to report data and events timeline --- monkey/monkey_island/cc/resources/reporting/report.py | 5 ++++- .../report-components/zerotrust/EventsTimeline.js | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 1b2895f8f..55c7766be 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -10,7 +10,7 @@ ZERO_TRUST_REPORT_TYPE = "zero_trust" GENERAL_REPORT_TYPE = "general" REPORT_TYPES = [GENERAL_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] -__author__ = "itay.mizeretz" +__author__ = ["itay.mizeretz", "shay.nehmad"] class Report(flask_restful.Resource): @@ -30,9 +30,11 @@ class Report(flask_restful.Resource): "events": [ { "timestamp": "2019-08-01 14:48:46.112000", + "title": "Monkey perform an action", "message": "log1" }, { "timestamp": "2019-08-01 14:48:42.112000", + "title": "Analysis", "message": "log2" }] }, @@ -43,6 +45,7 @@ class Report(flask_restful.Resource): "events": [ { "timestamp": "2019-08-01 14:48:46.112000", + "title": "Analysis", "message": "log3" }] } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index 821b6bd00..441a01636 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -8,7 +8,13 @@ export class EventsTimeline extends Component { { this.props["events"].map(event => ( - {event.message} + }> + {event.message} + )) } From 9c1abf08a91d85c266a50ed8973b27b40f143f8a Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 7 Aug 2019 11:08:30 +0300 Subject: [PATCH 013/186] Added event type and custom icons per type --- monkey/monkey_island/cc/resources/reporting/report.py | 5 ++++- .../report-components/zerotrust/EventsTimeline.js | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 55c7766be..3fe8f0fd9 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -30,11 +30,13 @@ class Report(flask_restful.Resource): "events": [ { "timestamp": "2019-08-01 14:48:46.112000", - "title": "Monkey perform an action", + "title": "Monkey performed an action", + "type": "MonkeyAction", "message": "log1" }, { "timestamp": "2019-08-01 14:48:42.112000", "title": "Analysis", + "type": "IslandAction", "message": "log2" }] }, @@ -46,6 +48,7 @@ class Report(flask_restful.Resource): { "timestamp": "2019-08-01 14:48:46.112000", "title": "Analysis", + "type": "MonkeyAction", "message": "log3" }] } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index 441a01636..ec0842309 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -1,6 +1,12 @@ import React, {Component} from "react"; import {Timeline, TimelineEvent} from "react-event-timeline"; +const eventTypeToIcon = { + "MonkeyAction": "fa fa-exclamation-circle fa-2x icon-warning", + "IslandAction": "fa fa-server fa-2x icon-info", + null: "fa fa-question-circle fa-2x icon-info", +}; + export class EventsTimeline extends Component { render() { return ( @@ -12,7 +18,7 @@ export class EventsTimeline extends Component { key={event.timestamp} createdAt={event.timestamp} title={event.title} - icon={}> + icon={}> {event.message} )) From 274b861adc5254fa2f3a82eb82c97d14d6332451 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 7 Aug 2019 15:32:31 +0300 Subject: [PATCH 014/186] Refactored ZeroTrust out of the names of all the things --- .../cc/ui/src/components/pages/ZeroTrustReportPage.js | 8 ++++---- .../{ZeroTrustReportFindingsTable.js => FindingsTable.js} | 4 ++-- .../{ZeroTrustReportPillarGrades.js => PillarGrades.js} | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{ZeroTrustReportFindingsTable.js => FindingsTable.js} (95%) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{ZeroTrustReportPillarGrades.js => PillarGrades.js} (85%) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 40c1b33cd..ec90b625f 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -2,8 +2,8 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; -import ZeroTrustReportPillarGrades from "../report-components/zerotrust/ZeroTrustReportPillarGrades"; -import ZeroTrustReportFindingsTable from "../report-components/zerotrust/ZeroTrustReportFindingsTable"; +import PillarGrades from "../report-components/zerotrust/PillarGrades"; +import FindingsTable from "../report-components/zerotrust/FindingsTable"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -43,9 +43,9 @@ class ZeroTrustReportPageComponent extends AuthComponent { } else { content =

    Pillars Overview

    - +

    Findings

    - +
    ; } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js similarity index 95% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 9088c5571..573a70bf0 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportFindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -67,7 +67,7 @@ const columns = [ const pageSize = 10; -class ZeroTrustReportFindingsTable extends Component { +class FindingsTable extends Component { render() { let defaultPageSize = this.props.findings.length > pageSize ? pageSize : this.props.findings.length; let showPagination = this.props.findings.length > pageSize; @@ -86,4 +86,4 @@ class ZeroTrustReportFindingsTable extends Component { } -export default ZeroTrustReportFindingsTable; +export default FindingsTable; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js similarity index 85% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index 20d97a90d..6afe2dd52 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustReportPillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -1,8 +1,7 @@ import React, {Component} from "react"; import ZeroTrustPillars from "./ZeroTrustPillars"; -import ZeroTrustReportFindingsTable from "./ZeroTrustReportFindingsTable"; -export class ZeroTrustReportPillarGrades extends Component { +export class PillarGrades extends Component { render() { let pillarsCounters = {}; for(const pillar in ZeroTrustPillars) { @@ -41,4 +40,4 @@ export class ZeroTrustReportPillarGrades extends Component { } } -export default ZeroTrustReportPillarGrades; +export default PillarGrades; From bcc12657a45f22275fd0c07716d24bd5f122f89d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 10:28:22 +0300 Subject: [PATCH 015/186] Added export events button --- monkey/monkey_island/cc/ui/package-lock.json | 5 +++++ monkey/monkey_island/cc/ui/package.json | 11 ++++++----- .../report-components/zerotrust/FindingsTable.js | 16 +++++++++++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 324112f9d..dfccd0ec3 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -5198,6 +5198,11 @@ } } }, + "file-saver": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", + "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 48bc1be31..1a60ee27c 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -65,12 +65,14 @@ "webpack-dev-server": "^3.1.9" }, "dependencies": { + "@emotion/core": "^10.0.10", "@kunukn/react-collapse": "^1.0.5", - "classnames": "^2.2.6", "bootstrap": "3.4.1", + "classnames": "^2.2.6", "core-js": "^2.5.7", "downloadjs": "^1.4.7", "fetch": "^1.1.0", + "file-saver": "^2.0.2", "filepond": "^4.2.0", "js-file-download": "^0.4.4", "json-loader": "^0.5.7", @@ -85,6 +87,7 @@ "react-bootstrap": "^0.32.4", "react-copy-to-clipboard": "^5.0.1", "react-data-components": "^1.2.0", + "react-desktop-notification": "^1.0.9", "react-dimensions": "^1.3.0", "react-dom": "^16.5.2", "react-fa": "^5.0.0", @@ -94,14 +97,12 @@ "react-jsonschema-form": "^1.0.5", "react-redux": "^5.1.1", "react-router-dom": "^4.3.1", + "react-spinners": "^0.5.4", "react-table": "^6.8.6", "react-toggle": "^4.0.1", "react-tooltip-lite": "^1.9.1", "redux": "^4.0.0", "sass-loader": "^7.1.0", - "sha3": "^2.0.0", - "react-spinners": "^0.5.4", - "@emotion/core": "^10.0.10", - "react-desktop-notification": "^1.0.9" + "sha3": "^2.0.0" } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 573a70bf0..3b7842b26 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -2,6 +2,7 @@ import React, {Component} from "react"; import ReactTable from "react-table"; import {Button} from "react-bootstrap"; import {EventsModal} from "./EventsModal"; +import FileSaver from "file-saver"; function PillarLabel(props) { return {props.pillar} @@ -28,11 +29,20 @@ class EventsAndButtonComponent extends Component { return (
    -

    - +

    ); @@ -58,7 +68,7 @@ const columns = [ }, { Header: 'Events', id:"events", accessor: x => { - return ; + return ; } } ] From 683e945506856d182f6f26d4fdb405a80c68601e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 11:56:37 +0300 Subject: [PATCH 016/186] Seperated report into 3 resources (DDR result) --- monkey/monkey_island/cc/app.py | 6 +- .../cc/resources/reporting/report.py | 151 +++++++++++++----- .../components/pages/ZeroTrustReportPage.js | 42 +++-- .../zerotrust/FindingsTable.js | 29 ++-- .../zerotrust/PillarGrades.js | 26 +-- 5 files changed, 168 insertions(+), 86 deletions(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 2c778e61b..f2c0e9ce1 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -124,7 +124,11 @@ def init_api_resources(api): api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') # report_type: zero_trust or general - api.add_resource(Report, '/api/report/') + api.add_resource( + Report, + '/api/report/', + '/api/report//') + api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') api.add_resource(Log, '/api/log', '/api/log/') api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/') diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 3fe8f0fd9..8ae1d8769 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -10,50 +10,129 @@ ZERO_TRUST_REPORT_TYPE = "zero_trust" GENERAL_REPORT_TYPE = "general" REPORT_TYPES = [GENERAL_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] +REPORT_DATA_PILLARS = "pillars" +REPORT_DATA_FINDINGS = "findings" +REPORT_DATA_TEST_STATUS = "tests" + __author__ = ["itay.mizeretz", "shay.nehmad"] class Report(flask_restful.Resource): @jwt_required() - def get(self, report_type): + def get(self, report_type=GENERAL_REPORT_TYPE, report_data=None): if report_type == GENERAL_REPORT_TYPE: return ReportService.get_report() elif report_type == ZERO_TRUST_REPORT_TYPE: - fakedict = { - "are_all_monkeys_done": False, - "findings": [ - { - "test": "Monkey 8 found a machine with no AV software active.", - "conclusive": False, - "pillars": ["Devices"], - "events": [ - { - "timestamp": "2019-08-01 14:48:46.112000", - "title": "Monkey performed an action", - "type": "MonkeyAction", - "message": "log1" - }, { - "timestamp": "2019-08-01 14:48:42.112000", - "title": "Analysis", - "type": "IslandAction", - "message": "log2" - }] - }, - { - "test": "Monkey 6 successfully exploited machine XXX with shellshock.", - "conclusive": True, - "pillars": ["Devices", "Networks"], - "events": [ - { - "timestamp": "2019-08-01 14:48:46.112000", - "title": "Analysis", - "type": "MonkeyAction", - "message": "log3" - }] - } - ] - } - return jsonify(fakedict) + if report_data == REPORT_DATA_FINDINGS: + return jsonify(get_all_findings()) + elif report_data == REPORT_DATA_PILLARS: + return jsonify(get_pillars_grades()) + elif report_data == REPORT_DATA_TEST_STATUS: + return jsonify(get_tests_status()) flask_restful.abort(httplib.NOT_FOUND) + + +def get_all_findings(): + return [ + { + "test": "Monkey 8 found a machine with no AV software active.", + "conclusive": False, + "pillars": ["Devices"], + "events": [ + { + "timestamp": "2019-08-01 14:48:46.112000", + "title": "Monkey performed an action", + "type": "MonkeyAction", + "message": "log1" + }, { + "timestamp": "2019-08-01 14:48:42.112000", + "title": "Analysis", + "type": "IslandAction", + "message": "log2" + }] + }, + { + "test": "Monkey 6 successfully exploited machine XXX with shellshock.", + "conclusive": True, + "pillars": ["Devices", "Networks"], + "events": [ + { + "timestamp": "2019-08-01 14:48:46.112000", + "title": "Analysis", + "type": "MonkeyAction", + "message": "log3" + }] + } + ] + + +def get_tests_status(): + return [ + { + "Test": "Segmentation", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": False # There were results meaning the test was executed. + }, + { + "Exploit": "network", + "Unexecuted": True # There were no results since the test wasn't executed. + }, + ] + + +def get_pillars_grades(): + return [ + { + "Pillar": "data", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": 6 + }, + { + "Pillar": "network", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": 6 + }, + { + "Pillar": "people", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": 6 + }, + { + "Pillar": "workloads", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": 6 + }, + { + "Pillar": "devices", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": 6 + }, + { + "Pillar": "visibility and analytics", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": 6 + }, + { + "Pillar": "automation and analytics", + "Conclusive": 6, + "Inconclusive": 6, + "Positive": 6, + "Unexecuted": 6 + }, + ] diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index ec90b625f..9d200beac 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -11,9 +11,6 @@ class ZeroTrustReportPageComponent extends AuthComponent { super(props); this.state = { - report: { - findings: undefined - }, allMonkeysAreDead: false, runStarted: false }; @@ -38,14 +35,16 @@ class ZeroTrustReportPageComponent extends AuthComponent { generateReportContent() { let content; - if (typeof this.state.report.findings === "undefined") { + if (typeof this.state.findings === "undefined") { content = "Still empty"; } else { content =

    Pillars Overview

    - + +

    Test Status

    + TODO

    Findings

    - +
    ; } @@ -63,9 +62,15 @@ class ZeroTrustReportPageComponent extends AuthComponent { {content}
    THIS IS THE RAW SERVER DATA -
    -            {JSON.stringify(this.state.report, undefined, 2)}
    -          
    +
    + PILLARS: +
    {JSON.stringify(this.state.pillars, undefined, 2)}
    +
    + TESTS: +
    {JSON.stringify(this.state.tests, undefined, 2)}
    +
    + FINDINGS: +
    {JSON.stringify(this.state.findings, undefined, 2)}
    ) @@ -76,11 +81,26 @@ class ZeroTrustReportPageComponent extends AuthComponent { } getZeroTrustReportFromServer() { - this.authFetch('/api/report/zero_trust') + let res; + this.authFetch('/api/report/zero_trust/findings') .then(res => res.json()) .then(res => { this.setState({ - report: res + findings: res + }); + }); + this.authFetch('/api/report/zero_trust/tests') + .then(res => res.json()) + .then(res => { + this.setState({ + tests: res + }); + }); + this.authFetch('/api/report/zero_trust/pillars') + .then(res => res.json()) + .then(res => { + this.setState({ + pillars: res }); }); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 3b7842b26..863d08670 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -63,7 +63,7 @@ const columns = [ const listItems = pillars.map((pillar) =>
  • ); - return
      {listItems}
    + return
      {listItems}
    ; } }, { Header: 'Events', id:"events", @@ -79,19 +79,22 @@ const pageSize = 10; class FindingsTable extends Component { render() { - let defaultPageSize = this.props.findings.length > pageSize ? pageSize : this.props.findings.length; - let showPagination = this.props.findings.length > pageSize; + if (this.props.findings.length > 0) { + let defaultPageSize = this.props.findings.length > pageSize ? pageSize : this.props.findings.length; + let showPagination = this.props.findings.length > pageSize; - return ( -
    - -
    - ); + return ( +
    + +
    + ); + } + else { return (
    BAYAZ
    );} } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index 6afe2dd52..8c3959b00 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -3,37 +3,13 @@ import ZeroTrustPillars from "./ZeroTrustPillars"; export class PillarGrades extends Component { render() { - let pillarsCounters = {}; - for(const pillar in ZeroTrustPillars) { - pillarsCounters[ZeroTrustPillars[pillar]] = { - "conclusive": 0, - "possible": 0 - }; - } - - if (this.props.findings !== null) { - for (const finding of this.props.findings) { - if (typeof finding === 'object' && finding !== null) { - if (finding.hasOwnProperty("pillars") && finding.hasOwnProperty("conclusive")) { - for (const pillar of finding["pillars"]) { - if (finding.conclusive) { - pillarsCounters[pillar]["conclusive"] += 1; - } else { - pillarsCounters[pillar]["possible"] += 1; - } - } - } - } - } - } - return (

    TODO: table with conditional colouring.

    -          {JSON.stringify(pillarsCounters, undefined, 2)}
    +          {JSON.stringify(this.props.pillars, undefined, 2)}
             
    ) From a337bb5800ab5e67bd2d82d810cae061f580b85c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 12:08:24 +0300 Subject: [PATCH 017/186] Added table for pillar grades --- .../components/pages/ZeroTrustReportPage.js | 6 ++- .../zerotrust/PillarGrades.js | 44 ++++++++++++++----- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 9d200beac..f7b5dc5bf 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -35,7 +35,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { generateReportContent() { let content; - if (typeof this.state.findings === "undefined") { + if (this.stillLoadingDataFromServer()) { content = "Still empty"; } else { content =
    @@ -76,6 +76,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { ) } + stillLoadingDataFromServer() { + return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.tests === "undefined"; + } + print() { alert("unimplemented"); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index 8c3959b00..896a1e4d2 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -1,18 +1,38 @@ import React, {Component} from "react"; -import ZeroTrustPillars from "./ZeroTrustPillars"; +import ReactTable from "react-table"; -export class PillarGrades extends Component { +const columns = [ + { + Header: 'Pillar Grading', + columns: [ + { Header: 'Pillar', accessor: 'Pillar'}, + { Header: 'Conclusive', accessor: 'Conclusive'}, + { Header: 'Inconclusive', accessor: 'Inconclusive'}, + { Header: 'Unexecuted', accessor: 'Unexecuted'}, + ] + } +]; + +const pageSize = 10; + +class PillarGrades extends Component { render() { - return ( -
    -

    - TODO: table with conditional colouring. -

    -
    -          {JSON.stringify(this.props.pillars, undefined, 2)}
    -        
    -
    - ) + if (this.props.pillars.length > 0) { + let defaultPageSize = this.props.pillars.length > pageSize ? pageSize : this.props.pillars.length; + let showPagination = this.props.pillars.length > pageSize; + + return ( +
    + +
    + ); + } + else { return (
    BAYAZ
    );} } } From 97c80c47af0cb3f1a5fa4a29321a339e4c4dd0b2 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 14:21:22 +0300 Subject: [PATCH 018/186] Added coloured labels --- .../cc/resources/reporting/report.py | 14 +++++----- .../components/pages/ZeroTrustReportPage.js | 1 + .../zerotrust/FindingsTable.js | 5 +--- .../zerotrust/PillarGrades.js | 5 +++- .../zerotrust/PillarLabel.js | 21 +++++++++++++++ .../cc/ui/src/styles/ZeroTrustPillars.css | 27 +++++++++++++++++++ 6 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js create mode 100644 monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 8ae1d8769..b9e16427d 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -87,49 +87,49 @@ def get_tests_status(): def get_pillars_grades(): return [ { - "Pillar": "data", + "Pillar": "Data", "Conclusive": 6, "Inconclusive": 6, "Positive": 6, "Unexecuted": 6 }, { - "Pillar": "network", + "Pillar": "Networks", "Conclusive": 6, "Inconclusive": 6, "Positive": 6, "Unexecuted": 6 }, { - "Pillar": "people", + "Pillar": "People", "Conclusive": 6, "Inconclusive": 6, "Positive": 6, "Unexecuted": 6 }, { - "Pillar": "workloads", + "Pillar": "Workloads", "Conclusive": 6, "Inconclusive": 6, "Positive": 6, "Unexecuted": 6 }, { - "Pillar": "devices", + "Pillar": "Devices", "Conclusive": 6, "Inconclusive": 6, "Positive": 6, "Unexecuted": 6 }, { - "Pillar": "visibility and analytics", + "Pillar": "Visibility & Analytics", "Conclusive": 6, "Inconclusive": 6, "Positive": 6, "Unexecuted": 6 }, { - "Pillar": "automation and analytics", + "Pillar": "Automation & Orchestration", "Conclusive": 6, "Inconclusive": 6, "Positive": 6, diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index f7b5dc5bf..c81509a8a 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -18,6 +18,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { render() { let res; + // Todo move to componentDidMount this.getZeroTrustReportFromServer(res); const content = this.generateReportContent(); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 863d08670..b3a54a01d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -3,10 +3,7 @@ import ReactTable from "react-table"; import {Button} from "react-bootstrap"; import {EventsModal} from "./EventsModal"; import FileSaver from "file-saver"; - -function PillarLabel(props) { - return {props.pillar} -} +import {PillarLabel} from "./PillarLabel"; class EventsAndButtonComponent extends Component { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index 896a1e4d2..8ebc0e13d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -1,11 +1,14 @@ import React, {Component} from "react"; import ReactTable from "react-table"; +import {PillarLabel} from "./PillarLabel"; const columns = [ { Header: 'Pillar Grading', columns: [ - { Header: 'Pillar', accessor: 'Pillar'}, + { Header: 'Pillar', id: 'Pillar', accessor: x => { + return (); + }}, { Header: 'Conclusive', accessor: 'Conclusive'}, { Header: 'Inconclusive', accessor: 'Inconclusive'}, { Header: 'Unexecuted', accessor: 'Unexecuted'}, diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js new file mode 100644 index 000000000..6827c95ce --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -0,0 +1,21 @@ +import React, {Component} from "react"; +import 'styles/ZeroTrustPillars.css' + +export class PillarLabel extends Component { + pillar; + + render() { + const pillarToColor = { + "Data": "label-zt-data", + "People": "label-zt-people", + "Networks": "label-zt-networks", + "Workloads": "label-zt-workloads", + "Devices": "label-zt-devices", + "Visibility & Analytics": "label-zt-analytics", + "Automation & Orchestration": "label-zt-automation", + }; + + const className = "label " + pillarToColor[this.props.pillar]; + return {this.props.pillar} + } +} diff --git a/monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css b/monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css new file mode 100644 index 000000000..09b5289dc --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css @@ -0,0 +1,27 @@ +.label-zt-data { + background-color: #FAD02C !important; +} + +.label-zt-people { + background-color: #507581 !important; +} + +.label-zt-networks { + background-color: #746233 !important; +} + +.label-zt-devices { + background-color: #2F3B29 !important; +} + +.label-zt-workloads { + background-color: #0C1440 !important; +} + +.label-zt-analytics { + background-color: #6B8836 !important; +} + +.label-zt-automation { + background-color: #B4BC82 !important; +} From 568257db261dc174f0c362b5b1c8467f644420e2 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 15:42:09 +0300 Subject: [PATCH 019/186] Extracted common code of PagenatedTable component --- .../common/PagenatedTable.js | 29 +++++++++++++++++++ .../zerotrust/FindingsTable.js | 25 ++++------------ .../zerotrust/PillarGrades.js | 21 ++------------ 3 files changed, 36 insertions(+), 39 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/PagenatedTable.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PagenatedTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PagenatedTable.js new file mode 100644 index 000000000..6a4652837 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PagenatedTable.js @@ -0,0 +1,29 @@ +import React, {Component} from "react"; +import ReactTable from "react-table"; + +class PagenatedTable extends Component { + render() { + if (this.props.data.length > 0) { + let defaultPageSize = this.props.data.length > this.props.pageSize ? this.props.pageSize : this.props.data.length; + let showPagination = this.props.data.length > this.props.pageSize; + + return ( +
    + +
    + ); + } + else { + return ( +
    + ); + } + } +} + +export default PagenatedTable; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index b3a54a01d..f2247461b 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -1,9 +1,9 @@ import React, {Component} from "react"; -import ReactTable from "react-table"; import {Button} from "react-bootstrap"; import {EventsModal} from "./EventsModal"; import FileSaver from "file-saver"; import {PillarLabel} from "./PillarLabel"; +import PagenatedTable from "../common/PagenatedTable"; class EventsAndButtonComponent extends Component { @@ -51,7 +51,7 @@ const columns = [ { Header: 'Findings', columns: [ - { Header: 'Test', accessor: 'test', + { Header: 'Finding', accessor: 'test', style: {'whiteSpace': 'unset'} // This enables word wrap }, { Header: 'Pillars', id: "pillars", @@ -72,26 +72,11 @@ const columns = [ } ]; -const pageSize = 10; - class FindingsTable extends Component { render() { - if (this.props.findings.length > 0) { - let defaultPageSize = this.props.findings.length > pageSize ? pageSize : this.props.findings.length; - let showPagination = this.props.findings.length > pageSize; - - return ( -
    - -
    - ); - } - else { return (
    BAYAZ
    );} + return ( + + ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index 8ebc0e13d..9b746afd2 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -1,6 +1,6 @@ import React, {Component} from "react"; -import ReactTable from "react-table"; import {PillarLabel} from "./PillarLabel"; +import PagenatedTable from "../common/PagenatedTable"; const columns = [ { @@ -16,26 +16,9 @@ const columns = [ } ]; -const pageSize = 10; - class PillarGrades extends Component { render() { - if (this.props.pillars.length > 0) { - let defaultPageSize = this.props.pillars.length > pageSize ? pageSize : this.props.pillars.length; - let showPagination = this.props.pillars.length > pageSize; - - return ( -
    - -
    - ); - } - else { return (
    BAYAZ
    );} + return ; } } From e4738d026c36cbd83d5741bd13af9ef58757808a Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 16:19:51 +0300 Subject: [PATCH 020/186] Added recommendation status table --- .../cc/resources/reporting/report.py | 63 +++++++++++++++---- .../components/pages/ZeroTrustReportPage.js | 15 ++--- .../zerotrust/RecommendationsStatus.js | 46 ++++++++++++++ 3 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatus.js diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index b9e16427d..5c9ff314a 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -12,7 +12,7 @@ REPORT_TYPES = [GENERAL_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] REPORT_DATA_PILLARS = "pillars" REPORT_DATA_FINDINGS = "findings" -REPORT_DATA_TEST_STATUS = "tests" +REPORT_DATA_RECOMMENDATION_STATUS = "recommendations" __author__ = ["itay.mizeretz", "shay.nehmad"] @@ -28,8 +28,8 @@ class Report(flask_restful.Resource): return jsonify(get_all_findings()) elif report_data == REPORT_DATA_PILLARS: return jsonify(get_pillars_grades()) - elif report_data == REPORT_DATA_TEST_STATUS: - return jsonify(get_tests_status()) + elif report_data == REPORT_DATA_RECOMMENDATION_STATUS: + return jsonify(get_recommendations_status()) flask_restful.abort(httplib.NOT_FOUND) @@ -68,18 +68,59 @@ def get_all_findings(): ] -def get_tests_status(): +def get_recommendations_status(): return [ { - "Test": "Segmentation", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": False # There were results meaning the test was executed. + "Recommendation": "Do network segmentation.", + "Status": "Positive", + "Tests": [ + { + "Test": "Test B for segmentation", + "Status": "Positive" + }, + { + "Test": "Test A for segmentation", + "Status": "Positive" + }, + ] }, { - "Exploit": "network", - "Unexecuted": True # There were no results since the test wasn't executed. + "Recommendation": "Install AV software.", + "Status": "Unexecuted", + "Tests": [ + { + "Test": "Search for active AV software processes", + "Status": "Unexecuted" + } + ] + }, + { + "Recommendation": "Analyze malicious network traffic.", + "Status": "Inconclusive", + "Tests": [ + { + "Test": "Use exploits.", + "Status": "Inconclusive" + }, + { + "Test": "Bruteforce passwords.", + "Status": "Inconclusive" + } + ] + }, + { + "Recommendation": "Data at trasnit should be...", + "Status": "Conclusive", + "Tests": [ + { + "Test": "Scan HTTP.", + "Status": "Conclusive" + }, + { + "Test": "Scan elastic.", + "Status": "Unexecuted" + } + ] }, ] diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index c81509a8a..dc0730102 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -4,6 +4,7 @@ import AuthComponent from '../AuthComponent'; import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; import PillarGrades from "../report-components/zerotrust/PillarGrades"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; +import RecommendationsStatusTable from "../report-components/zerotrust/RecommendationsStatus"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -42,8 +43,8 @@ class ZeroTrustReportPageComponent extends AuthComponent { content =

    Pillars Overview

    -

    Test Status

    - TODO +

    Recommendations Status

    +

    Findings

    ; @@ -67,8 +68,8 @@ class ZeroTrustReportPageComponent extends AuthComponent { PILLARS:
    {JSON.stringify(this.state.pillars, undefined, 2)}

    - TESTS: -
    {JSON.stringify(this.state.tests, undefined, 2)}
    + recommendations: +
    {JSON.stringify(this.state.recommendations, undefined, 2)}

    FINDINGS:
    {JSON.stringify(this.state.findings, undefined, 2)}
    @@ -78,7 +79,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { } stillLoadingDataFromServer() { - return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.tests === "undefined"; + return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.recommendations === "undefined"; } print() { @@ -94,11 +95,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { findings: res }); }); - this.authFetch('/api/report/zero_trust/tests') + this.authFetch('/api/report/zero_trust/recommendations') .then(res => res.json()) .then(res => { this.setState({ - tests: res + recommendations: res }); }); this.authFetch('/api/report/zero_trust/pillars') diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatus.js new file mode 100644 index 000000000..b23f84d16 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatus.js @@ -0,0 +1,46 @@ +import React, {Component} from "react"; +import PagenatedTable from "../common/PagenatedTable"; + +const columns = [ + { + Header: 'Recommendations status', + columns: [ + { Header: 'Recommendation', accessor: 'Recommendation', + style: {'whiteSpace': 'unset'} // This enables word wrap + }, + { Header: 'Status', id: "Status", + accessor: x => { + const statusToIcon = { + "Positive": "fa-shield alert-success", + "Inconclusive": "fa-question alert-info", + "Conclusive": "fa-unlock-alt alert-danger", + "Unexecuted": "fa-toggle-off", + }; + return ; + } + }, + { Header: 'Tests', id:"Tests", + accessor: x => { + console.log(x.Tests); + return ; + } + } + ] + } +]; + +class TestsStatus extends Component { + render() { + return ( +
    {JSON.stringify(this.props.tests,null,2)}
    + ); + } +} + +export class RecommendationsStatusTable extends Component { + render() { + return ; + } +} + +export default RecommendationsStatusTable; From a074d8e4a1360c74d9d00dce8a53e92daeaec83d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 16:50:32 +0300 Subject: [PATCH 021/186] Divided recommendations into pillars --- .../cc/resources/reporting/report.py | 57 +++++++++++++++---- .../components/pages/ZeroTrustReportPage.js | 32 ++++++++--- ...tatus.js => RecommendationsStatusTable.js} | 7 ++- .../SinglePillarRecommendationsStatus.js | 15 +++++ 4 files changed, 89 insertions(+), 22 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{RecommendationsStatus.js => RecommendationsStatusTable.js} (80%) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 5c9ff314a..384ca09fe 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -69,7 +69,7 @@ def get_all_findings(): def get_recommendations_status(): - return [ + network_recomms = [ { "Recommendation": "Do network segmentation.", "Status": "Positive", @@ -84,16 +84,6 @@ def get_recommendations_status(): }, ] }, - { - "Recommendation": "Install AV software.", - "Status": "Unexecuted", - "Tests": [ - { - "Test": "Search for active AV software processes", - "Status": "Unexecuted" - } - ] - }, { "Recommendation": "Analyze malicious network traffic.", "Status": "Inconclusive", @@ -124,6 +114,51 @@ def get_recommendations_status(): }, ] + device_recomms = [ + { + "Recommendation": "Install AV software.", + "Status": "Unexecuted", + "Tests": [ + { + "Test": "Search for active AV software processes", + "Status": "Unexecuted" + } + ] + }, + ] + + data_recommns = [ + { + "Recommendation": "Data at trasnit should be...", + "Status": "Conclusive", + "Tests": [ + { + "Test": "Scan HTTP.", + "Status": "Conclusive" + }, + { + "Test": "Scan elastic.", + "Status": "Unexecuted" + } + ] + }, + ] + + return [ + { + "pillar": "Networks", + "recommendationStatus": network_recomms + }, + { + "pillar": "Data", + "recommendationStatus": data_recommns + }, + { + "pillar": "Devices", + "recommendationStatus": device_recomms + }, + ] + def get_pillars_grades(): return [ diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index dc0730102..630952961 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,10 +1,10 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; -import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; +import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import PillarGrades from "../report-components/zerotrust/PillarGrades"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; -import RecommendationsStatusTable from "../report-components/zerotrust/RecommendationsStatus"; +import {SinglePillarRecommendationsStatus} from "../report-components/zerotrust/SinglePillarRecommendationsStatus"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -40,13 +40,29 @@ class ZeroTrustReportPageComponent extends AuthComponent { if (this.stillLoadingDataFromServer()) { content = "Still empty"; } else { - content =
    + const pillarsSection =

    Pillars Overview

    - -

    Recommendations Status

    - -

    Findings

    - + +
    ; + + const recommendationsSection =

    Recommendations Status

    + { + this.state.recommendations.map((recommendation) => + + ) + } +
    ; + + const findingSection =

    Findings

    +
    ; + + content =
    + {pillarsSection} + {recommendationsSection} + {findingSection}
    ; } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js similarity index 80% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatus.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js index b23f84d16..dda771717 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js @@ -1,5 +1,6 @@ import React, {Component} from "react"; import PagenatedTable from "../common/PagenatedTable"; +import AuthComponent from "../../AuthComponent"; const columns = [ { @@ -29,7 +30,7 @@ const columns = [ } ]; -class TestsStatus extends Component { +class TestsStatus extends AuthComponent { render() { return (
    {JSON.stringify(this.props.tests,null,2)}
    @@ -37,9 +38,9 @@ class TestsStatus extends Component { } } -export class RecommendationsStatusTable extends Component { +export class RecommendationsStatusTable extends AuthComponent { render() { - return ; + return ; } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js new file mode 100644 index 000000000..802b7a0a2 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js @@ -0,0 +1,15 @@ +import AuthComponent from "../../AuthComponent"; +import {PillarLabel} from "./PillarLabel"; +import RecommendationsStatusTable from "./RecommendationsStatusTable"; +import React from "react"; + +export class SinglePillarRecommendationsStatus extends AuthComponent { + render() { + return ( +
    +

    + +
    + ); + } +} From 96eb705b9c4b47a5b597b1e29c18b860f8685df6 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 18:15:15 +0300 Subject: [PATCH 022/186] Added icons to pillar labels --- .../report-components/zerotrust/PillarLabel.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js index 6827c95ce..51f5f988f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -15,7 +15,17 @@ export class PillarLabel extends Component { "Automation & Orchestration": "label-zt-automation", }; + const pillarToIcon = { + "Data": "fa fa-database", + "People": "fa fa-user", + "Networks": "fa fa-tty", + "Workloads": "fa fa-cloud", + "Devices": "fa fa-laptop", + "Visibility & Analytics": "fa fa-eye-slash", + "Automation & Orchestration": "fa fa-cogs", + }; + const className = "label " + pillarToColor[this.props.pillar]; - return {this.props.pillar} + return {this.props.pillar} } } From 1a2d61e3a1cdb7285174ce39c36d3e93df8e8e16 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 8 Aug 2019 20:57:04 +0300 Subject: [PATCH 023/186] Made the test cell of the recommendation table a list instead of raw JSON --- monkey/monkey_island/cc/ui/package-lock.json | 3 +- .../zerotrust/RecommendationsStatusTable.js | 28 +++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index dfccd0ec3..09ccebad9 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -19041,7 +19041,6 @@ "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -19211,7 +19210,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js index dda771717..acd7bc322 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js @@ -22,7 +22,6 @@ const columns = [ }, { Header: 'Tests', id:"Tests", accessor: x => { - console.log(x.Tests); return ; } } @@ -32,10 +31,35 @@ const columns = [ class TestsStatus extends AuthComponent { render() { + const positiveStatus = "Positive"; + const conclusiveStatus = "Conclusive"; + const inconclusiveStatus = "Inconclusive"; + const unexecutedStatus = "Unexecuted"; + return ( -
    {JSON.stringify(this.props.tests,null,2)}
    +
    + {this.getTestsOfStatusIfAny(conclusiveStatus)} + {this.getTestsOfStatusIfAny(inconclusiveStatus)} + {this.getTestsOfStatusIfAny(positiveStatus)} + {this.getTestsOfStatusIfAny(unexecutedStatus)} +
    ); } + + getTestsOfStatusIfAny(statusToFilter) { + const filteredTests = this.props.tests.filter((test) => { + return (test.Status === statusToFilter); + } + ); + + if (filteredTests.length > 0) { + const listItems = filteredTests.map((test) => { + return (
  • {test.Test}
  • ) + }); + return
    {statusToFilter}
      {listItems}
    ; + } + return
    ; + } } export class RecommendationsStatusTable extends AuthComponent { From e500068e45b25ba63f42efb6cf60f8049cc78211 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 11 Aug 2019 11:43:12 +0300 Subject: [PATCH 024/186] Added utility function for calculating power set --- monkey/common/utils/itertools_extensions.py | 9 +++++++++ monkey/common/utils/test_power_set.py | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 monkey/common/utils/itertools_extensions.py create mode 100644 monkey/common/utils/test_power_set.py diff --git a/monkey/common/utils/itertools_extensions.py b/monkey/common/utils/itertools_extensions.py new file mode 100644 index 000000000..b806a659b --- /dev/null +++ b/monkey/common/utils/itertools_extensions.py @@ -0,0 +1,9 @@ +from itertools import chain, combinations + + +def power_set(iterable): + """ + https://docs.python.org/3/library/itertools.html#itertools-recipes + """ + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(1, len(s) + 1)) \ No newline at end of file diff --git a/monkey/common/utils/test_power_set.py b/monkey/common/utils/test_power_set.py new file mode 100644 index 000000000..9c5798327 --- /dev/null +++ b/monkey/common/utils/test_power_set.py @@ -0,0 +1,18 @@ +from unittest import TestCase + + +class TestPower_set(TestCase): + def test_power_set(self): + before = ('a', 'b', 'c') + after_expected = [ + ('a', ), + ('b',), + ('c',), + ('a', 'b'), + ('a', 'c'), + ('b', 'c'), + ('a', 'b', 'c'), + ] + + from common.utils.itertools_extensions import power_set + self.assertEquals(list(power_set(before)), after_expected) From 547067c4dad2755f01215c968b6360447cd8cd98 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 11 Aug 2019 17:49:24 +0300 Subject: [PATCH 025/186] made power set return lists for ease of usage --- monkey/common/utils/itertools_extensions.py | 2 +- monkey/common/utils/test_power_set.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/monkey/common/utils/itertools_extensions.py b/monkey/common/utils/itertools_extensions.py index b806a659b..b27181ff6 100644 --- a/monkey/common/utils/itertools_extensions.py +++ b/monkey/common/utils/itertools_extensions.py @@ -6,4 +6,4 @@ def power_set(iterable): https://docs.python.org/3/library/itertools.html#itertools-recipes """ s = list(iterable) - return chain.from_iterable(combinations(s, r) for r in range(1, len(s) + 1)) \ No newline at end of file + return [list(x) for x in list(chain.from_iterable(combinations(s, r) for r in range(1, len(s) + 1)))] \ No newline at end of file diff --git a/monkey/common/utils/test_power_set.py b/monkey/common/utils/test_power_set.py index 9c5798327..22410fe30 100644 --- a/monkey/common/utils/test_power_set.py +++ b/monkey/common/utils/test_power_set.py @@ -5,14 +5,14 @@ class TestPower_set(TestCase): def test_power_set(self): before = ('a', 'b', 'c') after_expected = [ - ('a', ), - ('b',), - ('c',), - ('a', 'b'), - ('a', 'c'), - ('b', 'c'), - ('a', 'b', 'c'), + ['a'], + ['b'], + ['c'], + ['a', 'b'], + ['a', 'c'], + ['b', 'c'], + ['a', 'b', 'c'], ] from common.utils.itertools_extensions import power_set - self.assertEquals(list(power_set(before)), after_expected) + self.assertEquals(power_set(before), after_expected) From 14b5d5f658856591ee608a99f304d16f866995a7 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 11 Aug 2019 17:56:37 +0300 Subject: [PATCH 026/186] Added consts file for zero trust --- monkey/common/data/__init__.py | 0 monkey/common/data/zero_trust_consts.py | 101 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 monkey/common/data/__init__.py create mode 100644 monkey/common/data/zero_trust_consts.py diff --git a/monkey/common/data/__init__.py b/monkey/common/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py new file mode 100644 index 000000000..6d01fbfc4 --- /dev/null +++ b/monkey/common/data/zero_trust_consts.py @@ -0,0 +1,101 @@ +AUTOMATION_ORCHESTRATION = u"Automation & Orchestration" +VISIBILITY_ANALYTICS = u"Visibility & Analytics" +WORKLOADS = u"Workloads" +DEVICES = u"Devices" +NETWORKS = u"Networks" +PEOPLE = u"People" +DATA = u"Data" +PILLARS = (DATA, PEOPLE, NETWORKS, DEVICES, WORKLOADS, VISIBILITY_ANALYTICS, AUTOMATION_ORCHESTRATION) + +STATUS_UNEXECUTED = u"Unexecuted" +STATUS_POSITIVE = u"Positive" +STATUS_INCONCLUSIVE = u"Inconclusive" +STATUS_CONCLUSIVE = u"Conclusive" +TEST_STATUSES = (STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED) + +TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic" +TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http" +TEST_MACHINE_EXPLOITED = u"machine_exploited" +TEST_ENDPOINT_SECURITY_EXISTS = u"endpoint_security_exists" +TEST_SCHEDULED_EXECUTION = u"scheduled_execution" +TEST_ACTIVITY_TIMELINE = u"malicious_activity_timeline" +TEST_SEGMENTATION = u"segmentation" +TESTS = ( + TEST_SEGMENTATION, + TEST_ACTIVITY_TIMELINE, + TEST_SCHEDULED_EXECUTION, + TEST_ENDPOINT_SECURITY_EXISTS, + TEST_MACHINE_EXPLOITED, + TEST_DATA_ENDPOINT_HTTP, + TEST_DATA_ENDPOINT_ELASTIC +) + +DIRECTIVE_DATA_TRANSIT = u"data_transit" +DIRECTIVE_ENDPOINT_SECURITY = u"endpoint_security" +DIRECTIVE_USER_BEHAVIOUR = u"user_behaviour" +DIRECTIVE_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" +DIRECTIVE_SEGMENTATION = u"segmentation" +DIRECTIVES = { + DIRECTIVE_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", + DIRECTIVE_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", + DIRECTIVE_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", + DIRECTIVE_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", + DIRECTIVE_DATA_TRANSIT: u"Secure data at transit by encrypting it." +} + +POSSIBLE_STATUSES_KEY = u"possible_statuses" +PILLARS_KEY = u"pillars" +DIRECTIVE_KEY = u"directive_key" +FINDING_FORMAT_KEY = u"finding_format" +EXPLANATION_KEY = u"explanation" +TESTS_MAP = { + TEST_SEGMENTATION: { + EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.", + FINDING_FORMAT_KEY: u"The Monkey from {ORIGIN} communicated with a machine on a different segment.", + DIRECTIVE_KEY: DIRECTIVE_SEGMENTATION, + PILLARS_KEY: [NETWORKS], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_POSITIVE, STATUS_CONCLUSIVE] + }, + TEST_ACTIVITY_TIMELINE: { + EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", + FINDING_FORMAT_KEY: u"Malicious activity performed by the Monkeys. See 'events' for detailed information.", + DIRECTIVE_KEY: DIRECTIVE_ANALYZE_NETWORK_TRAFFIC, + PILLARS_KEY: [NETWORKS], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] + }, + TEST_ENDPOINT_SECURITY_EXISTS: { + EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.", + FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} found no active endpoint security processes.", + DIRECTIVE_KEY: DIRECTIVE_ENDPOINT_SECURITY, + PILLARS_KEY: [DEVICES], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] + }, + TEST_MACHINE_EXPLOITED: { + EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.", + FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} attempted to exploit a machine on {TARGET}.", + DIRECTIVE_KEY: DIRECTIVE_ENDPOINT_SECURITY, + PILLARS_KEY: [DEVICES], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE] + }, + TEST_SCHEDULED_EXECUTION: { + EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.", + FINDING_FORMAT_KEY: "The Monkey on {ORIGIN} started running in an executed manner.", + DIRECTIVE_KEY: DIRECTIVE_USER_BEHAVIOUR, + PILLARS_KEY: [PEOPLE, NETWORKS], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] + }, + TEST_DATA_ENDPOINT_ELASTIC: { + EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.", + FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} found an open ElasticSearch instance.", + DIRECTIVE_KEY: DIRECTIVE_DATA_TRANSIT, + PILLARS_KEY: [DATA], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] + }, + TEST_DATA_ENDPOINT_HTTP: { + EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to HTTP servers.", + FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} found an open HTTP server.", + DIRECTIVE_KEY: DIRECTIVE_DATA_TRANSIT, + PILLARS_KEY: [DATA], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] + }, +} From 1a38a8ca93b4c4202ebe19cf42a922693e2663c6 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 11 Aug 2019 17:57:11 +0300 Subject: [PATCH 027/186] Added basic finding and event data models --- monkey/monkey_island/cc/models/event.py | 10 ++++ monkey/monkey_island/cc/models/finding.py | 47 +++++++++++++++++++ .../monkey_island/cc/models/test_finding.py | 40 ++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 monkey/monkey_island/cc/models/event.py create mode 100644 monkey/monkey_island/cc/models/finding.py create mode 100644 monkey/monkey_island/cc/models/test_finding.py diff --git a/monkey/monkey_island/cc/models/event.py b/monkey/monkey_island/cc/models/event.py new file mode 100644 index 000000000..dd277c7c9 --- /dev/null +++ b/monkey/monkey_island/cc/models/event.py @@ -0,0 +1,10 @@ +from mongoengine import EmbeddedDocument, DateTimeField, StringField + +EVENT_TYPES = ("monkey_local_action", "monkey_network_action", "island_action") + + +class Event(EmbeddedDocument): + timestamp = DateTimeField(required=True) + title = StringField(required=True) + message = StringField() + event_type = StringField(required=True, choices=EVENT_TYPES) diff --git a/monkey/monkey_island/cc/models/finding.py b/monkey/monkey_island/cc/models/finding.py new file mode 100644 index 000000000..c01e6b955 --- /dev/null +++ b/monkey/monkey_island/cc/models/finding.py @@ -0,0 +1,47 @@ +""" +Define a Document Schema for Zero Trust findings. +""" + +from mongoengine import Document, StringField, ListField, EmbeddedDocumentField + +# Dummy import for mongoengine. +# noinspection PyUnresolvedReferences +from event import Event + +from common.data.zero_trust_consts import TEST_STATUSES, PILLARS, TESTS, TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY + + +class Finding(Document): + """ + This class has 2 main section: + * The schema section defines the DB fields in the document. This is the data of the object. + * The logic section defines complex questions we can ask about a single document which are asked multiple + times, or complex action we will perform - somewhat like an API. + """ + # SCHEMA + test = StringField(required=True, choices=TESTS) + status = StringField(required=True, choices=TEST_STATUSES) + events = ListField(field=EmbeddedDocumentField('Event')) + + # LOGIC + def get_test_explanation(self): + return TESTS_MAP[self.test][EXPLANATION_KEY] + + def get_pillars(self): + return TESTS_MAP[self.test][PILLARS_KEY] + + # Creation methods + @staticmethod + def save_finding(test, status, events): + finding = Finding( + test=test, + status=status, + events=events) + + finding.save() + + return finding + + +class UnknownTest(Exception): + pass diff --git a/monkey/monkey_island/cc/models/test_finding.py b/monkey/monkey_island/cc/models/test_finding.py new file mode 100644 index 000000000..69a3986c1 --- /dev/null +++ b/monkey/monkey_island/cc/models/test_finding.py @@ -0,0 +1,40 @@ +from datetime import datetime + +from mongoengine import ValidationError + +from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_CONCLUSIVE, NETWORKS +from finding import Finding, UnknownTest +from monkey_island.cc.models.event import Event + +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +class TestFinding(IslandTestCase): + """ + Make sure to set server environment to `testing` in server.json! Otherwise this will mess up your mongo instance and + won't work. + + Also, the working directory needs to be the working directory from which you usually run the island so the + server.json file is found and loaded. + """ + def test_save_finding_validation(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + with self.assertRaises(ValidationError): + _ = Finding.save_finding(test="bla bla", status="Conclusive", events=[]) + + with self.assertRaises(ValidationError): + _ = Finding.save_finding(test=TEST_SEGMENTATION, status="bla bla", events=[]) + + def test_save_finding_sanity(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0) + + event_example = Event(timestamp=datetime.now(), title="Event Title", message="event message", event_type="monkey_network_action") + Finding.save_finding(test=TEST_SEGMENTATION, status=STATUS_CONCLUSIVE, events=[event_example]) + + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 1) + self.assertEquals(len(Finding.objects(status=STATUS_CONCLUSIVE)), 1) From cabf2353439c3ec334e37c1b945f959d8b2848c4 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 11 Aug 2019 18:10:20 +0300 Subject: [PATCH 028/186] WIP added AV hook to sysinfo telem --- .../monkey_island/cc/resources/telemetry.py | 67 ++++++++++++++----- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index 279547dc1..9e71d586c 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -7,8 +7,10 @@ import dateutil import flask_restful from flask import request +from common.data.zero_trust_consts import TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, STATUS_CONCLUSIVE from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo +from monkey_island.cc.models.finding import Finding from monkey_island.cc.services import mimikatz_utils from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge import EdgeService @@ -176,8 +178,56 @@ class Telemetry(flask_restful.Resource): @staticmethod def process_system_info_telemetry(telemetry_json): - users_secrets = {} + Telemetry.process_ssh_info(telemetry_json) + Telemetry.process_credential_info(telemetry_json) monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') + Telemetry.process_mimikatz_and_wmi_info(monkey_id, telemetry_json) + Telemetry.process_aws_data(monkey_id, telemetry_json) + Telemetry.test_antivirus_existance(telemetry_json) + + @staticmethod + def test_antivirus_existance(telemetry_json): + anti_virus_software = [ + "SSPService.exe" + ] + if 'process_list' in telemetry_json['data']: + found_av = False + for process in telemetry_json['data']['process_list']: + if process['name'] in anti_virus_software: + found_av = True + # TODO add event here + if found_av: + Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_POSITIVE, events=[]) + else: + Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_CONCLUSIVE, events=[]) + + @staticmethod + def process_mimikatz_and_wmi_info(monkey_id, telemetry_json): + users_secrets = {} + if 'mimikatz' in telemetry_json['data']: + users_secrets = mimikatz_utils.MimikatzSecrets. \ + extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) + if 'wmi' in telemetry_json['data']: + wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) + wmi_handler.process_and_handle_wmi_info() + + @staticmethod + def process_aws_data(monkey_id, telemetry_json): + if 'aws' in telemetry_json['data']: + if 'instance_id' in telemetry_json['data']['aws']: + mongo.db.monkey.update_one({'_id': monkey_id}, + {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}}) + + @staticmethod + def process_credential_info(telemetry_json): + if 'credentials' in telemetry_json['data']: + creds = telemetry_json['data']['credentials'] + Telemetry.encrypt_system_info_creds(creds) + Telemetry.add_system_info_creds_to_config(creds) + Telemetry.replace_user_dot_with_comma(creds) + + @staticmethod + def process_ssh_info(telemetry_json): if 'ssh_info' in telemetry_json['data']: ssh_info = telemetry_json['data']['ssh_info'] Telemetry.encrypt_system_info_ssh_keys(ssh_info) @@ -185,21 +235,6 @@ class Telemetry(flask_restful.Resource): # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip from telemetry Telemetry.add_ip_to_ssh_keys(telemetry_json['data']['network_info']['networks'][0], ssh_info) Telemetry.add_system_info_ssh_keys_to_config(ssh_info) - if 'credentials' in telemetry_json['data']: - creds = telemetry_json['data']['credentials'] - Telemetry.encrypt_system_info_creds(creds) - Telemetry.add_system_info_creds_to_config(creds) - Telemetry.replace_user_dot_with_comma(creds) - if 'mimikatz' in telemetry_json['data']: - users_secrets = mimikatz_utils.MimikatzSecrets. \ - extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) - if 'wmi' in telemetry_json['data']: - wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) - wmi_handler.process_and_handle_wmi_info() - if 'aws' in telemetry_json['data']: - if 'instance_id' in telemetry_json['data']['aws']: - mongo.db.monkey.update_one({'_id': monkey_id}, - {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}}) @staticmethod def add_ip_to_ssh_keys(ip, ssh_info): From 40ba116a04fa9681a42bfed526ec9851cde13ab3 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 12 Aug 2019 11:49:16 +0300 Subject: [PATCH 029/186] Improved the event API and added UTs for validation --- monkey/common/data/zero_trust_consts.py | 4 +++ monkey/monkey_island/cc/models/event.py | 17 +++++++++- monkey/monkey_island/cc/models/finding.py | 7 ++--- monkey/monkey_island/cc/models/test_event.py | 31 +++++++++++++++++++ .../monkey_island/cc/models/test_finding.py | 5 +-- 5 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 monkey/monkey_island/cc/models/test_event.py diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 6d01fbfc4..c37b261fe 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -99,3 +99,7 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] }, } +EVENT_TYPE_ISLAND = "island" +EVENT_TYPE_MONKEY_NETWORK = "monkey_network" +EVENT_TYPE_MONKEY_LOCAL = "monkey_local" +EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK, EVENT_TYPE_ISLAND) \ No newline at end of file diff --git a/monkey/monkey_island/cc/models/event.py b/monkey/monkey_island/cc/models/event.py index dd277c7c9..0b8386ff8 100644 --- a/monkey/monkey_island/cc/models/event.py +++ b/monkey/monkey_island/cc/models/event.py @@ -1,6 +1,8 @@ +from datetime import datetime + from mongoengine import EmbeddedDocument, DateTimeField, StringField -EVENT_TYPES = ("monkey_local_action", "monkey_network_action", "island_action") +from common.data.zero_trust_consts import EVENT_TYPES class Event(EmbeddedDocument): @@ -8,3 +10,16 @@ class Event(EmbeddedDocument): title = StringField(required=True) message = StringField() event_type = StringField(required=True, choices=EVENT_TYPES) + + @staticmethod + def create_event(title, message, event_type): + event = Event( + timestamp=datetime.now(), + title=title, + message=message, + event_type=event_type + ) + + event.validate(clean=True) + + return event diff --git a/monkey/monkey_island/cc/models/finding.py b/monkey/monkey_island/cc/models/finding.py index c01e6b955..d67b10247 100644 --- a/monkey/monkey_island/cc/models/finding.py +++ b/monkey/monkey_island/cc/models/finding.py @@ -2,14 +2,13 @@ Define a Document Schema for Zero Trust findings. """ -from mongoengine import Document, StringField, ListField, EmbeddedDocumentField +from mongoengine import Document, StringField, EmbeddedDocumentListField +from common.data.zero_trust_consts import TEST_STATUSES, TESTS, TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY # Dummy import for mongoengine. # noinspection PyUnresolvedReferences from event import Event -from common.data.zero_trust_consts import TEST_STATUSES, PILLARS, TESTS, TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY - class Finding(Document): """ @@ -21,7 +20,7 @@ class Finding(Document): # SCHEMA test = StringField(required=True, choices=TESTS) status = StringField(required=True, choices=TEST_STATUSES) - events = ListField(field=EmbeddedDocumentField('Event')) + events = EmbeddedDocumentListField(document_type=Event) # LOGIC def get_test_explanation(self): diff --git a/monkey/monkey_island/cc/models/test_event.py b/monkey/monkey_island/cc/models/test_event.py new file mode 100644 index 000000000..f36f4f562 --- /dev/null +++ b/monkey/monkey_island/cc/models/test_event.py @@ -0,0 +1,31 @@ +from mongoengine import ValidationError + +from common.data.zero_trust_consts import EVENT_TYPE_ISLAND +from monkey_island.cc.models.event import Event +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +class TestEvent(IslandTestCase): + def test_create_event(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + with self.assertRaises(ValidationError): + Event.create_event( + title=None, # title required + message="bla bla", + event_type=EVENT_TYPE_ISLAND + ) + + with self.assertRaises(ValidationError): + Event.create_event( + title="skjs", + message="bla bla", + event_type="Unknown" # Unknown event type + ) + + _ = Event.create_event( + title="skjs", + message="bla bla", + event_type=EVENT_TYPE_ISLAND # Unknown event type + ) diff --git a/monkey/monkey_island/cc/models/test_finding.py b/monkey/monkey_island/cc/models/test_finding.py index 69a3986c1..2b52553da 100644 --- a/monkey/monkey_island/cc/models/test_finding.py +++ b/monkey/monkey_island/cc/models/test_finding.py @@ -2,7 +2,7 @@ from datetime import datetime from mongoengine import ValidationError -from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_CONCLUSIVE, NETWORKS +from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_CONCLUSIVE, NETWORKS, EVENT_TYPE_MONKEY_NETWORK from finding import Finding, UnknownTest from monkey_island.cc.models.event import Event @@ -33,7 +33,8 @@ class TestFinding(IslandTestCase): self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0) - event_example = Event(timestamp=datetime.now(), title="Event Title", message="event message", event_type="monkey_network_action") + event_example = Event.create_event( + title="Event Title", message="event message", event_type=EVENT_TYPE_MONKEY_NETWORK) Finding.save_finding(test=TEST_SEGMENTATION, status=STATUS_CONCLUSIVE, events=[event_example]) self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 1) From 829d9bc6f978f7d901290d6a18d767c981941ee8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 12 Aug 2019 11:49:37 +0300 Subject: [PATCH 030/186] Added AV test, not tested yet! --- .../monkey_island/cc/resources/telemetry.py | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index 9e71d586c..7e510a124 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -7,9 +7,11 @@ import dateutil import flask_restful from flask import request -from common.data.zero_trust_consts import TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, STATUS_CONCLUSIVE +from common.data.zero_trust_consts import TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, STATUS_CONCLUSIVE, \ + EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo +from monkey_island.cc.models.event import Event from monkey_island.cc.models.finding import Finding from monkey_island.cc.services import mimikatz_utils from monkey_island.cc.services.config import ConfigService @@ -183,23 +185,32 @@ class Telemetry(flask_restful.Resource): monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') Telemetry.process_mimikatz_and_wmi_info(monkey_id, telemetry_json) Telemetry.process_aws_data(monkey_id, telemetry_json) - Telemetry.test_antivirus_existance(telemetry_json) + Telemetry.test_antivirus_existence(telemetry_json, monkey_id) @staticmethod - def test_antivirus_existance(telemetry_json): + def test_antivirus_existence(telemetry_json, monkey_id): anti_virus_software = [ "SSPService.exe" ] if 'process_list' in telemetry_json['data']: + process_list_event = Event.create_event( + title="Process list", + message="Monkey {} scanned the process list".format(monkey_id), + event_type=EVENT_TYPE_MONKEY_LOCAL) + events = [process_list_event] found_av = False for process in telemetry_json['data']['process_list']: if process['name'] in anti_virus_software: found_av = True - # TODO add event here + events.append(Event.create_event( + title="Found AV process", + message="The process '{}' was recognized as an Anti Virus process. Process details: ".format(process['name'], str(process)), + event_type=EVENT_TYPE_ISLAND + )) if found_av: - Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_POSITIVE, events=[]) + Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_POSITIVE, events=events) else: - Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_CONCLUSIVE, events=[]) + Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_CONCLUSIVE, events=events) @staticmethod def process_mimikatz_and_wmi_info(monkey_id, telemetry_json): From 2eb34821f8d9d189ad570c8d922ca0bb3eac5a67 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 12 Aug 2019 16:56:11 +0300 Subject: [PATCH 031/186] Fixed telemetry access in AV test - it now works! --- monkey/common/data/zero_trust_consts.py | 7 ++++++- .../monkey_island/cc/resources/telemetry.py | 21 +++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index c37b261fe..db18f056a 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -102,4 +102,9 @@ TESTS_MAP = { EVENT_TYPE_ISLAND = "island" EVENT_TYPE_MONKEY_NETWORK = "monkey_network" EVENT_TYPE_MONKEY_LOCAL = "monkey_local" -EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK, EVENT_TYPE_ISLAND) \ No newline at end of file +EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK, EVENT_TYPE_ISLAND) + +ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ + u"SSPService.exe", + u"ipython.exe" +] diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index 7e510a124..fc2648589 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -8,7 +8,7 @@ import flask_restful from flask import request from common.data.zero_trust_consts import TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, STATUS_CONCLUSIVE, \ - EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND + EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, ANTI_VIRUS_KNOWN_PROCESS_NAMES from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo from monkey_island.cc.models.event import Event @@ -189,28 +189,31 @@ class Telemetry(flask_restful.Resource): @staticmethod def test_antivirus_existence(telemetry_json, monkey_id): - anti_virus_software = [ - "SSPService.exe" - ] if 'process_list' in telemetry_json['data']: process_list_event = Event.create_event( title="Process list", message="Monkey {} scanned the process list".format(monkey_id), event_type=EVENT_TYPE_MONKEY_LOCAL) events = [process_list_event] + found_av = False - for process in telemetry_json['data']['process_list']: - if process['name'] in anti_virus_software: + all_processes = telemetry_json['data']['process_list'].items() + for process in all_processes: + process_name = process[1]['name'] + if process_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES: found_av = True events.append(Event.create_event( title="Found AV process", - message="The process '{}' was recognized as an Anti Virus process. Process details: ".format(process['name'], str(process)), + message="The process '{}' was recognized as an Anti Virus process. Process " + "details: ".format(process_name, str(process)), event_type=EVENT_TYPE_ISLAND )) + if found_av: - Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_POSITIVE, events=events) + test_status = STATUS_POSITIVE else: - Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=STATUS_CONCLUSIVE, events=events) + test_status = STATUS_CONCLUSIVE + Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) @staticmethod def process_mimikatz_and_wmi_info(monkey_id, telemetry_json): From 47375efe42fb0a01441e27496cce7796fbfc9c08 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 12 Aug 2019 18:21:55 +0300 Subject: [PATCH 032/186] Findings resource is now real data from the DB instead of mock data. --- .../cc/resources/reporting/report.py | 53 ++++++++----------- .../zerotrust/EventsTimeline.js | 20 +++---- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 384ca09fe..61a034e45 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -1,9 +1,12 @@ import httplib +import json import flask_restful from flask import jsonify +from common.data.zero_trust_consts import TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY from monkey_island.cc.auth import jwt_required +from monkey_island.cc.models.finding import Finding from monkey_island.cc.services.reporting.report import ReportService ZERO_TRUST_REPORT_TYPE = "zero_trust" @@ -35,37 +38,25 @@ class Report(flask_restful.Resource): def get_all_findings(): - return [ - { - "test": "Monkey 8 found a machine with no AV software active.", - "conclusive": False, - "pillars": ["Devices"], - "events": [ - { - "timestamp": "2019-08-01 14:48:46.112000", - "title": "Monkey performed an action", - "type": "MonkeyAction", - "message": "log1" - }, { - "timestamp": "2019-08-01 14:48:42.112000", - "title": "Analysis", - "type": "IslandAction", - "message": "log2" - }] - }, - { - "test": "Monkey 6 successfully exploited machine XXX with shellshock.", - "conclusive": True, - "pillars": ["Devices", "Networks"], - "events": [ - { - "timestamp": "2019-08-01 14:48:46.112000", - "title": "Analysis", - "type": "MonkeyAction", - "message": "log3" - }] - } - ] + all_findings = Finding.objects() + enriched_findings = [get_enriched_finding(f) for f in all_findings] + return enriched_findings + + +def get_events_as_dict(events): + return [json.loads(event.to_json()) for event in events] + + +def get_enriched_finding(finding): + test_info = TESTS_MAP[finding.test] + enriched_finding = { + # TODO add test explanation per status. + "test": test_info[EXPLANATION_KEY], + "pillars": test_info[PILLARS_KEY], + "status": finding.status, + "events": get_events_as_dict(finding.events) + } + return enriched_finding def get_recommendations_status(): diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index ec0842309..f70d5df31 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -2,8 +2,9 @@ import React, {Component} from "react"; import {Timeline, TimelineEvent} from "react-event-timeline"; const eventTypeToIcon = { - "MonkeyAction": "fa fa-exclamation-circle fa-2x icon-warning", - "IslandAction": "fa fa-server fa-2x icon-info", + "monkey_local": "fa fa-exclamation-circle fa-2x icon-warning", + "monkey_network": "fa fa-exclamation-circle fa-2x icon-warning", + "island": "fa fa-server fa-2x icon-info", null: "fa fa-question-circle fa-2x icon-info", }; @@ -13,15 +14,16 @@ export class EventsTimeline extends Component {
    { - this.props["events"].map(event => ( - { + const event_time = new Date(event.timestamp['$date']).toString(); + return (}> + icon={}> {event.message} - - )) + ) + }) }
    From ae88764dc8e976a3c4d289c9f75a8718915e054e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 12 Aug 2019 18:48:13 +0300 Subject: [PATCH 033/186] Pillar grading resource is now real data --- monkey/common/data/zero_trust_consts.py | 22 +++++ .../cc/resources/reporting/report.py | 85 +++++++------------ .../zerotrust/PillarGrades.js | 1 + 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index db18f056a..72d2bfc43 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -99,6 +99,28 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] }, } + +PILLARS_TO_TESTS = { + DATA: [], + PEOPLE: [], + NETWORKS: [], + DEVICES: [], + WORKLOADS: [], + VISIBILITY_ANALYTICS: [], + AUTOMATION_ORCHESTRATION: [] +} + + +def populate_pillars_to_tests(): + for pillar in PILLARS: + for test, test_info in TESTS_MAP.items(): + if pillar in test_info[PILLARS_KEY]: + PILLARS_TO_TESTS[pillar].append(test) + + +populate_pillars_to_tests() + + EVENT_TYPE_ISLAND = "island" EVENT_TYPE_MONKEY_NETWORK = "monkey_network" EVENT_TYPE_MONKEY_LOCAL = "monkey_local" diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 61a034e45..e2a1a4722 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -4,7 +4,8 @@ import json import flask_restful from flask import jsonify -from common.data.zero_trust_consts import TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY +from common.data.zero_trust_consts import TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY, PILLARS, STATUS_CONCLUSIVE, \ + STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED, PILLARS_TO_TESTS from monkey_island.cc.auth import jwt_required from monkey_island.cc.models.finding import Finding from monkey_island.cc.services.reporting.report import ReportService @@ -151,55 +152,35 @@ def get_recommendations_status(): ] +def get_pillar_grade(pillar, all_findings): + pillar_grade = { + "Pillar": pillar, + STATUS_CONCLUSIVE: 0, + STATUS_INCONCLUSIVE: 0, + STATUS_POSITIVE: 0, + STATUS_UNEXECUTED: 0 + } + + tests_of_this_pillar = PILLARS_TO_TESTS[pillar] + + test_unexecuted = {} + for test in tests_of_this_pillar: + test_unexecuted[test] = True + + for finding in all_findings: + test_unexecuted[finding.test] = False + test_info = TESTS_MAP[finding.test] + if pillar in test_info[PILLARS_KEY]: + pillar_grade[finding.status] += 1 + + pillar_grade[STATUS_UNEXECUTED] = sum(1 for condition in test_unexecuted.values() if condition) + + return pillar_grade + + def get_pillars_grades(): - return [ - { - "Pillar": "Data", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": 6 - }, - { - "Pillar": "Networks", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": 6 - }, - { - "Pillar": "People", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": 6 - }, - { - "Pillar": "Workloads", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": 6 - }, - { - "Pillar": "Devices", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": 6 - }, - { - "Pillar": "Visibility & Analytics", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": 6 - }, - { - "Pillar": "Automation & Orchestration", - "Conclusive": 6, - "Inconclusive": 6, - "Positive": 6, - "Unexecuted": 6 - }, - ] + pillars_grades = [] + all_findings = Finding.objects() + for pillar in PILLARS: + pillars_grades.append(get_pillar_grade(pillar, all_findings)) + return pillars_grades diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index 9b746afd2..c2dd335f3 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -12,6 +12,7 @@ const columns = [ { Header: 'Conclusive', accessor: 'Conclusive'}, { Header: 'Inconclusive', accessor: 'Inconclusive'}, { Header: 'Unexecuted', accessor: 'Unexecuted'}, + { Header: 'Positive', accessor: 'Positive'}, ] } ]; From 6cd7af6eaac38c0d1d69f4eb490a9649ee293363 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 13 Aug 2019 11:54:50 +0300 Subject: [PATCH 034/186] Moved report generation logic to service, and now all report data is automatic also renamed fields to lowercase and renamed "recommendation" to "directive". --- monkey/common/data/zero_trust_consts.py | 28 ++- .../cc/resources/reporting/report.py | 161 +----------------- .../services/reporting/zero_trust_report.py | 114 +++++++++++++ .../components/pages/ZeroTrustReportPage.js | 26 +-- ...tatusTable.js => DirectivesStatusTable.js} | 39 ++--- .../zerotrust/PillarGrades.js | 2 +- .../zerotrust/SinglePillarDirectivesStatus.js | 22 +++ .../SinglePillarRecommendationsStatus.js | 15 -- 8 files changed, 203 insertions(+), 204 deletions(-) create mode 100644 monkey/monkey_island/cc/services/reporting/zero_trust_report.py rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{RecommendationsStatusTable.js => DirectivesStatusTable.js} (52%) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 72d2bfc43..8c74bf145 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -11,7 +11,8 @@ STATUS_UNEXECUTED = u"Unexecuted" STATUS_POSITIVE = u"Positive" STATUS_INCONCLUSIVE = u"Inconclusive" STATUS_CONCLUSIVE = u"Conclusive" -TEST_STATUSES = (STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED) +# Don't change order! +TEST_STATUSES = [STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED] TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic" TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http" @@ -120,6 +121,31 @@ def populate_pillars_to_tests(): populate_pillars_to_tests() +DIRECTIVES_TO_TESTS = {} + + +def populate_directives_to_tests(): + for single_directive in DIRECTIVES: + DIRECTIVES_TO_TESTS[single_directive] = [] + for test, test_info in TESTS_MAP.items(): + DIRECTIVES_TO_TESTS[test_info[DIRECTIVE_KEY]].append(test) + + +populate_directives_to_tests() + +DIRECTIVES_TO_PILLARS = {} + + +def populate_directives_to_pillars(): + for directive, directive_tests in DIRECTIVES_TO_TESTS.items(): + directive_pillars = set() + for test in directive_tests: + for pillar in TESTS_MAP[test][PILLARS_KEY]: + directive_pillars.add(pillar) + DIRECTIVES_TO_PILLARS[directive] = directive_pillars + + +populate_directives_to_pillars() EVENT_TYPE_ISLAND = "island" EVENT_TYPE_MONKEY_NETWORK = "monkey_network" diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index e2a1a4722..e120b27ae 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -1,14 +1,13 @@ import httplib -import json + import flask_restful from flask import jsonify -from common.data.zero_trust_consts import TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY, PILLARS, STATUS_CONCLUSIVE, \ - STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED, PILLARS_TO_TESTS from monkey_island.cc.auth import jwt_required -from monkey_island.cc.models.finding import Finding from monkey_island.cc.services.reporting.report import ReportService +from monkey_island.cc.services.reporting.zero_trust_report import get_all_findings, get_pillars_grades, \ + get_directives_status ZERO_TRUST_REPORT_TYPE = "zero_trust" GENERAL_REPORT_TYPE = "general" @@ -16,7 +15,7 @@ REPORT_TYPES = [GENERAL_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] REPORT_DATA_PILLARS = "pillars" REPORT_DATA_FINDINGS = "findings" -REPORT_DATA_RECOMMENDATION_STATUS = "recommendations" +REPORT_DATA_DIRECTIVES_STATUS = "directives" __author__ = ["itay.mizeretz", "shay.nehmad"] @@ -32,155 +31,7 @@ class Report(flask_restful.Resource): return jsonify(get_all_findings()) elif report_data == REPORT_DATA_PILLARS: return jsonify(get_pillars_grades()) - elif report_data == REPORT_DATA_RECOMMENDATION_STATUS: - return jsonify(get_recommendations_status()) + elif report_data == REPORT_DATA_DIRECTIVES_STATUS: + return jsonify(get_directives_status()) flask_restful.abort(httplib.NOT_FOUND) - - -def get_all_findings(): - all_findings = Finding.objects() - enriched_findings = [get_enriched_finding(f) for f in all_findings] - return enriched_findings - - -def get_events_as_dict(events): - return [json.loads(event.to_json()) for event in events] - - -def get_enriched_finding(finding): - test_info = TESTS_MAP[finding.test] - enriched_finding = { - # TODO add test explanation per status. - "test": test_info[EXPLANATION_KEY], - "pillars": test_info[PILLARS_KEY], - "status": finding.status, - "events": get_events_as_dict(finding.events) - } - return enriched_finding - - -def get_recommendations_status(): - network_recomms = [ - { - "Recommendation": "Do network segmentation.", - "Status": "Positive", - "Tests": [ - { - "Test": "Test B for segmentation", - "Status": "Positive" - }, - { - "Test": "Test A for segmentation", - "Status": "Positive" - }, - ] - }, - { - "Recommendation": "Analyze malicious network traffic.", - "Status": "Inconclusive", - "Tests": [ - { - "Test": "Use exploits.", - "Status": "Inconclusive" - }, - { - "Test": "Bruteforce passwords.", - "Status": "Inconclusive" - } - ] - }, - { - "Recommendation": "Data at trasnit should be...", - "Status": "Conclusive", - "Tests": [ - { - "Test": "Scan HTTP.", - "Status": "Conclusive" - }, - { - "Test": "Scan elastic.", - "Status": "Unexecuted" - } - ] - }, - ] - - device_recomms = [ - { - "Recommendation": "Install AV software.", - "Status": "Unexecuted", - "Tests": [ - { - "Test": "Search for active AV software processes", - "Status": "Unexecuted" - } - ] - }, - ] - - data_recommns = [ - { - "Recommendation": "Data at trasnit should be...", - "Status": "Conclusive", - "Tests": [ - { - "Test": "Scan HTTP.", - "Status": "Conclusive" - }, - { - "Test": "Scan elastic.", - "Status": "Unexecuted" - } - ] - }, - ] - - return [ - { - "pillar": "Networks", - "recommendationStatus": network_recomms - }, - { - "pillar": "Data", - "recommendationStatus": data_recommns - }, - { - "pillar": "Devices", - "recommendationStatus": device_recomms - }, - ] - - -def get_pillar_grade(pillar, all_findings): - pillar_grade = { - "Pillar": pillar, - STATUS_CONCLUSIVE: 0, - STATUS_INCONCLUSIVE: 0, - STATUS_POSITIVE: 0, - STATUS_UNEXECUTED: 0 - } - - tests_of_this_pillar = PILLARS_TO_TESTS[pillar] - - test_unexecuted = {} - for test in tests_of_this_pillar: - test_unexecuted[test] = True - - for finding in all_findings: - test_unexecuted[finding.test] = False - test_info = TESTS_MAP[finding.test] - if pillar in test_info[PILLARS_KEY]: - pillar_grade[finding.status] += 1 - - pillar_grade[STATUS_UNEXECUTED] = sum(1 for condition in test_unexecuted.values() if condition) - - return pillar_grade - - -def get_pillars_grades(): - pillars_grades = [] - all_findings = Finding.objects() - for pillar in PILLARS: - pillars_grades.append(get_pillar_grade(pillar, all_findings)) - return pillars_grades diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_report.py b/monkey/monkey_island/cc/services/reporting/zero_trust_report.py new file mode 100644 index 000000000..09227f134 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_report.py @@ -0,0 +1,114 @@ +import json +from common.data.zero_trust_consts import * +from monkey_island.cc.models.finding import Finding + + +def get_all_findings(): + all_findings = Finding.objects() + enriched_findings = [get_enriched_finding(f) for f in all_findings] + return enriched_findings + + +def get_events_as_dict(events): + return [json.loads(event.to_json()) for event in events] + + +def get_enriched_finding(finding): + test_info = TESTS_MAP[finding.test] + enriched_finding = { + # TODO add test explanation per status. + "test": test_info[EXPLANATION_KEY], + "pillars": test_info[PILLARS_KEY], + "status": finding.status, + "events": get_events_as_dict(finding.events) + } + return enriched_finding + + +def get_lcd_worst_status_for_test(all_findings_for_test): + current_status = STATUS_UNEXECUTED + for finding in all_findings_for_test: + if TEST_STATUSES.index(finding.status) < TEST_STATUSES.index(current_status): + current_status = finding.status + + return current_status + + +def get_tests_status(directive_tests): + results = [] + for test in directive_tests: + test_findings = Finding.objects(test=test) + results.append( + { + "test": test, + "status": get_lcd_worst_status_for_test(test_findings) + } + ) + return results + + +def get_directive_status(directive_tests): + worst_status = STATUS_UNEXECUTED + all_statuses = set() + for test in directive_tests: + all_statuses |= set(Finding.objects(test=test).distinct("status")) + + for status in all_statuses: + if TEST_STATUSES.index(status) < TEST_STATUSES.index(worst_status): + worst_status = status + + return worst_status + + +def get_directives_status(): + all_directive_statuses = {} + + # init with empty lists + for pillar in PILLARS: + all_directive_statuses[pillar] = [] + + for directive, directive_tests in DIRECTIVES_TO_TESTS.items(): + for pillar in DIRECTIVES_TO_PILLARS[directive]: + all_directive_statuses[pillar].append( + { + "directive": directive, + "tests": get_tests_status(directive_tests), + "status": get_directive_status(directive_tests) + } + ) + + return all_directive_statuses + + +def get_pillar_grade(pillar, all_findings): + pillar_grade = { + "pillar": pillar, + STATUS_CONCLUSIVE: 0, + STATUS_INCONCLUSIVE: 0, + STATUS_POSITIVE: 0, + STATUS_UNEXECUTED: 0 + } + + tests_of_this_pillar = PILLARS_TO_TESTS[pillar] + + test_unexecuted = {} + for test in tests_of_this_pillar: + test_unexecuted[test] = True + + for finding in all_findings: + test_unexecuted[finding.test] = False + test_info = TESTS_MAP[finding.test] + if pillar in test_info[PILLARS_KEY]: + pillar_grade[finding.status] += 1 + + pillar_grade[STATUS_UNEXECUTED] = sum(1 for condition in test_unexecuted.values() if condition) + + return pillar_grade + + +def get_pillars_grades(): + pillars_grades = [] + all_findings = Finding.objects() + for pillar in PILLARS: + pillars_grades.append(get_pillar_grade(pillar, all_findings)) + return pillars_grades diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 630952961..1dfe0c40b 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -4,7 +4,7 @@ import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import PillarGrades from "../report-components/zerotrust/PillarGrades"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; -import {SinglePillarRecommendationsStatus} from "../report-components/zerotrust/SinglePillarRecommendationsStatus"; +import {SinglePillarDirectivesStatus} from "../report-components/zerotrust/SinglePillarDirectivesStatus"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -45,13 +45,13 @@ class ZeroTrustReportPageComponent extends AuthComponent {
    ; - const recommendationsSection =

    Recommendations Status

    + const directivesSection =

    Directives status

    { - this.state.recommendations.map((recommendation) => - + Object.keys(this.state.directives).map((pillar) => + ) }
    ; @@ -61,7 +61,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { content =
    {pillarsSection} - {recommendationsSection} + {directivesSection} {findingSection}
    ; } @@ -84,8 +84,8 @@ class ZeroTrustReportPageComponent extends AuthComponent { PILLARS:
    {JSON.stringify(this.state.pillars, undefined, 2)}

    - recommendations: -
    {JSON.stringify(this.state.recommendations, undefined, 2)}
    + DIRECTIVES: +
    {JSON.stringify(this.state.directives, undefined, 2)}

    FINDINGS:
    {JSON.stringify(this.state.findings, undefined, 2)}
    @@ -95,7 +95,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { } stillLoadingDataFromServer() { - return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.recommendations === "undefined"; + return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.directives === "undefined"; } print() { @@ -111,11 +111,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { findings: res }); }); - this.authFetch('/api/report/zero_trust/recommendations') + this.authFetch('/api/report/zero_trust/directives') .then(res => res.json()) .then(res => { this.setState({ - recommendations: res + directives: res }); }); this.authFetch('/api/report/zero_trust/pillars') diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js similarity index 52% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js index acd7bc322..f1e2a5d43 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js @@ -1,28 +1,29 @@ -import React, {Component} from "react"; +import React from "react"; import PagenatedTable from "../common/PagenatedTable"; import AuthComponent from "../../AuthComponent"; +const statusToIcon = { + "Positive": "fa-shield alert-success", + "Inconclusive": "fa-question alert-info", + "Conclusive": "fa-unlock-alt alert-danger", + "Unexecuted": "fa-toggle-off", +}; + const columns = [ { - Header: 'Recommendations status', + Header: 'Directives status', columns: [ - { Header: 'Recommendation', accessor: 'Recommendation', + { Header: 'Directive', accessor: 'directive', style: {'whiteSpace': 'unset'} // This enables word wrap }, - { Header: 'Status', id: "Status", + { Header: 'Status', id: 'status', accessor: x => { - const statusToIcon = { - "Positive": "fa-shield alert-success", - "Inconclusive": "fa-question alert-info", - "Conclusive": "fa-unlock-alt alert-danger", - "Unexecuted": "fa-toggle-off", - }; - return ; + return ; } }, - { Header: 'Tests', id:"Tests", + { Header: 'Tests', id: 'tests', accessor: x => { - return ; + return ; } } ] @@ -48,24 +49,24 @@ class TestsStatus extends AuthComponent { getTestsOfStatusIfAny(statusToFilter) { const filteredTests = this.props.tests.filter((test) => { - return (test.Status === statusToFilter); + return (test.status === statusToFilter); } ); if (filteredTests.length > 0) { const listItems = filteredTests.map((test) => { - return (
  • {test.Test}
  • ) + return (
  • {test.test}
  • ) }); - return
    {statusToFilter}
      {listItems}
    ; + return
    {statusToFilter}
      {listItems}
    ; } return
    ; } } -export class RecommendationsStatusTable extends AuthComponent { +export class DirectivesStatusTable extends AuthComponent { render() { - return ; + return ; } } -export default RecommendationsStatusTable; +export default DirectivesStatusTable; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index c2dd335f3..5c0d0d903 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -7,7 +7,7 @@ const columns = [ Header: 'Pillar Grading', columns: [ { Header: 'Pillar', id: 'Pillar', accessor: x => { - return (); + return (); }}, { Header: 'Conclusive', accessor: 'Conclusive'}, { Header: 'Inconclusive', accessor: 'Inconclusive'}, diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js new file mode 100644 index 000000000..dad36d025 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js @@ -0,0 +1,22 @@ +import AuthComponent from "../../AuthComponent"; +import {PillarLabel} from "./PillarLabel"; +import DirectivesStatusTable from "./DirectivesStatusTable"; +import React, {Fragment} from "react"; + +export class SinglePillarDirectivesStatus extends AuthComponent { + directivesStatus; + + render() { + if (this.props.directivesStatus.length === 0) { + return null; + } + else { + return ( + +

    + +
    + ); + } + } +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js deleted file mode 100644 index 802b7a0a2..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js +++ /dev/null @@ -1,15 +0,0 @@ -import AuthComponent from "../../AuthComponent"; -import {PillarLabel} from "./PillarLabel"; -import RecommendationsStatusTable from "./RecommendationsStatusTable"; -import React from "react"; - -export class SinglePillarRecommendationsStatus extends AuthComponent { - render() { - return ( -
    -

    - -
    - ); - } -} From fb893089d91ad41f58404675e3b592ded3385fe5 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 13 Aug 2019 14:32:05 +0300 Subject: [PATCH 035/186] Fixed circular import in the testing env --- monkey/monkey_island/cc/environment/testing.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/environment/testing.py b/monkey/monkey_island/cc/environment/testing.py index 286e442dd..087c3a2e3 100644 --- a/monkey/monkey_island/cc/environment/testing.py +++ b/monkey/monkey_island/cc/environment/testing.py @@ -1,5 +1,4 @@ from monkey_island.cc.environment import Environment -import monkey_island.cc.auth class TestingEnvironment(Environment): @@ -7,11 +6,5 @@ class TestingEnvironment(Environment): super(TestingEnvironment, self).__init__() self.testing = True - # SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()' - NO_AUTH_CREDS = '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062' \ - '8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557' - def get_auth_users(self): - return [ - monkey_island.cc.auth.User(1, self.NO_AUTH_CREDS, self.NO_AUTH_CREDS) - ] + return [] From bfcd469e0486d53f2957e760ae6b478d58d7f8a8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 13 Aug 2019 14:32:55 +0300 Subject: [PATCH 036/186] Added finding db cleaning for the UTs in IslandTestCase --- monkey/monkey_island/cc/models/test_event.py | 4 ++-- monkey/monkey_island/cc/models/test_finding.py | 13 +++++-------- monkey/monkey_island/cc/testing/IslandTestCase.py | 5 +++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/models/test_event.py b/monkey/monkey_island/cc/models/test_event.py index f36f4f562..3bc201f62 100644 --- a/monkey/monkey_island/cc/models/test_event.py +++ b/monkey/monkey_island/cc/models/test_event.py @@ -8,7 +8,7 @@ from monkey_island.cc.testing.IslandTestCase import IslandTestCase class TestEvent(IslandTestCase): def test_create_event(self): self.fail_if_not_testing_env() - self.clean_monkey_db() + self.clean_finding_db() with self.assertRaises(ValidationError): Event.create_event( @@ -27,5 +27,5 @@ class TestEvent(IslandTestCase): _ = Event.create_event( title="skjs", message="bla bla", - event_type=EVENT_TYPE_ISLAND # Unknown event type + event_type=EVENT_TYPE_ISLAND ) diff --git a/monkey/monkey_island/cc/models/test_finding.py b/monkey/monkey_island/cc/models/test_finding.py index 2b52553da..d111b0513 100644 --- a/monkey/monkey_island/cc/models/test_finding.py +++ b/monkey/monkey_island/cc/models/test_finding.py @@ -1,11 +1,8 @@ -from datetime import datetime - from mongoengine import ValidationError -from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_CONCLUSIVE, NETWORKS, EVENT_TYPE_MONKEY_NETWORK -from finding import Finding, UnknownTest +from common.data.zero_trust_consts import * +from finding import Finding from monkey_island.cc.models.event import Event - from monkey_island.cc.testing.IslandTestCase import IslandTestCase @@ -19,17 +16,17 @@ class TestFinding(IslandTestCase): """ def test_save_finding_validation(self): self.fail_if_not_testing_env() - self.clean_monkey_db() + self.clean_finding_db() with self.assertRaises(ValidationError): - _ = Finding.save_finding(test="bla bla", status="Conclusive", events=[]) + _ = Finding.save_finding(test="bla bla", status=STATUS_CONCLUSIVE, events=[]) with self.assertRaises(ValidationError): _ = Finding.save_finding(test=TEST_SEGMENTATION, status="bla bla", events=[]) def test_save_finding_sanity(self): self.fail_if_not_testing_env() - self.clean_monkey_db() + self.clean_finding_db() self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0) diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py index e894f13df..5b050684c 100644 --- a/monkey/monkey_island/cc/testing/IslandTestCase.py +++ b/monkey/monkey_island/cc/testing/IslandTestCase.py @@ -1,6 +1,7 @@ import unittest from monkey_island.cc.environment.environment import env from monkey_island.cc.models import Monkey +from monkey_island.cc.models.finding import Finding class IslandTestCase(unittest.TestCase): @@ -10,3 +11,7 @@ class IslandTestCase(unittest.TestCase): @staticmethod def clean_monkey_db(): Monkey.objects().delete() + + @staticmethod + def clean_finding_db(): + Finding.objects().delete() From d4f922ab00f9bf44e5986944d7593675f2a5d1ef Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 13 Aug 2019 14:33:18 +0300 Subject: [PATCH 037/186] Added zero trust service with passing sanity UTs --- monkey/common/data/zero_trust_consts.py | 2 +- .../cc/resources/reporting/report.py | 13 +- .../reporting/test_zeroTrustService.py | 200 ++++++++++++++++++ .../services/reporting/zero_trust_report.py | 114 ---------- .../services/reporting/zero_trust_service.py | 117 ++++++++++ 5 files changed, 324 insertions(+), 122 deletions(-) create mode 100644 monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py delete mode 100644 monkey/monkey_island/cc/services/reporting/zero_trust_report.py create mode 100644 monkey/monkey_island/cc/services/reporting/zero_trust_service.py diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 8c74bf145..b6a8c8c33 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -61,7 +61,7 @@ TESTS_MAP = { EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", FINDING_FORMAT_KEY: u"Malicious activity performed by the Monkeys. See 'events' for detailed information.", DIRECTIVE_KEY: DIRECTIVE_ANALYZE_NETWORK_TRAFFIC, - PILLARS_KEY: [NETWORKS], + PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] }, TEST_ENDPOINT_SECURITY_EXISTS: { diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index e120b27ae..84e458398 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -6,8 +6,7 @@ from flask import jsonify from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.reporting.report import ReportService -from monkey_island.cc.services.reporting.zero_trust_report import get_all_findings, get_pillars_grades, \ - get_directives_status +from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService ZERO_TRUST_REPORT_TYPE = "zero_trust" GENERAL_REPORT_TYPE = "general" @@ -27,11 +26,11 @@ class Report(flask_restful.Resource): if report_type == GENERAL_REPORT_TYPE: return ReportService.get_report() elif report_type == ZERO_TRUST_REPORT_TYPE: - if report_data == REPORT_DATA_FINDINGS: - return jsonify(get_all_findings()) - elif report_data == REPORT_DATA_PILLARS: - return jsonify(get_pillars_grades()) + if report_data == REPORT_DATA_PILLARS: + return jsonify(ZeroTrustService.get_pillars_grades()) elif report_data == REPORT_DATA_DIRECTIVES_STATUS: - return jsonify(get_directives_status()) + return jsonify(ZeroTrustService.get_directives_status()) + elif report_data == REPORT_DATA_FINDINGS: + return jsonify(ZeroTrustService.get_all_findings()) flask_restful.abort(httplib.NOT_FOUND) diff --git a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py new file mode 100644 index 000000000..ff6e88982 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py @@ -0,0 +1,200 @@ +from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService + +from common.data.zero_trust_consts import * +from monkey_island.cc.models.finding import Finding +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +def save_example_findings(): + # arrange + Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, []) # devices positive = 1 + Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, []) # devices positive = 2 + Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_CONCLUSIVE, []) # devices conclusive = 1 + # devices unexecuted = 1 + # people inconclusive = 1 + # networks inconclusive = 1 + Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_INCONCLUSIVE, []) + # people inconclusive = 2 + # networks inconclusive = 2 + Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_INCONCLUSIVE, []) + # data conclusive 1 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) + # data conclusive 2 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) + # data conclusive 3 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) + # data conclusive 4 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) + # data conclusive 5 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) + # data inconclusive 1 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, []) + # data inconclusive 2 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, []) + # data positive 1 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_POSITIVE, []) + + +class TestZeroTrustService(IslandTestCase): + def test_get_pillars_grades(self): + self.fail_if_not_testing_env() + self.clean_finding_db() + + save_example_findings() + + expected = [ + { + "Conclusive": 5, + "Inconclusive": 2, + "Positive": 1, + "Unexecuted": 1, + "pillar": "Data" + }, + { + "Conclusive": 0, + "Inconclusive": 2, + "Positive": 0, + "Unexecuted": 0, + "pillar": "People" + }, + { + "Conclusive": 0, + "Inconclusive": 2, + "Positive": 0, + "Unexecuted": 2, + "pillar": "Networks" + }, + { + "Conclusive": 1, + "Inconclusive": 0, + "Positive": 2, + "Unexecuted": 1, + "pillar": "Devices" + }, + { + "Conclusive": 0, + "Inconclusive": 0, + "Positive": 0, + "Unexecuted": 0, + "pillar": "Workloads" + }, + { + "Conclusive": 0, + "Inconclusive": 0, + "Positive": 0, + "Unexecuted": 1, + "pillar": "Visibility & Analytics" + }, + { + "Conclusive": 0, + "Inconclusive": 0, + "Positive": 0, + "Unexecuted": 0, + "pillar": "Automation & Orchestration" + } + ] + + result = ZeroTrustService.get_pillars_grades() + + self.assertEquals(result, expected) + + def test_get_directives_status(self): + self.fail_if_not_testing_env() + self.clean_finding_db() + + save_example_findings() + + expected = { + AUTOMATION_ORCHESTRATION: [], + DATA: [ + { + "directive": DIRECTIVE_DATA_TRANSIT, + "status": STATUS_CONCLUSIVE, + "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TEST_DATA_ENDPOINT_ELASTIC + }, + { + "status": STATUS_CONCLUSIVE, + "test": TEST_DATA_ENDPOINT_HTTP + } + ] + } + ], + DEVICES: [ + { + "directive": "endpoint_security", + "status": "Conclusive", + "tests": [ + { + "status": "Conclusive", + "test": "endpoint_security_exists" + }, + { + "status": "Unexecuted", + "test": "machine_exploited" + } + ] + } + ], + NETWORKS: [ + { + "directive": "segmentation", + "status": "Unexecuted", + "tests": [ + { + "status": "Unexecuted", + "test": "segmentation" + } + ] + }, + { + "directive": "user_behaviour", + "status": STATUS_INCONCLUSIVE, + "tests": [ + { + "status": STATUS_INCONCLUSIVE, + "test": TEST_SCHEDULED_EXECUTION + } + ] + }, + { + "directive": "analyze_network_traffic", + "status": "Unexecuted", + "tests": [ + { + "status": "Unexecuted", + "test": "malicious_activity_timeline" + } + ] + } + ], + PEOPLE: [ + { + "directive": "user_behaviour", + "status": STATUS_INCONCLUSIVE, + "tests": [ + { + "status": STATUS_INCONCLUSIVE, + "test": TEST_SCHEDULED_EXECUTION + } + ] + } + ], + "Visibility & Analytics": [ + { + "directive": DIRECTIVE_ANALYZE_NETWORK_TRAFFIC, + "status": STATUS_UNEXECUTED, + "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TEST_ACTIVITY_TIMELINE + } + ] + } + ], + "Workloads": [] + } + + self.assertEquals(ZeroTrustService.get_directives_status(), expected) diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_report.py b/monkey/monkey_island/cc/services/reporting/zero_trust_report.py deleted file mode 100644 index 09227f134..000000000 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_report.py +++ /dev/null @@ -1,114 +0,0 @@ -import json -from common.data.zero_trust_consts import * -from monkey_island.cc.models.finding import Finding - - -def get_all_findings(): - all_findings = Finding.objects() - enriched_findings = [get_enriched_finding(f) for f in all_findings] - return enriched_findings - - -def get_events_as_dict(events): - return [json.loads(event.to_json()) for event in events] - - -def get_enriched_finding(finding): - test_info = TESTS_MAP[finding.test] - enriched_finding = { - # TODO add test explanation per status. - "test": test_info[EXPLANATION_KEY], - "pillars": test_info[PILLARS_KEY], - "status": finding.status, - "events": get_events_as_dict(finding.events) - } - return enriched_finding - - -def get_lcd_worst_status_for_test(all_findings_for_test): - current_status = STATUS_UNEXECUTED - for finding in all_findings_for_test: - if TEST_STATUSES.index(finding.status) < TEST_STATUSES.index(current_status): - current_status = finding.status - - return current_status - - -def get_tests_status(directive_tests): - results = [] - for test in directive_tests: - test_findings = Finding.objects(test=test) - results.append( - { - "test": test, - "status": get_lcd_worst_status_for_test(test_findings) - } - ) - return results - - -def get_directive_status(directive_tests): - worst_status = STATUS_UNEXECUTED - all_statuses = set() - for test in directive_tests: - all_statuses |= set(Finding.objects(test=test).distinct("status")) - - for status in all_statuses: - if TEST_STATUSES.index(status) < TEST_STATUSES.index(worst_status): - worst_status = status - - return worst_status - - -def get_directives_status(): - all_directive_statuses = {} - - # init with empty lists - for pillar in PILLARS: - all_directive_statuses[pillar] = [] - - for directive, directive_tests in DIRECTIVES_TO_TESTS.items(): - for pillar in DIRECTIVES_TO_PILLARS[directive]: - all_directive_statuses[pillar].append( - { - "directive": directive, - "tests": get_tests_status(directive_tests), - "status": get_directive_status(directive_tests) - } - ) - - return all_directive_statuses - - -def get_pillar_grade(pillar, all_findings): - pillar_grade = { - "pillar": pillar, - STATUS_CONCLUSIVE: 0, - STATUS_INCONCLUSIVE: 0, - STATUS_POSITIVE: 0, - STATUS_UNEXECUTED: 0 - } - - tests_of_this_pillar = PILLARS_TO_TESTS[pillar] - - test_unexecuted = {} - for test in tests_of_this_pillar: - test_unexecuted[test] = True - - for finding in all_findings: - test_unexecuted[finding.test] = False - test_info = TESTS_MAP[finding.test] - if pillar in test_info[PILLARS_KEY]: - pillar_grade[finding.status] += 1 - - pillar_grade[STATUS_UNEXECUTED] = sum(1 for condition in test_unexecuted.values() if condition) - - return pillar_grade - - -def get_pillars_grades(): - pillars_grades = [] - all_findings = Finding.objects() - for pillar in PILLARS: - pillars_grades.append(get_pillar_grade(pillar, all_findings)) - return pillars_grades diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py new file mode 100644 index 000000000..7a4a5ce43 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -0,0 +1,117 @@ +import json +from common.data.zero_trust_consts import * +from monkey_island.cc.models.finding import Finding + + +class ZeroTrustService(object): + @staticmethod + def get_pillars_grades(): + pillars_grades = [] + all_findings = Finding.objects() + for pillar in PILLARS: + pillars_grades.append(ZeroTrustService.__get_pillar_grade(pillar, all_findings)) + return pillars_grades + + @staticmethod + def __get_pillar_grade(pillar, all_findings): + pillar_grade = { + "pillar": pillar, + STATUS_CONCLUSIVE: 0, + STATUS_INCONCLUSIVE: 0, + STATUS_POSITIVE: 0, + STATUS_UNEXECUTED: 0 + } + + tests_of_this_pillar = PILLARS_TO_TESTS[pillar] + + test_unexecuted = {} + for test in tests_of_this_pillar: + test_unexecuted[test] = True + + for finding in all_findings: + test_unexecuted[finding.test] = False + test_info = TESTS_MAP[finding.test] + if pillar in test_info[PILLARS_KEY]: + pillar_grade[finding.status] += 1 + + pillar_grade[STATUS_UNEXECUTED] = sum(1 for condition in test_unexecuted.values() if condition) + + return pillar_grade + + @staticmethod + def get_directives_status(): + all_directive_statuses = {} + + # init with empty lists + for pillar in PILLARS: + all_directive_statuses[pillar] = [] + + for directive, directive_tests in DIRECTIVES_TO_TESTS.items(): + for pillar in DIRECTIVES_TO_PILLARS[directive]: + all_directive_statuses[pillar].append( + { + "directive": directive, + "tests": ZeroTrustService.__get_tests_status(directive_tests), + "status": ZeroTrustService.__get_directive_status(directive_tests) + } + ) + + return all_directive_statuses + + @staticmethod + def __get_directive_status(directive_tests): + worst_status = STATUS_UNEXECUTED + all_statuses = set() + for test in directive_tests: + all_statuses |= set(Finding.objects(test=test).distinct("status")) + + for status in all_statuses: + if TEST_STATUSES.index(status) < TEST_STATUSES.index(worst_status): + worst_status = status + + return worst_status + + @staticmethod + def __get_tests_status(directive_tests): + results = [] + for test in directive_tests: + test_findings = Finding.objects(test=test) + results.append( + { + "test": test, + "status": ZeroTrustService.__get_lcd_worst_status_for_test(test_findings) + } + ) + return results + + @staticmethod + def __get_lcd_worst_status_for_test(all_findings_for_test): + current_status = STATUS_UNEXECUTED + for finding in all_findings_for_test: + if TEST_STATUSES.index(finding.status) < TEST_STATUSES.index(current_status): + current_status = finding.status + + return current_status + + @staticmethod + def get_all_findings(): + all_findings = Finding.objects() + enriched_findings = [ZeroTrustService.__get_enriched_finding(f) for f in all_findings] + return enriched_findings + + @staticmethod + def __get_enriched_finding(finding): + test_info = TESTS_MAP[finding.test] + enriched_finding = { + # TODO add test explanation per status. + "test": test_info[EXPLANATION_KEY], + "pillars": test_info[PILLARS_KEY], + "status": finding.status, + "events": ZeroTrustService.__get_events_as_dict(finding.events) + } + return enriched_finding + + @staticmethod + def __get_events_as_dict(events): + return [json.loads(event.to_json()) for event in events] + From 776b9410768f2034121aaaccf67cfaa548ec3730 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 14 Aug 2019 09:31:45 +0300 Subject: [PATCH 038/186] Changed icons and fixed typo --- .../{PagenatedTable.js => PaginatedTable.js} | 4 ++-- .../zerotrust/DirectivesStatusTable.js | 13 +++++++------ .../report-components/zerotrust/FindingsTable.js | 4 ++-- .../report-components/zerotrust/PillarGrades.js | 4 ++-- monkey/monkey_island/cc/ui/src/styles/App.css | 16 ++++++++++++++++ 5 files changed, 29 insertions(+), 12 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/common/{PagenatedTable.js => PaginatedTable.js} (90%) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PagenatedTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js similarity index 90% rename from monkey/monkey_island/cc/ui/src/components/report-components/common/PagenatedTable.js rename to monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js index 6a4652837..53ae1774d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/PagenatedTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js @@ -1,7 +1,7 @@ import React, {Component} from "react"; import ReactTable from "react-table"; -class PagenatedTable extends Component { +class PaginatedTable extends Component { render() { if (this.props.data.length > 0) { let defaultPageSize = this.props.data.length > this.props.pageSize ? this.props.pageSize : this.props.data.length; @@ -26,4 +26,4 @@ class PagenatedTable extends Component { } } -export default PagenatedTable; +export default PaginatedTable; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js index f1e2a5d43..4f413f37f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js @@ -1,12 +1,13 @@ import React from "react"; -import PagenatedTable from "../common/PagenatedTable"; +import PaginatedTable from "../common/PaginatedTable"; import AuthComponent from "../../AuthComponent"; +import 'styles/ZeroTrustPillars.css' const statusToIcon = { - "Positive": "fa-shield alert-success", - "Inconclusive": "fa-question alert-info", - "Conclusive": "fa-unlock-alt alert-danger", - "Unexecuted": "fa-toggle-off", + "Positive": "fa-clipboard-check status-success", + "Inconclusive": "fa-exclamation-triangle status-warning", + "Conclusive": "fa-bomb status-danger", + "Unexecuted": "fa-question status-default", }; const columns = [ @@ -65,7 +66,7 @@ class TestsStatus extends AuthComponent { export class DirectivesStatusTable extends AuthComponent { render() { - return ; + return ; } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index f2247461b..ae1c41d49 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -3,7 +3,7 @@ import {Button} from "react-bootstrap"; import {EventsModal} from "./EventsModal"; import FileSaver from "file-saver"; import {PillarLabel} from "./PillarLabel"; -import PagenatedTable from "../common/PagenatedTable"; +import PaginatedTable from "../common/PaginatedTable"; class EventsAndButtonComponent extends Component { @@ -75,7 +75,7 @@ const columns = [ class FindingsTable extends Component { render() { return ( - + ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js index 5c0d0d903..15e38607c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js @@ -1,6 +1,6 @@ import React, {Component} from "react"; import {PillarLabel} from "./PillarLabel"; -import PagenatedTable from "../common/PagenatedTable"; +import PaginatedTable from "../common/PaginatedTable"; const columns = [ { @@ -19,7 +19,7 @@ const columns = [ class PillarGrades extends Component { render() { - return ; + return ; } } diff --git a/monkey/monkey_island/cc/ui/src/styles/App.css b/monkey/monkey_island/cc/ui/src/styles/App.css index 9fc468f77..b617ab5d5 100644 --- a/monkey/monkey_island/cc/ui/src/styles/App.css +++ b/monkey/monkey_island/cc/ui/src/styles/App.css @@ -541,6 +541,22 @@ body { color: #e0ddde !important; } +.status-success { + color: #24b716 !important; +} + +.status-warning { + color: #b1a91c !important; +} + +.status-danger { + color: #d91016 !important; +} + +.status-default { + color: #575556 !important; +} + .attack-legend { text-align: center; margin-bottom: 20px; From dd48a2e40d550a5a290095445d7764835e6b6d88 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 14 Aug 2019 10:03:43 +0300 Subject: [PATCH 039/186] Extracted not all monkeys done warning and added to zero trust report --- .../cc/ui/src/components/pages/ReportPage.js | 14 ++----- .../components/pages/ZeroTrustReportPage.js | 41 ++++++++++++++----- .../common/MonkeysStillAliveWarning.js | 21 ++++++++++ 3 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index d68a7b3f6..081271e12 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -12,7 +12,8 @@ import AuthComponent from '../AuthComponent'; import PassTheHashMapPageComponent from "./PassTheHashMapPage"; import StrongUsers from "components/report-components/security/StrongUsers"; import AttackReport from "components/report-components/security/AttackReport"; -import ReportHeader, { ReportTypes } from "../report-components/common/ReportHeader"; +import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; +import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); @@ -175,16 +176,7 @@ class ReportPageComponent extends AuthComponent { No critical security issues were detected.

    ) } - { - this.state.allMonkeysAreDead ? - '' - : - (

    - - Some monkeys are still running. To get the best report it's best to wait for all of them to finish - running. -

    ) - } + { this.state.report.glance.exploited.length > 0 ? '' diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 1dfe0c40b..665bf4b02 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -5,6 +5,7 @@ import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeade import PillarGrades from "../report-components/zerotrust/PillarGrades"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; import {SinglePillarDirectivesStatus} from "../report-components/zerotrust/SinglePillarDirectivesStatus"; +import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -13,16 +14,30 @@ class ZeroTrustReportPageComponent extends AuthComponent { this.state = { allMonkeysAreDead: false, - runStarted: false + runStarted: true }; } - render() { - let res; - // Todo move to componentDidMount - this.getZeroTrustReportFromServer(res); + componentDidMount() { + this.updateMonkeysRunning().then(res => this.getZeroTrustReportFromServer(res)); + } - const content = this.generateReportContent(); + updateMonkeysRunning = () => { + return this.authFetch('/api') + .then(res => res.json()) + .then(res => { + this.setState({ + allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']), + runStarted: res['completed_steps']['run_monkey'] + }); + return res; + }); + }; + + render() { + let content; + + content = this.generateReportContent(); return ( @@ -40,12 +55,13 @@ class ZeroTrustReportPageComponent extends AuthComponent { if (this.stillLoadingDataFromServer()) { content = "Still empty"; } else { - const pillarsSection =
    + const pillarsSection =

    Pillars Overview

    ; - const directivesSection =

    Directives status

    + const directivesSection =
    +

    Directives status

    { Object.keys(this.state.directives).map((pillar) => ; - const findingSection =

    Findings

    -
    ; + const findingSection =
    +

    Findings

    + +
    ; - content =
    + content =
    + {pillarsSection} {directivesSection} {findingSection} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js new file mode 100644 index 000000000..67fd9388a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js @@ -0,0 +1,21 @@ +import React, {Component} from "react"; +import * as PropTypes from "prop-types"; + +export class MonkeysStillAliveWarning extends Component { + render() { + return
    + { + this.props.allMonkeysAreDead ? + '' + : + (

    + + Some monkeys are still running. To get the best report it's best to wait for all of them to finish + running. +

    ) + } +
    + } +} + +MonkeysStillAliveWarning.propTypes = {allMonkeysAreDead: PropTypes.bool}; From 0325521936dcb367e2edaea6709e5aeab381685a Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 14 Aug 2019 10:34:04 +0300 Subject: [PATCH 040/186] Extracted MustRunMonkeyWarning and ReportLoader to seperate compoments and other small various fixes --- .../cc/ui/src/components/pages/ReportPage.js | 12 ++++----- .../components/pages/ZeroTrustReportPage.js | 22 ++++++---------- .../common/MustRunMonkeyWarning.js | 11 ++++++++ .../report-components/common/ReportLoader.js | 25 +++++++++++++++++++ 4 files changed, 50 insertions(+), 20 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 081271e12..4da1c0bac 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Component} from 'react'; import {Button, Col} from 'react-bootstrap'; import BreachedServers from 'components/report-components/security/BreachedServers'; import ScannedServers from 'components/report-components/security/ScannedServers'; @@ -14,10 +14,13 @@ import StrongUsers from "components/report-components/security/StrongUsers"; import AttackReport from "components/report-components/security/AttackReport"; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; +import ReportLoader from "../report-components/common/ReportLoader"; +import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); + class ReportPageComponent extends AuthComponent { Issue = @@ -70,13 +73,10 @@ class ReportPageComponent extends AuthComponent { let content; if (Object.keys(this.state.report).length === 0) { if (this.state.runStarted) { - content = (

    Generating Report...

    ); + content = (); } else { content = -

    - - You have to run a monkey before generating a report! -

    ; + ; } } else { content = this.generateReportContent(); diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 665bf4b02..737e524fe 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -6,6 +6,8 @@ import PillarGrades from "../report-components/zerotrust/PillarGrades"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; import {SinglePillarDirectivesStatus} from "../report-components/zerotrust/SinglePillarDirectivesStatus"; import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; +import ReportLoader from "../report-components/common/ReportLoader"; +import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -36,8 +38,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { render() { let content; - - content = this.generateReportContent(); + if (this.state.runStarted) { + content = this.generateReportContent(); + } else { + content = ; + } return ( @@ -53,7 +58,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { let content; if (this.stillLoadingDataFromServer()) { - content = "Still empty"; + content = ; } else { const pillarsSection =

    Pillars Overview

    @@ -97,17 +102,6 @@ class ZeroTrustReportPageComponent extends AuthComponent {
    {content} -
    - THIS IS THE RAW SERVER DATA -
    - PILLARS: -
    {JSON.stringify(this.state.pillars, undefined, 2)}
    -
    - DIRECTIVES: -
    {JSON.stringify(this.state.directives, undefined, 2)}
    -
    - FINDINGS: -
    {JSON.stringify(this.state.findings, undefined, 2)}
    ) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js new file mode 100644 index 000000000..f1d23e302 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js @@ -0,0 +1,11 @@ +import React, {Component} from "react"; +import {NavLink} from "react-router-dom"; + +export default class MustRunMonkeyWarning extends Component { + render() { + return

    + + You have to run a monkey before generating a report! +

    + } +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js new file mode 100644 index 000000000..873d70177 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js @@ -0,0 +1,25 @@ +import {css} from "@emotion/core"; +import React, {Component} from "react"; +import {GridLoader} from "react-spinners"; + +const loading_css_override = css` + display: block; + margin-right: auto; + margin-left: auto; +`; + + +export default class ReportLoader extends Component { + render() { + return
    +

    Generating Report...

    + +
    + } +} From cd7cc4011d9fdd42ae5726b1384d6d7ee3d049ba Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 15 Aug 2019 09:17:32 +0300 Subject: [PATCH 041/186] Added pillar summary as per Barak's idea --- monkey/common/data/zero_trust_consts.py | 2 +- monkey/monkey_island/cc/models/finding.py | 4 +-- .../cc/resources/reporting/report.py | 6 +++- .../services/reporting/zero_trust_service.py | 30 +++++++++++++++---- .../components/pages/ZeroTrustReportPage.js | 5 ++-- .../{PillarGrades.js => PillarOverview.js} | 11 +++++-- .../zerotrust/PillarsSummary.js | 29 ++++++++++++++++++ 7 files changed, 73 insertions(+), 14 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{PillarGrades.js => PillarOverview.js} (63%) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index b6a8c8c33..c55ee160d 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -12,7 +12,7 @@ STATUS_POSITIVE = u"Positive" STATUS_INCONCLUSIVE = u"Inconclusive" STATUS_CONCLUSIVE = u"Conclusive" # Don't change order! -TEST_STATUSES = [STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED] +ORDERED_TEST_STATUSES = [STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED] TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic" TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http" diff --git a/monkey/monkey_island/cc/models/finding.py b/monkey/monkey_island/cc/models/finding.py index d67b10247..5ee014cfb 100644 --- a/monkey/monkey_island/cc/models/finding.py +++ b/monkey/monkey_island/cc/models/finding.py @@ -4,7 +4,7 @@ Define a Document Schema for Zero Trust findings. from mongoengine import Document, StringField, EmbeddedDocumentListField -from common.data.zero_trust_consts import TEST_STATUSES, TESTS, TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY +from common.data.zero_trust_consts import ORDERED_TEST_STATUSES, TESTS, TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY # Dummy import for mongoengine. # noinspection PyUnresolvedReferences from event import Event @@ -19,7 +19,7 @@ class Finding(Document): """ # SCHEMA test = StringField(required=True, choices=TESTS) - status = StringField(required=True, choices=TEST_STATUSES) + status = StringField(required=True, choices=ORDERED_TEST_STATUSES) events = EmbeddedDocumentListField(document_type=Event) # LOGIC diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 84e458398..fba129e65 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -27,7 +27,11 @@ class Report(flask_restful.Resource): return ReportService.get_report() elif report_type == ZERO_TRUST_REPORT_TYPE: if report_data == REPORT_DATA_PILLARS: - return jsonify(ZeroTrustService.get_pillars_grades()) + return jsonify({ + "summary": ZeroTrustService.get_pillars_summary(), + "grades": ZeroTrustService.get_pillars_grades() + } + ) elif report_data == REPORT_DATA_DIRECTIVES_STATUS: return jsonify(ZeroTrustService.get_directives_status()) elif report_data == REPORT_DATA_FINDINGS: diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index 7a4a5ce43..835f869ea 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -7,13 +7,13 @@ class ZeroTrustService(object): @staticmethod def get_pillars_grades(): pillars_grades = [] - all_findings = Finding.objects() for pillar in PILLARS: - pillars_grades.append(ZeroTrustService.__get_pillar_grade(pillar, all_findings)) + pillars_grades.append(ZeroTrustService.__get_pillar_grade(pillar)) return pillars_grades @staticmethod - def __get_pillar_grade(pillar, all_findings): + def __get_pillar_grade(pillar): + all_findings = Finding.objects() pillar_grade = { "pillar": pillar, STATUS_CONCLUSIVE: 0, @@ -66,7 +66,7 @@ class ZeroTrustService(object): all_statuses |= set(Finding.objects(test=test).distinct("status")) for status in all_statuses: - if TEST_STATUSES.index(status) < TEST_STATUSES.index(worst_status): + if ORDERED_TEST_STATUSES.index(status) < ORDERED_TEST_STATUSES.index(worst_status): worst_status = status return worst_status @@ -88,7 +88,7 @@ class ZeroTrustService(object): def __get_lcd_worst_status_for_test(all_findings_for_test): current_status = STATUS_UNEXECUTED for finding in all_findings_for_test: - if TEST_STATUSES.index(finding.status) < TEST_STATUSES.index(current_status): + if ORDERED_TEST_STATUSES.index(finding.status) < ORDERED_TEST_STATUSES.index(current_status): current_status = finding.status return current_status @@ -115,3 +115,23 @@ class ZeroTrustService(object): def __get_events_as_dict(events): return [json.loads(event.to_json()) for event in events] + @staticmethod + def get_pillars_summary(): + results = { + STATUS_CONCLUSIVE: [], + STATUS_INCONCLUSIVE: [], + STATUS_POSITIVE: [], + STATUS_UNEXECUTED: [] + } + for pillar in PILLARS: + results[ZeroTrustService.__get_status_for_pillar(pillar)].append(pillar) + + return results + + @staticmethod + def __get_status_for_pillar(pillar): + grade = ZeroTrustService.__get_pillar_grade(pillar) + for status in ORDERED_TEST_STATUSES: + if grade[status] > 0: + return status + return STATUS_UNEXECUTED diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 737e524fe..3bc0e38e1 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -2,7 +2,7 @@ import React from 'react'; import {Button, Col} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; -import PillarGrades from "../report-components/zerotrust/PillarGrades"; +import PillarsOverview from "../report-components/zerotrust/PillarOverview"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; import {SinglePillarDirectivesStatus} from "../report-components/zerotrust/SinglePillarDirectivesStatus"; import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; @@ -60,9 +60,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { if (this.stillLoadingDataFromServer()) { content = ; } else { + console.log(this.state.pillars); const pillarsSection =

    Pillars Overview

    - +
    ; const directivesSection =
    diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js similarity index 63% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index 15e38607c..0636ab679 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarGrades.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -1,6 +1,7 @@ import React, {Component} from "react"; import {PillarLabel} from "./PillarLabel"; import PaginatedTable from "../common/PaginatedTable"; +import {PillarsSummary} from "./PillarsSummary"; const columns = [ { @@ -17,10 +18,14 @@ const columns = [ } ]; -class PillarGrades extends Component { +class PillarOverview extends Component { render() { - return ; + return (
    + +
    + +
    ); } } -export default PillarGrades; +export default PillarOverview; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js new file mode 100644 index 000000000..e029b50e6 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js @@ -0,0 +1,29 @@ +import React, {Component, Fragment} from "react"; +import {PillarLabel} from "./PillarLabel"; + +export class PillarsSummary extends Component { + render() { + return (
    + {this.getStatusSummary("Conclusive")} + {this.getStatusSummary("Inconclusive")} + {this.getStatusSummary("Positive")} + {this.getStatusSummary("Unexecuted")} +
    ); + } + + getStatusSummary(status) { + console.log(this.props.pillars); + if (this.props.pillars[status].length > 0) { + return +

    {status}

    +

    + { + this.props.pillars[status].map((pillar) => { + return + }) + } +

    +
    + } + } +} From 785bc4f109a87a2a356a938cd28b48fa4b32ee87 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 15 Aug 2019 10:17:38 +0300 Subject: [PATCH 042/186] Extracted the security issues glance component --- .../cc/ui/src/components/pages/ReportPage.js | 16 +++----------- .../common/SecurityIssuesGlance.js | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 4da1c0bac..12d51202d 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React from 'react'; import {Button, Col} from 'react-bootstrap'; import BreachedServers from 'components/report-components/security/BreachedServers'; import ScannedServers from 'components/report-components/security/ScannedServers'; @@ -16,11 +16,11 @@ import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeade import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; +import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); - class ReportPageComponent extends AuthComponent { Issue = @@ -165,17 +165,7 @@ class ReportPageComponent extends AuthComponent {

    Overview

    - { - this.state.report.glance.exploited.length > 0 ? - (

    - - Critical security issues were detected! -

    ) : - (

    - - No critical security issues were detected. -

    ) - } + 0}/> { this.state.report.glance.exploited.length > 0 ? diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js new file mode 100644 index 000000000..f734a1a28 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js @@ -0,0 +1,22 @@ +import React, {Component, Fragment} from "react"; +import * as PropTypes from "prop-types"; + +export class SecurityIssuesGlance extends Component { + render() { + return + { + this.props.issuesFound ? + (

    + + Critical security issues were detected! +

    ) : + (

    + + No critical security issues were detected. +

    ) + } +
    + } +} + +SecurityIssuesGlance.propTypes = {issuesFound: PropTypes.bool}; From db85dfe24afc82a3a00dcd390b7373659b14ca22 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 15 Aug 2019 10:23:06 +0300 Subject: [PATCH 043/186] Added sec issues to ZT report as well and gridified the overview section --- .../components/pages/ZeroTrustReportPage.js | 33 ++++++++++++++----- .../zerotrust/PillarLabel.js | 2 +- .../zerotrust/PillarOverview.js | 2 -- .../zerotrust/PillarsSummary.js | 4 +-- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 3bc0e38e1..fd464da80 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,5 +1,5 @@ import React from 'react'; -import {Button, Col} from 'react-bootstrap'; +import {Button, Col, Row, Grid} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import PillarsOverview from "../report-components/zerotrust/PillarOverview"; @@ -8,6 +8,8 @@ import {SinglePillarDirectivesStatus} from "../report-components/zerotrust/Singl import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; +import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; +import {PillarsSummary} from "../report-components/zerotrust/PillarsSummary"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -60,12 +62,6 @@ class ZeroTrustReportPageComponent extends AuthComponent { if (this.stillLoadingDataFromServer()) { content = ; } else { - console.log(this.state.pillars); - const pillarsSection =
    -

    Pillars Overview

    - -
    ; - const directivesSection =

    Directives status

    { @@ -84,8 +80,19 @@ class ZeroTrustReportPageComponent extends AuthComponent {
    ; content =
    - - {pillarsSection} +

    Overview

    + + + + + + + + + + + + {directivesSection} {findingSection}
    ; @@ -140,6 +147,14 @@ class ZeroTrustReportPageComponent extends AuthComponent { }); }); } + + anyIssuesFound() { + const severe = function(finding) { + return (finding.status === "Conclusive" || finding.status === "Inconclusive"); + }; + + return this.state.findings.some(severe); + } } export default ZeroTrustReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js index 51f5f988f..958327a37 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -26,6 +26,6 @@ export class PillarLabel extends Component { }; const className = "label " + pillarToColor[this.props.pillar]; - return {this.props.pillar} + return
    {this.props.pillar}
    } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index 0636ab679..a6c6f848f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -21,8 +21,6 @@ const columns = [ class PillarOverview extends Component { render() { return (
    - -
    ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js index e029b50e6..8881eba7d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js @@ -16,13 +16,13 @@ export class PillarsSummary extends Component { if (this.props.pillars[status].length > 0) { return

    {status}

    -

    +

    { this.props.pillars[status].map((pillar) => { return }) } -

    +
    } } From e4cf3706ec74a5a315a5472faf0560fb8398e107 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 15 Aug 2019 10:42:19 +0300 Subject: [PATCH 044/186] Extracted status label --- .../components/pages/ZeroTrustReportPage.js | 32 +++++++++++-------- .../zerotrust/DirectivesStatusTable.js | 23 +++++++------ .../zerotrust/PillarLabel.js | 2 +- .../zerotrust/PillarsSummary.js | 5 ++- .../zerotrust/StatusLabel.js | 24 ++++++++++++++ 5 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index fd464da80..5795c275f 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -62,8 +62,24 @@ class ZeroTrustReportPageComponent extends AuthComponent { if (this.stillLoadingDataFromServer()) { content = ; } else { + const overviewSection =
    +

    Overview

    + + + + + + + + + + + + +
    ; + const directivesSection =
    -

    Directives status

    +

    Directives

    { Object.keys(this.state.directives).map((pillar) => ; content =
    -

    Overview

    - - - - - - - - - - - - + {overviewSection} {directivesSection} {findingSection}
    ; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js index 4f413f37f..8b82761cc 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js @@ -1,14 +1,10 @@ -import React from "react"; +import React, {Fragment} from "react"; import PaginatedTable from "../common/PaginatedTable"; import AuthComponent from "../../AuthComponent"; import 'styles/ZeroTrustPillars.css' +import {StatusLabel} from "./StatusLabel"; + -const statusToIcon = { - "Positive": "fa-clipboard-check status-success", - "Inconclusive": "fa-exclamation-triangle status-warning", - "Conclusive": "fa-bomb status-danger", - "Unexecuted": "fa-question status-default", -}; const columns = [ { @@ -19,7 +15,7 @@ const columns = [ }, { Header: 'Status', id: 'status', accessor: x => { - return ; + return ; } }, { Header: 'Tests', id: 'tests', @@ -39,12 +35,12 @@ class TestsStatus extends AuthComponent { const unexecutedStatus = "Unexecuted"; return ( -
    + {this.getTestsOfStatusIfAny(conclusiveStatus)} {this.getTestsOfStatusIfAny(inconclusiveStatus)} {this.getTestsOfStatusIfAny(positiveStatus)} {this.getTestsOfStatusIfAny(unexecutedStatus)} -
    + ); } @@ -58,9 +54,12 @@ class TestsStatus extends AuthComponent { const listItems = filteredTests.map((test) => { return (
  • {test.test}
  • ) }); - return
    {statusToFilter}
      {listItems}
    ; + return + +
      {listItems}
    +
    ; } - return
    ; + return ; } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js index 958327a37..b6b588876 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -18,7 +18,7 @@ export class PillarLabel extends Component { const pillarToIcon = { "Data": "fa fa-database", "People": "fa fa-user", - "Networks": "fa fa-tty", + "Networks": "fa fa-wifi", "Workloads": "fa fa-cloud", "Devices": "fa fa-laptop", "Visibility & Analytics": "fa fa-eye-slash", diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js index 8881eba7d..962afdd3c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js @@ -1,5 +1,6 @@ import React, {Component, Fragment} from "react"; import {PillarLabel} from "./PillarLabel"; +import {StatusLabel} from "./StatusLabel"; export class PillarsSummary extends Component { render() { @@ -15,7 +16,9 @@ export class PillarsSummary extends Component { console.log(this.props.pillars); if (this.props.pillars[status].length > 0) { return -

    {status}

    +

    + +

    { this.props.pillars[status].map((pillar) => { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js new file mode 100644 index 000000000..98ac1edb6 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -0,0 +1,24 @@ +import React, {Component, Fragment} from "react"; +import * as PropTypes from "prop-types"; + +const statusToIcon = { + "Positive": "fa-check status-success", + "Inconclusive": "fa-exclamation-triangle status-warning", + "Conclusive": "fa-bomb status-danger", + "Unexecuted": "fa-question status-default", +}; + +export class StatusLabel extends Component { + render() { + const classname = "fa " + statusToIcon[this.props.status] + " " + this.props.size; + let text = ""; + if (this.props.showText) { + text = " " + this.props.status; + } + return ( + {text} + ); + } +} + +StatusLabel.propTypes = {status: PropTypes.string, showText: PropTypes.bool}; From 3d96f719884524b13970e4662797173ea2f4f432 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 15 Aug 2019 10:54:30 +0300 Subject: [PATCH 045/186] Made statuslabel look better --- .../zerotrust/StatusLabel.js | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js index 98ac1edb6..68ce5111e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -2,22 +2,29 @@ import React, {Component, Fragment} from "react"; import * as PropTypes from "prop-types"; const statusToIcon = { - "Positive": "fa-check status-success", - "Inconclusive": "fa-exclamation-triangle status-warning", - "Conclusive": "fa-bomb status-danger", - "Unexecuted": "fa-question status-default", + "Positive": "fa-check", + "Inconclusive": "fa-exclamation-triangle", + "Conclusive": "fa-bomb", + "Unexecuted": "fa-question", +}; + +const statusToLabelType = { + "Positive": "label-success", + "Inconclusive": "label-warning", + "Conclusive": "label-danger", + "Unexecuted": "label-default", }; export class StatusLabel extends Component { render() { - const classname = "fa " + statusToIcon[this.props.status] + " " + this.props.size; let text = ""; if (this.props.showText) { text = " " + this.props.status; } - return ( - {text} - ); + + return (
    + {text} +
    ); } } From 7006a2332f8c97bb25840ac7bb3d786f6c4e1372 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 15 Aug 2019 11:39:46 +0300 Subject: [PATCH 046/186] All labels now show status --- .../cc/resources/reporting/report.py | 3 +- .../reporting/test_zeroTrustService.py | 34 +++++++++++++++++++ .../services/reporting/zero_trust_service.py | 10 +++++- .../components/pages/ZeroTrustReportPage.js | 8 ++--- .../zerotrust/FindingsTable.js | 9 ++++- .../zerotrust/PillarLabel.js | 14 ++------ .../zerotrust/PillarOverview.js | 11 ++++-- .../zerotrust/StatusLabel.js | 2 +- ...Summary.js => StatusesToPillarsSummary.js} | 9 +++-- 9 files changed, 73 insertions(+), 27 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{PillarsSummary.js => StatusesToPillarsSummary.js} (76%) diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index fba129e65..aa13468a3 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -28,7 +28,8 @@ class Report(flask_restful.Resource): elif report_type == ZERO_TRUST_REPORT_TYPE: if report_data == REPORT_DATA_PILLARS: return jsonify({ - "summary": ZeroTrustService.get_pillars_summary(), + "statusesToPillars": ZeroTrustService.get_statuses_to_pillars(), + "pillarsToStatuses": ZeroTrustService.get_pillars_to_statuses(), "grades": ZeroTrustService.get_pillars_grades() } ) diff --git a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py index ff6e88982..7e7df7ad0 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py +++ b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py @@ -1,3 +1,5 @@ +from unittest import TestCase + from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService from common.data.zero_trust_consts import * @@ -198,3 +200,35 @@ class TestZeroTrustService(IslandTestCase): } self.assertEquals(ZeroTrustService.get_directives_status(), expected) + + def test_get_pillars_to_statuses(self): + self.fail_if_not_testing_env() + self.clean_finding_db() + + self.maxDiff = None + + expected = { + AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED, + DEVICES: STATUS_UNEXECUTED, + NETWORKS: STATUS_UNEXECUTED, + PEOPLE: STATUS_UNEXECUTED, + VISIBILITY_ANALYTICS: STATUS_UNEXECUTED, + WORKLOADS: STATUS_UNEXECUTED, + DATA: STATUS_UNEXECUTED + } + + self.assertEquals(ZeroTrustService.get_pillars_to_statuses(), expected) + + save_example_findings() + + expected = { + AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED, + DEVICES: STATUS_CONCLUSIVE, + NETWORKS: STATUS_INCONCLUSIVE, + PEOPLE: STATUS_INCONCLUSIVE, + VISIBILITY_ANALYTICS: STATUS_UNEXECUTED, + WORKLOADS: STATUS_UNEXECUTED, + DATA: STATUS_CONCLUSIVE + } + + self.assertEquals(ZeroTrustService.get_pillars_to_statuses(), expected) diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index 835f869ea..bf2b9d6e8 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -116,7 +116,7 @@ class ZeroTrustService(object): return [json.loads(event.to_json()) for event in events] @staticmethod - def get_pillars_summary(): + def get_statuses_to_pillars(): results = { STATUS_CONCLUSIVE: [], STATUS_INCONCLUSIVE: [], @@ -128,6 +128,14 @@ class ZeroTrustService(object): return results + @staticmethod + def get_pillars_to_statuses(): + results = {} + for pillar in PILLARS: + results[pillar] = ZeroTrustService.__get_status_for_pillar(pillar) + + return results + @staticmethod def __get_status_for_pillar(pillar): grade = ZeroTrustService.__get_pillar_grade(pillar) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 5795c275f..0f0326f14 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -9,7 +9,7 @@ import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStill import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; -import {PillarsSummary} from "../report-components/zerotrust/PillarsSummary"; +import {StatusesToPillarsSummary} from "../report-components/zerotrust/StatusesToPillarsSummary"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -67,12 +67,12 @@ class ZeroTrustReportPageComponent extends AuthComponent { - + - + @@ -92,7 +92,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { const findingSection =

    Findings

    - +
    ; content =
    diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index ae1c41d49..b50a0e436 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -58,7 +58,7 @@ const columns = [ accessor: x => { const pillars = x.pillars; const listItems = pillars.map((pillar) => -
  • +
  • ); return
      {listItems}
    ; } @@ -74,6 +74,13 @@ const columns = [ class FindingsTable extends Component { render() { + const data = this.props.findings.map((finding) => { + const newFinding = finding; + newFinding.pillars = finding.pillars.map((pillar) => { + return {name: pillar, status: this.props.pillarsToStatuses[pillar]} + }); + return newFinding; + }); return ( ); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js index b6b588876..4559e5cd9 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -1,20 +1,12 @@ import React, {Component} from "react"; import 'styles/ZeroTrustPillars.css' +import {statusToLabelType} from "./StatusLabel"; export class PillarLabel extends Component { pillar; + status; render() { - const pillarToColor = { - "Data": "label-zt-data", - "People": "label-zt-people", - "Networks": "label-zt-networks", - "Workloads": "label-zt-workloads", - "Devices": "label-zt-devices", - "Visibility & Analytics": "label-zt-analytics", - "Automation & Orchestration": "label-zt-automation", - }; - const pillarToIcon = { "Data": "fa fa-database", "People": "fa fa-user", @@ -25,7 +17,7 @@ export class PillarLabel extends Component { "Automation & Orchestration": "fa fa-cogs", }; - const className = "label " + pillarToColor[this.props.pillar]; + const className = "label " + statusToLabelType[this.props.status]; return
    {this.props.pillar}
    } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index a6c6f848f..558da5456 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -1,14 +1,13 @@ import React, {Component} from "react"; import {PillarLabel} from "./PillarLabel"; import PaginatedTable from "../common/PaginatedTable"; -import {PillarsSummary} from "./PillarsSummary"; const columns = [ { Header: 'Pillar Grading', columns: [ { Header: 'Pillar', id: 'Pillar', accessor: x => { - return (); + return (); }}, { Header: 'Conclusive', accessor: 'Conclusive'}, { Header: 'Inconclusive', accessor: 'Inconclusive'}, @@ -19,9 +18,15 @@ const columns = [ ]; class PillarOverview extends Component { + pillarsToStatuses; render() { + const data = this.props.grades.map((grade) => { + const newGrade = grade; + newGrade.pillar = {name: grade.pillar, status: this.props.pillarsToStatuses[grade.pillar]}; + return newGrade; + }); return (
    - +
    ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js index 68ce5111e..a53e4c919 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -8,7 +8,7 @@ const statusToIcon = { "Unexecuted": "fa-question", }; -const statusToLabelType = { +export const statusToLabelType = { "Positive": "label-success", "Inconclusive": "label-warning", "Conclusive": "label-danger", diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js similarity index 76% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js index 962afdd3c..bfcaceed9 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js @@ -2,7 +2,7 @@ import React, {Component, Fragment} from "react"; import {PillarLabel} from "./PillarLabel"; import {StatusLabel} from "./StatusLabel"; -export class PillarsSummary extends Component { +export class StatusesToPillarsSummary extends Component { render() { return (
    {this.getStatusSummary("Conclusive")} @@ -13,16 +13,15 @@ export class PillarsSummary extends Component { } getStatusSummary(status) { - console.log(this.props.pillars); - if (this.props.pillars[status].length > 0) { + if (this.props.statusesToPillars[status].length > 0) { return

    { - this.props.pillars[status].map((pillar) => { - return + this.props.statusesToPillars[status].map((pillar) => { + return }) }
    From 21c3c9bf4d897e8d87f9c66426183b70a467920f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 15 Aug 2019 12:27:21 +0300 Subject: [PATCH 047/186] Now returning full text data to the website --- monkey/common/data/zero_trust_consts.py | 51 +++++++++++++------ monkey/monkey_island/cc/models/finding.py | 4 +- .../reporting/test_zeroTrustService.py | 46 ++++++++--------- .../services/reporting/zero_trust_service.py | 6 +-- .../zerotrust/DirectivesStatusTable.js | 1 + 5 files changed, 64 insertions(+), 44 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index c55ee160d..05edc4faa 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -47,54 +47,73 @@ DIRECTIVES = { POSSIBLE_STATUSES_KEY = u"possible_statuses" PILLARS_KEY = u"pillars" DIRECTIVE_KEY = u"directive_key" -FINDING_FORMAT_KEY = u"finding_format" -EXPLANATION_KEY = u"explanation" +FINDING_EXPLANATION_BY_STATUS_KEY = u"finding_explanation" +TEST_EXPLANATION_KEY = u"explanation" TESTS_MAP = { TEST_SEGMENTATION: { - EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.", - FINDING_FORMAT_KEY: u"The Monkey from {ORIGIN} communicated with a machine on a different segment.", + TEST_EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_CONCLUSIVE: "Monkey performed cross-segment communication. Check firewall rules and logs.", + STATUS_POSITIVE: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." + }, DIRECTIVE_KEY: DIRECTIVE_SEGMENTATION, PILLARS_KEY: [NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_POSITIVE, STATUS_CONCLUSIVE] }, TEST_ACTIVITY_TIMELINE: { - EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", - FINDING_FORMAT_KEY: u"Malicious activity performed by the Monkeys. See 'events' for detailed information.", + TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_INCONCLUSIVE: "Monkey performed malicious actions in the network. Check SOC logs and alerts." + }, DIRECTIVE_KEY: DIRECTIVE_ANALYZE_NETWORK_TRAFFIC, PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] }, TEST_ENDPOINT_SECURITY_EXISTS: { - EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.", - FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} found no active endpoint security processes.", + TEST_EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_CONCLUSIVE: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.", + STATUS_POSITIVE: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern." + }, DIRECTIVE_KEY: DIRECTIVE_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] }, TEST_MACHINE_EXPLOITED: { - EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.", - FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} attempted to exploit a machine on {TARGET}.", + TEST_EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_CONCLUSIVE: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.", + STATUS_INCONCLUSIVE: "Monkey tried exploiting endpoints. Check IDS/IPS logs to see activity recognized." + }, DIRECTIVE_KEY: DIRECTIVE_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE] }, TEST_SCHEDULED_EXECUTION: { - EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.", - FINDING_FORMAT_KEY: "The Monkey on {ORIGIN} started running in an executed manner.", + TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_INCONCLUSIVE: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software." + }, DIRECTIVE_KEY: DIRECTIVE_USER_BEHAVIOUR, PILLARS_KEY: [PEOPLE, NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] }, TEST_DATA_ENDPOINT_ELASTIC: { - EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.", - FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} found an open ElasticSearch instance.", + TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_CONCLUSIVE: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.", + STATUS_POSITIVE: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them." + }, DIRECTIVE_KEY: DIRECTIVE_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] }, TEST_DATA_ENDPOINT_HTTP: { - EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to HTTP servers.", - FINDING_FORMAT_KEY: u"The Monkey on {ORIGIN} found an open HTTP server.", + TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to HTTP servers.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_CONCLUSIVE: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.", + STATUS_POSITIVE: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them." + }, DIRECTIVE_KEY: DIRECTIVE_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] diff --git a/monkey/monkey_island/cc/models/finding.py b/monkey/monkey_island/cc/models/finding.py index 5ee014cfb..77160534f 100644 --- a/monkey/monkey_island/cc/models/finding.py +++ b/monkey/monkey_island/cc/models/finding.py @@ -4,7 +4,7 @@ Define a Document Schema for Zero Trust findings. from mongoengine import Document, StringField, EmbeddedDocumentListField -from common.data.zero_trust_consts import ORDERED_TEST_STATUSES, TESTS, TESTS_MAP, EXPLANATION_KEY, PILLARS_KEY +from common.data.zero_trust_consts import ORDERED_TEST_STATUSES, TESTS, TESTS_MAP, TEST_EXPLANATION_KEY, PILLARS_KEY # Dummy import for mongoengine. # noinspection PyUnresolvedReferences from event import Event @@ -24,7 +24,7 @@ class Finding(Document): # LOGIC def get_test_explanation(self): - return TESTS_MAP[self.test][EXPLANATION_KEY] + return TESTS_MAP[self.test][TEST_EXPLANATION_KEY] def get_pillars(self): return TESTS_MAP[self.test][PILLARS_KEY] diff --git a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py index 7e7df7ad0..6ec2651a0 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py +++ b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py @@ -110,88 +110,88 @@ class TestZeroTrustService(IslandTestCase): AUTOMATION_ORCHESTRATION: [], DATA: [ { - "directive": DIRECTIVE_DATA_TRANSIT, + "directive": DIRECTIVES[DIRECTIVE_DATA_TRANSIT], "status": STATUS_CONCLUSIVE, "tests": [ { "status": STATUS_UNEXECUTED, - "test": TEST_DATA_ENDPOINT_ELASTIC + "test": TESTS_MAP[TEST_DATA_ENDPOINT_ELASTIC][TEST_EXPLANATION_KEY] }, { "status": STATUS_CONCLUSIVE, - "test": TEST_DATA_ENDPOINT_HTTP + "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY] } ] } ], DEVICES: [ { - "directive": "endpoint_security", - "status": "Conclusive", + "directive": DIRECTIVES[DIRECTIVE_ENDPOINT_SECURITY], + "status": STATUS_CONCLUSIVE, "tests": [ { - "status": "Conclusive", - "test": "endpoint_security_exists" + "status": STATUS_CONCLUSIVE, + "test": TESTS_MAP[TEST_ENDPOINT_SECURITY_EXISTS][TEST_EXPLANATION_KEY] }, { - "status": "Unexecuted", - "test": "machine_exploited" + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_MACHINE_EXPLOITED][TEST_EXPLANATION_KEY] } ] } ], NETWORKS: [ { - "directive": "segmentation", - "status": "Unexecuted", + "directive": DIRECTIVES[DIRECTIVE_SEGMENTATION], + "status": STATUS_UNEXECUTED, "tests": [ { - "status": "Unexecuted", - "test": "segmentation" + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_SEGMENTATION][TEST_EXPLANATION_KEY] } ] }, { - "directive": "user_behaviour", + "directive": DIRECTIVES[DIRECTIVE_USER_BEHAVIOUR], "status": STATUS_INCONCLUSIVE, "tests": [ { "status": STATUS_INCONCLUSIVE, - "test": TEST_SCHEDULED_EXECUTION + "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY] } ] }, { - "directive": "analyze_network_traffic", - "status": "Unexecuted", + "directive": DIRECTIVES[DIRECTIVE_ANALYZE_NETWORK_TRAFFIC], + "status": STATUS_UNEXECUTED, "tests": [ { - "status": "Unexecuted", - "test": "malicious_activity_timeline" + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] } ] } ], PEOPLE: [ { - "directive": "user_behaviour", + "directive": DIRECTIVES[DIRECTIVE_USER_BEHAVIOUR], "status": STATUS_INCONCLUSIVE, "tests": [ { "status": STATUS_INCONCLUSIVE, - "test": TEST_SCHEDULED_EXECUTION + "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY] } ] } ], "Visibility & Analytics": [ { - "directive": DIRECTIVE_ANALYZE_NETWORK_TRAFFIC, + "directive": DIRECTIVES[DIRECTIVE_ANALYZE_NETWORK_TRAFFIC], "status": STATUS_UNEXECUTED, "tests": [ { "status": STATUS_UNEXECUTED, - "test": TEST_ACTIVITY_TIMELINE + "test": TESTS_MAP[TEST_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] } ] } diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index bf2b9d6e8..1aa7291ed 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -50,7 +50,7 @@ class ZeroTrustService(object): for pillar in DIRECTIVES_TO_PILLARS[directive]: all_directive_statuses[pillar].append( { - "directive": directive, + "directive": DIRECTIVES[directive], "tests": ZeroTrustService.__get_tests_status(directive_tests), "status": ZeroTrustService.__get_directive_status(directive_tests) } @@ -78,7 +78,7 @@ class ZeroTrustService(object): test_findings = Finding.objects(test=test) results.append( { - "test": test, + "test": TESTS_MAP[test][TEST_EXPLANATION_KEY], "status": ZeroTrustService.__get_lcd_worst_status_for_test(test_findings) } ) @@ -104,7 +104,7 @@ class ZeroTrustService(object): test_info = TESTS_MAP[finding.test] enriched_finding = { # TODO add test explanation per status. - "test": test_info[EXPLANATION_KEY], + "test": test_info[FINDING_EXPLANATION_BY_STATUS_KEY][finding.status], "pillars": test_info[PILLARS_KEY], "status": finding.status, "events": ZeroTrustService.__get_events_as_dict(finding.events) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js index 8b82761cc..ad1a815cb 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js @@ -19,6 +19,7 @@ const columns = [ } }, { Header: 'Tests', id: 'tests', + style: {'whiteSpace': 'unset'}, // This enables word wrap accessor: x => { return ; } From 32a346fdcf41032d841f0adc8168d7dba772824d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 18 Aug 2019 09:41:57 +0300 Subject: [PATCH 048/186] Fixed missing pillar status (for label color) --- .../cc/ui/src/components/pages/ZeroTrustReportPage.js | 3 ++- .../zerotrust/SinglePillarDirectivesStatus.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 0f0326f14..21b1a7e3b 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -85,7 +85,8 @@ class ZeroTrustReportPageComponent extends AuthComponent { + directivesStatus={this.state.directives[pillar]} + pillarsToStatuses={this.state.pillars.pillarsToStatuses}/> ) }
    ; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js index dad36d025..feb56b204 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js @@ -13,7 +13,7 @@ export class SinglePillarDirectivesStatus extends AuthComponent { else { return ( -

    +

    ); From 73a6cb22b6b84fdc3fce58909e4337e6bb5ae9e1 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 11:39:12 +0300 Subject: [PATCH 049/186] Extracted antivirus ZTtest to different file in a new module under services. --- .../monkey_island/cc/resources/telemetry.py | 35 ++---------------- .../cc/services/zero_trust_tests/__init__.py | 0 .../zero_trust_tests/antivirus_existence.py | 36 +++++++++++++++++++ 3 files changed, 38 insertions(+), 33 deletions(-) create mode 100644 monkey/monkey_island/cc/services/zero_trust_tests/__init__.py create mode 100644 monkey/monkey_island/cc/services/zero_trust_tests/antivirus_existence.py diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index fc2648589..c89f91a85 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -7,12 +7,8 @@ import dateutil import flask_restful from flask import request -from common.data.zero_trust_consts import TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, STATUS_CONCLUSIVE, \ - EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, ANTI_VIRUS_KNOWN_PROCESS_NAMES from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo -from monkey_island.cc.models.event import Event -from monkey_island.cc.models.finding import Finding from monkey_island.cc.services import mimikatz_utils from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge import EdgeService @@ -20,6 +16,7 @@ from monkey_island.cc.services.node import NodeService from monkey_island.cc.encryptor import encryptor from monkey_island.cc.services.wmi_handler import WMIHandler from monkey_island.cc.models.monkey import Monkey +from monkey_island.cc.services.zero_trust_tests.antivirus_existence import test_antivirus_existence __author__ = 'Barak' @@ -185,35 +182,7 @@ class Telemetry(flask_restful.Resource): monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') Telemetry.process_mimikatz_and_wmi_info(monkey_id, telemetry_json) Telemetry.process_aws_data(monkey_id, telemetry_json) - Telemetry.test_antivirus_existence(telemetry_json, monkey_id) - - @staticmethod - def test_antivirus_existence(telemetry_json, monkey_id): - if 'process_list' in telemetry_json['data']: - process_list_event = Event.create_event( - title="Process list", - message="Monkey {} scanned the process list".format(monkey_id), - event_type=EVENT_TYPE_MONKEY_LOCAL) - events = [process_list_event] - - found_av = False - all_processes = telemetry_json['data']['process_list'].items() - for process in all_processes: - process_name = process[1]['name'] - if process_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES: - found_av = True - events.append(Event.create_event( - title="Found AV process", - message="The process '{}' was recognized as an Anti Virus process. Process " - "details: ".format(process_name, str(process)), - event_type=EVENT_TYPE_ISLAND - )) - - if found_av: - test_status = STATUS_POSITIVE - else: - test_status = STATUS_CONCLUSIVE - Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) + test_antivirus_existence(telemetry_json) @staticmethod def process_mimikatz_and_wmi_info(monkey_id, telemetry_json): diff --git a/monkey/monkey_island/cc/services/zero_trust_tests/__init__.py b/monkey/monkey_island/cc/services/zero_trust_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/zero_trust_tests/antivirus_existence.py new file mode 100644 index 000000000..f18e28a1a --- /dev/null +++ b/monkey/monkey_island/cc/services/zero_trust_tests/antivirus_existence.py @@ -0,0 +1,36 @@ +import json + +from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, ANTI_VIRUS_KNOWN_PROCESS_NAMES, EVENT_TYPE_ISLAND, \ + STATUS_POSITIVE, STATUS_CONCLUSIVE, TEST_ENDPOINT_SECURITY_EXISTS +from monkey_island.cc.models import Monkey +from monkey_island.cc.models.event import Event +from monkey_island.cc.models.finding import Finding + + +def test_antivirus_existence(telemetry_json): + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + if 'process_list' in telemetry_json['data']: + process_list_event = Event.create_event( + title="Process list", + message="Monkey on {} scanned the process list".format(current_monkey.hostname), + event_type=EVENT_TYPE_MONKEY_LOCAL) + events = [process_list_event] + + found_av = False + all_processes = telemetry_json['data']['process_list'].items() + for process in all_processes: + process_name = process[1]['name'] + if process_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES: + found_av = True + events.append(Event.create_event( + title="Found AV process", + message="The process '{}' was recognized as an Anti Virus process. Process " + "details: {}".format(process_name, json.dumps(process[1])), + event_type=EVENT_TYPE_ISLAND + )) + + if found_av: + test_status = STATUS_POSITIVE + else: + test_status = STATUS_CONCLUSIVE + Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) \ No newline at end of file From ba1667372b0e839dee1b100b313b5a8dedc2f747 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 11:41:04 +0300 Subject: [PATCH 050/186] create telemetry services directory - soon all the telemetry resources functions will be moved there. --- monkey/monkey_island/cc/resources/telemetry.py | 2 +- .../cc/services/{zero_trust_tests => telemetry}/__init__.py | 0 .../cc/services/telemetry/zero_trust_tests/__init__.py | 0 .../{ => telemetry}/zero_trust_tests/antivirus_existence.py | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename monkey/monkey_island/cc/services/{zero_trust_tests => telemetry}/__init__.py (100%) create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/__init__.py rename monkey/monkey_island/cc/services/{ => telemetry}/zero_trust_tests/antivirus_existence.py (100%) diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index c89f91a85..fb8116c14 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -16,7 +16,7 @@ from monkey_island.cc.services.node import NodeService from monkey_island.cc.encryptor import encryptor from monkey_island.cc.services.wmi_handler import WMIHandler from monkey_island.cc.models.monkey import Monkey -from monkey_island.cc.services.zero_trust_tests.antivirus_existence import test_antivirus_existence +from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence __author__ = 'Barak' diff --git a/monkey/monkey_island/cc/services/zero_trust_tests/__init__.py b/monkey/monkey_island/cc/services/telemetry/__init__.py similarity index 100% rename from monkey/monkey_island/cc/services/zero_trust_tests/__init__.py rename to monkey/monkey_island/cc/services/telemetry/__init__.py diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/__init__.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py similarity index 100% rename from monkey/monkey_island/cc/services/zero_trust_tests/antivirus_existence.py rename to monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py From aaab4a479c5f65b9914dca20da992a02a5f4ed90 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 11:56:05 +0300 Subject: [PATCH 051/186] Added doc to consts file, and moved AV list to AV file --- monkey/common/data/zero_trust_consts.py | 15 ++-- .../zero_trust_tests/antivirus_existence.py | 77 ++++++++++++++++++- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 05edc4faa..dbb28a991 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -1,3 +1,11 @@ +""" +This file contains all the static data relating to Zero Trust. It is mostly used in the zero trust report generation and +in creating findings. + +This file contains static mappings between zero trust components such as: pillars, directives, tests, statuses. Some of +the mappings are computed when this module is loaded. +""" + AUTOMATION_ORCHESTRATION = u"Automation & Orchestration" VISIBILITY_ANALYTICS = u"Visibility & Analytics" WORKLOADS = u"Workloads" @@ -11,7 +19,7 @@ STATUS_UNEXECUTED = u"Unexecuted" STATUS_POSITIVE = u"Positive" STATUS_INCONCLUSIVE = u"Inconclusive" STATUS_CONCLUSIVE = u"Conclusive" -# Don't change order! +# Don't change order! The statuses are ordered by importance/severity. ORDERED_TEST_STATUSES = [STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED] TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic" @@ -170,8 +178,3 @@ EVENT_TYPE_ISLAND = "island" EVENT_TYPE_MONKEY_NETWORK = "monkey_network" EVENT_TYPE_MONKEY_LOCAL = "monkey_local" EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK, EVENT_TYPE_ISLAND) - -ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ - u"SSPService.exe", - u"ipython.exe" -] diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index f18e28a1a..e272ae699 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -1,11 +1,81 @@ import json -from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, ANTI_VIRUS_KNOWN_PROCESS_NAMES, EVENT_TYPE_ISLAND, \ +from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, \ STATUS_POSITIVE, STATUS_CONCLUSIVE, TEST_ENDPOINT_SECURITY_EXISTS from monkey_island.cc.models import Monkey from monkey_island.cc.models.event import Event from monkey_island.cc.models.finding import Finding +ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ + u"AvastSvc.exe", + u"AvastUI.exe", + u"avcenter.exe", + u"avconfig.exe", + u"avgcsrvx.exe", + u"avgidsagent.exe", + u"avgnt.exe", + u"avgrsx.exe", + u"avguard.exe", + u"avgui.exe", + u"avgwdsvc.exe", + u"avp.exe", + u"avscan.exe", + u"bdagent.exe", + u"ccuac.exe", + u"egui.exe", + u"hijackthis.exe", + u"instup.exe", + u"keyscrambler.exe", + u"mbam.exe", + u"mbamgui.exe", + u"mbampt.exe", + u"mbamscheduler.exe", + u"mbamservice.exe", + u"MpCmdRun.exe", + u"MSASCui.exe", + u"MsMpEng.exe", + u"rstrui.exe", + u"spybotsd.exe", + u"zlclient.exe", + u"SymCorpUI.exe", + u"ccSvcHst.exe", + u"ccApp.exe", + u"LUALL.exe", + u"SMC.exe", + u"SMCgui.exe", + u"Rtvscan.exe", + u"LuComServer.exe", + u"ProtectionUtilSurrogate.exe", + u"ClientRemote.exe", + u"SemSvc.exe", + u"SemLaunchSvc.exe", + u"sesmcontinst.exe", + u"LuCatalog.exe", + u"LUALL.exe", + u"LuCallbackProxy.exe", + u"LuComServer_3_3.exe", + u"httpd.exe", + u"dbisqlc.exe", + u"dbsrv16.exe", + u"semapisrv.exe", + u"snac64.exe", + u"AutoExcl.exe", + u"DoScan.exe", + u"nlnhook.exe", + u"SavUI.exe", + u"SepLiveUpdate.exe", + u"Smc.exe", + u"SmcGui.exe", + u"SymCorpUI.exe", + u"symerr.exe", + u"ccSvcHst.exe", + u"DevViewer.exe", + u"DWHWizrd.exe", + u"RtvStart.exe", + u"roru.exe", + u"WSCSAvNotifier" +] + def test_antivirus_existence(telemetry_json): current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) @@ -20,7 +90,8 @@ def test_antivirus_existence(telemetry_json): all_processes = telemetry_json['data']['process_list'].items() for process in all_processes: process_name = process[1]['name'] - if process_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES: + # This is for case-insensitive in. Generator expression for memory savings. + if process_name.upper() in (known_av_name.upper() for known_av_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES): found_av = True events.append(Event.create_event( title="Found AV process", @@ -33,4 +104,4 @@ def test_antivirus_existence(telemetry_json): test_status = STATUS_POSITIVE else: test_status = STATUS_CONCLUSIVE - Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) \ No newline at end of file + Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) From d693b216a7a058c589543309c65d861c404668ba Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 11:57:39 +0300 Subject: [PATCH 052/186] Removed unused files --- monkey/common/utils/itertools_extensions.py | 9 --------- monkey/common/utils/test_power_set.py | 18 ------------------ 2 files changed, 27 deletions(-) delete mode 100644 monkey/common/utils/itertools_extensions.py delete mode 100644 monkey/common/utils/test_power_set.py diff --git a/monkey/common/utils/itertools_extensions.py b/monkey/common/utils/itertools_extensions.py deleted file mode 100644 index b27181ff6..000000000 --- a/monkey/common/utils/itertools_extensions.py +++ /dev/null @@ -1,9 +0,0 @@ -from itertools import chain, combinations - - -def power_set(iterable): - """ - https://docs.python.org/3/library/itertools.html#itertools-recipes - """ - s = list(iterable) - return [list(x) for x in list(chain.from_iterable(combinations(s, r) for r in range(1, len(s) + 1)))] \ No newline at end of file diff --git a/monkey/common/utils/test_power_set.py b/monkey/common/utils/test_power_set.py deleted file mode 100644 index 22410fe30..000000000 --- a/monkey/common/utils/test_power_set.py +++ /dev/null @@ -1,18 +0,0 @@ -from unittest import TestCase - - -class TestPower_set(TestCase): - def test_power_set(self): - before = ('a', 'b', 'c') - after_expected = [ - ['a'], - ['b'], - ['c'], - ['a', 'b'], - ['a', 'c'], - ['b', 'c'], - ['a', 'b', 'c'], - ] - - from common.utils.itertools_extensions import power_set - self.assertEquals(power_set(before), after_expected) From d50095b570935bae9d948f361713bb0ac30f7727 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 12:03:42 +0300 Subject: [PATCH 053/186] Changed "general" report to "security" all over --- monkey/monkey_island/cc/app.py | 2 +- monkey/monkey_island/cc/resources/reporting/report.py | 8 ++++---- monkey/monkey_island/cc/ui/src/components/Main.js | 4 ++-- .../cc/ui/src/components/pages/ReportPage.js | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index f2c0e9ce1..35e932cb1 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -123,7 +123,7 @@ def init_api_resources(api): api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/') api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') - # report_type: zero_trust or general + # report_type: zero_trust or security api.add_resource( Report, '/api/report/', diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index aa13468a3..fa2973759 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -9,8 +9,8 @@ from monkey_island.cc.services.reporting.report import ReportService from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService ZERO_TRUST_REPORT_TYPE = "zero_trust" -GENERAL_REPORT_TYPE = "general" -REPORT_TYPES = [GENERAL_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] +SECURITY_REPORT_TYPE = "security" +REPORT_TYPES = [SECURITY_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] REPORT_DATA_PILLARS = "pillars" REPORT_DATA_FINDINGS = "findings" @@ -22,8 +22,8 @@ __author__ = ["itay.mizeretz", "shay.nehmad"] class Report(flask_restful.Resource): @jwt_required() - def get(self, report_type=GENERAL_REPORT_TYPE, report_data=None): - if report_type == GENERAL_REPORT_TYPE: + def get(self, report_type=SECURITY_REPORT_TYPE, report_data=None): + if report_type == SECURITY_REPORT_TYPE: return ReportService.get_report() elif report_type == ZERO_TRUST_REPORT_TYPE: if report_data == REPORT_DATA_PILLARS: diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 5dd17659b..982791782 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -148,7 +148,7 @@ class AppComponent extends AuthComponent {
  • - + 4. Security Report {this.state.completedSteps.report_done ? @@ -199,7 +199,7 @@ class AppComponent extends AuthComponent { {this.renderRoute('/infection/map', )} {this.renderRoute('/infection/telemetry', )} {this.renderRoute('/start-over', )} - {this.renderRoute('/report/general', )} + {this.renderRoute('/report/security', )} {this.renderRoute('/report/zero_trust', )} {this.renderRoute('/license', )} diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 12d51202d..72fba9c48 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -119,7 +119,7 @@ class ReportPageComponent extends AuthComponent { getReportFromServer(res) { if (res['completed_steps']['run_monkey']) { - this.authFetch('/api/report/general') + this.authFetch('/api/report/security') .then(res => res.json()) .then(res => { this.setState({ From 453c8f9eb4057a8e50b27bd5d91a0da9002c54c8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 13:34:32 +0300 Subject: [PATCH 054/186] Moved ZT models to own folder and added documentation --- .../monkey_island/cc/models/zero_trust/__init__.py | 0 .../cc/models/{ => zero_trust}/event.py | 11 +++++++++++ .../cc/models/{ => zero_trust}/finding.py | 13 ++++++++++++- .../cc/models/{ => zero_trust}/test_event.py | 2 +- .../cc/models/{ => zero_trust}/test_finding.py | 4 ++-- .../cc/services/reporting/test_zeroTrustService.py | 4 +--- .../cc/services/reporting/zero_trust_service.py | 2 +- .../zero_trust_tests/antivirus_existence.py | 4 ++-- monkey/monkey_island/cc/testing/IslandTestCase.py | 2 +- 9 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 monkey/monkey_island/cc/models/zero_trust/__init__.py rename monkey/monkey_island/cc/models/{ => zero_trust}/event.py (55%) rename monkey/monkey_island/cc/models/{ => zero_trust}/finding.py (67%) rename monkey/monkey_island/cc/models/{ => zero_trust}/test_event.py (93%) rename monkey/monkey_island/cc/models/{ => zero_trust}/test_finding.py (92%) diff --git a/monkey/monkey_island/cc/models/zero_trust/__init__.py b/monkey/monkey_island/cc/models/zero_trust/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/models/event.py b/monkey/monkey_island/cc/models/zero_trust/event.py similarity index 55% rename from monkey/monkey_island/cc/models/event.py rename to monkey/monkey_island/cc/models/zero_trust/event.py index 0b8386ff8..01c7f2f47 100644 --- a/monkey/monkey_island/cc/models/event.py +++ b/monkey/monkey_island/cc/models/zero_trust/event.py @@ -6,11 +6,22 @@ from common.data.zero_trust_consts import EVENT_TYPES class Event(EmbeddedDocument): + """ + This model represents a single event within a Finding (it is an EmbeddedDocument within Finding). It is meant to + hold a detail of the Finding. + + This class has 2 main section: + * The schema section defines the DB fields in the document. This is the data of the object. + * The logic section defines complex questions we can ask about a single document which are asked multiple + times, or complex action we will perform - somewhat like an API. + """ + # SCHEMA timestamp = DateTimeField(required=True) title = StringField(required=True) message = StringField() event_type = StringField(required=True, choices=EVENT_TYPES) + # LOGIC @staticmethod def create_event(title, message, event_type): event = Event( diff --git a/monkey/monkey_island/cc/models/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py similarity index 67% rename from monkey/monkey_island/cc/models/finding.py rename to monkey/monkey_island/cc/models/zero_trust/finding.py index 77160534f..51b336eca 100644 --- a/monkey/monkey_island/cc/models/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -1,3 +1,4 @@ +# coding=utf-8 """ Define a Document Schema for Zero Trust findings. """ @@ -7,11 +8,21 @@ from mongoengine import Document, StringField, EmbeddedDocumentListField from common.data.zero_trust_consts import ORDERED_TEST_STATUSES, TESTS, TESTS_MAP, TEST_EXPLANATION_KEY, PILLARS_KEY # Dummy import for mongoengine. # noinspection PyUnresolvedReferences -from event import Event +from monkey_island.cc.models.zero_trust.event import Event class Finding(Document): """ + This model represents a Zero-Trust finding: A result of a test the monkey/island might perform to see if a + specific directive of zero trust is upheld or broken. + + Findings might be + Negative ❌ + Conclusive, meaning that we are sure that something is wrong (example: segmentation issue). + Inconclusive, meaning that we need the user to check something himself (example: 2FA logs, AV missing). + Positive ✔ + Conclusive, meaning that we are sure that something is correct (example: Monkey failed exploiting). + This class has 2 main section: * The schema section defines the DB fields in the document. This is the data of the object. * The logic section defines complex questions we can ask about a single document which are asked multiple diff --git a/monkey/monkey_island/cc/models/test_event.py b/monkey/monkey_island/cc/models/zero_trust/test_event.py similarity index 93% rename from monkey/monkey_island/cc/models/test_event.py rename to monkey/monkey_island/cc/models/zero_trust/test_event.py index 3bc201f62..5ab478166 100644 --- a/monkey/monkey_island/cc/models/test_event.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_event.py @@ -1,7 +1,7 @@ from mongoengine import ValidationError from common.data.zero_trust_consts import EVENT_TYPE_ISLAND -from monkey_island.cc.models.event import Event +from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.testing.IslandTestCase import IslandTestCase diff --git a/monkey/monkey_island/cc/models/test_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_finding.py similarity index 92% rename from monkey/monkey_island/cc/models/test_finding.py rename to monkey/monkey_island/cc/models/zero_trust/test_finding.py index d111b0513..6ac0a9fc8 100644 --- a/monkey/monkey_island/cc/models/test_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_finding.py @@ -1,8 +1,8 @@ from mongoengine import ValidationError from common.data.zero_trust_consts import * -from finding import Finding -from monkey_island.cc.models.event import Event +from monkey_island.cc.models.zero_trust.finding import Finding +from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.testing.IslandTestCase import IslandTestCase diff --git a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py index 6ec2651a0..d3fe01db9 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py +++ b/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py @@ -1,9 +1,7 @@ -from unittest import TestCase - from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService from common.data.zero_trust_consts import * -from monkey_island.cc.models.finding import Finding +from monkey_island.cc.models.zero_trust.finding import Finding from monkey_island.cc.testing.IslandTestCase import IslandTestCase diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index 1aa7291ed..bbe82f049 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -1,6 +1,6 @@ import json from common.data.zero_trust_consts import * -from monkey_island.cc.models.finding import Finding +from monkey_island.cc.models.zero_trust.finding import Finding class ZeroTrustService(object): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index e272ae699..0363a85cb 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -3,8 +3,8 @@ import json from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, \ STATUS_POSITIVE, STATUS_CONCLUSIVE, TEST_ENDPOINT_SECURITY_EXISTS from monkey_island.cc.models import Monkey -from monkey_island.cc.models.event import Event -from monkey_island.cc.models.finding import Finding +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.models.zero_trust.finding import Finding ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ u"AvastSvc.exe", diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py index 5b050684c..6bca20f4a 100644 --- a/monkey/monkey_island/cc/testing/IslandTestCase.py +++ b/monkey/monkey_island/cc/testing/IslandTestCase.py @@ -1,7 +1,7 @@ import unittest from monkey_island.cc.environment.environment import env from monkey_island.cc.models import Monkey -from monkey_island.cc.models.finding import Finding +from monkey_island.cc.models.zero_trust.finding import Finding class IslandTestCase(unittest.TestCase): From 524859f0110d0c9567b08a6c975954d4edb66148 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 13:35:37 +0300 Subject: [PATCH 055/186] Removed unused exception --- monkey/monkey_island/cc/models/zero_trust/finding.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index 51b336eca..1869d6f18 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -51,7 +51,3 @@ class Finding(Document): finding.save() return finding - - -class UnknownTest(Exception): - pass From c1ac45470c29d50c5e9c0d236e290c30b3f6eaa1 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 13:40:35 +0300 Subject: [PATCH 056/186] Added doc to test_event --- monkey/monkey_island/cc/models/zero_trust/test_event.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_event.py b/monkey/monkey_island/cc/models/zero_trust/test_event.py index 5ab478166..2542df8ef 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_event.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_event.py @@ -11,19 +11,20 @@ class TestEvent(IslandTestCase): self.clean_finding_db() with self.assertRaises(ValidationError): - Event.create_event( + _ = Event.create_event( title=None, # title required message="bla bla", event_type=EVENT_TYPE_ISLAND ) with self.assertRaises(ValidationError): - Event.create_event( + _ = Event.create_event( title="skjs", message="bla bla", event_type="Unknown" # Unknown event type ) + # Assert that nothing is raised. _ = Event.create_event( title="skjs", message="bla bla", From 757af1c6af3a401b431e04b793e1420a3ff97758 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 14:34:21 +0300 Subject: [PATCH 057/186] Moved all processing code into services/telemetry/processing Telemetry class line count: 87. :smile: --- .../monkey_island/cc/resources/telemetry.py | 225 +----------------- .../services/telemetry/processing/__init__.py | 7 + .../services/telemetry/processing/exploit.py | 45 ++++ .../cc/services/telemetry/processing/hooks.py | 14 ++ .../telemetry/processing/post_breach.py | 7 + .../cc/services/telemetry/processing/scan.py | 33 +++ .../cc/services/telemetry/processing/state.py | 9 + .../telemetry/processing/system_info.py | 99 ++++++++ .../services/telemetry/processing/tunnel.py | 10 + .../cc/services/telemetry/processing/utils.py | 13 + 10 files changed, 240 insertions(+), 222 deletions(-) create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/__init__.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/exploit.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/hooks.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/post_breach.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/scan.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/state.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/system_info.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/tunnel.py create mode 100644 monkey/monkey_island/cc/services/telemetry/processing/utils.py diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index fb8116c14..eb53d00c8 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -1,6 +1,5 @@ import json import logging -import copy from datetime import datetime import dateutil @@ -9,14 +8,9 @@ from flask import request from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo -from monkey_island.cc.services import mimikatz_utils -from monkey_island.cc.services.config import ConfigService -from monkey_island.cc.services.edge import EdgeService from monkey_island.cc.services.node import NodeService -from monkey_island.cc.encryptor import encryptor -from monkey_island.cc.services.wmi_handler import WMIHandler +from monkey_island.cc.services.telemetry.processing.hooks import TELEMETRY_CATEGORY_TO_PROCESSING_FUNC from monkey_island.cc.models.monkey import Monkey -from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence __author__ = 'Barak' @@ -58,8 +52,8 @@ class Telemetry(flask_restful.Resource): try: NodeService.update_monkey_modify_time(monkey["_id"]) telem_category = telemetry_json.get('telem_category') - if telem_category in TELEM_PROCESS_DICT: - TELEM_PROCESS_DICT[telem_category](telemetry_json) + if telem_category in TELEMETRY_CATEGORY_TO_PROCESSING_FUNC: + TELEMETRY_CATEGORY_TO_PROCESSING_FUNC[telem_category](telemetry_json) else: logger.info('Got unknown type of telemetry: %s' % telem_category) except Exception as ex: @@ -90,216 +84,3 @@ class Telemetry(flask_restful.Resource): x['data']['credentials'][new_user] = x['data']['credentials'].pop(user) return objects - - @staticmethod - def get_edge_by_scan_or_exploit_telemetry(telemetry_json): - dst_ip = telemetry_json['data']['machine']['ip_addr'] - dst_domain_name = telemetry_json['data']['machine']['domain_name'] - src_monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) - dst_node = NodeService.get_monkey_by_ip(dst_ip) - if dst_node is None: - dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name) - - return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"]) - - @staticmethod - def process_tunnel_telemetry(telemetry_json): - monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"] - if telemetry_json['data']['proxy'] is not None: - tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "") - NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip) - else: - NodeService.unset_all_monkey_tunnels(monkey_id) - - @staticmethod - def process_state_telemetry(telemetry_json): - monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) - if telemetry_json['data']['done']: - NodeService.set_monkey_dead(monkey, True) - else: - NodeService.set_monkey_dead(monkey, False) - - @staticmethod - def process_exploit_telemetry(telemetry_json): - edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json) - Telemetry.encrypt_exploit_creds(telemetry_json) - telemetry_json['data']['info']['started'] = dateutil.parser.parse(telemetry_json['data']['info']['started']) - telemetry_json['data']['info']['finished'] = dateutil.parser.parse(telemetry_json['data']['info']['finished']) - - new_exploit = copy.deepcopy(telemetry_json['data']) - - new_exploit.pop('machine') - new_exploit['timestamp'] = telemetry_json['timestamp'] - - mongo.db.edge.update( - {'_id': edge['_id']}, - {'$push': {'exploits': new_exploit}} - ) - if new_exploit['result']: - EdgeService.set_edge_exploited(edge) - - for attempt in telemetry_json['data']['attempts']: - if attempt['result']: - found_creds = {'user': attempt['user']} - for field in ['password', 'lm_hash', 'ntlm_hash', 'ssh_key']: - if len(attempt[field]) != 0: - found_creds[field] = attempt[field] - NodeService.add_credentials_to_node(edge['to'], found_creds) - - @staticmethod - def process_scan_telemetry(telemetry_json): - edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json) - data = copy.deepcopy(telemetry_json['data']['machine']) - ip_address = data.pop("ip_addr") - domain_name = data.pop("domain_name") - new_scan = \ - { - "timestamp": telemetry_json["timestamp"], - "data": data - } - mongo.db.edge.update( - {"_id": edge["_id"]}, - {"$push": {"scans": new_scan}, - "$set": {"ip_address": ip_address, 'domain_name': domain_name}} - ) - - node = mongo.db.node.find_one({"_id": edge["to"]}) - if node is not None: - scan_os = new_scan["data"]["os"] - if "type" in scan_os: - mongo.db.node.update({"_id": node["_id"]}, - {"$set": {"os.type": scan_os["type"]}}, - upsert=False) - if "version" in scan_os: - mongo.db.node.update({"_id": node["_id"]}, - {"$set": {"os.version": scan_os["version"]}}, - upsert=False) - - @staticmethod - def process_system_info_telemetry(telemetry_json): - Telemetry.process_ssh_info(telemetry_json) - Telemetry.process_credential_info(telemetry_json) - monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') - Telemetry.process_mimikatz_and_wmi_info(monkey_id, telemetry_json) - Telemetry.process_aws_data(monkey_id, telemetry_json) - test_antivirus_existence(telemetry_json) - - @staticmethod - def process_mimikatz_and_wmi_info(monkey_id, telemetry_json): - users_secrets = {} - if 'mimikatz' in telemetry_json['data']: - users_secrets = mimikatz_utils.MimikatzSecrets. \ - extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) - if 'wmi' in telemetry_json['data']: - wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) - wmi_handler.process_and_handle_wmi_info() - - @staticmethod - def process_aws_data(monkey_id, telemetry_json): - if 'aws' in telemetry_json['data']: - if 'instance_id' in telemetry_json['data']['aws']: - mongo.db.monkey.update_one({'_id': monkey_id}, - {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}}) - - @staticmethod - def process_credential_info(telemetry_json): - if 'credentials' in telemetry_json['data']: - creds = telemetry_json['data']['credentials'] - Telemetry.encrypt_system_info_creds(creds) - Telemetry.add_system_info_creds_to_config(creds) - Telemetry.replace_user_dot_with_comma(creds) - - @staticmethod - def process_ssh_info(telemetry_json): - if 'ssh_info' in telemetry_json['data']: - ssh_info = telemetry_json['data']['ssh_info'] - Telemetry.encrypt_system_info_ssh_keys(ssh_info) - if telemetry_json['data']['network_info']['networks']: - # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip from telemetry - Telemetry.add_ip_to_ssh_keys(telemetry_json['data']['network_info']['networks'][0], ssh_info) - Telemetry.add_system_info_ssh_keys_to_config(ssh_info) - - @staticmethod - def add_ip_to_ssh_keys(ip, ssh_info): - for key in ssh_info: - key['ip'] = ip['addr'] - - @staticmethod - def process_trace_telemetry(telemetry_json): - # Nothing to do - return - - @staticmethod - def replace_user_dot_with_comma(creds): - for user in creds: - if -1 != user.find('.'): - new_user = user.replace('.', ',') - creds[new_user] = creds.pop(user) - - @staticmethod - def encrypt_system_info_creds(creds): - for user in creds: - for field in ['password', 'lm_hash', 'ntlm_hash']: - if field in creds[user]: - # this encoding is because we might run into passwords which are not pure ASCII - creds[user][field] = encryptor.enc(creds[user][field].encode('utf-8')) - - @staticmethod - def encrypt_system_info_ssh_keys(ssh_info): - for idx, user in enumerate(ssh_info): - for field in ['public_key', 'private_key', 'known_hosts']: - if ssh_info[idx][field]: - ssh_info[idx][field] = encryptor.enc(ssh_info[idx][field].encode('utf-8')) - - @staticmethod - def add_system_info_creds_to_config(creds): - for user in creds: - ConfigService.creds_add_username(user) - if 'password' in creds[user]: - ConfigService.creds_add_password(creds[user]['password']) - if 'lm_hash' in creds[user]: - ConfigService.creds_add_lm_hash(creds[user]['lm_hash']) - if 'ntlm_hash' in creds[user]: - ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash']) - - @staticmethod - def add_system_info_ssh_keys_to_config(ssh_info): - for user in ssh_info: - ConfigService.creds_add_username(user['name']) - # Public key is useless without private key - if user['public_key'] and user['private_key']: - ConfigService.ssh_add_keys(user['public_key'], user['private_key'], - user['name'], user['ip']) - - @staticmethod - def encrypt_exploit_creds(telemetry_json): - attempts = telemetry_json['data']['attempts'] - for i in range(len(attempts)): - for field in ['password', 'lm_hash', 'ntlm_hash']: - credential = attempts[i][field] - if len(credential) > 0: - attempts[i][field] = encryptor.enc(credential.encode('utf-8')) - - @staticmethod - def process_post_breach_telemetry(telemetry_json): - mongo.db.monkey.update( - {'guid': telemetry_json['monkey_guid']}, - {'$push': {'pba_results': telemetry_json['data']}}) - - @staticmethod - def process_attack_telemetry(telemetry_json): - # No processing required - pass - - -TELEM_PROCESS_DICT = \ - { - 'tunnel': Telemetry.process_tunnel_telemetry, - 'state': Telemetry.process_state_telemetry, - 'exploit': Telemetry.process_exploit_telemetry, - 'scan': Telemetry.process_scan_telemetry, - 'system_info': Telemetry.process_system_info_telemetry, - 'trace': Telemetry.process_trace_telemetry, - 'post_breach': Telemetry.process_post_breach_telemetry, - 'attack': Telemetry.process_attack_telemetry - } diff --git a/monkey/monkey_island/cc/services/telemetry/processing/__init__.py b/monkey/monkey_island/cc/services/telemetry/processing/__init__.py new file mode 100644 index 000000000..d90143c09 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/__init__.py @@ -0,0 +1,7 @@ +# import all implemented hooks, for brevity of hooks.py file +from tunnel import process_tunnel_telemetry +from state import process_state_telemetry +from exploit import process_exploit_telemetry +from scan import process_scan_telemetry +from system_info import process_system_info_telemetry +from post_breach import process_post_breach_telemetry diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py new file mode 100644 index 000000000..98ca76248 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -0,0 +1,45 @@ +import copy + +import dateutil + +from monkey_island.cc.database import mongo +from monkey_island.cc.encryptor import encryptor +from monkey_island.cc.services.edge import EdgeService +from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry + + +def process_exploit_telemetry(telemetry_json): + edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json) + encrypt_exploit_creds(telemetry_json) + telemetry_json['data']['info']['started'] = dateutil.parser.parse(telemetry_json['data']['info']['started']) + telemetry_json['data']['info']['finished'] = dateutil.parser.parse(telemetry_json['data']['info']['finished']) + + new_exploit = copy.deepcopy(telemetry_json['data']) + + new_exploit.pop('machine') + new_exploit['timestamp'] = telemetry_json['timestamp'] + + mongo.db.edge.update( + {'_id': edge['_id']}, + {'$push': {'exploits': new_exploit}} + ) + if new_exploit['result']: + EdgeService.set_edge_exploited(edge) + + for attempt in telemetry_json['data']['attempts']: + if attempt['result']: + found_creds = {'user': attempt['user']} + for field in ['password', 'lm_hash', 'ntlm_hash', 'ssh_key']: + if len(attempt[field]) != 0: + found_creds[field] = attempt[field] + NodeService.add_credentials_to_node(edge['to'], found_creds) + + +def encrypt_exploit_creds(telemetry_json): + attempts = telemetry_json['data']['attempts'] + for i in range(len(attempts)): + for field in ['password', 'lm_hash', 'ntlm_hash']: + credential = attempts[i][field] + if len(credential) > 0: + attempts[i][field] = encryptor.enc(credential.encode('utf-8')) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/hooks.py b/monkey/monkey_island/cc/services/telemetry/processing/hooks.py new file mode 100644 index 000000000..125bb8b53 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/hooks.py @@ -0,0 +1,14 @@ +from monkey_island.cc.services.telemetry.processing import * + +TELEMETRY_CATEGORY_TO_PROCESSING_FUNC = \ + { + 'tunnel': process_tunnel_telemetry, + 'state': process_state_telemetry, + 'exploit': process_exploit_telemetry, + 'scan': process_scan_telemetry, + 'system_info': process_system_info_telemetry, + 'post_breach': process_post_breach_telemetry, + # `lambda *args, **kwargs: None` is a no-op. + 'trace': lambda *args, **kwargs: None, + 'attack': lambda *args, **kwargs: None, + } diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py new file mode 100644 index 000000000..b086d5ff4 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -0,0 +1,7 @@ +from monkey_island.cc.database import mongo + + +def process_post_breach_telemetry(telemetry_json): + mongo.db.monkey.update( + {'guid': telemetry_json['monkey_guid']}, + {'$push': {'pba_results': telemetry_json['data']}}) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/scan.py b/monkey/monkey_island/cc/services/telemetry/processing/scan.py new file mode 100644 index 000000000..4e34b9a19 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/scan.py @@ -0,0 +1,33 @@ +import copy + +from monkey_island.cc.database import mongo +from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry + + +def process_scan_telemetry(telemetry_json): + edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json) + data = copy.deepcopy(telemetry_json['data']['machine']) + ip_address = data.pop("ip_addr") + domain_name = data.pop("domain_name") + new_scan = \ + { + "timestamp": telemetry_json["timestamp"], + "data": data + } + mongo.db.edge.update( + {"_id": edge["_id"]}, + {"$push": {"scans": new_scan}, + "$set": {"ip_address": ip_address, 'domain_name': domain_name}} + ) + + node = mongo.db.node.find_one({"_id": edge["to"]}) + if node is not None: + scan_os = new_scan["data"]["os"] + if "type" in scan_os: + mongo.db.node.update({"_id": node["_id"]}, + {"$set": {"os.type": scan_os["type"]}}, + upsert=False) + if "version" in scan_os: + mongo.db.node.update({"_id": node["_id"]}, + {"$set": {"os.version": scan_os["version"]}}, + upsert=False) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py new file mode 100644 index 000000000..e71abacd7 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py @@ -0,0 +1,9 @@ +from monkey_island.cc.services.node import NodeService + + +def process_state_telemetry(telemetry_json): + monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) + if telemetry_json['data']['done']: + NodeService.set_monkey_dead(monkey, True) + else: + NodeService.set_monkey_dead(monkey, False) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py new file mode 100644 index 000000000..ebf11c219 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -0,0 +1,99 @@ +from monkey_island.cc.database import mongo +from monkey_island.cc.services import mimikatz_utils +from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.config import ConfigService +from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence +from monkey_island.cc.services.wmi_handler import WMIHandler +from monkey_island.cc.encryptor import encryptor + + +def process_system_info_telemetry(telemetry_json): + process_ssh_info(telemetry_json) + process_credential_info(telemetry_json) + process_mimikatz_and_wmi_info(telemetry_json) + process_aws_data(telemetry_json) + test_antivirus_existence(telemetry_json) + + +def process_ssh_info(telemetry_json): + if 'ssh_info' in telemetry_json['data']: + ssh_info = telemetry_json['data']['ssh_info'] + encrypt_system_info_ssh_keys(ssh_info) + if telemetry_json['data']['network_info']['networks']: + # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip from telemetry + add_ip_to_ssh_keys(telemetry_json['data']['network_info']['networks'][0], ssh_info) + add_system_info_ssh_keys_to_config(ssh_info) + + +def add_system_info_ssh_keys_to_config(ssh_info): + for user in ssh_info: + ConfigService.creds_add_username(user['name']) + # Public key is useless without private key + if user['public_key'] and user['private_key']: + ConfigService.ssh_add_keys(user['public_key'], user['private_key'], + user['name'], user['ip']) + + +def add_ip_to_ssh_keys(ip, ssh_info): + for key in ssh_info: + key['ip'] = ip['addr'] + + +def encrypt_system_info_ssh_keys(ssh_info): + for idx, user in enumerate(ssh_info): + for field in ['public_key', 'private_key', 'known_hosts']: + if ssh_info[idx][field]: + ssh_info[idx][field] = encryptor.enc(ssh_info[idx][field].encode('utf-8')) + + +def process_credential_info(telemetry_json): + if 'credentials' in telemetry_json['data']: + creds = telemetry_json['data']['credentials'] + encrypt_system_info_creds(creds) + add_system_info_creds_to_config(creds) + replace_user_dot_with_comma(creds) + + +def replace_user_dot_with_comma(creds): + for user in creds: + if -1 != user.find('.'): + new_user = user.replace('.', ',') + creds[new_user] = creds.pop(user) + + +def add_system_info_creds_to_config(creds): + for user in creds: + ConfigService.creds_add_username(user) + if 'password' in creds[user]: + ConfigService.creds_add_password(creds[user]['password']) + if 'lm_hash' in creds[user]: + ConfigService.creds_add_lm_hash(creds[user]['lm_hash']) + if 'ntlm_hash' in creds[user]: + ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash']) + + +def encrypt_system_info_creds(creds): + for user in creds: + for field in ['password', 'lm_hash', 'ntlm_hash']: + if field in creds[user]: + # this encoding is because we might run into passwords which are not pure ASCII + creds[user][field] = encryptor.enc(creds[user][field].encode('utf-8')) + + +def process_mimikatz_and_wmi_info(telemetry_json): + users_secrets = {} + if 'mimikatz' in telemetry_json['data']: + users_secrets = mimikatz_utils.MimikatzSecrets. \ + extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', '')) + if 'wmi' in telemetry_json['data']: + monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') + wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) + wmi_handler.process_and_handle_wmi_info() + + +def process_aws_data(telemetry_json): + if 'aws' in telemetry_json['data']: + if 'instance_id' in telemetry_json['data']['aws']: + monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') + mongo.db.monkey.update_one({'_id': monkey_id}, + {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}}) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py new file mode 100644 index 000000000..ed57f3c7b --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py @@ -0,0 +1,10 @@ +from monkey_island.cc.services.node import NodeService + + +def process_tunnel_telemetry(telemetry_json): + monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"] + if telemetry_json['data']['proxy'] is not None: + tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "") + NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip) + else: + NodeService.unset_all_monkey_tunnels(monkey_id) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/utils.py b/monkey/monkey_island/cc/services/telemetry/processing/utils.py new file mode 100644 index 000000000..9bafb505f --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/utils.py @@ -0,0 +1,13 @@ +from monkey_island.cc.services.edge import EdgeService +from monkey_island.cc.services.node import NodeService + + +def get_edge_by_scan_or_exploit_telemetry(telemetry_json): + dst_ip = telemetry_json['data']['machine']['ip_addr'] + dst_domain_name = telemetry_json['data']['machine']['domain_name'] + src_monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) + dst_node = NodeService.get_monkey_by_ip(dst_ip) + if dst_node is None: + dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name) + + return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"]) From a6789a53b28a85cf969852913fd6552f921ab9ba Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 14:43:23 +0300 Subject: [PATCH 058/186] Extracted process telem to hooks.py --- monkey/monkey_island/cc/resources/telemetry.py | 13 +++---------- .../cc/services/telemetry/processing/hooks.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index eb53d00c8..e07207707 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -9,7 +9,7 @@ from flask import request from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo from monkey_island.cc.services.node import NodeService -from monkey_island.cc.services.telemetry.processing.hooks import TELEMETRY_CATEGORY_TO_PROCESSING_FUNC +from monkey_island.cc.services.telemetry.processing.hooks import process_telemetry from monkey_island.cc.models.monkey import Monkey __author__ = 'Barak' @@ -48,16 +48,9 @@ class Telemetry(flask_restful.Resource): Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']).renew_ttl() monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) + NodeService.update_monkey_modify_time(monkey["_id"]) - try: - NodeService.update_monkey_modify_time(monkey["_id"]) - telem_category = telemetry_json.get('telem_category') - if telem_category in TELEMETRY_CATEGORY_TO_PROCESSING_FUNC: - TELEMETRY_CATEGORY_TO_PROCESSING_FUNC[telem_category](telemetry_json) - else: - logger.info('Got unknown type of telemetry: %s' % telem_category) - except Exception as ex: - logger.error("Exception caught while processing telemetry. Info: {}".format(ex.message), exc_info=True) + process_telemetry(telemetry_json) telem_id = mongo.db.telemetry.insert(telemetry_json) return mongo.db.telemetry.find_one_or_404({"_id": telem_id}) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/hooks.py b/monkey/monkey_island/cc/services/telemetry/processing/hooks.py index 125bb8b53..154096f79 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/hooks.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/hooks.py @@ -1,5 +1,9 @@ +import logging + from monkey_island.cc.services.telemetry.processing import * +logger = logging.getLogger(__name__) + TELEMETRY_CATEGORY_TO_PROCESSING_FUNC = \ { 'tunnel': process_tunnel_telemetry, @@ -12,3 +16,14 @@ TELEMETRY_CATEGORY_TO_PROCESSING_FUNC = \ 'trace': lambda *args, **kwargs: None, 'attack': lambda *args, **kwargs: None, } + + +def process_telemetry(telemetry_json): + try: + telem_category = telemetry_json.get('telem_category') + if telem_category in TELEMETRY_CATEGORY_TO_PROCESSING_FUNC: + TELEMETRY_CATEGORY_TO_PROCESSING_FUNC[telem_category](telemetry_json) + else: + logger.info('Got unknown type of telemetry: %s' % telem_category) + except Exception as ex: + logger.error("Exception caught while processing telemetry. Info: {}".format(ex.message), exc_info=True) From 6ca4df1c261eae36bd9f4628e120fc9394af9229 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 14:53:15 +0300 Subject: [PATCH 059/186] extracted filter av logiv to seperate function --- .../zero_trust_tests/antivirus_existence.py | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index 0363a85cb..c93d63b72 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -86,22 +86,29 @@ def test_antivirus_existence(telemetry_json): event_type=EVENT_TYPE_MONKEY_LOCAL) events = [process_list_event] - found_av = False - all_processes = telemetry_json['data']['process_list'].items() - for process in all_processes: - process_name = process[1]['name'] - # This is for case-insensitive in. Generator expression for memory savings. - if process_name.upper() in (known_av_name.upper() for known_av_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES): - found_av = True - events.append(Event.create_event( - title="Found AV process", - message="The process '{}' was recognized as an Anti Virus process. Process " - "details: {}".format(process_name, json.dumps(process[1])), - event_type=EVENT_TYPE_ISLAND - )) + av_processes = filter_av_processes(telemetry_json) - if found_av: + for process in av_processes: + events.append(Event.create_event( + title="Found AV process", + message="The process '{}' was recognized as an Anti Virus process. Process " + "details: {}".format(process[1]['name'], json.dumps(process[1])), + event_type=EVENT_TYPE_ISLAND + )) + + if len(av_processes) > 0: test_status = STATUS_POSITIVE else: test_status = STATUS_CONCLUSIVE Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) + + +def filter_av_processes(telemetry_json): + all_processes = telemetry_json['data']['process_list'].items() + av_processes = [] + for process in all_processes: + process_name = process[1]['name'] + # This is for case-insensitive `in`. Generator expression is to save memory. + if process_name.upper() in (known_av_name.upper() for known_av_name in ANTI_VIRUS_KNOWN_PROCESS_NAMES): + av_processes.append(process) + return av_processes From 231de96e8c226f2f59ac0b4fea2f3ed641ec9b5e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 14:55:51 +0300 Subject: [PATCH 060/186] renamed test files to convention --- .../reporting/{test_PTHReportService.py => test_pth_report.py} | 0 .../{test_zeroTrustService.py => test_zero_trust_service.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename monkey/monkey_island/cc/services/reporting/{test_PTHReportService.py => test_pth_report.py} (100%) rename monkey/monkey_island/cc/services/reporting/{test_zeroTrustService.py => test_zero_trust_service.py} (100%) diff --git a/monkey/monkey_island/cc/services/reporting/test_PTHReportService.py b/monkey/monkey_island/cc/services/reporting/test_pth_report.py similarity index 100% rename from monkey/monkey_island/cc/services/reporting/test_PTHReportService.py rename to monkey/monkey_island/cc/services/reporting/test_pth_report.py diff --git a/monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py similarity index 100% rename from monkey/monkey_island/cc/services/reporting/test_zeroTrustService.py rename to monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py From 92156946fe5e5fd6cf8bcfed013497cd40603f2f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 14:59:10 +0300 Subject: [PATCH 061/186] Added docs for function --- .../cc/services/reporting/zero_trust_service.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index bbe82f049..d28b072d8 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -86,12 +86,17 @@ class ZeroTrustService(object): @staticmethod def __get_lcd_worst_status_for_test(all_findings_for_test): - current_status = STATUS_UNEXECUTED + """ + :param all_findings_for_test: All findings of a specific test (get this using Finding.objects(test={A_TEST})) + :return: the "worst" (i.e. most severe) status out of the given findings. + lcd stands for lowest common denominator. + """ + current_worst_status = STATUS_UNEXECUTED for finding in all_findings_for_test: - if ORDERED_TEST_STATUSES.index(finding.status) < ORDERED_TEST_STATUSES.index(current_status): - current_status = finding.status + if ORDERED_TEST_STATUSES.index(finding.status) < ORDERED_TEST_STATUSES.index(current_worst_status): + current_worst_status = finding.status - return current_status + return current_worst_status @staticmethod def get_all_findings(): From e88c2baf648f572ba05448ef1aaa76e5ad003749 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 15:02:27 +0300 Subject: [PATCH 062/186] renamed funtion to be clearer. --- .../cc/services/reporting/zero_trust_service.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index d28b072d8..e3151dd7a 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -108,7 +108,6 @@ class ZeroTrustService(object): def __get_enriched_finding(finding): test_info = TESTS_MAP[finding.test] enriched_finding = { - # TODO add test explanation per status. "test": test_info[FINDING_EXPLANATION_BY_STATUS_KEY][finding.status], "pillars": test_info[PILLARS_KEY], "status": finding.status, @@ -129,7 +128,7 @@ class ZeroTrustService(object): STATUS_UNEXECUTED: [] } for pillar in PILLARS: - results[ZeroTrustService.__get_status_for_pillar(pillar)].append(pillar) + results[ZeroTrustService.__get_status_of_single_pillar(pillar)].append(pillar) return results @@ -137,12 +136,12 @@ class ZeroTrustService(object): def get_pillars_to_statuses(): results = {} for pillar in PILLARS: - results[pillar] = ZeroTrustService.__get_status_for_pillar(pillar) + results[pillar] = ZeroTrustService.__get_status_of_single_pillar(pillar) return results @staticmethod - def __get_status_for_pillar(pillar): + def __get_status_of_single_pillar(pillar): grade = ZeroTrustService.__get_pillar_grade(pillar) for status in ORDERED_TEST_STATUSES: if grade[status] > 0: From c4805b70e2757a596de746d77b3532a54e4322a6 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 15:21:55 +0300 Subject: [PATCH 063/186] Unified style and look of two reports loading location, + added print buttom component. --- .../cc/ui/src/components/pages/ReportPage.js | 63 ++++++++++--------- .../components/pages/ZeroTrustReportPage.js | 17 ++--- .../common/PrintReportButton.js | 14 +++++ 3 files changed, 58 insertions(+), 36 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 72fba9c48..cd224ea0c 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Fragment} from 'react'; import {Button, Col} from 'react-bootstrap'; import BreachedServers from 'components/report-components/security/BreachedServers'; import ScannedServers from 'components/report-components/security/ScannedServers'; @@ -17,6 +17,7 @@ import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStill import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; +import PrintReportButton from "../report-components/common/PrintReportButton"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); @@ -71,15 +72,11 @@ class ReportPageComponent extends AuthComponent { render() { let content; - if (Object.keys(this.state.report).length === 0) { - if (this.state.runStarted) { - content = (); - } else { - content = - ; - } - } else { + + if (this.state.runStarted) { content = this.generateReportContent(); + } else { + content = ; } return ( @@ -92,6 +89,10 @@ class ReportPageComponent extends AuthComponent { ); } + stillLoadingDataFromServer() { + return Object.keys(this.state.report).length === 0; + } + updateMonkeysRunning = () => { return this.authFetch('/api') .then(res => res.json()) @@ -130,32 +131,38 @@ class ReportPageComponent extends AuthComponent { } generateReportContent() { + let content; + + if (this.stillLoadingDataFromServer()) { + console.log("still loading?: " + this.stillLoadingDataFromServer()); + content = ; + } else { + console.log("not still loading: " + this.stillLoadingDataFromServer()); + content = +
    + {this.generateReportOverviewSection()} + {this.generateReportFindingsSection()} + {this.generateReportRecommendationsSection()} + {this.generateReportGlanceSection()} + {this.generateAttackSection()} + {this.generateReportFooter()} +
    ; + } + return ( -
    - { - // extract to print component. - } -
    - + +
    + {print();}} />

    - {this.generateReportOverviewSection()} - {this.generateReportFindingsSection()} - {this.generateReportRecommendationsSection()} - {this.generateReportGlanceSection()} - {this.generateAttackSection()} - {this.generateReportFooter()} + {content}
    -
    - +
    + {print();}} />
    -
    +
    ); } diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 21b1a7e3b..282662b94 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Fragment} from 'react'; import {Button, Col, Row, Grid} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; @@ -10,6 +10,7 @@ import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; import {StatusesToPillarsSummary} from "../report-components/zerotrust/StatusesToPillarsSummary"; +import PrintReportButton from "../report-components/common/PrintReportButton"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -104,19 +105,19 @@ class ZeroTrustReportPageComponent extends AuthComponent { } return ( -
    -
    - + +
    + {this.print();}} />
    -

    {content}
    -
    +
    + {this.print();}} /> +
    + ) } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js new file mode 100644 index 000000000..1a692bd68 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js @@ -0,0 +1,14 @@ +import React, {Component} from "react"; +import {Button} from "react-bootstrap"; +import * as PropTypes from "prop-types"; + +export default class PrintReportButton extends Component { + render() { + return
    + +
    + } +} + +PrintReportButton.propTypes = {onClick: PropTypes.func}; From 2fa2c049400a32bf5984419db75cb1bcb0ce974f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 15:57:48 +0300 Subject: [PATCH 064/186] Extracted execution status function to reduce code duplication --- .../cc/ui/src/components/pages/ReportPage.js | 9 ++------- .../cc/ui/src/components/pages/ZeroTrustReportPage.js | 8 +++----- .../report-components/common/ExecutionStatus.js | 6 ++++++ .../report-components/security/AttackReport.js | 7 ++----- .../report-components/zerotrust/PillarOverview.js | 2 ++ 5 files changed, 15 insertions(+), 17 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/common/ExecutionStatus.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index cd224ea0c..ffdb32a6a 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -18,6 +18,7 @@ import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; import PrintReportButton from "../report-components/common/PrintReportButton"; +import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); @@ -97,11 +98,7 @@ class ReportPageComponent extends AuthComponent { return this.authFetch('/api') .then(res => res.json()) .then(res => { - // This check is used to prevent unnecessary re-rendering - this.setState({ - allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']), - runStarted: res['completed_steps']['run_monkey'] - }); + this.setState(extractExecutionStatusFromServerResponse(res)); return res; }); }; @@ -134,10 +131,8 @@ class ReportPageComponent extends AuthComponent { let content; if (this.stillLoadingDataFromServer()) { - console.log("still loading?: " + this.stillLoadingDataFromServer()); content = ; } else { - console.log("not still loading: " + this.stillLoadingDataFromServer()); content =
    {this.generateReportOverviewSection()} diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 282662b94..b859e306b 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,5 +1,5 @@ import React, {Fragment} from 'react'; -import {Button, Col, Row, Grid} from 'react-bootstrap'; +import {Col, Grid, Row} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import PillarsOverview from "../report-components/zerotrust/PillarOverview"; @@ -11,6 +11,7 @@ import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarni import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; import {StatusesToPillarsSummary} from "../report-components/zerotrust/StatusesToPillarsSummary"; import PrintReportButton from "../report-components/common/PrintReportButton"; +import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -31,10 +32,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { return this.authFetch('/api') .then(res => res.json()) .then(res => { - this.setState({ - allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']), - runStarted: res['completed_steps']['run_monkey'] - }); + this.setState(extractExecutionStatusFromServerResponse(res)); return res; }); }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ExecutionStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ExecutionStatus.js new file mode 100644 index 000000000..840e570d7 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ExecutionStatus.js @@ -0,0 +1,6 @@ +export function extractExecutionStatusFromServerResponse(res) { + return { + allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']), + runStarted: res['completed_steps']['run_monkey'] + }; +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js index c5c58c92f..01a8cfe70 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js @@ -17,6 +17,7 @@ import T1082 from "../../attack/techniques/T1082"; import T1145 from "../../attack/techniques/T1145"; import T1107 from "../../attack/techniques/T1107"; import T1065 from "../../attack/techniques/T1065"; +import {extractExecutionStatusFromServerResponse} from "../common/ExecutionStatus"; const tech_components = { 'T1210': T1210, @@ -54,11 +55,7 @@ class AttackReportPageComponent extends AuthComponent { return this.authFetch('/api') .then(res => res.json()) .then(res => { - // This check is used to prevent unnecessary re-rendering - this.setState({ - allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']), - runStarted: res['completed_steps']['run_monkey'] - }); + this.setState(extractExecutionStatusFromServerResponse(res)); return res; }); }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index 558da5456..c865ecf43 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -19,6 +19,8 @@ const columns = [ class PillarOverview extends Component { pillarsToStatuses; + grades; + render() { const data = this.props.grades.map((grade) => { const newGrade = grade; From d52a6eab578460f83bd98a5e1d7fe38f41b8c897 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 18:09:05 +0300 Subject: [PATCH 065/186] Separated sections to methods for readability --- .../components/pages/ZeroTrustReportPage.js | 81 ++++++++++--------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index b859e306b..0b9855707 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -61,44 +61,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { if (this.stillLoadingDataFromServer()) { content = ; } else { - const overviewSection =
    -

    Overview

    - - - - - - - - - - - - -
    ; - - const directivesSection =
    -

    Directives

    - { - Object.keys(this.state.directives).map((pillar) => - - ) - } -
    ; - - const findingSection =
    -

    Findings

    - -
    ; - content =
    - {overviewSection} - {directivesSection} - {findingSection} + {this.generateOverviewSection()} + {this.generateDirectivesSection()} + {this.generateFindingsSection()}
    ; } @@ -119,6 +85,47 @@ class ZeroTrustReportPageComponent extends AuthComponent { ) } + generateFindingsSection() { + return (
    +

    Findings

    + +
    ); + } + + generateDirectivesSection() { + return (
    +

    Directives

    + { + Object.keys(this.state.directives).map((pillar) => + + ) + } +
    ); + } + + generateOverviewSection() { + return (
    +

    Overview

    + + + + + + + + + + + + +
    ); + } + stillLoadingDataFromServer() { return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.directives === "undefined"; } From f26ab7f62dc077ad78223ce831b6b40828465b30 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 18:13:30 +0300 Subject: [PATCH 066/186] Using regular print. Doesn't look great, but better than nothing. --- .../cc/ui/src/components/pages/ZeroTrustReportPage.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 0b9855707..fd7500216 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -71,7 +71,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { return (
    - {this.print();}} /> + {print();}} />
    @@ -79,7 +79,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { {content}
    - {this.print();}} /> + {print();}} />
    ) @@ -130,10 +130,6 @@ class ZeroTrustReportPageComponent extends AuthComponent { return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.directives === "undefined"; } - print() { - alert("unimplemented"); - } - getZeroTrustReportFromServer() { let res; this.authFetch('/api/report/zero_trust/findings') From db58bf9a87902accf8a16016075a0fa6f84304a5 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 19 Aug 2019 19:00:18 +0300 Subject: [PATCH 067/186] Fixed all small UI comments --- .../cc/ui/src/components/pages/ReportPage.js | 4 +- .../components/pages/ZeroTrustReportPage.js | 6 +-- .../common/MonkeysStillAliveWarning.js | 2 +- .../common/PaginatedTable.js | 7 +++ .../report-components/common/ReportHeader.js | 5 ++ .../report-components/common/ReportLoader.js | 3 ++ .../common/SecurityIssuesGlance.js | 2 +- .../zerotrust/DirectivesStatusTable.js | 16 +++--- .../zerotrust/EventsAndButtonComponent.js | 51 +++++++++++++++++++ .../zerotrust/EventsModal.js | 11 +++- .../zerotrust/EventsTimeline.js | 7 ++- .../zerotrust/FindingsTable.js | 49 ++---------------- .../zerotrust/PillarLabel.js | 29 ++++++----- .../zerotrust/PillarOverview.js | 11 ++-- .../zerotrust/SinglePillarDirectivesStatus.js | 10 ++-- .../zerotrust/StatusLabel.js | 10 ++-- .../zerotrust/StatusesToPillarsSummary.js | 11 ++-- 17 files changed, 144 insertions(+), 90 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index ffdb32a6a..a72e0c56d 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -13,10 +13,10 @@ import PassTheHashMapPageComponent from "./PassTheHashMapPage"; import StrongUsers from "components/report-components/security/StrongUsers"; import AttackReport from "components/report-components/security/AttackReport"; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; -import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; +import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; -import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; +import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance"; import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index fd7500216..f68321023 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -5,11 +5,11 @@ import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeade import PillarsOverview from "../report-components/zerotrust/PillarOverview"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; import {SinglePillarDirectivesStatus} from "../report-components/zerotrust/SinglePillarDirectivesStatus"; -import {MonkeysStillAliveWarning} from "../report-components/common/MonkeysStillAliveWarning"; +import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; -import {SecurityIssuesGlance} from "../report-components/common/SecurityIssuesGlance"; -import {StatusesToPillarsSummary} from "../report-components/zerotrust/StatusesToPillarsSummary"; +import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance"; +import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary"; import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js index 67fd9388a..7b72570fa 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js @@ -1,7 +1,7 @@ import React, {Component} from "react"; import * as PropTypes from "prop-types"; -export class MonkeysStillAliveWarning extends Component { +export default class MonkeysStillAliveWarning extends Component { render() { return
    { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js index 53ae1774d..5bc6183fd 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js @@ -1,5 +1,6 @@ import React, {Component} from "react"; import ReactTable from "react-table"; +import * as PropTypes from "prop-types"; class PaginatedTable extends Component { render() { @@ -27,3 +28,9 @@ class PaginatedTable extends Component { } export default PaginatedTable; + +PaginatedTable.propTypes = { + data: PropTypes.array, + columns: PropTypes.array, + pageSize: PropTypes.number, +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js index 0db9cb93e..44d470f7e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js @@ -1,5 +1,6 @@ import React, {Component} from "react"; import {Col} from "react-bootstrap"; +import * as PropTypes from "prop-types"; let monkeyLogoImage = require('../../../images/monkey-icon.svg'); @@ -38,3 +39,7 @@ export class ReportHeader extends Component { } export default ReportHeader; + +ReportHeader.propTypes = { + report_type: PropTypes.string, +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js index 873d70177..e389f7532 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js @@ -1,6 +1,7 @@ import {css} from "@emotion/core"; import React, {Component} from "react"; import {GridLoader} from "react-spinners"; +import * as PropTypes from "prop-types"; const loading_css_override = css` display: block; @@ -23,3 +24,5 @@ export default class ReportLoader extends Component {
    } } + +ReportLoader.propTypes = {loading: PropTypes.bool}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js index f734a1a28..41a45edad 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js @@ -1,7 +1,7 @@ import React, {Component, Fragment} from "react"; import * as PropTypes from "prop-types"; -export class SecurityIssuesGlance extends Component { +export default class SecurityIssuesGlance extends Component { render() { return { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js index ad1a815cb..84e003a6b 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js @@ -2,8 +2,8 @@ import React, {Fragment} from "react"; import PaginatedTable from "../common/PaginatedTable"; import AuthComponent from "../../AuthComponent"; import 'styles/ZeroTrustPillars.css' -import {StatusLabel} from "./StatusLabel"; - +import StatusLabel from "./StatusLabel"; +import * as PropTypes from "prop-types"; const columns = [ @@ -37,15 +37,15 @@ class TestsStatus extends AuthComponent { return ( - {this.getTestsOfStatusIfAny(conclusiveStatus)} - {this.getTestsOfStatusIfAny(inconclusiveStatus)} - {this.getTestsOfStatusIfAny(positiveStatus)} - {this.getTestsOfStatusIfAny(unexecutedStatus)} + {this.getFilteredTestsByStatusIfAny(conclusiveStatus)} + {this.getFilteredTestsByStatusIfAny(inconclusiveStatus)} + {this.getFilteredTestsByStatusIfAny(positiveStatus)} + {this.getFilteredTestsByStatusIfAny(unexecutedStatus)} ); } - getTestsOfStatusIfAny(statusToFilter) { + getFilteredTestsByStatusIfAny(statusToFilter) { const filteredTests = this.props.tests.filter((test) => { return (test.status === statusToFilter); } @@ -71,3 +71,5 @@ export class DirectivesStatusTable extends AuthComponent { } export default DirectivesStatusTable; + +DirectivesStatusTable.propTypes = {directivesStatus: PropTypes.array}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js new file mode 100644 index 000000000..0222b375f --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js @@ -0,0 +1,51 @@ +import React, {Component} from "react"; +import EventsModal from "./EventsModal"; +import {Button} from "react-bootstrap"; +import FileSaver from "file-saver"; +import * as PropTypes from "prop-types"; + +export default class EventsAndButtonComponent extends Component { + constructor(props) { + super(props); + this.state = { + isShow: false + } + } + + hide = () => { + this.setState({isShow: false}); + }; + + show = () => { + this.setState({isShow: true}); + }; + + render() { + return ( +
    + +

    + + +

    +
    + ); + } + +} + +EventsAndButtonComponent.propTypes = { + events: PropTypes.array, + exportFilename: PropTypes.string, +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js index 19dd7761a..9543603bd 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js @@ -1,8 +1,9 @@ import React, {Component} from "react"; import {Modal} from "react-bootstrap"; -import {EventsTimeline} from "./EventsTimeline"; +import EventsTimeline from "./EventsTimeline"; +import * as PropTypes from "prop-types"; -export class EventsModal extends Component { +export default class EventsModal extends Component { constructor(props) { super(props); } @@ -30,3 +31,9 @@ export class EventsModal extends Component { ); } } + +EventsModal.propTypes = { + showEvents: PropTypes.bool, + events: PropTypes.array, + hideCallback: PropTypes.func, +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index f70d5df31..8ba994c65 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -1,5 +1,6 @@ import React, {Component} from "react"; import {Timeline, TimelineEvent} from "react-event-timeline"; +import * as PropTypes from "prop-types"; const eventTypeToIcon = { "monkey_local": "fa fa-exclamation-circle fa-2x icon-warning", @@ -8,13 +9,13 @@ const eventTypeToIcon = { null: "fa fa-question-circle fa-2x icon-info", }; -export class EventsTimeline extends Component { +export default class EventsTimeline extends Component { render() { return (
    { - this.props["events"].map(event => { + this.props.events.map(event => { const event_time = new Date(event.timestamp['$date']).toString(); return ( { - this.setState({show: false}); - }; - - show = () => { - this.setState({show: true}); - }; - - render() { - return ( -
    - -

    - - -

    -
    - ); - } - -} - const columns = [ { Header: 'Findings', @@ -82,7 +39,7 @@ class FindingsTable extends Component { return newFinding; }); return ( - + ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js index 4559e5cd9..0724f9a13 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -1,23 +1,26 @@ import React, {Component} from "react"; import 'styles/ZeroTrustPillars.css' import {statusToLabelType} from "./StatusLabel"; +import * as PropTypes from "prop-types"; -export class PillarLabel extends Component { - pillar; - status; +const pillarToIcon = { + "Data": "fa fa-database", + "People": "fa fa-user", + "Networks": "fa fa-wifi", + "Workloads": "fa fa-cloud", + "Devices": "fa fa-laptop", + "Visibility & Analytics": "fa fa-eye-slash", + "Automation & Orchestration": "fa fa-cogs", +}; +export default class PillarLabel extends Component { render() { - const pillarToIcon = { - "Data": "fa fa-database", - "People": "fa fa-user", - "Networks": "fa fa-wifi", - "Workloads": "fa fa-cloud", - "Devices": "fa fa-laptop", - "Visibility & Analytics": "fa fa-eye-slash", - "Automation & Orchestration": "fa fa-cogs", - }; - const className = "label " + statusToLabelType[this.props.status]; return
    {this.props.pillar}
    } } + +PillarLabel.propTypes = { + status: PropTypes.string, + pillar: PropTypes.string, +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index c865ecf43..660e6ad5a 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -1,6 +1,7 @@ import React, {Component} from "react"; -import {PillarLabel} from "./PillarLabel"; +import PillarLabel from "./PillarLabel"; import PaginatedTable from "../common/PaginatedTable"; +import * as PropTypes from "prop-types"; const columns = [ { @@ -18,9 +19,6 @@ const columns = [ ]; class PillarOverview extends Component { - pillarsToStatuses; - grades; - render() { const data = this.props.grades.map((grade) => { const newGrade = grade; @@ -34,3 +32,8 @@ class PillarOverview extends Component { } export default PillarOverview; + +PillarOverview.propTypes = { + grades: PropTypes.array, + status: PropTypes.string, +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js index feb56b204..b8ded2d5a 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js @@ -1,11 +1,10 @@ import AuthComponent from "../../AuthComponent"; -import {PillarLabel} from "./PillarLabel"; +import PillarLabel from "./PillarLabel"; import DirectivesStatusTable from "./DirectivesStatusTable"; import React, {Fragment} from "react"; +import * as PropTypes from "prop-types"; export class SinglePillarDirectivesStatus extends AuthComponent { - directivesStatus; - render() { if (this.props.directivesStatus.length === 0) { return null; @@ -20,3 +19,8 @@ export class SinglePillarDirectivesStatus extends AuthComponent { } } } + +SinglePillarDirectivesStatus.propTypes = { + directivesStatus: PropTypes.array, + pillar: PropTypes.string, +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js index a53e4c919..f9c885b2c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -1,4 +1,4 @@ -import React, {Component, Fragment} from "react"; +import React, {Component} from "react"; import * as PropTypes from "prop-types"; const statusToIcon = { @@ -15,7 +15,7 @@ export const statusToLabelType = { "Unexecuted": "label-default", }; -export class StatusLabel extends Component { +export default class StatusLabel extends Component { render() { let text = ""; if (this.props.showText) { @@ -28,4 +28,8 @@ export class StatusLabel extends Component { } } -StatusLabel.propTypes = {status: PropTypes.string, showText: PropTypes.bool}; +StatusLabel.propTypes = { + status: PropTypes.string, + showText: PropTypes.bool, + size: PropTypes.string +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js index bfcaceed9..486a59d9f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js @@ -1,8 +1,9 @@ import React, {Component, Fragment} from "react"; -import {PillarLabel} from "./PillarLabel"; -import {StatusLabel} from "./StatusLabel"; +import PillarLabel from "./PillarLabel"; +import StatusLabel from "./StatusLabel"; +import * as PropTypes from "prop-types"; -export class StatusesToPillarsSummary extends Component { +export default class StatusesToPillarsSummary extends Component { render() { return (
    {this.getStatusSummary("Conclusive")} @@ -29,3 +30,7 @@ export class StatusesToPillarsSummary extends Component { } } } + +StatusesToPillarsSummary.propTypes = { + statusesToPillars: PropTypes.array +}; From 3f85c336b91cc6f58beb3dfccdd2909ad65f6f97 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 21 Aug 2019 18:32:39 +0300 Subject: [PATCH 068/186] Moved init of mappings to init file --- monkey/common/data/__init__.py | 2 ++ monkey/common/data/zero_trust_consts.py | 39 ++++++++++++------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/monkey/common/data/__init__.py b/monkey/common/data/__init__.py index e69de29bb..a8c1a93f7 100644 --- a/monkey/common/data/__init__.py +++ b/monkey/common/data/__init__.py @@ -0,0 +1,2 @@ +from zero_trust_consts import populate_mappings +populate_mappings() diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index dbb28a991..5e3791d40 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -61,9 +61,9 @@ TESTS_MAP = { TEST_SEGMENTATION: { TEST_EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_CONCLUSIVE: "Monkey performed cross-segment communication. Check firewall rules and logs.", - STATUS_POSITIVE: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." - }, + STATUS_CONCLUSIVE: "Monkey performed cross-segment communication. Check firewall rules and logs.", + STATUS_POSITIVE: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." + }, DIRECTIVE_KEY: DIRECTIVE_SEGMENTATION, PILLARS_KEY: [NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_POSITIVE, STATUS_CONCLUSIVE] @@ -128,6 +128,11 @@ TESTS_MAP = { }, } +EVENT_TYPE_ISLAND = "island" +EVENT_TYPE_MONKEY_NETWORK = "monkey_network" +EVENT_TYPE_MONKEY_LOCAL = "monkey_local" +EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK, EVENT_TYPE_ISLAND) + PILLARS_TO_TESTS = { DATA: [], PEOPLE: [], @@ -138,6 +143,16 @@ PILLARS_TO_TESTS = { AUTOMATION_ORCHESTRATION: [] } +DIRECTIVES_TO_TESTS = {} + +DIRECTIVES_TO_PILLARS = {} + + +def populate_mappings(): + populate_pillars_to_tests() + populate_directives_to_tests() + populate_directives_to_pillars() + def populate_pillars_to_tests(): for pillar in PILLARS: @@ -146,11 +161,6 @@ def populate_pillars_to_tests(): PILLARS_TO_TESTS[pillar].append(test) -populate_pillars_to_tests() - -DIRECTIVES_TO_TESTS = {} - - def populate_directives_to_tests(): for single_directive in DIRECTIVES: DIRECTIVES_TO_TESTS[single_directive] = [] @@ -158,11 +168,6 @@ def populate_directives_to_tests(): DIRECTIVES_TO_TESTS[test_info[DIRECTIVE_KEY]].append(test) -populate_directives_to_tests() - -DIRECTIVES_TO_PILLARS = {} - - def populate_directives_to_pillars(): for directive, directive_tests in DIRECTIVES_TO_TESTS.items(): directive_pillars = set() @@ -170,11 +175,3 @@ def populate_directives_to_pillars(): for pillar in TESTS_MAP[test][PILLARS_KEY]: directive_pillars.add(pillar) DIRECTIVES_TO_PILLARS[directive] = directive_pillars - - -populate_directives_to_pillars() - -EVENT_TYPE_ISLAND = "island" -EVENT_TYPE_MONKEY_NETWORK = "monkey_network" -EVENT_TYPE_MONKEY_LOCAL = "monkey_local" -EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK, EVENT_TYPE_ISLAND) From bfaa05aa519c1e834aea71283aad4209affd2ee7 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 21 Aug 2019 18:48:02 +0300 Subject: [PATCH 069/186] Minor CR fixes - changed downlaod filename, refactored some names --- .../monkey_island/cc/resources/telemetry.py | 2 +- .../services/reporting/zero_trust_service.py | 1 + .../processing/{hooks.py => processing.py} | 0 .../zero_trust_tests/antivirus_existence.py | 71 +------------------ .../zero_trust_tests/known_anti_viruses.py | 69 ++++++++++++++++++ .../components/pages/ZeroTrustReportPage.js | 2 +- .../zerotrust/FindingsTable.js | 2 +- .../zerotrust/SinglePillarDirectivesStatus.js | 2 +- .../zerotrust/StatusesToPillarsSummary.js | 2 +- 9 files changed, 76 insertions(+), 75 deletions(-) rename monkey/monkey_island/cc/services/telemetry/processing/{hooks.py => processing.py} (100%) create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index e07207707..2f674f7bf 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -9,7 +9,7 @@ from flask import request from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo from monkey_island.cc.services.node import NodeService -from monkey_island.cc.services.telemetry.processing.hooks import process_telemetry +from monkey_island.cc.services.telemetry.processing.processing import process_telemetry from monkey_island.cc.models.monkey import Monkey __author__ = 'Barak' diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index e3151dd7a..8039d6e16 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -109,6 +109,7 @@ class ZeroTrustService(object): test_info = TESTS_MAP[finding.test] enriched_finding = { "test": test_info[FINDING_EXPLANATION_BY_STATUS_KEY][finding.status], + "test_key": finding.test, "pillars": test_info[PILLARS_KEY], "status": finding.status, "events": ZeroTrustService.__get_events_as_dict(finding.events) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/hooks.py b/monkey/monkey_island/cc/services/telemetry/processing/processing.py similarity index 100% rename from monkey/monkey_island/cc/services/telemetry/processing/hooks.py rename to monkey/monkey_island/cc/services/telemetry/processing/processing.py diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index c93d63b72..c86838476 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -5,76 +5,7 @@ from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_IS from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.finding import Finding - -ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ - u"AvastSvc.exe", - u"AvastUI.exe", - u"avcenter.exe", - u"avconfig.exe", - u"avgcsrvx.exe", - u"avgidsagent.exe", - u"avgnt.exe", - u"avgrsx.exe", - u"avguard.exe", - u"avgui.exe", - u"avgwdsvc.exe", - u"avp.exe", - u"avscan.exe", - u"bdagent.exe", - u"ccuac.exe", - u"egui.exe", - u"hijackthis.exe", - u"instup.exe", - u"keyscrambler.exe", - u"mbam.exe", - u"mbamgui.exe", - u"mbampt.exe", - u"mbamscheduler.exe", - u"mbamservice.exe", - u"MpCmdRun.exe", - u"MSASCui.exe", - u"MsMpEng.exe", - u"rstrui.exe", - u"spybotsd.exe", - u"zlclient.exe", - u"SymCorpUI.exe", - u"ccSvcHst.exe", - u"ccApp.exe", - u"LUALL.exe", - u"SMC.exe", - u"SMCgui.exe", - u"Rtvscan.exe", - u"LuComServer.exe", - u"ProtectionUtilSurrogate.exe", - u"ClientRemote.exe", - u"SemSvc.exe", - u"SemLaunchSvc.exe", - u"sesmcontinst.exe", - u"LuCatalog.exe", - u"LUALL.exe", - u"LuCallbackProxy.exe", - u"LuComServer_3_3.exe", - u"httpd.exe", - u"dbisqlc.exe", - u"dbsrv16.exe", - u"semapisrv.exe", - u"snac64.exe", - u"AutoExcl.exe", - u"DoScan.exe", - u"nlnhook.exe", - u"SavUI.exe", - u"SepLiveUpdate.exe", - u"Smc.exe", - u"SmcGui.exe", - u"SymCorpUI.exe", - u"symerr.exe", - u"ccSvcHst.exe", - u"DevViewer.exe", - u"DWHWizrd.exe", - u"RtvStart.exe", - u"roru.exe", - u"WSCSAvNotifier" -] +from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES def test_antivirus_existence(telemetry_json): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py new file mode 100644 index 000000000..e10792d0c --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py @@ -0,0 +1,69 @@ +ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ + u"AvastSvc.exe", + u"AvastUI.exe", + u"avcenter.exe", + u"avconfig.exe", + u"avgcsrvx.exe", + u"avgidsagent.exe", + u"avgnt.exe", + u"avgrsx.exe", + u"avguard.exe", + u"avgui.exe", + u"avgwdsvc.exe", + u"avp.exe", + u"avscan.exe", + u"bdagent.exe", + u"ccuac.exe", + u"egui.exe", + u"hijackthis.exe", + u"instup.exe", + u"keyscrambler.exe", + u"mbam.exe", + u"mbamgui.exe", + u"mbampt.exe", + u"mbamscheduler.exe", + u"mbamservice.exe", + u"MpCmdRun.exe", + u"MSASCui.exe", + u"MsMpEng.exe", + u"rstrui.exe", + u"spybotsd.exe", + u"zlclient.exe", + u"SymCorpUI.exe", + u"ccSvcHst.exe", + u"ccApp.exe", + u"LUALL.exe", + u"SMC.exe", + u"SMCgui.exe", + u"Rtvscan.exe", + u"LuComServer.exe", + u"ProtectionUtilSurrogate.exe", + u"ClientRemote.exe", + u"SemSvc.exe", + u"SemLaunchSvc.exe", + u"sesmcontinst.exe", + u"LuCatalog.exe", + u"LUALL.exe", + u"LuCallbackProxy.exe", + u"LuComServer_3_3.exe", + u"httpd.exe", + u"dbisqlc.exe", + u"dbsrv16.exe", + u"semapisrv.exe", + u"snac64.exe", + u"AutoExcl.exe", + u"DoScan.exe", + u"nlnhook.exe", + u"SavUI.exe", + u"SepLiveUpdate.exe", + u"Smc.exe", + u"SmcGui.exe", + u"SymCorpUI.exe", + u"symerr.exe", + u"ccSvcHst.exe", + u"DevViewer.exe", + u"DWHWizrd.exe", + u"RtvStart.exe", + u"roru.exe", + u"WSCSAvNotifier" +] diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index f68321023..2fe43c42e 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -4,7 +4,7 @@ import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import PillarsOverview from "../report-components/zerotrust/PillarOverview"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; -import {SinglePillarDirectivesStatus} from "../report-components/zerotrust/SinglePillarDirectivesStatus"; +import SinglePillarDirectivesStatus from "../report-components/zerotrust/SinglePillarDirectivesStatus"; import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 3838bd245..18160846a 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -22,7 +22,7 @@ const columns = [ }, { Header: 'Events', id:"events", accessor: x => { - return ; + return ; } } ] diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js index b8ded2d5a..47f477dfd 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js @@ -4,7 +4,7 @@ import DirectivesStatusTable from "./DirectivesStatusTable"; import React, {Fragment} from "react"; import * as PropTypes from "prop-types"; -export class SinglePillarDirectivesStatus extends AuthComponent { +export default class SinglePillarDirectivesStatus extends AuthComponent { render() { if (this.props.directivesStatus.length === 0) { return null; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js index 486a59d9f..8cdf12af5 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js @@ -32,5 +32,5 @@ export default class StatusesToPillarsSummary extends Component { } StatusesToPillarsSummary.propTypes = { - statusesToPillars: PropTypes.array + statusesToPillars: PropTypes.object }; From 6843606a4f453e571b5c3e05d77a300f5f37df07 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 21 Aug 2019 19:03:51 +0300 Subject: [PATCH 070/186] Merge commit leftovers --- monkey/monkey_island/cc/services/telemetry/processing/state.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py index e71abacd7..ac8e32939 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/state.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py @@ -3,6 +3,7 @@ from monkey_island.cc.services.node import NodeService def process_state_telemetry(telemetry_json): monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) + NodeService.add_communication_info(monkey, telemetry_json['command_control_channel']) if telemetry_json['data']['done']: NodeService.set_monkey_dead(monkey, True) else: From d6104bbcf997b1fa86da189c7cec78d6fb9e4810 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 21 Aug 2019 21:28:15 +0300 Subject: [PATCH 071/186] Started implementing the open_data_endpoints test, still not creating findings --- .../cc/services/telemetry/processing/scan.py | 7 ++++++- .../telemetry/zero_trust_tests/data_endpoints.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py diff --git a/monkey/monkey_island/cc/services/telemetry/processing/scan.py b/monkey/monkey_island/cc/services/telemetry/processing/scan.py index 4e34b9a19..3b532ff22 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/scan.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/scan.py @@ -2,9 +2,15 @@ import copy from monkey_island.cc.database import mongo from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry +from monkey_island.cc.services.telemetry.zero_trust_tests.data_endpoints import test_open_data_endpoints def process_scan_telemetry(telemetry_json): + update_edges_and_nodes_based_on_scan_telemetry(telemetry_json) + test_open_data_endpoints(telemetry_json) + + +def update_edges_and_nodes_based_on_scan_telemetry(telemetry_json): edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json) data = copy.deepcopy(telemetry_json['data']['machine']) ip_address = data.pop("ip_addr") @@ -19,7 +25,6 @@ def process_scan_telemetry(telemetry_json): {"$push": {"scans": new_scan}, "$set": {"ip_address": ip_address, 'domain_name': domain_name}} ) - node = mongo.db.node.find_one({"_id": edge["to"]}) if node is not None: scan_os = new_scan["data"]["os"] diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py new file mode 100644 index 000000000..e0aabf413 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -0,0 +1,16 @@ +import json + +BAD_ENDPOINTS = { + "tcp-80": "Open HTTP server." +} + + +def test_open_data_endpoints(telemetry_json): + services = telemetry_json["data"]["machine"]["services"] + for service_name, service_data in services.items(): + if service_name in BAD_ENDPOINTS: + # TODO Create finding + print("found open {} service on address {}, details: {}".format( + service_data["display_name"], + telemetry_json["data"]["machine"]["ip_addr"], + json.dumps(service_data))) From 83ed12249ed88bbb1d6991cbdf1677035aee4e94 Mon Sep 17 00:00:00 2001 From: vkuchinov Date: Thu, 22 Aug 2019 10:13:10 +0300 Subject: [PATCH 072/186] VennDiagram component --- .../zerotrust/VennDiagram/ArcNode/index.js | 46 +++ .../VennDiagram/CircularNode/index.js | 57 +++ .../zerotrust/VennDiagram/Tooltip/index.js | 49 +++ .../zerotrust/VennDiagram/index.css | 14 + .../zerotrust/VennDiagram/index.js | 369 ++++++++++++++++++ .../zerotrust/VennDiagram/utility.js | 8 + 6 files changed, 543 insertions(+) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/CircularNode/index.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/Tooltip/index.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.css create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/utility.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js new file mode 100644 index 000000000..b9861a55e --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js @@ -0,0 +1,46 @@ +import React from 'react' +import PropTypes from 'prop-types'; +import * as d3 from 'd3' + +class ArcNode extends React.Component{ + + render() { + + let {prefix, ref, index, data} = this.props;; + + let arc = d3.arc().innerRadius(data.inner).outerRadius(data.outer).startAngle(0).endAngle(Math.PI * 2.0); + + return ( + + + + + + {data.label} + + + + + ) + + } + +} + +ArcNode.propTypes = { + + prefix: PropTypes.string, + index: PropTypes.number, + data: PropTypes.object + +} + +export default ArcNode; \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/CircularNode/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/CircularNode/index.js new file mode 100644 index 000000000..a8caf5bb0 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/CircularNode/index.js @@ -0,0 +1,57 @@ +import React from 'react' +import PropTypes from 'prop-types'; + +class CircularNode extends React.Component{ + + render() { + + let {prefix, ref, index, data} = this.props; + + let tspans = data.label.split("|").map((d_, i_) =>{ + + let halfTextHeight = data.fontStyle.size * data.label.split("|").length / 2; + let key = 'vennDiagramCircularNode' + index + '_Tspan' + i_; + + return ( + + {d_} + + ) + + }) + + let translate = 'translate(' + data.cx + ',' + data.cy + ')'; + + return ( + + + + + {tspans} + + + + ) + + } + +} + +CircularNode.propTypes = { + + prefix: PropTypes.string, + index: PropTypes.number, + data: PropTypes.object + +} + +export default CircularNode; \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/Tooltip/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/Tooltip/index.js new file mode 100644 index 000000000..579b36a5b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/Tooltip/index.js @@ -0,0 +1,49 @@ +import React from 'react' +import PropTypes from 'prop-types'; + +class Tooltip extends React.Component{ + + render() { + + const {prefix, bcolor, top, left, display, html } = this.props; + + const style = { + + backgroundColor: bcolor, + border : '1px solid #FFFFFF', + borderRadius: '2px', + fontSize: 10, + padding: 8, + display, + opacity: 0.9, + position: 'fixed', + top, + left, + pointerEvents: 'none' + + }; + + return ( + +
    + {html.split('\n').map((i_, key_) => { return
    {i_}
    ; })} +
    + + ); + + } + +} + +Tooltip.propTypes = { + + prefix: PropTypes.string, + bcolor: PropTypes.string, + top: PropTypes.number, + left: PropTypes.number, + display: PropTypes.string, + html: PropTypes.string + +} + +export default Tooltip; \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.css b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.css new file mode 100644 index 000000000..6c7cd778e --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.css @@ -0,0 +1,14 @@ +@import url('https://fonts.googleapis.com/css?family=Noto+Sans&display=swap'); + +body { margin: 0; font-family: "Noto Sans", sans-serif; } + +svg { + + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */ + +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js new file mode 100644 index 000000000..b221d4159 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js @@ -0,0 +1,369 @@ +import React from 'react' +import PropTypes from 'prop-types'; +import Dimensions from 'react-dimensions' +import Tooltip from './Tooltip' +import CircularNode from './CircularNode' +import ArcNode from './ArcNode' +import { TypographicUtilities } from './utility.js' +import './index.css' + +/* + +TODO LIST + +UPDATED [21.08.2019] + +[-] SVG > ViewBox 0 0 512 512, so it would be responsive and scalable +[-] Add resize listener to ResponsiveVennDiagram wrapper +[-] I have noticed that you have PillarGrades at ZeroTrustReportPage, so how I can fetch the data out of it? + +UPDATED [20.08.2019] + +[x] I've seen that lots of D3 responsive examples are using 'wrapper' around the main component to + get parent container dimensions. And lister for resize. + + So, here it's Responsive(VennDiagram) + + If it doesn't work, I have another alternative: + + import React, { useRef, useEffect, useState, useLayoutEffect } from 'react' + + const ResponsiveVennDiagram = props => { + + const minWidth = 256; + const targetRef = useRef(); + const [dimensions, setDimensions] = useState({ width: 0, heigth: 0 }); + let movement_timer = null; + const RESET_TIMEOUT = 100; + + const checkForDimensionsUpdate = () => { + + if (targetRef.current) { + + setDimensions({ + + width: Math.min(targetRef.current.offsetWidth, targetRef.current.offsetHeight), + height: targetRef.current.offsetHeight + + }); + + } + }; + + useLayoutEffect(() => { checkForDimensionsUpdate(); }, []); + + window.addEventListener("resize", () => { clearInterval(movement_timer); movement_timer = setTimeout(checkForDimensionsUpdate, RESET_TIMEOUT); }); + + return ( + +
    + +
    + + ); + + }; + + ResponsiveVennDiagram.propTypes = { + + pillarsGrades: PropTypes.array + + } + + export default ResponsiveVennDiagram; + + While your diagram laout is squared, the VennDiaagram gets the min(width, height) + +[x] Colours have been updated. + +[x] String prototypes have been moved to '.utilities.js' + +[x] I have use the prefix 'vennDiagram' for all children elements to prevent naming conflicts with other components + DOM objects. + +[x] I have used PropTypes already a year ago on ThreeJS/ReactJS stack project. + +[!] External callback is still on my list, can you make a mockup for external function which have to get pillar variable? + +[!] Z-indices sorting on mouseover + Would be n my 21.08.2019 TODO list. With D3.JS it's an easy task, however would try do + make these z-soring without D3 framework. + +UPDATED [20.08.2019] + +[!] By now, there are three input props for VennDiagram component: + data, width and height. + +[-] Since layout has to be hardcoded, it's driven by this.layout object. + There're many ways of setting circles/arc positions, now I'm using the + stright-forward one. + + Usually, I am put all hardcoded params to external JSON file, i.e config.json. + Let me know if it's a good idea. + +[-] Could rearange z-indecies for nodes on hover, so highlighted node would have highest z-index. +[-] If you want callback on click even, please provide additional info what are you expecting to pass + through this. + +[!] I don't used to make lots of comments in my code, but treying to name everything in a way, + so third person could instantly get its job and concept. + + If it is not enoough just let me know. + +[!] I have tried to avoid using D3 so much, especially its mouse events, cause it creates a bunch + of listeners for every children DOM elements. I have tried to use raw SVG objects. + The ArcNode is the only component where I've to call d3.arc constrictor. + +[!] There are lots of discussion over different blogs an forums that ReactJS and D3.JS have a DOM + issue conflict, [could find lots of articels at medium.org about that, for example, + https://medium.com/@tibotiber/react-d3-js-balancing-performance-developer-experience-4da35f912484]. + + Since the current component has only a few DOM elements, I don't thing we would have any troubles, + but please keep in mind that I could tweak current version with react-faux-dom. + + Actually, by now, I'm using D3 only for math, for arc path calculations. + +[!] Don't mind about code spacings, it's just for me, the final code would be clear out of them. + +[-] On click, an EXTERNAL callback should be called with the pillar name as a parameter. That is to enable us to expand the click functionality in future without editing the internal implementation of the component. + +[-] planned, [x] done, [!] see comments + +@author Vladimir V KUCHINOV +@email helloworld@vkuchinov.co.uk + +*/ + +const VENN_MIN_WIDTH = 256; + +class ResponsiveVennDiagram extends React.Component { + + constructor(props) { super(props); } + render() { + + const {options, pillarsGrades} = this.props; + let childrenWidth = this.props.containerWidth, childrenHeight = this.props.containerHeight; + + if(childrenHeight === 0 || childrenHeight === NaN){ childrenHeight = childrenWidth; } + else{ childrenWidth = Math.min(childrenWidth, childrenHeight) } + + return ( + +
    + +
    ) + + } + +} + +ResponsiveVennDiagram.propTypes = { + + pillarsGrades: PropTypes.array + +} + +export default Dimensions()(ResponsiveVennDiagram); + +class VennDiagram extends React.Component{ + + constructor(props_){ + + super(props_); + + this.state = { tooltip: { top: 0, left: 0, display: 'none', html: '' } } + + this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C']; + this.prefix = 'vennDiagram'; + this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed']; + this.fontStyles = [{size: Math.max(9, this.props.width / 32), color: 'white'}, {size: Math.max(6, this.props.width / 52), color: 'black'}]; + this.offset = this.props.width / 16; + + this.thirdWidth = this.props.width / 3; + this.sixthWidth = this.props.width / 6; + this.width2By7 = 2 * this.props.width / 7 + this.width1By11 = this.props.width / 11; + this.width1By28 = this.props.width / 28; + + this.toggle = false; + + this.layout = { + + Data: { cx: 0, cy: 0, r: this.thirdWidth - this.offset * 2, offset: {x: 0, y: 0} }, + People: { cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0} }, + Networks: { cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0} }, + Devices: { cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11} }, + Workloads : { cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11} }, + VisibilityAndAnalytics : { inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth }, + AutomationAndOrchestration: { inner: this.thirdWidth - this.width1By28 * 2, outer: this.thirdWidth - this.width1By28 } + + }; + + } + + componentDidMount() { + + this.parseData(); + + } + + _onMouseMove(e) { + + if(!this.toggle){ + + let hidden = 'none'; + let html = ''; + let bcolor = '#DEDEDE'; + + e.stopPropagation(); + + document.querySelectorAll('circle, path').forEach((d_, i_) => { d_.setAttribute('opacity', 0.8)}); + + if(e.target.id.includes('Node')) { + + html = e.target.dataset.tooltip; + this.divElement.style.cursor = 'pointer'; + hidden = 'block'; e.target.setAttribute('opacity', 1.0); + bcolor = e.target.getAttribute('fill'); + + }else{ + + this.divElement.style.cursor = 'default'; + + } + + this.setState({target: e, tooltip: { target: e.target, bcolor: bcolor, top: e.clientY + 8, left: e.clientX + 8, display: hidden, html: html } }); + + } + } + + _onClick(e) { + + if(this.state.tooltip.target === e.target) { this.toggle = true; } else { this.toggle = false; } + + //variable to external callback + //e.target.parentNode.id) + + } + + relativeCoords (e) { + + let bounds = e.target.getBoundingClientRect(); + var x = e.clientX - bounds.left; + var y = e.clientY - bounds.top; + return {x: x, y: y}; + + } + + parseData(){ + + let self = this; + let data = []; + const omit = (prop, { [prop]: _, ...rest }) => rest; + + this.props.pillarsGrades.forEach((d_, i_) => { + + let params = omit('pillar', d_); + let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_]||0), 0); + let key = TypographicUtilities.removeAmpersand(d_.pillar); + let html = self.buildTooltipHtmlContent(d_); + let rule = 3; + + if(sum === 0){ rule = 0 } + else if(d_['Conclusive'] > 0){ rule = 1 } + else if(d_['Conclusive'] === 0 && d_['Inconclusive'] > 0) { rule = 2 } + + self.setLayoutElement(rule, key, html, d_); + data.push(this.layout[key]) + + }) + + this.setState({ data: data }); + this.render(); + + } + + buildTooltipHtmlContent(object_){ return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', ''); } + + setLayoutElement(rule_, key_, html_, d_){ + + if(key_ === 'Data'){ this.layout[key_].fontStyle = this.fontStyles[0]; } + else {this.layout[key_].fontStyle = this.fontStyles[1]; } + + this.layout[key_].hex = this.colors[rule_]; + this.layout[key_].label = d_.pillar + this.suffices[rule_]; + this.layout[key_].node = d_; + this.layout[key_].tooltip = html_; + + } + + render() { + + if(this.state.data === undefined) { return null; } + else { + + let { width, height } = this.props; + let translate = 'translate(' + width /2 + ',' + height/2 + ')'; + + let nodes = Object.values(this.layout).map((d_, i_) =>{ + + if(d_.hasOwnProperty('cx')){ + + return ( + + + ); + + }else{ + + d_.label = TypographicUtilities.removeBrokenBar(d_.label); + + return ( + + + ); + + } + + }); + + return ( + +
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)}> + + + {nodes} + + + +
    + + ) + + } + + } + +} + +VennDiagram.propTypes = { + + pillarsGrades: PropTypes.array, + width: PropTypes.number, + height: PropTypes.number + +} \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/utility.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/utility.js new file mode 100644 index 000000000..c9816c721 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/utility.js @@ -0,0 +1,8 @@ +export class TypographicUtilities { + + static removeAmpersand(string_) { return string_.replace(' & ', 'And'); } + static removeBrokenBar(string_) { return string_.replace(/\|/g, ' '); } + static setTitle(string_) { return string_.charAt(0).toUpperCase() + string_.substr(1).toLowerCase(); } + +} + \ No newline at end of file From 4581376d8d304dd3e7373f50478d4d367b24bc51 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 22 Aug 2019 10:52:33 +0300 Subject: [PATCH 073/186] Added the open http endpoint test --- monkey/common/data/zero_trust_consts.py | 6 +- .../cc/models/zero_trust/event.py | 4 +- .../reporting/test_zero_trust_service.py | 4 +- .../zero_trust_tests/data_endpoints.py | 57 ++++++++++++++++--- 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 5e3791d40..a92e386fe 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -27,11 +27,11 @@ TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http" TEST_MACHINE_EXPLOITED = u"machine_exploited" TEST_ENDPOINT_SECURITY_EXISTS = u"endpoint_security_exists" TEST_SCHEDULED_EXECUTION = u"scheduled_execution" -TEST_ACTIVITY_TIMELINE = u"malicious_activity_timeline" +TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline" TEST_SEGMENTATION = u"segmentation" TESTS = ( TEST_SEGMENTATION, - TEST_ACTIVITY_TIMELINE, + TEST_MALICIOUS_ACTIVITY_TIMELINE, TEST_SCHEDULED_EXECUTION, TEST_ENDPOINT_SECURITY_EXISTS, TEST_MACHINE_EXPLOITED, @@ -68,7 +68,7 @@ TESTS_MAP = { PILLARS_KEY: [NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_POSITIVE, STATUS_CONCLUSIVE] }, - TEST_ACTIVITY_TIMELINE: { + TEST_MALICIOUS_ACTIVITY_TIMELINE: { TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_INCONCLUSIVE: "Monkey performed malicious actions in the network. Check SOC logs and alerts." diff --git a/monkey/monkey_island/cc/models/zero_trust/event.py b/monkey/monkey_island/cc/models/zero_trust/event.py index 01c7f2f47..6ad728d66 100644 --- a/monkey/monkey_island/cc/models/zero_trust/event.py +++ b/monkey/monkey_island/cc/models/zero_trust/event.py @@ -23,9 +23,9 @@ class Event(EmbeddedDocument): # LOGIC @staticmethod - def create_event(title, message, event_type): + def create_event(title, message, event_type, timestamp=datetime.now()): event = Event( - timestamp=datetime.now(), + timestamp=timestamp, title=title, message=message, event_type=event_type diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py index d3fe01db9..30a1a08fe 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py @@ -165,7 +165,7 @@ class TestZeroTrustService(IslandTestCase): "tests": [ { "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] + "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] } ] } @@ -189,7 +189,7 @@ class TestZeroTrustService(IslandTestCase): "tests": [ { "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] + "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] } ] } diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index e0aabf413..119871420 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -1,16 +1,55 @@ import json -BAD_ENDPOINTS = { - "tcp-80": "Open HTTP server." -} +from common.data.zero_trust_consts import * +from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.models.zero_trust.finding import Finding + +HTTP_SERVERS_SERVICES_NAMES = ['tcp-80'] def test_open_data_endpoints(telemetry_json): services = telemetry_json["data"]["machine"]["services"] + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + found_http_server_status = STATUS_POSITIVE + + events = [ + Event.create_event( + title="Scan Telemetry", + message="Monkey on {} tried to perform a network scan, the target was {}.".format( + current_monkey.hostname, + telemetry_json["data"]["machine"]["ip_addr"]), + event_type=EVENT_TYPE_MONKEY_NETWORK, + timestamp=telemetry_json["timestamp"] + ) + ] + for service_name, service_data in services.items(): - if service_name in BAD_ENDPOINTS: - # TODO Create finding - print("found open {} service on address {}, details: {}".format( - service_data["display_name"], - telemetry_json["data"]["machine"]["ip_addr"], - json.dumps(service_data))) + events.append(Event.create_event( + title="Scan telemetry analysis", + message="Scanned service: {}.".format(service_name), + event_type=EVENT_TYPE_ISLAND + )) + if service_name in HTTP_SERVERS_SERVICES_NAMES: + found_http_server_status = STATUS_CONCLUSIVE + events.append(Event.create_event( + title="Scan telemetry analysis", + message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( + service_data["display_name"], + telemetry_json["data"]["machine"]["ip_addr"], + json.dumps(service_data) + ), + event_type=EVENT_TYPE_ISLAND + )) + + Finding.save_finding( + test=TEST_DATA_ENDPOINT_HTTP, + status=found_http_server_status, + events=events + ) + + Finding.save_finding( + test=TEST_MALICIOUS_ACTIVITY_TIMELINE, + status=STATUS_INCONCLUSIVE, + events=events + ) From 7f98f55e64c455177bede1f1c8d40b0963888bc7 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 22 Aug 2019 11:21:52 +0300 Subject: [PATCH 074/186] Fixed error +warning in UI Error - didn't use deep copy and caused error Warning - 2 events might have the same timestamp --- .../components/report-components/zerotrust/EventsTimeline.js | 4 ++-- .../components/report-components/zerotrust/PillarOverview.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index 8ba994c65..9f9e1f899 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -15,10 +15,10 @@ export default class EventsTimeline extends Component {
    { - this.props.events.map(event => { + this.props.events.map((event, index) => { const event_time = new Date(event.timestamp['$date']).toString(); return (}> diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index 660e6ad5a..e2b16c91b 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -21,7 +21,7 @@ const columns = [ class PillarOverview extends Component { render() { const data = this.props.grades.map((grade) => { - const newGrade = grade; + const newGrade = JSON.parse(JSON.stringify(grade)); newGrade.pillar = {name: grade.pillar, status: this.props.pillarsToStatuses[grade.pillar]}; return newGrade; }); From 2174f43a849874226cff98dc19adb9ab9b32a409 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 22 Aug 2019 13:33:40 +0300 Subject: [PATCH 075/186] Added d3 to package.json and now using the ResponsiveVennDiagram in Pillaroverview --- monkey/monkey_island/cc/ui/package-lock.json | 315 ++++++++++++++++-- monkey/monkey_island/cc/ui/package.json | 1 + .../zerotrust/PillarOverview.js | 10 +- 3 files changed, 286 insertions(+), 40 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 09ccebad9..f366d73bd 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -3087,8 +3087,7 @@ "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" }, "commondir": { "version": "1.0.1", @@ -3657,6 +3656,270 @@ "es5-ext": "^0.10.9" } }, + "d3": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.11.0.tgz", + "integrity": "sha512-LXgMVUAEAzQh6WfEEOa8tJX4RA64ZJ6twC3CJ+Xzid+fXWLTZkkglagXav/eOoQgzQi5rzV0xC4Sfspd6hFDHA==", + "requires": { + "d3-array": "1", + "d3-axis": "1", + "d3-brush": "1", + "d3-chord": "1", + "d3-collection": "1", + "d3-color": "1", + "d3-contour": "1", + "d3-dispatch": "1", + "d3-drag": "1", + "d3-dsv": "1", + "d3-ease": "1", + "d3-fetch": "1", + "d3-force": "1", + "d3-format": "1", + "d3-geo": "1", + "d3-hierarchy": "1", + "d3-interpolate": "1", + "d3-path": "1", + "d3-polygon": "1", + "d3-quadtree": "1", + "d3-random": "1", + "d3-scale": "2", + "d3-scale-chromatic": "1", + "d3-selection": "1", + "d3-shape": "1", + "d3-time": "1", + "d3-time-format": "2", + "d3-timer": "1", + "d3-transition": "1", + "d3-voronoi": "1", + "d3-zoom": "1" + } + }, + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + }, + "d3-brush": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.3.tgz", + "integrity": "sha512-v8bbYyCFKjyCzFk/tdWqXwDykY8YWqhXYjcYxfILIit085VZOpj4XJKOMccTsvWxgzSLMJQg5SiqHjslsipEDg==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "d3-chord": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", + "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "requires": { + "d3-array": "1", + "d3-path": "1" + } + }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.3.0.tgz", + "integrity": "sha512-NHODMBlj59xPAwl2BDiO2Mog6V+PrGRtBfWKqKRrs9MCqlSkIEb0Z/SfY7jW29ReHTDC/j+vwXhnZcXI3+3fbg==" + }, + "d3-contour": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", + "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "requires": { + "d3-array": "^1.1.1" + } + }, + "d3-dispatch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", + "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==" + }, + "d3-drag": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.4.tgz", + "integrity": "sha512-ICPurDETFAelF1CTHdIyiUM4PsyZLaM+7oIBhmyP+cuVjze5vDZ8V//LdOFjg0jGnFIZD/Sfmk0r95PSiu78rw==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-dsv": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz", + "integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==", + "requires": { + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" + } + }, + "d3-ease": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz", + "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==" + }, + "d3-fetch": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz", + "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==", + "requires": { + "d3-dsv": "1" + } + }, + "d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "requires": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "d3-format": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz", + "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" + }, + "d3-geo": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.6.tgz", + "integrity": "sha512-z0J8InXR9e9wcgNtmVnPTj0TU8nhYT6lD/ak9may2PdKqXIeHUr8UbFLoCtrPYNsjv6YaLvSDQVl578k6nm7GA==", + "requires": { + "d3-array": "1" + } + }, + "d3-hierarchy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz", + "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==" + }, + "d3-interpolate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", + "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg==" + }, + "d3-polygon": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz", + "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w==" + }, + "d3-quadtree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz", + "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==" + }, + "d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + }, + "d3-scale": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", + "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "requires": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "d3-scale-chromatic": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", + "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "requires": { + "d3-color": "1", + "d3-interpolate": "1" + } + }, + "d3-selection": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz", + "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg==" + }, + "d3-shape": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", + "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz", + "integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw==" + }, + "d3-time-format": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", + "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", + "requires": { + "d3-time": "1" + } + }, + "d3-timer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", + "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==" + }, + "d3-transition": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz", + "integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==", + "requires": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, + "d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -8419,8 +8682,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -8441,14 +8703,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8463,20 +8723,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -8593,8 +8850,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -8606,7 +8862,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8621,7 +8876,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -8629,14 +8883,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -8655,7 +8907,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -8736,8 +8987,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -8749,7 +8999,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -8835,8 +9084,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -8872,7 +9120,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8892,7 +9139,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8936,14 +9182,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -15091,6 +15335,11 @@ "aproba": "^1.1.1" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -19041,6 +19290,7 @@ "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -19209,6 +19459,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 1a60ee27c..872a22bdc 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -70,6 +70,7 @@ "bootstrap": "3.4.1", "classnames": "^2.2.6", "core-js": "^2.5.7", + "d3": "^5.11.0", "downloadjs": "^1.4.7", "fetch": "^1.1.0", "file-saver": "^2.0.2", diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index e2b16c91b..f772e0652 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -1,7 +1,7 @@ import React, {Component} from "react"; import PillarLabel from "./PillarLabel"; -import PaginatedTable from "../common/PaginatedTable"; import * as PropTypes from "prop-types"; +import ResponsiveVennDiagram from "./VennDiagram"; const columns = [ { @@ -20,13 +20,8 @@ const columns = [ class PillarOverview extends Component { render() { - const data = this.props.grades.map((grade) => { - const newGrade = JSON.parse(JSON.stringify(grade)); - newGrade.pillar = {name: grade.pillar, status: this.props.pillarsToStatuses[grade.pillar]}; - return newGrade; - }); return (
    - +
    ); } } @@ -35,5 +30,4 @@ export default PillarOverview; PillarOverview.propTypes = { grades: PropTypes.array, - status: PropTypes.string, }; From bd97c965f1ee6846d9c13d99deba0fde0e1f8975 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 22 Aug 2019 14:39:40 +0300 Subject: [PATCH 076/186] Fixed another state bug in ZT report using deep copy --- .../components/report-components/zerotrust/FindingsTable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 18160846a..9d706b3f2 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -32,8 +32,8 @@ const columns = [ class FindingsTable extends Component { render() { const data = this.props.findings.map((finding) => { - const newFinding = finding; - newFinding.pillars = finding.pillars.map((pillar) => { + const newFinding = JSON.parse(JSON.stringify(finding)); + newFinding.pillars = newFinding.pillars.map((pillar) => { return {name: pillar, status: this.props.pillarsToStatuses[pillar]} }); return newFinding; From bf417ab01dfe17f6f4ac8239a83f384aeafcde6e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 22 Aug 2019 14:40:05 +0300 Subject: [PATCH 077/186] Added machine exploited ZT test --- monkey/common/data/zero_trust_consts.py | 2 +- .../services/telemetry/processing/exploit.py | 32 +++++++------ .../zero_trust_tests/machine_exploited.py | 46 +++++++++++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index a92e386fe..99b4f2a2c 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -91,7 +91,7 @@ TESTS_MAP = { TEST_EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_CONCLUSIVE: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.", - STATUS_INCONCLUSIVE: "Monkey tried exploiting endpoints. Check IDS/IPS logs to see activity recognized." + STATUS_POSITIVE: "Monkey didn't manage to exploit an endpoint." }, DIRECTIVE_KEY: DIRECTIVE_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 98ca76248..7464722f9 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -7,26 +7,18 @@ from monkey_island.cc.encryptor import encryptor from monkey_island.cc.services.edge import EdgeService from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry +from monkey_island.cc.services.telemetry.zero_trust_tests.machine_exploited import test_machine_exploited def process_exploit_telemetry(telemetry_json): edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json) encrypt_exploit_creds(telemetry_json) - telemetry_json['data']['info']['started'] = dateutil.parser.parse(telemetry_json['data']['info']['started']) - telemetry_json['data']['info']['finished'] = dateutil.parser.parse(telemetry_json['data']['info']['finished']) + update_edge_info_with_new_exploit(edge, telemetry_json) + update_node_credentials_from_successful_attempts(edge, telemetry_json) + test_machine_exploited(telemetry_json) - new_exploit = copy.deepcopy(telemetry_json['data']) - - new_exploit.pop('machine') - new_exploit['timestamp'] = telemetry_json['timestamp'] - - mongo.db.edge.update( - {'_id': edge['_id']}, - {'$push': {'exploits': new_exploit}} - ) - if new_exploit['result']: - EdgeService.set_edge_exploited(edge) +def update_node_credentials_from_successful_attempts(edge, telemetry_json): for attempt in telemetry_json['data']['attempts']: if attempt['result']: found_creds = {'user': attempt['user']} @@ -36,6 +28,20 @@ def process_exploit_telemetry(telemetry_json): NodeService.add_credentials_to_node(edge['to'], found_creds) +def update_edge_info_with_new_exploit(edge, telemetry_json): + telemetry_json['data']['info']['started'] = dateutil.parser.parse(telemetry_json['data']['info']['started']) + telemetry_json['data']['info']['finished'] = dateutil.parser.parse(telemetry_json['data']['info']['finished']) + new_exploit = copy.deepcopy(telemetry_json['data']) + new_exploit.pop('machine') + new_exploit['timestamp'] = telemetry_json['timestamp'] + mongo.db.edge.update( + {'_id': edge['_id']}, + {'$push': {'exploits': new_exploit}} + ) + if new_exploit['result']: + EdgeService.set_edge_exploited(edge) + + def encrypt_exploit_creds(telemetry_json): attempts = telemetry_json['data']['attempts'] for i in range(len(attempts)): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py new file mode 100644 index 000000000..3a5f78bcb --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -0,0 +1,46 @@ +from common.data.zero_trust_consts import * +from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.models.zero_trust.finding import Finding + + +def test_machine_exploited(telemetry_json): + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + events = [ + Event.create_event( + title="Exploit attempt", + message="Monkey on {} attempted to exploit {} using {}.".format( + current_monkey.hostname, + telemetry_json['data']['machine']['ip_addr'], + telemetry_json['data']['exploiter']), + event_type=EVENT_TYPE_MONKEY_NETWORK, + timestamp=telemetry_json['timestamp'] + ) + ] + + status = STATUS_POSITIVE + + if telemetry_json['data']['result']: + events.append( + Event.create_event( + title="Exploit success!", + message="Monkey on {} successfully exploited {} using {}.".format( + current_monkey.hostname, + telemetry_json['data']['machine']['ip_addr'], + telemetry_json['data']['exploiter']), + event_type=EVENT_TYPE_MONKEY_NETWORK, + timestamp=telemetry_json['timestamp']) + ) + status = STATUS_CONCLUSIVE + + Finding.save_finding( + test=TEST_MACHINE_EXPLOITED, + status=status, + events=events + ) + + Finding.save_finding( + test=TEST_MALICIOUS_ACTIVITY_TIMELINE, + status=STATUS_INCONCLUSIVE, + events=events + ) From 20e282f5fb998a7495d63ad5d9f8623df966c8f4 Mon Sep 17 00:00:00 2001 From: vkuchinov Date: Thu, 22 Aug 2019 14:50:07 +0300 Subject: [PATCH 078/186] Update index.js [x] Z-Indices sorting on hover [x] Resize (viewBox solution] [x] Rule correction, have excluded 'Unexecuted' parameter from the sum for Rule #1 Still on my today's TODO list: [-] Still looking for an elegant solution to scrolling glitch. Yes, the easiest way is to hide tooltip on scrolling, but that's not cool [-] Need some coding refining --- .../zerotrust/VennDiagram/index.js | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js index b221d4159..6e61261e6 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js @@ -134,7 +134,7 @@ UPDATED [20.08.2019] */ -const VENN_MIN_WIDTH = 256; +const VENN_MIN_WIDTH = '300px'; class ResponsiveVennDiagram extends React.Component { @@ -149,8 +149,8 @@ class ResponsiveVennDiagram extends React.Component { return ( -
    - +
    +
    ) } @@ -173,17 +173,19 @@ class VennDiagram extends React.Component{ this.state = { tooltip: { top: 0, left: 0, display: 'none', html: '' } } + this.width = this.height = 512, this.zOrder = []; + this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C']; this.prefix = 'vennDiagram'; this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed']; - this.fontStyles = [{size: Math.max(9, this.props.width / 32), color: 'white'}, {size: Math.max(6, this.props.width / 52), color: 'black'}]; - this.offset = this.props.width / 16; + this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, {size: Math.max(6, this.width / 52), color: 'black'}]; + this.offset = this.width / 16; - this.thirdWidth = this.props.width / 3; - this.sixthWidth = this.props.width / 6; - this.width2By7 = 2 * this.props.width / 7 - this.width1By11 = this.props.width / 11; - this.width1By28 = this.props.width / 28; + this.thirdWidth = this.width / 3; + this.sixthWidth = this.width / 6; + this.width2By7 = 2 * this.width / 7 + this.width1By11 = this.width / 11; + this.width1By28 = this.width / 28; this.toggle = false; @@ -207,8 +209,10 @@ class VennDiagram extends React.Component{ } - _onMouseMove(e) { + _onMouseMove(e) { + let self = this; + if(!this.toggle){ let hidden = 'none'; @@ -218,17 +222,22 @@ class VennDiagram extends React.Component{ e.stopPropagation(); document.querySelectorAll('circle, path').forEach((d_, i_) => { d_.setAttribute('opacity', 0.8)}); - + if(e.target.id.includes('Node')) { html = e.target.dataset.tooltip; this.divElement.style.cursor = 'pointer'; - hidden = 'block'; e.target.setAttribute('opacity', 1.0); + hidden = 'block'; e.target.setAttribute('opacity', 0.95); bcolor = e.target.getAttribute('fill'); + //set highest z-index + e.target.parentNode.parentNode.appendChild(e.target.parentNode); }else{ this.divElement.style.cursor = 'default'; + + //return z indices to default + Object.keys(this.layout).forEach(function(d_, i_){ document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); }) } @@ -237,6 +246,13 @@ class VennDiagram extends React.Component{ } } + _onMouseLeave(e){ + + let hidden = 'none'; + + this.setState({target: null, tooltip: { target: null, bcolor: 'none', top: 0, left: 0, display: hidden, html: '' } }); + + } _onClick(e) { if(this.state.tooltip.target === e.target) { this.toggle = true; } else { this.toggle = false; } @@ -263,7 +279,7 @@ class VennDiagram extends React.Component{ this.props.pillarsGrades.forEach((d_, i_) => { - let params = omit('pillar', d_); + let params = omit('Unexpected', omit('pillar', d_)); let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_]||0), 0); let key = TypographicUtilities.removeAmpersand(d_.pillar); let html = self.buildTooltipHtmlContent(d_); @@ -302,8 +318,9 @@ class VennDiagram extends React.Component{ if(this.state.data === undefined) { return null; } else { - let { width, height } = this.props; - let translate = 'translate(' + width /2 + ',' + height/2 + ')'; + //equivalent to center translate (width/2, height/2) + let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height; + let translate = 'translate(' + this.width /2 + ',' + this.height/2 + ')'; let nodes = Object.values(this.layout).map((d_, i_) =>{ @@ -343,11 +360,9 @@ class VennDiagram extends React.Component{ return ( -
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)}> - - - {nodes} - +
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onMouseLeave={this._onMouseLeave.bind(this)} onClick={this._onClick.bind(this)}> + + {nodes}
    @@ -362,8 +377,6 @@ class VennDiagram extends React.Component{ VennDiagram.propTypes = { - pillarsGrades: PropTypes.array, - width: PropTypes.number, - height: PropTypes.number + pillarsGrades: PropTypes.array } \ No newline at end of file From 3fef55eefaf1b2fc6414a33b1db31257afee8cf2 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 22 Aug 2019 15:00:50 +0300 Subject: [PATCH 079/186] Now periodically updating the report. --- .../cc/ui/src/components/pages/ZeroTrustReportPage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 2fe43c42e..989af82a8 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -25,7 +25,8 @@ class ZeroTrustReportPageComponent extends AuthComponent { } componentDidMount() { - this.updateMonkeysRunning().then(res => this.getZeroTrustReportFromServer(res)); + this.updatePageState(); + setInterval(this.updatePageState, 8000) } updateMonkeysRunning = () => { @@ -37,6 +38,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { }); }; + updatePageState = () => { + this.updateMonkeysRunning().then(res => this.getZeroTrustReportFromServer(res)); + }; + render() { let content; if (this.state.runStarted) { From af8c7dc29fb1702a4a6c1b39e7ee223a8099fc1d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 22 Aug 2019 16:18:37 +0300 Subject: [PATCH 080/186] Added elasticsearch test --- .../zero_trust_tests/data_endpoints.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index 119871420..c7b0f5219 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -12,6 +12,7 @@ def test_open_data_endpoints(telemetry_json): services = telemetry_json["data"]["machine"]["services"] current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) found_http_server_status = STATUS_POSITIVE + found_elastic_search_server = STATUS_POSITIVE events = [ Event.create_event( @@ -41,6 +42,17 @@ def test_open_data_endpoints(telemetry_json): ), event_type=EVENT_TYPE_ISLAND )) + if service_name in 'elastic-search-9200': + found_elastic_search_server = STATUS_CONCLUSIVE + events.append(Event.create_event( + title="Scan telemetry analysis", + message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( + service_data["display_name"], + telemetry_json["data"]["machine"]["ip_addr"], + json.dumps(service_data) + ), + event_type=EVENT_TYPE_ISLAND + )) Finding.save_finding( test=TEST_DATA_ENDPOINT_HTTP, @@ -48,6 +60,12 @@ def test_open_data_endpoints(telemetry_json): events=events ) + Finding.save_finding( + test=TEST_DATA_ENDPOINT_ELASTIC, + status=found_elastic_search_server, + events=events + ) + Finding.save_finding( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, status=STATUS_INCONCLUSIVE, From 244be146bb3ceb6f733012a0ad4ab7dd936aa15b Mon Sep 17 00:00:00 2001 From: vkuchinov Date: Fri, 23 Aug 2019 02:41:00 +0300 Subject: [PATCH 081/186] Update VennDiagram/index.js [x] Scrolling issue. Since only window/document have 'scroll' event, the only option to fix tooltip issue on scrolling is just simply hide it. That works well if after scrolling the mouse pointer doesn't stay on any venn nodes. Otherwise, you have to move mouse for the tooltip. Theoretically, I could store hovered node coordinates and use them in _onScroll function to check if mouse is still on top one Venn nodes find window.pageYOffset difference. --- .../zerotrust/VennDiagram/index.js | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js index 6e61261e6..de28fc012 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js @@ -52,7 +52,7 @@ UPDATED [20.08.2019] useLayoutEffect(() => { checkForDimensionsUpdate(); }, []); - window.addEventListener("resize", () => { clearInterval(movement_timer); movement_timer = setTimeout(checkForDimensionsUpdate, RESET_TIMEOUT); }); + window.addEventListener('resize', () => { clearInterval(movement_timer); movement_timer = setTimeout(checkForDimensionsUpdate, RESET_TIMEOUT); }); return ( @@ -200,12 +200,15 @@ class VennDiagram extends React.Component{ AutomationAndOrchestration: { inner: this.thirdWidth - this.width1By28 * 2, outer: this.thirdWidth - this.width1By28 } }; + + this._onScroll = this._onScroll.bind(this); } componentDidMount() { this.parseData(); + window.addEventListener('scroll', this._onScroll); } @@ -218,9 +221,7 @@ class VennDiagram extends React.Component{ let hidden = 'none'; let html = ''; let bcolor = '#DEDEDE'; - - e.stopPropagation(); - + document.querySelectorAll('circle, path').forEach((d_, i_) => { d_.setAttribute('opacity', 0.8)}); if(e.target.id.includes('Node')) { @@ -229,6 +230,7 @@ class VennDiagram extends React.Component{ this.divElement.style.cursor = 'pointer'; hidden = 'block'; e.target.setAttribute('opacity', 0.95); bcolor = e.target.getAttribute('fill'); + //set highest z-index e.target.parentNode.parentNode.appendChild(e.target.parentNode); @@ -245,14 +247,13 @@ class VennDiagram extends React.Component{ } } + _onScroll(e){ + + this.divElement.style.cursor = 'default'; + this.setState({target: e, tooltip: { target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: '' } }); - _onMouseLeave(e){ - - let hidden = 'none'; - - this.setState({target: null, tooltip: { target: null, bcolor: 'none', top: 0, left: 0, display: hidden, html: '' } }); - } + _onClick(e) { if(this.state.tooltip.target === e.target) { this.toggle = true; } else { this.toggle = false; } @@ -360,7 +361,7 @@ class VennDiagram extends React.Component{ return ( -
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onMouseLeave={this._onMouseLeave.bind(this)} onClick={this._onClick.bind(this)}> +
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)} > {nodes} From fb01bface6b6341a2c7a327cca9dcd4634a149d6 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 25 Aug 2019 10:30:17 +0300 Subject: [PATCH 082/186] Extracted config utility function to new package - in future all config should move here --- monkey/monkey_island/cc/services/configuration/__init__.py | 0 monkey/monkey_island/cc/services/configuration/utils.py | 5 +++++ monkey/monkey_island/cc/services/reporting/report.py | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/services/configuration/__init__.py create mode 100644 monkey/monkey_island/cc/services/configuration/utils.py diff --git a/monkey/monkey_island/cc/services/configuration/__init__.py b/monkey/monkey_island/cc/services/configuration/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/configuration/utils.py b/monkey/monkey_island/cc/services/configuration/utils.py new file mode 100644 index 000000000..34d6a9bb5 --- /dev/null +++ b/monkey/monkey_island/cc/services/configuration/utils.py @@ -0,0 +1,5 @@ +from monkey_island.cc.services.config import ConfigService + + +def get_config_network_segments_as_subnet_groups(): + return [ConfigService.get_config_value(['basic_network', 'network_analysis', 'inaccessible_subnets'])] diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 1d7ac162d..af3d2673b 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -13,6 +13,7 @@ from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey from monkey_island.cc.report_exporter_manager import ReportExporterManager from monkey_island.cc.services.config import ConfigService +from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups from monkey_island.cc.services.edge import EdgeService from monkey_island.cc.services.node import NodeService from monkey_island.cc.utils import local_ip_addresses, get_subnets @@ -552,7 +553,7 @@ class ReportService: cross_segment_issues = [] # For now the feature is limited to 1 group. - subnet_groups = [ConfigService.get_config_value(['basic_network', 'network_analysis', 'inaccessible_subnets'])] + subnet_groups = get_config_network_segments_as_subnet_groups() for subnet_group in subnet_groups: cross_segment_issues += ReportService.get_cross_segment_issues_per_subnet_group(scans, subnet_group) From 6ec4e613cf32e8200c94d3b67b1e50d02fe3601d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 25 Aug 2019 11:31:31 +0300 Subject: [PATCH 083/186] Extracted segmentation utils function --- monkey/common/network/segmentation_utils.py | 19 +++++++++++++++ .../common/network/segmentation_utils_test.py | 19 +++++++++++++++ .../cc/services/reporting/report.py | 24 ++++--------------- 3 files changed, 42 insertions(+), 20 deletions(-) create mode 100644 monkey/common/network/segmentation_utils.py create mode 100644 monkey/common/network/segmentation_utils_test.py diff --git a/monkey/common/network/segmentation_utils.py b/monkey/common/network/segmentation_utils.py new file mode 100644 index 000000000..68122f398 --- /dev/null +++ b/monkey/common/network/segmentation_utils.py @@ -0,0 +1,19 @@ +from common.network.network_range import NetworkRange + + +def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet): + # type: (List[str], NetworkRange, NetworkRange) -> Union[str, None] + """ + Finds an IP address in ip_addresses which is in source_subnet but not in target_subnet. + :param ip_addresses: List of IP addresses to test. + :param source_subnet: Subnet to want an IP to not be in. + :param target_subnet: Subnet we want an IP to be in. + :return: The cross segment IP if in source but not in target, else None. + """ + for ip_address in ip_addresses: + if target_subnet.is_in_range(ip_address): + return None + for ip_address in ip_addresses: + if source_subnet.is_in_range(ip_address): + return ip_address + return None diff --git a/monkey/common/network/segmentation_utils_test.py b/monkey/common/network/segmentation_utils_test.py new file mode 100644 index 000000000..7ef3e4450 --- /dev/null +++ b/monkey/common/network/segmentation_utils_test.py @@ -0,0 +1,19 @@ +from common.network.network_range import * +from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +class TestSegmentationUtils(IslandTestCase): + def test_get_ip_in_src_and_not_in_dst(self): + self.fail_if_not_testing_env() + source = CidrRange("1.1.1.0/24") + target = CidrRange("2.2.2.0/24") + self.assertIsNone(get_ip_in_src_and_not_in_dst( + [text_type("2.2.2.2")], source, target + )) + self.assertIsNone(get_ip_in_src_and_not_in_dst( + [text_type("3.3.3.3"), text_type("4.4.4.4")], source, target + )) + self.assertIsNotNone(get_ip_in_src_and_not_in_dst( + [text_type("8.8.8.8"), text_type("1.1.1.1")], source, target + )) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index af3d2673b..fdba3b549 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -9,6 +9,7 @@ from enum import Enum from six import text_type +from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey from monkey_island.cc.report_exporter_manager import ReportExporterManager @@ -424,23 +425,6 @@ class ReportService: return issues - @staticmethod - def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet): - """ - Finds an IP address in ip_addresses which is in source_subnet but not in target_subnet. - :param ip_addresses: List of IP addresses to test. - :param source_subnet: Subnet to want an IP to not be in. - :param target_subnet: Subnet we want an IP to be in. - :return: - """ - for ip_address in ip_addresses: - if target_subnet.is_in_range(ip_address): - return None - for ip_address in ip_addresses: - if source_subnet.is_in_range(ip_address): - return ip_address - return None - @staticmethod def get_cross_segment_issues_of_single_machine(source_subnet_range, target_subnet_range): """ @@ -503,9 +487,9 @@ class ReportService: target_ip = scan['data']['machine']['ip_addr'] if target_subnet_range.is_in_range(text_type(target_ip)): monkey = NodeService.get_monkey_by_guid(scan['monkey_guid']) - cross_segment_ip = ReportService.get_ip_in_src_and_not_in_dst(monkey['ip_addresses'], - source_subnet_range, - target_subnet_range) + cross_segment_ip = get_ip_in_src_and_not_in_dst(monkey['ip_addresses'], + source_subnet_range, + target_subnet_range) if cross_segment_ip is not None: cross_segment_issues.append( From 5c4797108ea2cb36540eacad651fdba98271233b Mon Sep 17 00:00:00 2001 From: vkuchinov Date: Sun, 25 Aug 2019 13:05:56 +0300 Subject: [PATCH 084/186] Rules update The rules are now set at this.rules array. While some of them have two conditions, i.e. Rule #2 shoud check if Conclusive is 0 and Inconclusive > 0, all rules has its own function (formula), which returns true or false. Eventually, I could shorten variable naming, for example, d_['Conclusive'] to something more prompt, but keeping this helps understand formulas even without referencing to upper comments. --- .../zerotrust/VennDiagram/index.js | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js index de28fc012..6a0b41356 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js @@ -201,6 +201,34 @@ class VennDiagram extends React.Component{ }; + /* + + RULE #1: All scores have to be equal 0, except Unexecuted [U] which could be also a negative integer + sum(C, I, P, U) has to be <=0 + + RULE #2: Conclusive [C] has to be > 0, + sum(C) > 0 + + RULE #3: Inconclusive [I] has to be > 0 while Conclusive has to be 0, + sum(C, I) > 0 and C * I = 0, while C has to be 0 + + RULE #4: Positive [P] and Unexecuted have to be positive + sum(P, U) >= 2 and P * U = positive integer, while + if the P is bigger by 2 then negative U, first conditional + would be true. + + */ + + this.rules = [ + + { id: 'Rule #1', f: function(d_){ return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] + d_['Unexecuted'] <= 0; } }, + { id: 'Rule #2', f: function(d_){ return d_['Conclusive'] > 0; } }, + { id: 'Rule #3', f: function(d_){ return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0; } }, + { id: 'Rule #4', f: function(d_){ return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0; } } + + ]; + + this._onScroll = this._onScroll.bind(this); } @@ -213,7 +241,7 @@ class VennDiagram extends React.Component{ } _onMouseMove(e) { - + let self = this; if(!this.toggle){ @@ -247,11 +275,12 @@ class VennDiagram extends React.Component{ } } + _onScroll(e){ - this.divElement.style.cursor = 'default'; - this.setState({target: e, tooltip: { target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: '' } }); - + this.divElement.style.cursor = 'default'; + this.setState({target: null, tooltip: { target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: '' } }); + } _onClick(e) { @@ -277,19 +306,17 @@ class VennDiagram extends React.Component{ let self = this; let data = []; const omit = (prop, { [prop]: _, ...rest }) => rest; - + this.props.pillarsGrades.forEach((d_, i_) => { - let params = omit('Unexpected', omit('pillar', d_)); + let params = omit('pillar', d_); let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_]||0), 0); let key = TypographicUtilities.removeAmpersand(d_.pillar); let html = self.buildTooltipHtmlContent(d_); - let rule = 3; + let rule = null; - if(sum === 0){ rule = 0 } - else if(d_['Conclusive'] > 0){ rule = 1 } - else if(d_['Conclusive'] === 0 && d_['Inconclusive'] > 0) { rule = 2 } - + for(let j = 0; j < self.rules.length; j++){ if(self.rules[j].f(d_)) { rule = j; break; }} + self.setLayoutElement(rule, key, html, d_); data.push(this.layout[key]) @@ -304,6 +331,8 @@ class VennDiagram extends React.Component{ setLayoutElement(rule_, key_, html_, d_){ + if(rule_ == null) { throw Error('The node scores are invalid'); } + if(key_ === 'Data'){ this.layout[key_].fontStyle = this.fontStyles[0]; } else {this.layout[key_].fontStyle = this.fontStyles[1]; } @@ -361,7 +390,7 @@ class VennDiagram extends React.Component{ return ( -
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)} > +
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)} > {nodes} From b9cb6551146f6981f77662295bac026532b70bf7 Mon Sep 17 00:00:00 2001 From: vkuchinov Date: Sun, 25 Aug 2019 17:32:21 +0300 Subject: [PATCH 085/186] Fixes Fixes for issues posted by Shay at VennDiagram component #412 [x] ResponsiveVennDiagram.js, VennDiagram.js [x] VennDiagram.css (rename) [x] ArcNode, CicularNode, Tooltip as .js and other minor issues --- .../components/pages/ZeroTrustReportPage.js | 203 ++++++++++-------- .../zerotrust/VennDiagram/ArcNode/index.js | 46 ---- .../zerotrust/venn-components/.DS_Store | Bin 0 -> 8196 bytes .../zerotrust/venn-components/ArcNode.js | 44 ++++ .../CircularNode.js} | 27 ++- .../venn-components/ResponsiveVennDiagram.js | 36 ++++ .../index.js => venn-components/Tooltip.js} | 0 .../utility.js => venn-components/Utility.js} | 0 .../VennDiagram.css} | 0 .../VennDiagram.js} | 184 +--------------- 10 files changed, 210 insertions(+), 330 deletions(-) mode change 100644 => 100755 monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/.DS_Store create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{VennDiagram/CircularNode/index.js => venn-components/CircularNode.js} (61%) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{VennDiagram/Tooltip/index.js => venn-components/Tooltip.js} (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{VennDiagram/utility.js => venn-components/Utility.js} (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{VennDiagram/index.css => venn-components/VennDiagram.css} (100%) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{VennDiagram/index.js => venn-components/VennDiagram.js} (57%) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js old mode 100644 new mode 100755 index 2fe43c42e..e80af2366 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,17 +1,64 @@ -import React, {Fragment} from 'react'; -import {Col, Grid, Row} from 'react-bootstrap'; +import React from 'react'; +import {Button, Col} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; -import PillarsOverview from "../report-components/zerotrust/PillarOverview"; +import PillarGrades from "../report-components/zerotrust/PillarGrades"; +import PillarLabel from "../report-components/zerotrust/PillarLabel"; +import ResponsiveVennDiagram from "../report-components/zerotrust/venn-components/ResponsiveVennDiagram"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; -import SinglePillarDirectivesStatus from "../report-components/zerotrust/SinglePillarDirectivesStatus"; -import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; -import ReportLoader from "../report-components/common/ReportLoader"; -import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; -import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance"; -import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary"; -import PrintReportButton from "../report-components/common/PrintReportButton"; -import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; +import {SinglePillarRecommendationsStatus} from "../report-components/zerotrust/SinglePillarRecommendationsStatus"; + +let mockup = [ + { + "Conclusive": 4, + "Inconclusive": 0, + "Positive": 1, + "Unexecuted": 2, + "pillar": "Data" + }, + { + "Conclusive": 0, + "Inconclusive": 5, + "Positive": 0, + "Unexecuted": 2, + "pillar": "People" + }, + { + "Conclusive": 0, + "Inconclusive": 0, + "Positive": 6, + "Unexecuted": 3, + "pillar": "Networks" + }, + { + "Conclusive": 2, + "Inconclusive": 0, + "Positive": 1, + "Unexecuted": 1, + "pillar": "Devices" + }, + { + "Conclusive": 0, + "Inconclusive": 0, + "Positive": 0, + "Unexecuted": 0, + "pillar": "Workloads" + }, + { + "Conclusive": 0, + "Inconclusive": 2, + "Positive": 0, + "Unexecuted": 0, + "pillar": "Visibility & Analytics" + }, + { + "Conclusive": 0, + "Inconclusive": 0, + "Positive": 0, + "Unexecuted": 0, + "pillar": "Automation & Orchestration" + } +]; class ZeroTrustReportPageComponent extends AuthComponent { @@ -20,30 +67,16 @@ class ZeroTrustReportPageComponent extends AuthComponent { this.state = { allMonkeysAreDead: false, - runStarted: true + runStarted: false }; } - componentDidMount() { - this.updateMonkeysRunning().then(res => this.getZeroTrustReportFromServer(res)); - } - - updateMonkeysRunning = () => { - return this.authFetch('/api') - .then(res => res.json()) - .then(res => { - this.setState(extractExecutionStatusFromServerResponse(res)); - return res; - }); - }; - render() { - let content; - if (this.state.runStarted) { - content = this.generateReportContent(); - } else { - content = ; - } + let res; + // Todo move to componentDidMount + this.getZeroTrustReportFromServer(res); + + const content = this.generateReportContent(); return ( @@ -59,75 +92,63 @@ class ZeroTrustReportPageComponent extends AuthComponent { let content; if (this.stillLoadingDataFromServer()) { - content = ; + content = "Still empty"; } else { - content =
    - {this.generateOverviewSection()} - {this.generateDirectivesSection()} - {this.generateFindingsSection()} + const pillarsSection =
    +

    Pillars Overview

    + +
    ; + + const recommendationsSection =

    Recommendations Status

    + { + this.state.recommendations.map((recommendation) => + + ) + } +
    ; + + const findingSection =

    Findings

    +
    ; + + content =
    + {pillarsSection} + {recommendationsSection} + {findingSection}
    ; } return ( - -
    - {print();}} /> +
    +
    +

    {content} +
    +
    {JSON.stringify(this.state.pillars, undefined, 2)}
    +
    + +
    {JSON.stringify(this.state.recommendations, undefined, 2)}
    +
    +
    {JSON.stringify(this.state.findings, undefined, 2)}
    -
    - {print();}} /> -
    - +
    ) } - generateFindingsSection() { - return (
    -

    Findings

    - -
    ); - } - - generateDirectivesSection() { - return (
    -

    Directives

    - { - Object.keys(this.state.directives).map((pillar) => - - ) - } -
    ); - } - - generateOverviewSection() { - return (
    -

    Overview

    - - - - - - - - - - - - -
    ); - } - stillLoadingDataFromServer() { - return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.directives === "undefined"; + return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.recommendations === "undefined"; + } + + print() { + alert("unimplemented"); } getZeroTrustReportFromServer() { @@ -139,11 +160,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { findings: res }); }); - this.authFetch('/api/report/zero_trust/directives') + this.authFetch('/api/report/zero_trust/recommendations') .then(res => res.json()) .then(res => { this.setState({ - directives: res + recommendations: res }); }); this.authFetch('/api/report/zero_trust/pillars') @@ -154,14 +175,6 @@ class ZeroTrustReportPageComponent extends AuthComponent { }); }); } - - anyIssuesFound() { - const severe = function(finding) { - return (finding.status === "Conclusive" || finding.status === "Inconclusive"); - }; - - return this.state.findings.some(severe); - } } export default ZeroTrustReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js deleted file mode 100644 index b9861a55e..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/ArcNode/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types'; -import * as d3 from 'd3' - -class ArcNode extends React.Component{ - - render() { - - let {prefix, ref, index, data} = this.props;; - - let arc = d3.arc().innerRadius(data.inner).outerRadius(data.outer).startAngle(0).endAngle(Math.PI * 2.0); - - return ( - - - - - - {data.label} - - - - - ) - - } - -} - -ArcNode.propTypes = { - - prefix: PropTypes.string, - index: PropTypes.number, - data: PropTypes.object - -} - -export default ArcNode; \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/.DS_Store b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..344923cf9c56b32713b678e0b337a23ff212fa20 GIT binary patch literal 8196 zcmeHMT}&KR6h3F6<<5+h0V@S;%NlBhbc-x#!M3E!0<~0{R(JU;2;F6-Fgnc8o!O<7 zVu(#NCdL{yG4bcc#wUsPMHB6dFUH2G@rR@uV|>sTAB{#6pFH>8ffOhY`k*$QOYS{) z?m1`fIp6(e?%n|aFqt>v0963MsB)=m)ZCzOJ+JFZBp4|siR2I9g9X#s8E-mkud@yZ zLIgqtLIgqtLIgqt{tpPyp3Mu}VBeS8uniFi5x6B0;O~bhRW1{OT#!<{I;asx0FwL& zAPV*A93Xt6flLH)K}z9D_mrmx3|uidVxV*1n4SEh z!W=LmW!Q!Ygb1uhfImJZkOr6j?Ca0(Zk9BkWT$P*^P8HkVWg;d)8-P!RHl}ykESNw znUtUL^IkHO8|GIhGX7*X=%-!RUg>AD{gyd1rf=KP%>_ zjcV$ML9rZj4L*yZzsnS%*%j5aVXo=SnbaDLiQ)PNO-*||5jxaXFwH=0G8g+?!x#|K1r7qDFkbproG}1t3@ZF~nWbNm{YWPFij z(jE)q4FrBqos!iLCy|yut_jkP-jKB2ht(th5oy0Il}z1wM`i7YJqYezjF0BJ0IcyI(!Xh_Cu)sXe52DEv{=L0<3H6ovf* z4qk#qxB?%;r^LN$@B{n=zrdduL53BAciV9X)?z(2;2w-&Ber52G4KGk<6(Rdd+;c+ zun+rj5QlJ>SZE3sK87CpSil)#JidKsh* + + + + {data.label} + + + + + ) + + } + +} + +ArcNode.propTypes = { + + data: PropTypes.object + +} + +export default ArcNode; \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/CircularNode/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js similarity index 61% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/CircularNode/index.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js index a8caf5bb0..8d8df10bf 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/CircularNode/index.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js @@ -5,7 +5,7 @@ class CircularNode extends React.Component{ render() { - let {prefix, ref, index, data} = this.props; + let {prefix, index, data} = this.props; let tspans = data.label.split("|").map((d_, i_) =>{ @@ -21,23 +21,23 @@ class CircularNode extends React.Component{ }) let translate = 'translate(' + data.cx + ',' + data.cy + ')'; - + return ( - - - {tspans} - + /> + + {tspans} + ) @@ -48,7 +48,6 @@ class CircularNode extends React.Component{ CircularNode.propTypes = { - prefix: PropTypes.string, index: PropTypes.number, data: PropTypes.object diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js new file mode 100644 index 000000000..d20abf94a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js @@ -0,0 +1,36 @@ +import React from 'react' +import PropTypes from 'prop-types' +import Dimensions from 'react-dimensions' +import VennDiagram from './VennDiagram' + +const VENN_MIN_WIDTH = '300px'; + +class ResponsiveVennDiagram extends React.Component { + + constructor(props) { super(props); } + + render() { + + const {pillarsGrades} = this.props; + let childrenWidth = this.props.containerWidth, childrenHeight = this.props.containerHeight; + + if(childrenHeight === 0 || isNaN(childrenHeight)){ childrenHeight = childrenWidth; } + else{ childrenWidth = Math.min(childrenWidth, childrenHeight) } + + return ( + +
    + +
    ) + + } + +} + +ResponsiveVennDiagram.propTypes = { + + pillarsGrades: PropTypes.array + +} + +export default Dimensions()(ResponsiveVennDiagram); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/Tooltip/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/Tooltip/index.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/utility.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/utility.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.css b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.css rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js similarity index 57% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 6a0b41356..0f154cdfe 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/VennDiagram/index.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -1,169 +1,10 @@ import React from 'react' -import PropTypes from 'prop-types'; -import Dimensions from 'react-dimensions' +import PropTypes from 'prop-types' import Tooltip from './Tooltip' import CircularNode from './CircularNode' import ArcNode from './ArcNode' -import { TypographicUtilities } from './utility.js' -import './index.css' - -/* - -TODO LIST - -UPDATED [21.08.2019] - -[-] SVG > ViewBox 0 0 512 512, so it would be responsive and scalable -[-] Add resize listener to ResponsiveVennDiagram wrapper -[-] I have noticed that you have PillarGrades at ZeroTrustReportPage, so how I can fetch the data out of it? - -UPDATED [20.08.2019] - -[x] I've seen that lots of D3 responsive examples are using 'wrapper' around the main component to - get parent container dimensions. And lister for resize. - - So, here it's Responsive(VennDiagram) - - If it doesn't work, I have another alternative: - - import React, { useRef, useEffect, useState, useLayoutEffect } from 'react' - - const ResponsiveVennDiagram = props => { - - const minWidth = 256; - const targetRef = useRef(); - const [dimensions, setDimensions] = useState({ width: 0, heigth: 0 }); - let movement_timer = null; - const RESET_TIMEOUT = 100; - - const checkForDimensionsUpdate = () => { - - if (targetRef.current) { - - setDimensions({ - - width: Math.min(targetRef.current.offsetWidth, targetRef.current.offsetHeight), - height: targetRef.current.offsetHeight - - }); - - } - }; - - useLayoutEffect(() => { checkForDimensionsUpdate(); }, []); - - window.addEventListener('resize', () => { clearInterval(movement_timer); movement_timer = setTimeout(checkForDimensionsUpdate, RESET_TIMEOUT); }); - - return ( - -
    - -
    - - ); - - }; - - ResponsiveVennDiagram.propTypes = { - - pillarsGrades: PropTypes.array - - } - - export default ResponsiveVennDiagram; - - While your diagram laout is squared, the VennDiaagram gets the min(width, height) - -[x] Colours have been updated. - -[x] String prototypes have been moved to '.utilities.js' - -[x] I have use the prefix 'vennDiagram' for all children elements to prevent naming conflicts with other components - DOM objects. - -[x] I have used PropTypes already a year ago on ThreeJS/ReactJS stack project. - -[!] External callback is still on my list, can you make a mockup for external function which have to get pillar variable? - -[!] Z-indices sorting on mouseover - Would be n my 21.08.2019 TODO list. With D3.JS it's an easy task, however would try do - make these z-soring without D3 framework. - -UPDATED [20.08.2019] - -[!] By now, there are three input props for VennDiagram component: - data, width and height. - -[-] Since layout has to be hardcoded, it's driven by this.layout object. - There're many ways of setting circles/arc positions, now I'm using the - stright-forward one. - - Usually, I am put all hardcoded params to external JSON file, i.e config.json. - Let me know if it's a good idea. - -[-] Could rearange z-indecies for nodes on hover, so highlighted node would have highest z-index. -[-] If you want callback on click even, please provide additional info what are you expecting to pass - through this. - -[!] I don't used to make lots of comments in my code, but treying to name everything in a way, - so third person could instantly get its job and concept. - - If it is not enoough just let me know. - -[!] I have tried to avoid using D3 so much, especially its mouse events, cause it creates a bunch - of listeners for every children DOM elements. I have tried to use raw SVG objects. - The ArcNode is the only component where I've to call d3.arc constrictor. - -[!] There are lots of discussion over different blogs an forums that ReactJS and D3.JS have a DOM - issue conflict, [could find lots of articels at medium.org about that, for example, - https://medium.com/@tibotiber/react-d3-js-balancing-performance-developer-experience-4da35f912484]. - - Since the current component has only a few DOM elements, I don't thing we would have any troubles, - but please keep in mind that I could tweak current version with react-faux-dom. - - Actually, by now, I'm using D3 only for math, for arc path calculations. - -[!] Don't mind about code spacings, it's just for me, the final code would be clear out of them. - -[-] On click, an EXTERNAL callback should be called with the pillar name as a parameter. That is to enable us to expand the click functionality in future without editing the internal implementation of the component. - -[-] planned, [x] done, [!] see comments - -@author Vladimir V KUCHINOV -@email helloworld@vkuchinov.co.uk - -*/ - -const VENN_MIN_WIDTH = '300px'; - -class ResponsiveVennDiagram extends React.Component { - - constructor(props) { super(props); } - render() { - - const {options, pillarsGrades} = this.props; - let childrenWidth = this.props.containerWidth, childrenHeight = this.props.containerHeight; - - if(childrenHeight === 0 || childrenHeight === NaN){ childrenHeight = childrenWidth; } - else{ childrenWidth = Math.min(childrenWidth, childrenHeight) } - - return ( - -
    - -
    ) - - } - -} - -ResponsiveVennDiagram.propTypes = { - - pillarsGrades: PropTypes.array - -} - -export default Dimensions()(ResponsiveVennDiagram); +import { TypographicUtilities } from './Utility.js' +import './VennDiagram.css' class VennDiagram extends React.Component{ @@ -292,15 +133,6 @@ class VennDiagram extends React.Component{ } - relativeCoords (e) { - - let bounds = e.target.getBoundingClientRect(); - var x = e.clientX - bounds.left; - var y = e.clientY - bounds.top; - return {x: x, y: y}; - - } - parseData(){ let self = this; @@ -318,7 +150,7 @@ class VennDiagram extends React.Component{ for(let j = 0; j < self.rules.length; j++){ if(self.rules[j].f(d_)) { rule = j; break; }} self.setLayoutElement(rule, key, html, d_); - data.push(this.layout[key]) + data.push(this.layout[key]); }) @@ -361,7 +193,7 @@ class VennDiagram extends React.Component{ Date: Sun, 25 Aug 2019 18:07:49 +0300 Subject: [PATCH 086/186] Added a custom segmentation finding type --- .../cc/models/zero_trust/finding.py | 2 + .../models/zero_trust/segmentation_finding.py | 52 +++++++++++++++++++ .../zero_trust/test_segmentation_finding.py | 52 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py create mode 100644 monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index 1869d6f18..5454ad9e1 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -32,6 +32,8 @@ class Finding(Document): test = StringField(required=True, choices=TESTS) status = StringField(required=True, choices=ORDERED_TEST_STATUSES) events = EmbeddedDocumentListField(document_type=Event) + # http://docs.mongoengine.org/guide/defining-documents.html#document-inheritance + meta = {'allow_inheritance': True} # LOGIC def get_test_explanation(self): diff --git a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py new file mode 100644 index 000000000..428af72cb --- /dev/null +++ b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py @@ -0,0 +1,52 @@ +from mongoengine import StringField + +from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_CONCLUSIVE, STATUS_POSITIVE +from monkey_island.cc.models.zero_trust.finding import Finding + + +def need_to_overwrite_status(saved_status, new_status): + return (saved_status == STATUS_POSITIVE) and (new_status == STATUS_CONCLUSIVE) + + +class SegmentationFinding(Finding): + """ + trying to add conclusive: + If the finding doesn't exist at all: create conclusive + else: + if positive, turn to conclusive + add event + + trying to add positive: + If the finding doesn't exist at all: create positive + else: add event + """ + first_subnet = StringField() + second_subnet = StringField() + + @staticmethod + def create_or_add_to_existing_finding(subnets, status, segmentation_event): + assert len(subnets) == 2 + + # Sort them so A -> B and B -> A segmentation findings will be the same one. + subnets.sort() + + existing_findings = SegmentationFinding.objects(first_subnet=subnets[0], second_subnet=subnets[1]) + + if len(existing_findings) == 0: + # No finding exists - create. + new_finding = SegmentationFinding( + first_subnet=subnets[0], + second_subnet=subnets[1], + test=TEST_SEGMENTATION, + status=status, + events=[segmentation_event] + ) + new_finding.save() + else: + # A finding exists (should be one). Add the event to it. + assert len(existing_findings) == 1 + existing_finding = existing_findings[0] + existing_finding.events.append(segmentation_event) + if need_to_overwrite_status(existing_finding.status, status): + existing_finding.status = status + existing_finding.save() diff --git a/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py new file mode 100644 index 000000000..ad3ff9b97 --- /dev/null +++ b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py @@ -0,0 +1,52 @@ +from common.data.zero_trust_consts import STATUS_CONCLUSIVE, EVENT_TYPE_MONKEY_NETWORK +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.testing.IslandTestCase import IslandTestCase +from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding + + +class TestSegmentationFinding(IslandTestCase): + def test_create_or_add_to_existing_finding(self): + self.fail_if_not_testing_env() + self.clean_finding_db() + + first_segment = "1.1.1.0/24" + second_segment = "2.2.2.0-2.2.2.254" + third_segment = "3.3.3.3" + event = Event.create_event("bla", "bla", EVENT_TYPE_MONKEY_NETWORK) + + SegmentationFinding.create_or_add_to_existing_finding( + subnets=[first_segment, second_segment], + status=STATUS_CONCLUSIVE, + segmentation_event=event + ) + + self.assertEquals(len(SegmentationFinding.objects()), 1) + self.assertEquals(len(SegmentationFinding.objects()[0].events), 1) + + SegmentationFinding.create_or_add_to_existing_finding( + # !!! REVERSE ORDER + subnets=[second_segment, first_segment], + status=STATUS_CONCLUSIVE, + segmentation_event=event + ) + + self.assertEquals(len(SegmentationFinding.objects()), 1) + self.assertEquals(len(SegmentationFinding.objects()[0].events), 2) + + SegmentationFinding.create_or_add_to_existing_finding( + # !!! REVERSE ORDER + subnets=[first_segment, third_segment], + status=STATUS_CONCLUSIVE, + segmentation_event=event + ) + + self.assertEquals(len(SegmentationFinding.objects()), 2) + + SegmentationFinding.create_or_add_to_existing_finding( + # !!! REVERSE ORDER + subnets=[second_segment, third_segment], + status=STATUS_CONCLUSIVE, + segmentation_event=event + ) + + self.assertEquals(len(SegmentationFinding.objects()), 3) From 470806f3bc51e90e0381fcf2d7d31c9015a3618b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 25 Aug 2019 18:08:21 +0300 Subject: [PATCH 087/186] Added segmentation violation test --- monkey/common/network/segmentation_utils.py | 9 ++- .../cc/services/telemetry/processing/scan.py | 2 + .../zero_trust_tests/segmentation.py | 68 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py diff --git a/monkey/common/network/segmentation_utils.py b/monkey/common/network/segmentation_utils.py index 68122f398..97adbd203 100644 --- a/monkey/common/network/segmentation_utils.py +++ b/monkey/common/network/segmentation_utils.py @@ -10,9 +10,12 @@ def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet): :param target_subnet: Subnet we want an IP to be in. :return: The cross segment IP if in source but not in target, else None. """ - for ip_address in ip_addresses: - if target_subnet.is_in_range(ip_address): - return None + if get_ip_if_in_subnet(ip_addresses, target_subnet) is not None: + return None + return get_ip_if_in_subnet(ip_addresses, source_subnet) + + +def get_ip_if_in_subnet(ip_addresses, source_subnet): for ip_address in ip_addresses: if source_subnet.is_in_range(ip_address): return ip_address diff --git a/monkey/monkey_island/cc/services/telemetry/processing/scan.py b/monkey/monkey_island/cc/services/telemetry/processing/scan.py index 3b532ff22..8ae386388 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/scan.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/scan.py @@ -3,11 +3,13 @@ import copy from monkey_island.cc.database import mongo from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry from monkey_island.cc.services.telemetry.zero_trust_tests.data_endpoints import test_open_data_endpoints +from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import test_segmentation_violation def process_scan_telemetry(telemetry_json): update_edges_and_nodes_based_on_scan_telemetry(telemetry_json) test_open_data_endpoints(telemetry_json) + test_segmentation_violation(telemetry_json) def update_edges_and_nodes_based_on_scan_telemetry(telemetry_json): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py new file mode 100644 index 000000000..ca308aafd --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -0,0 +1,68 @@ +import itertools +from six import text_type + +from common.data.zero_trust_consts import STATUS_CONCLUSIVE, EVENT_TYPE_MONKEY_NETWORK +from common.network.network_range import NetworkRange +from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst, get_ip_if_in_subnet +from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding +from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups + +SEGMENTATION_VIOLATION_EVENT_TEXT = \ + "Segmentation violation! Monkey on '{hostname}', with the {source_ip} IP address (in segment {source_seg}) " \ + "managed to communicate cross segment to {target_ip} (in segment {target_seg})." + + +def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet): + if source_subnet == target_subnet: + return False + source_subnet_range = NetworkRange.get_range_obj(source_subnet) + target_subnet_range = NetworkRange.get_range_obj(target_subnet) + + if target_subnet_range.is_in_range(text_type(target_ip)): + cross_segment_ip = get_ip_in_src_and_not_in_dst( + current_monkey.ip_addresses, + source_subnet_range, + target_subnet_range) + + return cross_segment_ip is not None + + +def test_segmentation_violation(telemetry_json): + """ + + :param telemetry_json: A SCAN telemetry sent from a Monkey. + """ + # TODO - lower code duplication between this and report.py. + # TODO - single machine + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + target_ip = telemetry_json['data']['machine']['ip_addr'] + subnet_groups = get_config_network_segments_as_subnet_groups() + for subnet_group in subnet_groups: + subnet_pairs = itertools.product(subnet_group, subnet_group) + for subnet_pair in subnet_pairs: + source_subnet = subnet_pair[0] + target_subnet = subnet_pair[1] + if is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet): + event = get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet) + SegmentationFinding.create_or_add_to_existing_finding( + subnets=[source_subnet, target_subnet], + status=STATUS_CONCLUSIVE, + segmentation_event=event + ) + + +def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet): + return Event.create_event( + title="Segmentation event", + message=SEGMENTATION_VIOLATION_EVENT_TEXT.format( + hostname=current_monkey.hostname, + source_ip=get_ip_if_in_subnet(current_monkey.ip_addresses, NetworkRange.get_range_obj(source_subnet)), + source_seg=source_subnet, + target_ip=target_ip, + target_seg=target_subnet + ), + event_type=EVENT_TYPE_MONKEY_NETWORK + ) + From 223adb0f3327938d3f99bf7028232e1bd230355c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 25 Aug 2019 18:14:15 +0300 Subject: [PATCH 088/186] Added state function, WIP --- .../monkey_island/cc/services/telemetry/processing/state.py | 5 +++++ .../cc/services/telemetry/zero_trust_tests/segmentation.py | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py index ac8e32939..46176c9b9 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/state.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py @@ -1,4 +1,6 @@ from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import \ + test_positive_findings_for_unreached_segments def process_state_telemetry(telemetry_json): @@ -8,3 +10,6 @@ def process_state_telemetry(telemetry_json): NodeService.set_monkey_dead(monkey, True) else: NodeService.set_monkey_dead(monkey, False) + + if telemetry_json['data']['done']: + test_positive_findings_for_unreached_segments(telemetry_json) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index ca308aafd..647db59fc 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -66,3 +66,7 @@ def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, t event_type=EVENT_TYPE_MONKEY_NETWORK ) + +def test_positive_findings_for_unreached_segments(telemetry_json): + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + subnet_groups = get_config_network_segments_as_subnet_groups() From 0a044e2295fcdc09319c45b2cb4d978ac8423c30 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 25 Aug 2019 18:30:56 +0300 Subject: [PATCH 089/186] Reverted changed to ZT report page and fixed rule bug in diagram + whitespace. --- .../components/pages/ZeroTrustReportPage.js | 201 ++++++++---------- .../zerotrust/PillarOverview.js | 2 +- .../zerotrust/venn-components/VennDiagram.js | 152 +++++-------- 3 files changed, 152 insertions(+), 203 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index e80af2366..2fe43c42e 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,64 +1,17 @@ -import React from 'react'; -import {Button, Col} from 'react-bootstrap'; +import React, {Fragment} from 'react'; +import {Col, Grid, Row} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; -import PillarGrades from "../report-components/zerotrust/PillarGrades"; -import PillarLabel from "../report-components/zerotrust/PillarLabel"; -import ResponsiveVennDiagram from "../report-components/zerotrust/venn-components/ResponsiveVennDiagram"; +import PillarsOverview from "../report-components/zerotrust/PillarOverview"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; -import {SinglePillarRecommendationsStatus} from "../report-components/zerotrust/SinglePillarRecommendationsStatus"; - -let mockup = [ - { - "Conclusive": 4, - "Inconclusive": 0, - "Positive": 1, - "Unexecuted": 2, - "pillar": "Data" - }, - { - "Conclusive": 0, - "Inconclusive": 5, - "Positive": 0, - "Unexecuted": 2, - "pillar": "People" - }, - { - "Conclusive": 0, - "Inconclusive": 0, - "Positive": 6, - "Unexecuted": 3, - "pillar": "Networks" - }, - { - "Conclusive": 2, - "Inconclusive": 0, - "Positive": 1, - "Unexecuted": 1, - "pillar": "Devices" - }, - { - "Conclusive": 0, - "Inconclusive": 0, - "Positive": 0, - "Unexecuted": 0, - "pillar": "Workloads" - }, - { - "Conclusive": 0, - "Inconclusive": 2, - "Positive": 0, - "Unexecuted": 0, - "pillar": "Visibility & Analytics" - }, - { - "Conclusive": 0, - "Inconclusive": 0, - "Positive": 0, - "Unexecuted": 0, - "pillar": "Automation & Orchestration" - } -]; +import SinglePillarDirectivesStatus from "../report-components/zerotrust/SinglePillarDirectivesStatus"; +import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; +import ReportLoader from "../report-components/common/ReportLoader"; +import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; +import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance"; +import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary"; +import PrintReportButton from "../report-components/common/PrintReportButton"; +import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -67,16 +20,30 @@ class ZeroTrustReportPageComponent extends AuthComponent { this.state = { allMonkeysAreDead: false, - runStarted: false + runStarted: true }; } - render() { - let res; - // Todo move to componentDidMount - this.getZeroTrustReportFromServer(res); + componentDidMount() { + this.updateMonkeysRunning().then(res => this.getZeroTrustReportFromServer(res)); + } - const content = this.generateReportContent(); + updateMonkeysRunning = () => { + return this.authFetch('/api') + .then(res => res.json()) + .then(res => { + this.setState(extractExecutionStatusFromServerResponse(res)); + return res; + }); + }; + + render() { + let content; + if (this.state.runStarted) { + content = this.generateReportContent(); + } else { + content = ; + } return ( @@ -92,63 +59,75 @@ class ZeroTrustReportPageComponent extends AuthComponent { let content; if (this.stillLoadingDataFromServer()) { - content = "Still empty"; + content = ; } else { - const pillarsSection =
    -

    Pillars Overview

    - -
    ; - - const recommendationsSection =

    Recommendations Status

    - { - this.state.recommendations.map((recommendation) => - - ) - } -
    ; - - const findingSection =

    Findings

    -
    ; - - content =
    - {pillarsSection} - {recommendationsSection} - {findingSection} + content =
    + {this.generateOverviewSection()} + {this.generateDirectivesSection()} + {this.generateFindingsSection()}
    ; } return ( -
    -
    - + +
    + {print();}} />

    {content} -
    -
    {JSON.stringify(this.state.pillars, undefined, 2)}
    -
    - -
    {JSON.stringify(this.state.recommendations, undefined, 2)}
    -
    -
    {JSON.stringify(this.state.findings, undefined, 2)}
    -
    +
    + {print();}} /> +
    + ) } - stillLoadingDataFromServer() { - return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.recommendations === "undefined"; + generateFindingsSection() { + return (
    +

    Findings

    + +
    ); } - print() { - alert("unimplemented"); + generateDirectivesSection() { + return (
    +

    Directives

    + { + Object.keys(this.state.directives).map((pillar) => + + ) + } +
    ); + } + + generateOverviewSection() { + return (
    +

    Overview

    + + + + + + + + + + + + +
    ); + } + + stillLoadingDataFromServer() { + return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.directives === "undefined"; } getZeroTrustReportFromServer() { @@ -160,11 +139,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { findings: res }); }); - this.authFetch('/api/report/zero_trust/recommendations') + this.authFetch('/api/report/zero_trust/directives') .then(res => res.json()) .then(res => { this.setState({ - recommendations: res + directives: res }); }); this.authFetch('/api/report/zero_trust/pillars') @@ -175,6 +154,14 @@ class ZeroTrustReportPageComponent extends AuthComponent { }); }); } + + anyIssuesFound() { + const severe = function(finding) { + return (finding.status === "Conclusive" || finding.status === "Inconclusive"); + }; + + return this.state.findings.some(severe); + } } export default ZeroTrustReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index f772e0652..824885cad 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -1,7 +1,7 @@ import React, {Component} from "react"; import PillarLabel from "./PillarLabel"; import * as PropTypes from "prop-types"; -import ResponsiveVennDiagram from "./VennDiagram"; +import ResponsiveVennDiagram from "./venn-components/ResponsiveVennDiagram"; const columns = [ { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 0f154cdfe..1e0be3b16 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -7,31 +7,29 @@ import { TypographicUtilities } from './Utility.js' import './VennDiagram.css' class VennDiagram extends React.Component{ - - constructor(props_){ - - super(props_); - - this.state = { tooltip: { top: 0, left: 0, display: 'none', html: '' } } - - this.width = this.height = 512, this.zOrder = []; - + constructor(props_){ + super(props_); + + this.state = { tooltip: { top: 0, left: 0, display: 'none', html: '' } }; + + this.width = this.height = 512; + this.zOrder = []; + this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C']; this.prefix = 'vennDiagram'; this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed']; this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, {size: Math.max(6, this.width / 52), color: 'black'}]; this.offset = this.width / 16; - - this.thirdWidth = this.width / 3; + + this.thirdWidth = this.width / 3; this.sixthWidth = this.width / 6; - this.width2By7 = 2 * this.width / 7 + this.width2By7 = 2 * this.width / 7; this.width1By11 = this.width / 11; this.width1By28 = this.width / 28; - + this.toggle = false; - + this.layout = { - Data: { cx: 0, cy: 0, r: this.thirdWidth - this.offset * 2, offset: {x: 0, y: 0} }, People: { cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0} }, Networks: { cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0} }, @@ -39,15 +37,14 @@ class VennDiagram extends React.Component{ Workloads : { cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11} }, VisibilityAndAnalytics : { inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth }, AutomationAndOrchestration: { inner: this.thirdWidth - this.width1By28 * 2, outer: this.thirdWidth - this.width1By28 } - }; - + /* RULE #1: All scores have to be equal 0, except Unexecuted [U] which could be also a negative integer - sum(C, I, P, U) has to be <=0 + sum(C, I, P) has to be <=0 - RULE #2: Conclusive [C] has to be > 0, + RULE #2: Conclusive [C] has to be > 0, sum(C) > 0 RULE #3: Inconclusive [I] has to be > 0 while Conclusive has to be 0, @@ -62,185 +59,150 @@ class VennDiagram extends React.Component{ this.rules = [ - { id: 'Rule #1', f: function(d_){ return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] + d_['Unexecuted'] <= 0; } }, + { id: 'Rule #1', f: function(d_){ return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0; } }, { id: 'Rule #2', f: function(d_){ return d_['Conclusive'] > 0; } }, { id: 'Rule #3', f: function(d_){ return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0; } }, { id: 'Rule #4', f: function(d_){ return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0; } } ]; - - - this._onScroll = this._onScroll.bind(this); - - } - - componentDidMount() { + this._onScroll = this._onScroll.bind(this); + } + + componentDidMount() { this.parseData(); window.addEventListener('scroll', this._onScroll); - } _onMouseMove(e) { - let self = this; - + if(!this.toggle){ - let hidden = 'none'; let html = ''; let bcolor = '#DEDEDE'; - + document.querySelectorAll('circle, path').forEach((d_, i_) => { d_.setAttribute('opacity', 0.8)}); - - if(e.target.id.includes('Node')) { + + if(e.target.id.includes('Node')) { html = e.target.dataset.tooltip; this.divElement.style.cursor = 'pointer'; - hidden = 'block'; e.target.setAttribute('opacity', 0.95); + hidden = 'block'; e.target.setAttribute('opacity', 0.95); bcolor = e.target.getAttribute('fill'); - - //set highest z-index + + // Set highest z-index e.target.parentNode.parentNode.appendChild(e.target.parentNode); }else{ this.divElement.style.cursor = 'default'; - - //return z indices to default + + // Return z indices to default Object.keys(this.layout).forEach(function(d_, i_){ document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); }) } this.setState({target: e, tooltip: { target: e.target, bcolor: bcolor, top: e.clientY + 8, left: e.clientX + 8, display: hidden, html: html } }); - } } _onScroll(e){ - this.divElement.style.cursor = 'default'; this.setState({target: null, tooltip: { target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: '' } }); - } _onClick(e) { + this.toggle = this.state.tooltip.target === e.target; - if(this.state.tooltip.target === e.target) { this.toggle = true; } else { this.toggle = false; } - //variable to external callback //e.target.parentNode.id) - } parseData(){ - let self = this; let data = []; const omit = (prop, { [prop]: _, ...rest }) => rest; this.props.pillarsGrades.forEach((d_, i_) => { - + let params = omit('pillar', d_); - let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_]||0), 0); + let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_]||0), 0); let key = TypographicUtilities.removeAmpersand(d_.pillar); let html = self.buildTooltipHtmlContent(d_); let rule = null; - + for(let j = 0; j < self.rules.length; j++){ if(self.rules[j].f(d_)) { rule = j; break; }} self.setLayoutElement(rule, key, html, d_); data.push(this.layout[key]); - - }) - - this.setState({ data: data }); - this.render(); + }); + + this.setState({ data: data }); + this.render(); } - + buildTooltipHtmlContent(object_){ return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', ''); } setLayoutElement(rule_, key_, html_, d_){ - + console.log(rule_, key_, html_, d_); if(rule_ == null) { throw Error('The node scores are invalid'); } - + if(key_ === 'Data'){ this.layout[key_].fontStyle = this.fontStyles[0]; } else {this.layout[key_].fontStyle = this.fontStyles[1]; } - - this.layout[key_].hex = this.colors[rule_]; - this.layout[key_].label = d_.pillar + this.suffices[rule_]; + + this.layout[key_].hex = this.colors[rule_]; + this.layout[key_].label = d_.pillar + this.suffices[rule_]; this.layout[key_].node = d_; this.layout[key_].tooltip = html_; - } render() { - if(this.state.data === undefined) { return null; } - else { - - //equivalent to center translate (width/2, height/2) + else { + // equivalent to center translate (width/2, height/2) let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height; let translate = 'translate(' + this.width /2 + ',' + this.height/2 + ')'; - let nodes = Object.values(this.layout).map((d_, i_) =>{ - - if(d_.hasOwnProperty('cx')){ - + let nodes = Object.values(this.layout).map((d_, i_) => { + if(d_.hasOwnProperty('cx')) { return ( - ); - - }else{ - + } else { d_.label = TypographicUtilities.removeBrokenBar(d_.label); - + return ( - ); - } - }); - - return ( + return (
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)} > - + {nodes} - - -
    - + + +
    ) - } - } - } VennDiagram.propTypes = { - pillarsGrades: PropTypes.array - -} +}; -export default VennDiagram; \ No newline at end of file +export default VennDiagram; From 79fabb1ac1e3018dda1942913f5d582bfa8b646d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 25 Aug 2019 18:49:57 +0300 Subject: [PATCH 090/186] Whitespace fixes --- .../zerotrust/venn-components/VennDiagram.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 1e0be3b16..f238b7f63 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -85,7 +85,6 @@ class VennDiagram extends React.Component{ document.querySelectorAll('circle, path').forEach((d_, i_) => { d_.setAttribute('opacity', 0.8)}); if(e.target.id.includes('Node')) { - html = e.target.dataset.tooltip; this.divElement.style.cursor = 'pointer'; hidden = 'block'; e.target.setAttribute('opacity', 0.95); @@ -93,14 +92,11 @@ class VennDiagram extends React.Component{ // Set highest z-index e.target.parentNode.parentNode.appendChild(e.target.parentNode); - - }else{ - + } else { this.divElement.style.cursor = 'default'; // Return z indices to default Object.keys(this.layout).forEach(function(d_, i_){ document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); }) - } this.setState({target: e, tooltip: { target: e.target, bcolor: bcolor, top: e.clientY + 8, left: e.clientX + 8, display: hidden, html: html } }); @@ -145,8 +141,7 @@ class VennDiagram extends React.Component{ buildTooltipHtmlContent(object_){ return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', ''); } - setLayoutElement(rule_, key_, html_, d_){ - console.log(rule_, key_, html_, d_); + setLayoutElement(rule_, key_, html_, d_) { if(rule_ == null) { throw Error('The node scores are invalid'); } if(key_ === 'Data'){ this.layout[key_].fontStyle = this.fontStyles[0]; } From f0d43e033e7eff3933036daa76e3e1f21219af4b Mon Sep 17 00:00:00 2001 From: vkuchinov Date: Mon, 26 Aug 2019 08:12:51 +0300 Subject: [PATCH 091/186] Update ArcNode.js HAve returned missing labels --- .../report-components/zerotrust/venn-components/ArcNode.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js index 1306ecc54..506ec0b7d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js @@ -9,13 +9,14 @@ class ArcNode extends React.Component{ let {prefix, index, data} = this.props; let arc = d3.arc().innerRadius(data.inner).outerRadius(data.outer).startAngle(0).endAngle(Math.PI * 2.0); + let id = prefix + 'Node_' + index; return ( - + {data.label} From 9367b6ce8e2357857cbabfaccd785c873324a05b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 26 Aug 2019 09:20:59 +0300 Subject: [PATCH 092/186] Fixed arcnode text bug + formatting --- .../zerotrust/venn-components/ArcNode.js | 58 ++- .../zerotrust/venn-components/CircularNode.js | 81 ++-- .../venn-components/ResponsiveVennDiagram.js | 43 +- .../zerotrust/venn-components/Tooltip.js | 74 ++-- .../zerotrust/venn-components/Utility.js | 17 +- .../zerotrust/venn-components/VennDiagram.css | 17 +- .../zerotrust/venn-components/VennDiagram.js | 390 ++++++++++-------- 7 files changed, 358 insertions(+), 322 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js index 1306ecc54..95d86db2f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js @@ -2,43 +2,37 @@ import React from 'react' import PropTypes from 'prop-types'; import * as d3 from 'd3' -class ArcNode extends React.Component{ - - render() { +class ArcNode extends React.Component { + render() { + let {prefix, index, data} = this.props; - let {prefix, index, data} = this.props; + let arc = d3.arc().innerRadius(data.inner).outerRadius(data.outer).startAngle(0).endAngle(Math.PI * 2.0); + let id = prefix + 'Node_' + index; - let arc = d3.arc().innerRadius(data.inner).outerRadius(data.outer).startAngle(0).endAngle(Math.PI * 2.0); - - return ( - - - + - - - {data.label} - - - + id={prefix + 'Node_' + index} + className={'arcNode'} + data-tooltip={data.tooltip} + d={arc()} + fill={data.hex} - ) - - } - + /> + + + {data.label} + + + + ); + } } ArcNode.propTypes = { + data: PropTypes.object +}; - data: PropTypes.object - -} - -export default ArcNode; \ No newline at end of file +export default ArcNode; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js index 8d8df10bf..43ef529f3 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js @@ -1,56 +1,43 @@ import React from 'react' import PropTypes from 'prop-types'; -class CircularNode extends React.Component{ - - render() { +class CircularNode extends React.Component { + render() { + let {prefix, index, data} = this.props; - let {prefix, index, data} = this.props; + let tspans = data.label.split("|").map((d_, i_) => { + let halfTextHeight = data.fontStyle.size * data.label.split("|").length / 2; + let key = 'vennDiagramCircularNode' + index + '_Tspan' + i_; - let tspans = data.label.split("|").map((d_, i_) =>{ - - let halfTextHeight = data.fontStyle.size * data.label.split("|").length / 2; - let key = 'vennDiagramCircularNode' + index + '_Tspan' + i_; - - return ( - - {d_} - - ) - - }) - - let translate = 'translate(' + data.cx + ',' + data.cy + ')'; - - return ( - - - - - {tspans} - - - - ) - - } - + return ( + {d_} + ); + }); + + let translate = 'translate(' + data.cx + ',' + data.cy + ')'; + + return ( + + + + {tspans} + + + ); + } } CircularNode.propTypes = { - - index: PropTypes.number, - data: PropTypes.object - -} + index: PropTypes.number, + data: PropTypes.object +}; -export default CircularNode; \ No newline at end of file +export default CircularNode; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js index d20abf94a..8cf096d69 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js @@ -6,31 +6,30 @@ import VennDiagram from './VennDiagram' const VENN_MIN_WIDTH = '300px'; class ResponsiveVennDiagram extends React.Component { - - constructor(props) { super(props); } - - render() { - - const {pillarsGrades} = this.props; - let childrenWidth = this.props.containerWidth, childrenHeight = this.props.containerHeight; - - if(childrenHeight === 0 || isNaN(childrenHeight)){ childrenHeight = childrenWidth; } - else{ childrenWidth = Math.min(childrenWidth, childrenHeight) } - - return ( - -
    - -
    ) - + constructor(props) { + super(props); + } + + render() { + const {pillarsGrades} = this.props; + let childrenWidth = this.props.containerWidth, childrenHeight = this.props.containerHeight; + + if (childrenHeight === 0 || isNaN(childrenHeight)) { + childrenHeight = childrenWidth; + } else { + childrenWidth = Math.min(childrenWidth, childrenHeight) + } + + return ( +
    + +
    + ); } - } ResponsiveVennDiagram.propTypes = { - - pillarsGrades: PropTypes.array - -} + pillarsGrades: PropTypes.array +}; export default Dimensions()(ResponsiveVennDiagram); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js index 579b36a5b..e103af3c3 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js @@ -1,49 +1,43 @@ import React from 'react' import PropTypes from 'prop-types'; -class Tooltip extends React.Component{ +class Tooltip extends React.Component { - render() { - - const {prefix, bcolor, top, left, display, html } = this.props; + render() { + const {prefix, bcolor, top, left, display, html} = this.props; - const style = { - - backgroundColor: bcolor, - border : '1px solid #FFFFFF', - borderRadius: '2px', - fontSize: 10, - padding: 8, - display, - opacity: 0.9, - position: 'fixed', - top, - left, - pointerEvents: 'none' - - }; + const style = { + backgroundColor: bcolor, + border: '1px solid #FFFFFF', + borderRadius: '2px', + fontSize: 10, + padding: 8, + display, + opacity: 0.9, + position: 'fixed', + top, + left, + pointerEvents: 'none' + }; - return ( - -
    - {html.split('\n').map((i_, key_) => { return
    {i_}
    ; })} -
    - - ); - - } - + return ( + +
    + {html.split('\n').map((i_, key_) => { + return
    {i_}
    ; + })} +
    + ); + } } Tooltip.propTypes = { - - prefix: PropTypes.string, - bcolor: PropTypes.string, - top: PropTypes.number, - left: PropTypes.number, - display: PropTypes.string, - html: PropTypes.string - -} - -export default Tooltip; \ No newline at end of file + prefix: PropTypes.string, + bcolor: PropTypes.string, + top: PropTypes.number, + left: PropTypes.number, + display: PropTypes.string, + html: PropTypes.string +}; + +export default Tooltip; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js index c9816c721..230e277f6 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js @@ -1,8 +1,15 @@ export class TypographicUtilities { - - static removeAmpersand(string_) { return string_.replace(' & ', 'And'); } - static removeBrokenBar(string_) { return string_.replace(/\|/g, ' '); } - static setTitle(string_) { return string_.charAt(0).toUpperCase() + string_.substr(1).toLowerCase(); } + + static removeAmpersand(string_) { + return string_.replace(' & ', 'And'); + } + + static removeBrokenBar(string_) { + return string_.replace(/\|/g, ' '); + } + + static setTitle(string_) { + return string_.charAt(0).toUpperCase() + string_.substr(1).toLowerCase(); + } } - \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css index 6c7cd778e..dd4883125 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css @@ -1,14 +1,17 @@ @import url('https://fonts.googleapis.com/css?family=Noto+Sans&display=swap'); -body { margin: 0; font-family: "Noto Sans", sans-serif; } +body { + margin: 0; + font-family: "Noto Sans", sans-serif; +} svg { - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Safari */ - -khtml-user-select: none; /* Konqueror HTML */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */ + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */ } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index f238b7f63..2b36266c0 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -3,201 +3,253 @@ import PropTypes from 'prop-types' import Tooltip from './Tooltip' import CircularNode from './CircularNode' import ArcNode from './ArcNode' -import { TypographicUtilities } from './Utility.js' +import {TypographicUtilities} from './Utility.js' import './VennDiagram.css' -class VennDiagram extends React.Component{ - constructor(props_){ - super(props_); +class VennDiagram extends React.Component { + constructor(props_) { + super(props_); - this.state = { tooltip: { top: 0, left: 0, display: 'none', html: '' } }; + this.state = {tooltip: {top: 0, left: 0, display: 'none', html: ''}}; - this.width = this.height = 512; - this.zOrder = []; + this.width = this.height = 512; + this.zOrder = []; - this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C']; - this.prefix = 'vennDiagram'; - this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed']; - this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, {size: Math.max(6, this.width / 52), color: 'black'}]; - this.offset = this.width / 16; + this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C']; + this.prefix = 'vennDiagram'; + this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed']; + this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, { + size: Math.max(6, this.width / 52), + color: 'black' + }]; + this.offset = this.width / 16; - this.thirdWidth = this.width / 3; - this.sixthWidth = this.width / 6; - this.width2By7 = 2 * this.width / 7; - this.width1By11 = this.width / 11; - this.width1By28 = this.width / 28; + this.thirdWidth = this.width / 3; + this.sixthWidth = this.width / 6; + this.width2By7 = 2 * this.width / 7; + this.width1By11 = this.width / 11; + this.width1By28 = this.width / 28; - this.toggle = false; + this.toggle = false; - this.layout = { - Data: { cx: 0, cy: 0, r: this.thirdWidth - this.offset * 2, offset: {x: 0, y: 0} }, - People: { cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0} }, - Networks: { cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0} }, - Devices: { cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11} }, - Workloads : { cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11} }, - VisibilityAndAnalytics : { inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth }, - AutomationAndOrchestration: { inner: this.thirdWidth - this.width1By28 * 2, outer: this.thirdWidth - this.width1By28 } - }; + this.layout = { + Data: {cx: 0, cy: 0, r: this.thirdWidth - this.offset * 2, offset: {x: 0, y: 0}}, + People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}}, + Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}}, + Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}}, + Workloads: {cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11}}, + VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth}, + AutomationAndOrchestration: { + inner: this.thirdWidth - this.width1By28 * 2, + outer: this.thirdWidth - this.width1By28 + } + }; - /* + /* - RULE #1: All scores have to be equal 0, except Unexecuted [U] which could be also a negative integer - sum(C, I, P) has to be <=0 + RULE #1: All scores have to be equal 0, except Unexecuted [U] which could be also a negative integer + sum(C, I, P) has to be <=0 - RULE #2: Conclusive [C] has to be > 0, - sum(C) > 0 + RULE #2: Conclusive [C] has to be > 0, + sum(C) > 0 - RULE #3: Inconclusive [I] has to be > 0 while Conclusive has to be 0, - sum(C, I) > 0 and C * I = 0, while C has to be 0 + RULE #3: Inconclusive [I] has to be > 0 while Conclusive has to be 0, + sum(C, I) > 0 and C * I = 0, while C has to be 0 - RULE #4: Positive [P] and Unexecuted have to be positive - sum(P, U) >= 2 and P * U = positive integer, while - if the P is bigger by 2 then negative U, first conditional - would be true. + RULE #4: Positive [P] and Unexecuted have to be positive + sum(P, U) >= 2 and P * U = positive integer, while + if the P is bigger by 2 then negative U, first conditional + would be true. - */ + */ - this.rules = [ + this.rules = [ - { id: 'Rule #1', f: function(d_){ return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0; } }, - { id: 'Rule #2', f: function(d_){ return d_['Conclusive'] > 0; } }, - { id: 'Rule #3', f: function(d_){ return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0; } }, - { id: 'Rule #4', f: function(d_){ return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0; } } - - ]; - - this._onScroll = this._onScroll.bind(this); - } - - componentDidMount() { - this.parseData(); - window.addEventListener('scroll', this._onScroll); - } - - _onMouseMove(e) { - let self = this; - - if(!this.toggle){ - let hidden = 'none'; - let html = ''; - let bcolor = '#DEDEDE'; - - document.querySelectorAll('circle, path').forEach((d_, i_) => { d_.setAttribute('opacity', 0.8)}); - - if(e.target.id.includes('Node')) { - html = e.target.dataset.tooltip; - this.divElement.style.cursor = 'pointer'; - hidden = 'block'; e.target.setAttribute('opacity', 0.95); - bcolor = e.target.getAttribute('fill'); - - // Set highest z-index - e.target.parentNode.parentNode.appendChild(e.target.parentNode); - } else { - this.divElement.style.cursor = 'default'; - - // Return z indices to default - Object.keys(this.layout).forEach(function(d_, i_){ document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); }) - } - - this.setState({target: e, tooltip: { target: e.target, bcolor: bcolor, top: e.clientY + 8, left: e.clientX + 8, display: hidden, html: html } }); + { + id: 'Rule #1', f: function (d_) { + return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0; } - } + }, + { + id: 'Rule #2', f: function (d_) { + return d_['Conclusive'] > 0; + } + }, + { + id: 'Rule #3', f: function (d_) { + return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0; + } + }, + { + id: 'Rule #4', f: function (d_) { + return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0; + } + } - _onScroll(e){ + ]; + + this._onScroll = this._onScroll.bind(this); + } + + componentDidMount() { + this.parseData(); + window.addEventListener('scroll', this._onScroll); + } + + _onMouseMove(e) { + let self = this; + + if (!this.toggle) { + let hidden = 'none'; + let html = ''; + let bcolor = '#DEDEDE'; + + document.querySelectorAll('circle, path').forEach((d_, i_) => { + d_.setAttribute('opacity', "0.8"); + }); + + if (e.target.id.includes('Node')) { + html = e.target.dataset.tooltip; + this.divElement.style.cursor = 'pointer'; + hidden = 'block'; + e.target.setAttribute('opacity', 0.95); + bcolor = e.target.getAttribute('fill'); + + // Set highest z-index + e.target.parentNode.parentNode.appendChild(e.target.parentNode); + } else { this.divElement.style.cursor = 'default'; - this.setState({target: null, tooltip: { target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: '' } }); - } - _onClick(e) { - this.toggle = this.state.tooltip.target === e.target; + // Return z indices to default + Object.keys(this.layout).forEach(function (d_, i_) { + document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); + }) + } - //variable to external callback - //e.target.parentNode.id) - } - - parseData(){ - let self = this; - let data = []; - const omit = (prop, { [prop]: _, ...rest }) => rest; - - this.props.pillarsGrades.forEach((d_, i_) => { - - let params = omit('pillar', d_); - let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_]||0), 0); - let key = TypographicUtilities.removeAmpersand(d_.pillar); - let html = self.buildTooltipHtmlContent(d_); - let rule = null; - - for(let j = 0; j < self.rules.length; j++){ if(self.rules[j].f(d_)) { rule = j; break; }} - - self.setLayoutElement(rule, key, html, d_); - data.push(this.layout[key]); - - }); - - this.setState({ data: data }); - this.render(); - } - - buildTooltipHtmlContent(object_){ return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', ''); } - - setLayoutElement(rule_, key_, html_, d_) { - if(rule_ == null) { throw Error('The node scores are invalid'); } - - if(key_ === 'Data'){ this.layout[key_].fontStyle = this.fontStyles[0]; } - else {this.layout[key_].fontStyle = this.fontStyles[1]; } - - this.layout[key_].hex = this.colors[rule_]; - this.layout[key_].label = d_.pillar + this.suffices[rule_]; - this.layout[key_].node = d_; - this.layout[key_].tooltip = html_; - } - - render() { - if(this.state.data === undefined) { return null; } - else { - // equivalent to center translate (width/2, height/2) - let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height; - let translate = 'translate(' + this.width /2 + ',' + this.height/2 + ')'; - - let nodes = Object.values(this.layout).map((d_, i_) => { - if(d_.hasOwnProperty('cx')) { - return ( - - ); - } else { - d_.label = TypographicUtilities.removeBrokenBar(d_.label); - - return ( - - ); - } - }); - - return ( -
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)} > - - {nodes} - - -
    - ) + this.setState({ + target: e, + tooltip: { + target: e.target, + bcolor: bcolor, + top: e.clientY + 8, + left: e.clientX + 8, + display: hidden, + html: html } + }); } + } + + _onScroll(e) { + this.divElement.style.cursor = 'default'; + this.setState({target: null, tooltip: {target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: ''}}); + } + + _onClick(e) { + this.toggle = this.state.tooltip.target === e.target; + + //variable to external callback + //e.target.parentNode.id) + } + + parseData() { + let self = this; + let data = []; + const omit = (prop, {[prop]: _, ...rest}) => rest; + + this.props.pillarsGrades.forEach((d_, i_) => { + + let params = omit('pillar', d_); + let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_] || 0), 0); + let key = TypographicUtilities.removeAmpersand(d_.pillar); + let html = self.buildTooltipHtmlContent(d_); + let rule = null; + + for (let j = 0; j < self.rules.length; j++) { + if (self.rules[j].f(d_)) { + rule = j; + break; + } + } + + self.setLayoutElement(rule, key, html, d_); + data.push(this.layout[key]); + + }); + + this.setState({data: data}); + this.render(); + } + + buildTooltipHtmlContent(object_) { + return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', ''); + } + + setLayoutElement(rule_, key_, html_, d_) { + if (rule_ == null) { + throw Error('The node scores are invalid'); + } + + if (key_ === 'Data') { + this.layout[key_].fontStyle = this.fontStyles[0]; + } else { + this.layout[key_].fontStyle = this.fontStyles[1]; + } + + this.layout[key_].hex = this.colors[rule_]; + this.layout[key_].label = d_.pillar + this.suffices[rule_]; + this.layout[key_].node = d_; + this.layout[key_].tooltip = html_; + } + + render() { + if (this.state.data === undefined) { + return null; + } else { + // equivalent to center translate (width/2, height/2) + let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height; + let translate = 'translate(' + this.width / 2 + ',' + this.height / 2 + ')'; + + let nodes = Object.values(this.layout).map((d_, i_) => { + if (d_.hasOwnProperty('cx')) { + return ( + + ); + } else { + d_.label = TypographicUtilities.removeBrokenBar(d_.label); + + return ( + + ); + } + }); + + return ( +
    this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} + onClick={this._onClick.bind(this)}> + + {nodes} + + +
    + ) + } + } } VennDiagram.propTypes = { - pillarsGrades: PropTypes.array + pillarsGrades: PropTypes.array }; export default VennDiagram; From e4a03ce3e0b83df1d8c550d2d4391a5532a54430 Mon Sep 17 00:00:00 2001 From: vkuchinov Date: Mon, 26 Aug 2019 09:59:36 +0300 Subject: [PATCH 093/186] Removing rudimentary variables from ResponsiveVennDiagram and VennDiagram [x] childrenWidth/childrenHeight were removed, have beed used previously for non-responsive SVG [x] this.ZOrder array was removed, since there is another way of soring z-indices [x] translate was removed, since now it's the part of viewPortParameters string ((-this.width / 2) + ' ' + (-this.height / 2)) --- .../zerotrust/venn-components/ResponsiveVennDiagram.js | 7 ------- .../zerotrust/venn-components/VennDiagram.js | 2 -- 2 files changed, 9 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js index 8cf096d69..4b2069f06 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ResponsiveVennDiagram.js @@ -12,13 +12,6 @@ class ResponsiveVennDiagram extends React.Component { render() { const {pillarsGrades} = this.props; - let childrenWidth = this.props.containerWidth, childrenHeight = this.props.containerHeight; - - if (childrenHeight === 0 || isNaN(childrenHeight)) { - childrenHeight = childrenWidth; - } else { - childrenWidth = Math.min(childrenWidth, childrenHeight) - } return (
    diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 2b36266c0..25e57c2dd 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -13,7 +13,6 @@ class VennDiagram extends React.Component { this.state = {tooltip: {top: 0, left: 0, display: 'none', html: ''}}; this.width = this.height = 512; - this.zOrder = []; this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C']; this.prefix = 'vennDiagram'; @@ -208,7 +207,6 @@ class VennDiagram extends React.Component { } else { // equivalent to center translate (width/2, height/2) let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height; - let translate = 'translate(' + this.width / 2 + ',' + this.height / 2 + ')'; let nodes = Object.values(this.layout).map((d_, i_) => { if (d_.hasOwnProperty('cx')) { From f865c4b4b94638769b4e1aa6d29bee770bf0700d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 26 Aug 2019 14:08:18 +0300 Subject: [PATCH 094/186] Added sent telemetry logging. --- monkey/infection_monkey/telemetry/base_telem.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/telemetry/base_telem.py b/monkey/infection_monkey/telemetry/base_telem.py index c232ab975..31d7332bd 100644 --- a/monkey/infection_monkey/telemetry/base_telem.py +++ b/monkey/infection_monkey/telemetry/base_telem.py @@ -1,7 +1,11 @@ import abc +import json +import logging from infection_monkey.control import ControlClient +logger = logging.getLogger(__name__) + __author__ = 'itay.mizeretz' @@ -19,7 +23,9 @@ class BaseTelem(object): """ Sends telemetry to island """ - ControlClient.send_telemetry(self.telem_category, self.get_data()) + data = self.get_data() + logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, json.dumps(data))) + ControlClient.send_telemetry(self.telem_category, data) @abc.abstractproperty def telem_category(self): From fbb82f412b376626f3c43690a4d8a5f70268c89d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 26 Aug 2019 14:08:45 +0300 Subject: [PATCH 095/186] Fixed copy-pasta bug about state telemetry. --- monkey/infection_monkey/monkey.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 692e278fb..3cd20d9c2 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -113,7 +113,7 @@ class InfectionMonkey(object): if monkey_tunnel: monkey_tunnel.start() - StateTelem(False).send() + StateTelem(is_done=False).send() TunnelTelem().send() if WormConfiguration.collect_system_info: @@ -225,7 +225,7 @@ class InfectionMonkey(object): InfectionMonkey.close_tunnel() firewall.close() else: - StateTelem(False).send() # Signal the server (before closing the tunnel) + StateTelem(is_done=True).send() # Signal the server (before closing the tunnel) InfectionMonkey.close_tunnel() firewall.close() if WormConfiguration.send_log_to_server: From a9ba3273dddc4191c83413624010e392b8b33386 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 26 Aug 2019 14:23:14 +0300 Subject: [PATCH 096/186] Added positive segmentation findings --- .../zero_trust_tests/segmentation.py | 50 ++++++++++++++---- .../test_segmentation_zt_tests.py | 51 +++++++++++++++++++ 2 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index 647db59fc..bb447d992 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -1,7 +1,8 @@ import itertools from six import text_type -from common.data.zero_trust_consts import STATUS_CONCLUSIVE, EVENT_TYPE_MONKEY_NETWORK +from common.data.zero_trust_consts import STATUS_CONCLUSIVE, EVENT_TYPE_MONKEY_NETWORK, STATUS_POSITIVE, \ + EVENT_TYPE_ISLAND from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst, get_ip_if_in_subnet from monkey_island.cc.models import Monkey @@ -29,15 +30,11 @@ def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_s return cross_segment_ip is not None -def test_segmentation_violation(telemetry_json): - """ - - :param telemetry_json: A SCAN telemetry sent from a Monkey. - """ +def test_segmentation_violation(scan_telemetry_json): # TODO - lower code duplication between this and report.py. # TODO - single machine - current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) - target_ip = telemetry_json['data']['machine']['ip_addr'] + current_monkey = Monkey.get_single_monkey_by_guid(scan_telemetry_json['monkey_guid']) + target_ip = scan_telemetry_json['data']['machine']['ip_addr'] subnet_groups = get_config_network_segments_as_subnet_groups() for subnet_group in subnet_groups: subnet_pairs = itertools.product(subnet_group, subnet_group) @@ -67,6 +64,37 @@ def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, t ) -def test_positive_findings_for_unreached_segments(telemetry_json): - current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) - subnet_groups = get_config_network_segments_as_subnet_groups() +def test_positive_findings_for_unreached_segments(state_telemetry_json): + flat_all_subnets = [item for sublist in get_config_network_segments_as_subnet_groups() for item in sublist] + current_monkey = Monkey.get_single_monkey_by_guid(state_telemetry_json['monkey_guid']) + create_or_add_findings_for_all_pairs(flat_all_subnets, current_monkey) + + +def create_or_add_findings_for_all_pairs(all_subnets, current_monkey): + # Filter the subnets that this monkey is part of. + this_monkey_subnets = [] + for subnet in all_subnets: + if get_ip_if_in_subnet(current_monkey.ip_addresses, NetworkRange.get_range_obj(subnet)) is not None: + this_monkey_subnets.append(subnet) + + # Get all the other subnets. + other_subnets = list(set(all_subnets) - set(this_monkey_subnets)) + + # Calculate the cartesian product - (this monkey subnets X other subnets). These pairs are the pairs that the monkey + # should have tested. + all_subnets_pairs_for_this_monkey = itertools.product(this_monkey_subnets, other_subnets) + + for subnet_pair in all_subnets_pairs_for_this_monkey: + SegmentationFinding.create_or_add_to_existing_finding( + subnets=list(subnet_pair), + status=STATUS_POSITIVE, + segmentation_event=Event.create_event( + "Segmentation test done", + message="Monkey on {hostname} is done attempting cross-segment communications from `{src_seg}` " + "segments to `{dst_seg}` segments.".format( + hostname=current_monkey.hostname, + src_seg=subnet_pair[0], + dst_seg=subnet_pair[1]), + event_type=EVENT_TYPE_ISLAND + ) + ) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py new file mode 100644 index 000000000..f345d4482 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py @@ -0,0 +1,51 @@ +import uuid + +from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_POSITIVE, STATUS_CONCLUSIVE, \ + EVENT_TYPE_MONKEY_NETWORK +from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.models.zero_trust.finding import Finding +from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding +from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import create_or_add_findings_for_all_pairs +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + +FIRST_SUBNET = "1.1.1.1" +SECOND_SUBNET = "2.2.2.0/24" +THIRD_SUBNET = "3.3.3.3-3.3.3.200" + + +class TestSegmentationTests(IslandTestCase): + def test_create_findings_for_all_done_pairs(self): + self.fail_if_not_testing_env() + self.clean_finding_db() + + all_subnets = [FIRST_SUBNET, SECOND_SUBNET, THIRD_SUBNET] + + monkey = Monkey( + guid=str(uuid.uuid4()), + ip_addresses=[FIRST_SUBNET]) + + # no findings + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0) + + # This is like the monkey is done and sent done telem + create_or_add_findings_for_all_pairs(all_subnets, monkey) + + # There are 2 subnets in which the monkey is NOT + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_POSITIVE)), 2) + + # This is a monkey from 2nd subnet communicated with 1st subnet. + SegmentationFinding.create_or_add_to_existing_finding( + [FIRST_SUBNET, SECOND_SUBNET], + STATUS_CONCLUSIVE, + Event.create_event(title="sdf", message="asd", event_type=EVENT_TYPE_MONKEY_NETWORK) + ) + + print("Printing all segmentation findings") + all_findings = Finding.objects(test=TEST_SEGMENTATION) + for f in all_findings: + print(f.to_json()) + + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_POSITIVE)), 1) + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_CONCLUSIVE)), 1) + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 2) From 05a1b2d235e2454ec93a58ed3507688746362e0c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 26 Aug 2019 15:24:55 +0300 Subject: [PATCH 097/186] Remove type hint as it doesn't work well with python2. :cry: --- monkey/common/network/segmentation_utils.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/monkey/common/network/segmentation_utils.py b/monkey/common/network/segmentation_utils.py index 97adbd203..6569d636b 100644 --- a/monkey/common/network/segmentation_utils.py +++ b/monkey/common/network/segmentation_utils.py @@ -1,14 +1,10 @@ -from common.network.network_range import NetworkRange - - def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet): - # type: (List[str], NetworkRange, NetworkRange) -> Union[str, None] """ Finds an IP address in ip_addresses which is in source_subnet but not in target_subnet. - :param ip_addresses: List of IP addresses to test. - :param source_subnet: Subnet to want an IP to not be in. - :param target_subnet: Subnet we want an IP to be in. - :return: The cross segment IP if in source but not in target, else None. + :param ip_addresses: List[str]: List of IP addresses to test. + :param source_subnet: NetworkRange: Subnet to want an IP to not be in. + :param target_subnet: NetworkRange: Subnet we want an IP to be in. + :return: The cross segment IP if in source but not in target, else None. Union[str, None] """ if get_ip_if_in_subnet(ip_addresses, target_subnet) is not None: return None From 6cd5cff8182c6a4ac73718cf9309746463bc0b03 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 27 Aug 2019 11:25:41 +0300 Subject: [PATCH 098/186] Added a collapsible report legend and redid the Summary section --- .../components/pages/ZeroTrustReportPage.js | 16 ++++- .../zerotrust/ReportLegend.js | 69 +++++++++++++++++++ .../zerotrust/StatusLabel.js | 8 ++- .../zerotrust/ZeroTrustPillars.js | 7 ++ 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 989af82a8..f1a4de673 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -12,6 +12,7 @@ import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlan import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary"; import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; +import ZeroTrustReportLegend from "../report-components/zerotrust/ReportLegend"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -114,16 +115,25 @@ class ZeroTrustReportPageComponent extends AuthComponent { generateOverviewSection() { return (
    -

    Overview

    +

    Summary

    + + + +

    + Get a quick glance of the status for each of Zero Trust's seven pillars. +

    + + + + +
    - - diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js new file mode 100644 index 000000000..b12cbf5ba --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -0,0 +1,69 @@ +import React, {Component} from "react"; +import StatusLabel from "./StatusLabel"; +import {ZeroTrustStatuses} from "./ZeroTrustPillars"; +import {NavLink} from "react-router-dom"; +import {Panel} from "react-bootstrap"; + + +class ZeroTrustReportLegend extends Component { + render() { + const legendContent = this.getLegendContent(); + + return ( + + + +

    🔽 Legend

    +
    +
    + + + {legendContent} + + +
    + ); + } + + getLegendContent() { + return
    +

    What is this?

    +

    + The Zero Trust eXtended framework categorizes its recommendations into 7 pillars. Infection Monkey + Zero Trust edition tests some of those recommendations. The tests that the monkey executes + produce findings. The tests, recommendations and pillars are then granted a status in accordance + with the tests results. +

    +

    Statuses

    +
      +
    • +
      + +
      + {"\t"}The test failed; the monkeys found something wrong. +
    • +
    • +
      + +
      + {"\t"}The test was executed, but manual verification is required to determine the results. +
    • +
    • +
      + +
      + {"\t"}This status means the test passed 🙂 +
    • +
    • +
      + +
      + {"\t"}This status means the test wasn't executed. Some of the tests can be activated or deactivated using + the configuration. +
    • +
    +
    ; + } +} + +export default ZeroTrustReportLegend; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js index f9c885b2c..6b3243280 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -22,9 +22,11 @@ export default class StatusLabel extends Component { text = " " + this.props.status; } - return (
    - {text} -
    ); + return ( +
    + {text} +
    + ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js index bd8898205..6bc425be0 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js @@ -8,4 +8,11 @@ export const ZeroTrustPillars = { automation: "Automation & Orchestration" }; +export const ZeroTrustStatuses = { + conclusive: "Conclusive", + inconclusive: "Inconclusive", + positive: "Positive", + unexecuted: "Unexecuted" +}; + export default ZeroTrustPillars; From 07eb9ec32f62bb086ec47627d73ff9bfb159b864 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 27 Aug 2019 11:58:15 +0300 Subject: [PATCH 099/186] A lot of small UI changes, trying to make the report look more polished. --- .../components/pages/ZeroTrustReportPage.js | 10 +++++++- .../zerotrust/DirectivesStatusTable.js | 12 +++++----- .../zerotrust/EventsAndButtonComponent.js | 24 ++++++++----------- .../zerotrust/EventsModal.js | 7 ++++++ .../zerotrust/ExportEventsButton.js | 15 ++++++++++++ .../zerotrust/FindingsTable.js | 23 ++++++++++-------- .../zerotrust/SinglePillarDirectivesStatus.js | 19 +++++++++++---- 7 files changed, 75 insertions(+), 35 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index f1a4de673..7b1369dbc 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -8,7 +8,6 @@ import SinglePillarDirectivesStatus from "../report-components/zerotrust/SingleP import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; -import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance"; import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary"; import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; @@ -94,6 +93,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { generateFindingsSection() { return (

    Findings

    +

    + Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things + happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper + insight as to what exactly happened during this test. +

    ); } @@ -101,6 +105,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { generateDirectivesSection() { return (

    Directives

    +

    + Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results + to understand how the monkey tested your adherence to that recommendation. +

    { Object.keys(this.state.directives).map((pillar) => { return ; - } + }, + maxWidth: 80 + }, + { Header: 'Directive', accessor: 'directive', + style: {'whiteSpace': 'unset'} // This enables word wrap }, { Header: 'Tests', id: 'tests', style: {'whiteSpace': 'unset'}, // This enables word wrap @@ -56,7 +56,7 @@ class TestsStatus extends AuthComponent { return (
  • {test.test}
  • ) }); return - +
      {listItems}
    ; } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js index 0222b375f..0b96f8fc2 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js @@ -3,6 +3,7 @@ import EventsModal from "./EventsModal"; import {Button} from "react-bootstrap"; import FileSaver from "file-saver"; import * as PropTypes from "prop-types"; +import ExportEventsButton from "./ExportEventsButton"; export default class EventsAndButtonComponent extends Component { constructor(props) { @@ -23,22 +24,17 @@ export default class EventsAndButtonComponent extends Component { render() { return (
    - -

    - - -

    + { + const content = JSON.stringify(this.props.events, null, 2); + const blob = new Blob([content], {type: "text/plain;charset=utf-8"}); + FileSaver.saveAs(blob, this.props.exportFilename + ".json"); + }}/> +
    ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js index 9543603bd..5da053242 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js @@ -2,6 +2,8 @@ import React, {Component} from "react"; import {Modal} from "react-bootstrap"; import EventsTimeline from "./EventsTimeline"; import * as PropTypes from "prop-types"; +import FileSaver from "file-saver"; +import ExportEventsButton from "./ExportEventsButton"; export default class EventsModal extends Component { constructor(props) { @@ -24,6 +26,11 @@ export default class EventsModal extends Component { onClick={() => this.props.hideCallback()}> Close + { + const content = JSON.stringify(this.props.events, null, 2); + const blob = new Blob([content], {type: "text/plain;charset=utf-8"}); + FileSaver.saveAs(blob, this.props.exportFilename + ".json"); + }}/>
    diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js new file mode 100644 index 000000000..b75516e2c --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js @@ -0,0 +1,15 @@ +import React, {Component} from "react"; +import {Button} from "react-bootstrap"; +import * as PropTypes from "prop-types"; + +export default class ExportEventsButton extends Component { + render() { + return + } +} + +ExportEventsButton.propTypes = {onClick: PropTypes.func}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 9d706b3f2..658d6d039 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -1,4 +1,4 @@ -import React, {Component} from "react"; +import React, {Component, Fragment} from "react"; import PillarLabel from "./PillarLabel"; import PaginatedTable from "../common/PaginatedTable"; import EventsAndButtonComponent from "./EventsAndButtonComponent"; @@ -6,24 +6,27 @@ import EventsAndButtonComponent from "./EventsAndButtonComponent"; const columns = [ { - Header: 'Findings', columns: [ - { Header: 'Finding', accessor: 'test', - style: {'whiteSpace': 'unset'} // This enables word wrap - }, { Header: 'Pillars', id: "pillars", accessor: x => { const pillars = x.pillars; - const listItems = pillars.map((pillar) => -
  • + const pillarLabels = pillars.map((pillar) => + ); - return
      {listItems}
    ; - } + return {pillarLabels}; + }, + maxWidth: 200, + style: {'whiteSpace': 'unset'} }, + { Header: 'Finding', accessor: 'test', + style: {'whiteSpace': 'unset'} // This enables word wrap + }, + { Header: 'Events', id:"events", accessor: x => { return ; - } + }, + maxWidth: 160, } ] } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js index 47f477dfd..9a5f9723d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js @@ -3,6 +3,7 @@ import PillarLabel from "./PillarLabel"; import DirectivesStatusTable from "./DirectivesStatusTable"; import React, {Fragment} from "react"; import * as PropTypes from "prop-types"; +import {Panel} from "react-bootstrap"; export default class SinglePillarDirectivesStatus extends AuthComponent { render() { @@ -11,10 +12,20 @@ export default class SinglePillarDirectivesStatus extends AuthComponent { } else { return ( - -

    - -
    + + + +

    + 🔽 +

    +
    +
    + + + + + +
    ); } } From 32bc318c69579e4f2e84f8cce3f5613decc780f6 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 27 Aug 2019 14:33:29 +0300 Subject: [PATCH 100/186] Refactored directives to recommendations (still WIP) --- .../components/pages/ZeroTrustReportPage.js | 20 +++++----- .../zerotrust/FindingsTable.js | 2 +- ...Table.js => RecommendationsStatusTable.js} | 8 ++-- .../zerotrust/ReportLegend.js | 2 +- .../zerotrust/SinglePillarDirectivesStatus.js | 37 ------------------- .../SinglePillarRecommendationsStatus.js | 37 +++++++++++++++++++ 6 files changed, 54 insertions(+), 52 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{DirectivesStatusTable.js => RecommendationsStatusTable.js} (88%) delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 7b1369dbc..5c6b24614 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -4,7 +4,7 @@ import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import PillarsOverview from "../report-components/zerotrust/PillarOverview"; import FindingsTable from "../report-components/zerotrust/FindingsTable"; -import SinglePillarDirectivesStatus from "../report-components/zerotrust/SinglePillarDirectivesStatus"; +import SinglePillarRecommendationsStatus from "../report-components/zerotrust/SinglePillarRecommendationsStatus"; import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; @@ -103,18 +103,18 @@ class ZeroTrustReportPageComponent extends AuthComponent { } generateDirectivesSection() { - return (
    -

    Directives

    + return (
    +

    Recommendations

    Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results to understand how the monkey tested your adherence to that recommendation.

    { Object.keys(this.state.directives).map((pillar) => - ) } @@ -126,15 +126,12 @@ class ZeroTrustReportPageComponent extends AuthComponent {

    Summary

    - +

    Get a quick glance of the status for each of Zero Trust's seven pillars.

    - - -
    @@ -145,6 +142,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { + + + + +
    ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 658d6d039..6d43f3cda 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -13,7 +13,7 @@ const columns = [ const pillarLabels = pillars.map((pillar) => ); - return {pillarLabels}; + return
    {pillarLabels}
    ; }, maxWidth: 200, style: {'whiteSpace': 'unset'} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js similarity index 88% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js index 26e272200..67891da64 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/DirectivesStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js @@ -15,7 +15,7 @@ const columns = [ }, maxWidth: 80 }, - { Header: 'Directive', accessor: 'directive', + { Header: 'Recommendation', accessor: 'directive', style: {'whiteSpace': 'unset'} // This enables word wrap }, { Header: 'Tests', id: 'tests', @@ -64,12 +64,12 @@ class TestsStatus extends AuthComponent { } } -export class DirectivesStatusTable extends AuthComponent { +export class RecommendationsStatusTable extends AuthComponent { render() { return ; } } -export default DirectivesStatusTable; +export default RecommendationsStatusTable; -DirectivesStatusTable.propTypes = {directivesStatus: PropTypes.array}; +RecommendationsStatusTable.propTypes = {directivesStatus: PropTypes.array}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index b12cbf5ba..05905bccc 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -13,7 +13,7 @@ class ZeroTrustReportLegend extends Component { -

    🔽 Legend

    +

    Legend

    diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js deleted file mode 100644 index 9a5f9723d..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarDirectivesStatus.js +++ /dev/null @@ -1,37 +0,0 @@ -import AuthComponent from "../../AuthComponent"; -import PillarLabel from "./PillarLabel"; -import DirectivesStatusTable from "./DirectivesStatusTable"; -import React, {Fragment} from "react"; -import * as PropTypes from "prop-types"; -import {Panel} from "react-bootstrap"; - -export default class SinglePillarDirectivesStatus extends AuthComponent { - render() { - if (this.props.directivesStatus.length === 0) { - return null; - } - else { - return ( - - - -

    - 🔽 -

    -
    -
    - - - - - -
    - ); - } - } -} - -SinglePillarDirectivesStatus.propTypes = { - directivesStatus: PropTypes.array, - pillar: PropTypes.string, -}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js new file mode 100644 index 000000000..cd2ce6420 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js @@ -0,0 +1,37 @@ +import AuthComponent from "../../AuthComponent"; +import PillarLabel from "./PillarLabel"; +import RecommendationsStatusTable from "./RecommendationsStatusTable"; +import React from "react"; +import * as PropTypes from "prop-types"; +import {Panel} from "react-bootstrap"; + +export default class SinglePillarRecommendationsStatus extends AuthComponent { + render() { + if (this.props.recommendationsStatus.length === 0) { + return null; + } + else { + return ( + + + +

    + +

    +
    +
    + + + + + +
    + ); + } + } +} + +SinglePillarRecommendationsStatus.propTypes = { + recommendationsStatus: PropTypes.array, + pillar: PropTypes.string, +}; From bb1ee6ff14740e23520bfba9b5c0fb9eaf9b9830 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 28 Aug 2019 10:35:47 +0300 Subject: [PATCH 101/186] Refactored directives to recommendations --- monkey/common/data/zero_trust_consts.py | 70 +++++++++---------- .../cc/models/zero_trust/finding.py | 2 +- .../cc/resources/reporting/report.py | 6 +- monkey/monkey_island/cc/server_config.json | 2 +- .../reporting/test_zero_trust_service.py | 18 ++--- .../services/reporting/zero_trust_service.py | 28 ++++---- .../components/pages/ZeroTrustReportPage.js | 14 ++-- .../zerotrust/RecommendationsStatusTable.js | 6 +- .../SinglePillarRecommendationsStatus.js | 2 +- 9 files changed, 74 insertions(+), 74 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 99b4f2a2c..62d44b554 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -2,8 +2,8 @@ This file contains all the static data relating to Zero Trust. It is mostly used in the zero trust report generation and in creating findings. -This file contains static mappings between zero trust components such as: pillars, directives, tests, statuses. Some of -the mappings are computed when this module is loaded. +This file contains static mappings between zero trust components such as: pillars, recommendations, tests, statuses. +Some of the mappings are computed when this module is loaded. """ AUTOMATION_ORCHESTRATION = u"Automation & Orchestration" @@ -39,22 +39,22 @@ TESTS = ( TEST_DATA_ENDPOINT_ELASTIC ) -DIRECTIVE_DATA_TRANSIT = u"data_transit" -DIRECTIVE_ENDPOINT_SECURITY = u"endpoint_security" -DIRECTIVE_USER_BEHAVIOUR = u"user_behaviour" -DIRECTIVE_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" -DIRECTIVE_SEGMENTATION = u"segmentation" -DIRECTIVES = { - DIRECTIVE_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", - DIRECTIVE_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", - DIRECTIVE_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", - DIRECTIVE_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", - DIRECTIVE_DATA_TRANSIT: u"Secure data at transit by encrypting it." +RECOMMENDATION_DATA_TRANSIT = u"data_transit" +RECOMMENDATION_ENDPOINT_SECURITY = u"endpoint_security" +RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour" +RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" +RECOMMENDATION_SEGMENTATION = u"segmentation" +RECOMMENDATIONS = { + RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", + RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", + RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", + RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", + RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it." } POSSIBLE_STATUSES_KEY = u"possible_statuses" PILLARS_KEY = u"pillars" -DIRECTIVE_KEY = u"directive_key" +RECOMMENDATION_KEY = u"recommendation_key" FINDING_EXPLANATION_BY_STATUS_KEY = u"finding_explanation" TEST_EXPLANATION_KEY = u"explanation" TESTS_MAP = { @@ -64,7 +64,7 @@ TESTS_MAP = { STATUS_CONCLUSIVE: "Monkey performed cross-segment communication. Check firewall rules and logs.", STATUS_POSITIVE: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." }, - DIRECTIVE_KEY: DIRECTIVE_SEGMENTATION, + RECOMMENDATION_KEY: RECOMMENDATION_SEGMENTATION, PILLARS_KEY: [NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_POSITIVE, STATUS_CONCLUSIVE] }, @@ -73,7 +73,7 @@ TESTS_MAP = { FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_INCONCLUSIVE: "Monkey performed malicious actions in the network. Check SOC logs and alerts." }, - DIRECTIVE_KEY: DIRECTIVE_ANALYZE_NETWORK_TRAFFIC, + RECOMMENDATION_KEY: RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC, PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] }, @@ -83,7 +83,7 @@ TESTS_MAP = { STATUS_CONCLUSIVE: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.", STATUS_POSITIVE: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern." }, - DIRECTIVE_KEY: DIRECTIVE_ENDPOINT_SECURITY, + RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] }, @@ -93,7 +93,7 @@ TESTS_MAP = { STATUS_CONCLUSIVE: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.", STATUS_POSITIVE: "Monkey didn't manage to exploit an endpoint." }, - DIRECTIVE_KEY: DIRECTIVE_ENDPOINT_SECURITY, + RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE] }, @@ -102,7 +102,7 @@ TESTS_MAP = { FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_INCONCLUSIVE: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software." }, - DIRECTIVE_KEY: DIRECTIVE_USER_BEHAVIOUR, + RECOMMENDATION_KEY: RECOMMENDATION_USER_BEHAVIOUR, PILLARS_KEY: [PEOPLE, NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] }, @@ -112,7 +112,7 @@ TESTS_MAP = { STATUS_CONCLUSIVE: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.", STATUS_POSITIVE: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them." }, - DIRECTIVE_KEY: DIRECTIVE_DATA_TRANSIT, + RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] }, @@ -122,7 +122,7 @@ TESTS_MAP = { STATUS_CONCLUSIVE: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.", STATUS_POSITIVE: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them." }, - DIRECTIVE_KEY: DIRECTIVE_DATA_TRANSIT, + RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] }, @@ -143,15 +143,15 @@ PILLARS_TO_TESTS = { AUTOMATION_ORCHESTRATION: [] } -DIRECTIVES_TO_TESTS = {} +RECOMMENDATIONS_TO_TESTS = {} -DIRECTIVES_TO_PILLARS = {} +RECOMMENDATIONS_TO_PILLARS = {} def populate_mappings(): populate_pillars_to_tests() - populate_directives_to_tests() - populate_directives_to_pillars() + populate_recommendations_to_tests() + populate_recommendations_to_pillars() def populate_pillars_to_tests(): @@ -161,17 +161,17 @@ def populate_pillars_to_tests(): PILLARS_TO_TESTS[pillar].append(test) -def populate_directives_to_tests(): - for single_directive in DIRECTIVES: - DIRECTIVES_TO_TESTS[single_directive] = [] +def populate_recommendations_to_tests(): + for single_recommendation in RECOMMENDATIONS: + RECOMMENDATIONS_TO_TESTS[single_recommendation] = [] for test, test_info in TESTS_MAP.items(): - DIRECTIVES_TO_TESTS[test_info[DIRECTIVE_KEY]].append(test) + RECOMMENDATIONS_TO_TESTS[test_info[RECOMMENDATION_KEY]].append(test) -def populate_directives_to_pillars(): - for directive, directive_tests in DIRECTIVES_TO_TESTS.items(): - directive_pillars = set() - for test in directive_tests: +def populate_recommendations_to_pillars(): + for recommendation, recommendation_tests in RECOMMENDATIONS_TO_TESTS.items(): + recommendations_pillars = set() + for test in recommendation_tests: for pillar in TESTS_MAP[test][PILLARS_KEY]: - directive_pillars.add(pillar) - DIRECTIVES_TO_PILLARS[directive] = directive_pillars + recommendations_pillars.add(pillar) + RECOMMENDATIONS_TO_PILLARS[recommendation] = recommendations_pillars diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index 5454ad9e1..382f7e5fb 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -14,7 +14,7 @@ from monkey_island.cc.models.zero_trust.event import Event class Finding(Document): """ This model represents a Zero-Trust finding: A result of a test the monkey/island might perform to see if a - specific directive of zero trust is upheld or broken. + specific recommendation of zero trust is upheld or broken. Findings might be Negative ❌ diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index fa2973759..db2f40518 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -14,7 +14,7 @@ REPORT_TYPES = [SECURITY_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] REPORT_DATA_PILLARS = "pillars" REPORT_DATA_FINDINGS = "findings" -REPORT_DATA_DIRECTIVES_STATUS = "directives" +REPORT_DATA_RECOMMENDATIONS_STATUS = "recommendations" __author__ = ["itay.mizeretz", "shay.nehmad"] @@ -33,8 +33,8 @@ class Report(flask_restful.Resource): "grades": ZeroTrustService.get_pillars_grades() } ) - elif report_data == REPORT_DATA_DIRECTIVES_STATUS: - return jsonify(ZeroTrustService.get_directives_status()) + elif report_data == REPORT_DATA_RECOMMENDATIONS_STATUS: + return jsonify(ZeroTrustService.get_recommendations_status()) elif report_data == REPORT_DATA_FINDINGS: return jsonify(ZeroTrustService.get_all_findings()) diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index 420f1b303..7bf106194 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,4 +1,4 @@ { - "server_config": "standard", + "server_config": "testing", "deployment": "develop" } diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py index 30a1a08fe..790f757dd 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py @@ -98,7 +98,7 @@ class TestZeroTrustService(IslandTestCase): self.assertEquals(result, expected) - def test_get_directives_status(self): + def test_get_recommendations_status(self): self.fail_if_not_testing_env() self.clean_finding_db() @@ -108,7 +108,7 @@ class TestZeroTrustService(IslandTestCase): AUTOMATION_ORCHESTRATION: [], DATA: [ { - "directive": DIRECTIVES[DIRECTIVE_DATA_TRANSIT], + "recommendation": RECOMMENDATIONS[RECOMMENDATION_DATA_TRANSIT], "status": STATUS_CONCLUSIVE, "tests": [ { @@ -124,7 +124,7 @@ class TestZeroTrustService(IslandTestCase): ], DEVICES: [ { - "directive": DIRECTIVES[DIRECTIVE_ENDPOINT_SECURITY], + "recommendation": RECOMMENDATIONS[RECOMMENDATION_ENDPOINT_SECURITY], "status": STATUS_CONCLUSIVE, "tests": [ { @@ -140,7 +140,7 @@ class TestZeroTrustService(IslandTestCase): ], NETWORKS: [ { - "directive": DIRECTIVES[DIRECTIVE_SEGMENTATION], + "recommendation": RECOMMENDATIONS[RECOMMENDATION_SEGMENTATION], "status": STATUS_UNEXECUTED, "tests": [ { @@ -150,7 +150,7 @@ class TestZeroTrustService(IslandTestCase): ] }, { - "directive": DIRECTIVES[DIRECTIVE_USER_BEHAVIOUR], + "recommendation": RECOMMENDATIONS[RECOMMENDATION_USER_BEHAVIOUR], "status": STATUS_INCONCLUSIVE, "tests": [ { @@ -160,7 +160,7 @@ class TestZeroTrustService(IslandTestCase): ] }, { - "directive": DIRECTIVES[DIRECTIVE_ANALYZE_NETWORK_TRAFFIC], + "recommendation": RECOMMENDATIONS[RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC], "status": STATUS_UNEXECUTED, "tests": [ { @@ -172,7 +172,7 @@ class TestZeroTrustService(IslandTestCase): ], PEOPLE: [ { - "directive": DIRECTIVES[DIRECTIVE_USER_BEHAVIOUR], + "recommendation": RECOMMENDATIONS[RECOMMENDATION_USER_BEHAVIOUR], "status": STATUS_INCONCLUSIVE, "tests": [ { @@ -184,7 +184,7 @@ class TestZeroTrustService(IslandTestCase): ], "Visibility & Analytics": [ { - "directive": DIRECTIVES[DIRECTIVE_ANALYZE_NETWORK_TRAFFIC], + "recommendation": RECOMMENDATIONS[RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC], "status": STATUS_UNEXECUTED, "tests": [ { @@ -197,7 +197,7 @@ class TestZeroTrustService(IslandTestCase): "Workloads": [] } - self.assertEquals(ZeroTrustService.get_directives_status(), expected) + self.assertEquals(ZeroTrustService.get_recommendations_status(), expected) def test_get_pillars_to_statuses(self): self.fail_if_not_testing_env() diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index 8039d6e16..2db61cdc5 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -39,30 +39,30 @@ class ZeroTrustService(object): return pillar_grade @staticmethod - def get_directives_status(): - all_directive_statuses = {} + def get_recommendations_status(): + all_recommendations_statuses = {} # init with empty lists for pillar in PILLARS: - all_directive_statuses[pillar] = [] + all_recommendations_statuses[pillar] = [] - for directive, directive_tests in DIRECTIVES_TO_TESTS.items(): - for pillar in DIRECTIVES_TO_PILLARS[directive]: - all_directive_statuses[pillar].append( + for recommendation, recommendation_tests in RECOMMENDATIONS_TO_TESTS.items(): + for pillar in RECOMMENDATIONS_TO_PILLARS[recommendation]: + all_recommendations_statuses[pillar].append( { - "directive": DIRECTIVES[directive], - "tests": ZeroTrustService.__get_tests_status(directive_tests), - "status": ZeroTrustService.__get_directive_status(directive_tests) + "recommendation": RECOMMENDATIONS[recommendation], + "tests": ZeroTrustService.__get_tests_status(recommendation_tests), + "status": ZeroTrustService.__get_recommendation_status(recommendation_tests) } ) - return all_directive_statuses + return all_recommendations_statuses @staticmethod - def __get_directive_status(directive_tests): + def __get_recommendation_status(recommendation_tests): worst_status = STATUS_UNEXECUTED all_statuses = set() - for test in directive_tests: + for test in recommendation_tests: all_statuses |= set(Finding.objects(test=test).distinct("status")) for status in all_statuses: @@ -72,9 +72,9 @@ class ZeroTrustService(object): return worst_status @staticmethod - def __get_tests_status(directive_tests): + def __get_tests_status(recommendation_tests): results = [] - for test in directive_tests: + for test in recommendation_tests: test_findings = Finding.objects(test=test) results.append( { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 5c6b24614..6b598357f 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -68,7 +68,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { } else { content =
    {this.generateOverviewSection()} - {this.generateDirectivesSection()} + {this.generateRecommendationsSection()} {this.generateFindingsSection()}
    ; } @@ -102,7 +102,7 @@ class ZeroTrustReportPageComponent extends AuthComponent {
    ); } - generateDirectivesSection() { + generateRecommendationsSection() { return (

    Recommendations

    @@ -110,11 +110,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { to understand how the monkey tested your adherence to that recommendation.

    { - Object.keys(this.state.directives).map((pillar) => + Object.keys(this.state.recommendations).map((pillar) => ) } @@ -152,7 +152,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { } stillLoadingDataFromServer() { - return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.directives === "undefined"; + return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.recommendations === "undefined"; } getZeroTrustReportFromServer() { @@ -164,11 +164,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { findings: res }); }); - this.authFetch('/api/report/zero_trust/directives') + this.authFetch('/api/report/zero_trust/recommendations') .then(res => res.json()) .then(res => { this.setState({ - directives: res + recommendations: res }); }); this.authFetch('/api/report/zero_trust/pillars') diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js index 67891da64..e6a488a4f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js @@ -15,7 +15,7 @@ const columns = [ }, maxWidth: 80 }, - { Header: 'Recommendation', accessor: 'directive', + { Header: 'Recommendation', accessor: 'recommendation', style: {'whiteSpace': 'unset'} // This enables word wrap }, { Header: 'Tests', id: 'tests', @@ -66,10 +66,10 @@ class TestsStatus extends AuthComponent { export class RecommendationsStatusTable extends AuthComponent { render() { - return ; + return ; } } export default RecommendationsStatusTable; -RecommendationsStatusTable.propTypes = {directivesStatus: PropTypes.array}; +RecommendationsStatusTable.propTypes = {recommendationsStatus: PropTypes.array}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js index cd2ce6420..4b437b837 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js @@ -22,7 +22,7 @@ export default class SinglePillarRecommendationsStatus extends AuthComponent { - + From 9538c3f0e6c29342c4eb4e426ee7f8b4f06abbc2 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 28 Aug 2019 10:51:52 +0300 Subject: [PATCH 102/186] Updated the VennDiagram --- .../zerotrust/venn-components/ArcNode.js | 36 +++++---- .../zerotrust/venn-components/CircularNode.js | 48 ++++++------ .../zerotrust/venn-components/Tooltip.js | 43 ----------- .../zerotrust/venn-components/Utility.js | 6 -- .../zerotrust/venn-components/VennDiagram.js | 76 ++++++++----------- 5 files changed, 78 insertions(+), 131 deletions(-) delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js index 95d86db2f..1b4343045 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js @@ -1,5 +1,6 @@ import React from 'react' import PropTypes from 'prop-types'; +import {Popover, OverlayTrigger} from 'react-bootstrap'; import * as d3 from 'd3' class ArcNode extends React.Component { @@ -10,23 +11,28 @@ class ArcNode extends React.Component { let id = prefix + 'Node_' + index; return ( - - {data.tooltip}}> + + - - - {data.label} - - - + /> + + + {data.label} + + + + ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js index 43ef529f3..a5cbc6698 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js @@ -1,36 +1,36 @@ import React from 'react' +import PillarLabel from "../PillarLabel"; +import {Popover, OverlayTrigger} from 'react-bootstrap'; import PropTypes from 'prop-types'; class CircularNode extends React.Component { render() { let {prefix, index, data} = this.props; - let tspans = data.label.split("|").map((d_, i_) => { - let halfTextHeight = data.fontStyle.size * data.label.split("|").length / 2; - let key = 'vennDiagramCircularNode' + index + '_Tspan' + i_; - - return ( - {d_} - ); - }); - let translate = 'translate(' + data.cx + ',' + data.cy + ')'; - return ( - - - - {tspans} - - + {data.tooltip}}> + + + + + + + ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js deleted file mode 100644 index e103af3c3..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Tooltip.js +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types'; - -class Tooltip extends React.Component { - - render() { - const {prefix, bcolor, top, left, display, html} = this.props; - - const style = { - backgroundColor: bcolor, - border: '1px solid #FFFFFF', - borderRadius: '2px', - fontSize: 10, - padding: 8, - display, - opacity: 0.9, - position: 'fixed', - top, - left, - pointerEvents: 'none' - }; - - return ( - -
    - {html.split('\n').map((i_, key_) => { - return
    {i_}
    ; - })} -
    - ); - } -} - -Tooltip.propTypes = { - prefix: PropTypes.string, - bcolor: PropTypes.string, - top: PropTypes.number, - left: PropTypes.number, - display: PropTypes.string, - html: PropTypes.string -}; - -export default Tooltip; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js index 230e277f6..fa9309506 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/Utility.js @@ -1,5 +1,4 @@ export class TypographicUtilities { - static removeAmpersand(string_) { return string_.replace(' & ', 'And'); } @@ -7,9 +6,4 @@ export class TypographicUtilities { static removeBrokenBar(string_) { return string_.replace(/\|/g, ' '); } - - static setTitle(string_) { - return string_.charAt(0).toUpperCase() + string_.substr(1).toLowerCase(); - } - } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 25e57c2dd..6985607a0 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -1,6 +1,5 @@ import React from 'react' import PropTypes from 'prop-types' -import Tooltip from './Tooltip' import CircularNode from './CircularNode' import ArcNode from './ArcNode' import {TypographicUtilities} from './Utility.js' @@ -14,7 +13,6 @@ class VennDiagram extends React.Component { this.width = this.height = 512; - this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C']; this.prefix = 'vennDiagram'; this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed']; this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, { @@ -28,19 +26,27 @@ class VennDiagram extends React.Component { this.width2By7 = 2 * this.width / 7; this.width1By11 = this.width / 11; this.width1By28 = this.width / 28; + this.arcNodesGap = 4; this.toggle = false; this.layout = { - Data: {cx: 0, cy: 0, r: this.thirdWidth - this.offset * 2, offset: {x: 0, y: 0}}, - People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}}, - Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}}, - Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}}, - Workloads: {cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11}}, - VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth}, + Data: {cx: 0, cy: 0, r: this.sixthWidth, offset: {x: 0, y: 0}, popover: 'top'}, + People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}, popover: 'right'}, + Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}, popover: 'left'}, + Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}, popover: 'top'}, + Workloads: { + cx: 0, + cy: -this.width2By7, + r: this.sixthWidth, + offset: {x: 0, y: this.width1By11}, + popover: 'bottom' + }, + VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, popover: 'left'}, AutomationAndOrchestration: { - inner: this.thirdWidth - this.width1By28 * 2, - outer: this.thirdWidth - this.width1By28 + inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap, + outer: this.thirdWidth - this.width1By28 - this.arcNodesGap, + popover: 'right' } }; @@ -65,37 +71,36 @@ class VennDiagram extends React.Component { this.rules = [ { - id: 'Rule #1', f: function (d_) { + id: 'Rule #1', status: 'Unexecuted', hex: '#777777', f: function (d_) { return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0; } }, { - id: 'Rule #2', f: function (d_) { + id: 'Rule #2', status: 'Conclusive', hex: '#D9534F', f: function (d_) { return d_['Conclusive'] > 0; } }, { - id: 'Rule #3', f: function (d_) { + id: 'Rule #3', status: 'Inconclusive', hex: '#F0AD4E', f: function (d_) { return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0; } }, { - id: 'Rule #4', f: function (d_) { + id: 'Rule #4', status: 'Positive', hex: '#5CB85C', f: function (d_) { return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0; } } ]; - this._onScroll = this._onScroll.bind(this); } componentDidMount() { this.parseData(); - window.addEventListener('scroll', this._onScroll); } _onMouseMove(e) { + let self = this; if (!this.toggle) { @@ -108,16 +113,12 @@ class VennDiagram extends React.Component { }); if (e.target.id.includes('Node')) { - html = e.target.dataset.tooltip; - this.divElement.style.cursor = 'pointer'; - hidden = 'block'; + e.target.setAttribute('opacity', 0.95); - bcolor = e.target.getAttribute('fill'); // Set highest z-index e.target.parentNode.parentNode.appendChild(e.target.parentNode); } else { - this.divElement.style.cursor = 'default'; // Return z indices to default Object.keys(this.layout).forEach(function (d_, i_) { @@ -125,25 +126,9 @@ class VennDiagram extends React.Component { }) } - this.setState({ - target: e, - tooltip: { - target: e.target, - bcolor: bcolor, - top: e.clientY + 8, - left: e.clientX + 8, - display: hidden, - html: html - } - }); } } - _onScroll(e) { - this.divElement.style.cursor = 'default'; - this.setState({target: null, tooltip: {target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: ''}}); - } - _onClick(e) { this.toggle = this.state.tooltip.target === e.target; @@ -152,6 +137,7 @@ class VennDiagram extends React.Component { } parseData() { + let self = this; let data = []; const omit = (prop, {[prop]: _, ...rest}) => rest; @@ -159,9 +145,8 @@ class VennDiagram extends React.Component { this.props.pillarsGrades.forEach((d_, i_) => { let params = omit('pillar', d_); - let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_] || 0), 0); let key = TypographicUtilities.removeAmpersand(d_.pillar); - let html = self.buildTooltipHtmlContent(d_); + let html = self.buildTooltipHtmlContent(params); let rule = null; for (let j = 0; j < self.rules.length; j++) { @@ -181,7 +166,13 @@ class VennDiagram extends React.Component { } buildTooltipHtmlContent(object_) { - return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', ''); + + var out = []; + Object.keys(object_).forEach(function (d_) { + out.push([d_ + ': ' + object_[d_],
    ]); + }); + return out; + } setLayoutElement(rule_, key_, html_, d_) { @@ -195,7 +186,8 @@ class VennDiagram extends React.Component { this.layout[key_].fontStyle = this.fontStyles[1]; } - this.layout[key_].hex = this.colors[rule_]; + this.layout[key_].hex = this.rules[rule_].hex; + this.layout[key_].status = this.rules[rule_].status; this.layout[key_].label = d_.pillar + this.suffices[rule_]; this.layout[key_].node = d_; this.layout[key_].tooltip = html_; @@ -220,7 +212,6 @@ class VennDiagram extends React.Component { ); } else { d_.label = TypographicUtilities.removeBrokenBar(d_.label); - return ( {nodes} -
    ) } From 04005b14d7c548a963b043c80b3e7e44d539ff13 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 28 Aug 2019 11:04:50 +0300 Subject: [PATCH 103/186] Fixed style name errors --- .../report-components/zerotrust/FindingsTable.js | 2 +- .../report-components/zerotrust/ReportLegend.js | 2 +- .../zerotrust/SinglePillarRecommendationsStatus.js | 2 +- .../zerotrust/venn-components/VennDiagram.js | 10 ++-------- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 6d43f3cda..353689b86 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -13,7 +13,7 @@ const columns = [ const pillarLabels = pillars.map((pillar) => ); - return
    {pillarLabels}
    ; + return
    {pillarLabels}
    ; }, maxWidth: 200, style: {'whiteSpace': 'unset'} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index 05905bccc..0564b8364 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -35,7 +35,7 @@ class ZeroTrustReportLegend extends Component { with the tests results.

    Statuses

    -
      +
      • diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js index 4b437b837..1ce02afce 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js @@ -15,7 +15,7 @@ export default class SinglePillarRecommendationsStatus extends AuthComponent { -

        +

        diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 6985607a0..c7a13d52a 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -35,14 +35,8 @@ class VennDiagram extends React.Component { People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}, popover: 'right'}, Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}, popover: 'left'}, Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}, popover: 'top'}, - Workloads: { - cx: 0, - cy: -this.width2By7, - r: this.sixthWidth, - offset: {x: 0, y: this.width1By11}, - popover: 'bottom' - }, - VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, popover: 'left'}, + Workloads: {cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11}, popover: 'bottom'}, + VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, popover: 'right'}, AutomationAndOrchestration: { inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap, outer: this.thirdWidth - this.width1By28 - this.arcNodesGap, From dfebf5e841ff91287b03f6261bcd4cd12638f45f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 28 Aug 2019 11:59:33 +0300 Subject: [PATCH 104/186] Replaced Conclusive with Failed, and Positive with Passed --- monkey/common/data/zero_trust_consts.py | 36 +++---- .../cc/models/zero_trust/finding.py | 13 +-- .../models/zero_trust/segmentation_finding.py | 30 +++--- .../cc/models/zero_trust/test_finding.py | 6 +- .../zero_trust/test_segmentation_finding.py | 10 +- monkey/monkey_island/cc/server_config.json | 2 +- .../reporting/test_zero_trust_service.py | 98 +++++++++---------- .../services/reporting/zero_trust_service.py | 8 +- .../cc/services/telemetry/processing/state.py | 4 +- .../zero_trust_tests/antivirus_existence.py | 6 +- .../zero_trust_tests/data_endpoints.py | 8 +- .../zero_trust_tests/machine_exploited.py | 4 +- .../zero_trust_tests/segmentation.py | 8 +- .../test_segmentation_zt_tests.py | 15 +-- .../components/pages/ZeroTrustReportPage.js | 9 +- .../zerotrust/PillarOverview.js | 18 +--- .../zerotrust/RecommendationsStatusTable.js | 14 +-- .../zerotrust/ReportLegend.js | 4 +- .../zerotrust/StatusLabel.js | 8 +- .../zerotrust/StatusesToPillarsSummary.js | 9 +- .../zerotrust/ZeroTrustPillars.js | 4 +- .../zerotrust/venn-components/VennDiagram.js | 17 ++-- 22 files changed, 153 insertions(+), 178 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 62d44b554..8c67f5ebf 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -16,11 +16,11 @@ DATA = u"Data" PILLARS = (DATA, PEOPLE, NETWORKS, DEVICES, WORKLOADS, VISIBILITY_ANALYTICS, AUTOMATION_ORCHESTRATION) STATUS_UNEXECUTED = u"Unexecuted" -STATUS_POSITIVE = u"Positive" +STATUS_PASSED = u"Passed" STATUS_INCONCLUSIVE = u"Inconclusive" -STATUS_CONCLUSIVE = u"Conclusive" +STATUS_FAILED = u"Failed" # Don't change order! The statuses are ordered by importance/severity. -ORDERED_TEST_STATUSES = [STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE, STATUS_POSITIVE, STATUS_UNEXECUTED] +ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_INCONCLUSIVE, STATUS_PASSED, STATUS_UNEXECUTED] TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic" TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http" @@ -61,12 +61,12 @@ TESTS_MAP = { TEST_SEGMENTATION: { TEST_EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_CONCLUSIVE: "Monkey performed cross-segment communication. Check firewall rules and logs.", - STATUS_POSITIVE: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." + STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.", + STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." }, RECOMMENDATION_KEY: RECOMMENDATION_SEGMENTATION, PILLARS_KEY: [NETWORKS], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_POSITIVE, STATUS_CONCLUSIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED] }, TEST_MALICIOUS_ACTIVITY_TIMELINE: { TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", @@ -80,22 +80,22 @@ TESTS_MAP = { TEST_ENDPOINT_SECURITY_EXISTS: { TEST_EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_CONCLUSIVE: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.", - STATUS_POSITIVE: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern." + STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.", + STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern." }, RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, TEST_MACHINE_EXPLOITED: { TEST_EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_CONCLUSIVE: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.", - STATUS_POSITIVE: "Monkey didn't manage to exploit an endpoint." + STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.", + STATUS_PASSED: "Monkey didn't manage to exploit an endpoint." }, RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_INCONCLUSIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_INCONCLUSIVE] }, TEST_SCHEDULED_EXECUTION: { TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.", @@ -109,22 +109,22 @@ TESTS_MAP = { TEST_DATA_ENDPOINT_ELASTIC: { TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_CONCLUSIVE: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.", - STATUS_POSITIVE: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them." + STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.", + STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them." }, RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT, PILLARS_KEY: [DATA], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, TEST_DATA_ENDPOINT_HTTP: { TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to HTTP servers.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_CONCLUSIVE: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.", - STATUS_POSITIVE: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them." + STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.", + STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them." }, RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT, PILLARS_KEY: [DATA], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_CONCLUSIVE, STATUS_POSITIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, } diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index 382f7e5fb..4027690c8 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -16,12 +16,13 @@ class Finding(Document): This model represents a Zero-Trust finding: A result of a test the monkey/island might perform to see if a specific recommendation of zero trust is upheld or broken. - Findings might be - Negative ❌ - Conclusive, meaning that we are sure that something is wrong (example: segmentation issue). - Inconclusive, meaning that we need the user to check something himself (example: 2FA logs, AV missing). - Positive ✔ - Conclusive, meaning that we are sure that something is correct (example: Monkey failed exploiting). + Findings might have the following statuses: + Failed ❌ + Meaning that we are sure that something is wrong (example: segmentation issue). + Inconclusive ⁉ + Meaning that we need the user to check something himself (example: 2FA logs, AV missing). + Passed ✔ + Meaning that we are sure that something is correct (example: Monkey failed exploiting). This class has 2 main section: * The schema section defines the DB fields in the document. This is the data of the object. diff --git a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py index 428af72cb..716548453 100644 --- a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py @@ -1,30 +1,34 @@ from mongoengine import StringField -from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_CONCLUSIVE, STATUS_POSITIVE +from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_FAILED, STATUS_PASSED from monkey_island.cc.models.zero_trust.finding import Finding def need_to_overwrite_status(saved_status, new_status): - return (saved_status == STATUS_POSITIVE) and (new_status == STATUS_CONCLUSIVE) + return (saved_status == STATUS_PASSED) and (new_status == STATUS_FAILED) class SegmentationFinding(Finding): - """ - trying to add conclusive: - If the finding doesn't exist at all: create conclusive - else: - if positive, turn to conclusive - add event - - trying to add positive: - If the finding doesn't exist at all: create positive - else: add event - """ first_subnet = StringField() second_subnet = StringField() @staticmethod def create_or_add_to_existing_finding(subnets, status, segmentation_event): + """ + If you're trying to add a Failed finding: + If the finding doesn't exist at all: create failed + else: + if pass, turn to fail + add event + + If you're trying to add a Passed finding: + If the finding doesn't exist at all: create Passed + else: add event + + :param subnets: the 2 subnets of this finding. + :param status: STATUS_PASSED or STATUS_FAILED + :param segmentation_event: The specific event + """ assert len(subnets) == 2 # Sort them so A -> B and B -> A segmentation findings will be the same one. diff --git a/monkey/monkey_island/cc/models/zero_trust/test_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_finding.py index 6ac0a9fc8..88a33d5d3 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_finding.py @@ -19,7 +19,7 @@ class TestFinding(IslandTestCase): self.clean_finding_db() with self.assertRaises(ValidationError): - _ = Finding.save_finding(test="bla bla", status=STATUS_CONCLUSIVE, events=[]) + _ = Finding.save_finding(test="bla bla", status=STATUS_FAILED, events=[]) with self.assertRaises(ValidationError): _ = Finding.save_finding(test=TEST_SEGMENTATION, status="bla bla", events=[]) @@ -32,7 +32,7 @@ class TestFinding(IslandTestCase): event_example = Event.create_event( title="Event Title", message="event message", event_type=EVENT_TYPE_MONKEY_NETWORK) - Finding.save_finding(test=TEST_SEGMENTATION, status=STATUS_CONCLUSIVE, events=[event_example]) + Finding.save_finding(test=TEST_SEGMENTATION, status=STATUS_FAILED, events=[event_example]) self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 1) - self.assertEquals(len(Finding.objects(status=STATUS_CONCLUSIVE)), 1) + self.assertEquals(len(Finding.objects(status=STATUS_FAILED)), 1) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py index ad3ff9b97..80e564a17 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py @@ -1,4 +1,4 @@ -from common.data.zero_trust_consts import STATUS_CONCLUSIVE, EVENT_TYPE_MONKEY_NETWORK +from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.testing.IslandTestCase import IslandTestCase from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding @@ -16,7 +16,7 @@ class TestSegmentationFinding(IslandTestCase): SegmentationFinding.create_or_add_to_existing_finding( subnets=[first_segment, second_segment], - status=STATUS_CONCLUSIVE, + status=STATUS_FAILED, segmentation_event=event ) @@ -26,7 +26,7 @@ class TestSegmentationFinding(IslandTestCase): SegmentationFinding.create_or_add_to_existing_finding( # !!! REVERSE ORDER subnets=[second_segment, first_segment], - status=STATUS_CONCLUSIVE, + status=STATUS_FAILED, segmentation_event=event ) @@ -36,7 +36,7 @@ class TestSegmentationFinding(IslandTestCase): SegmentationFinding.create_or_add_to_existing_finding( # !!! REVERSE ORDER subnets=[first_segment, third_segment], - status=STATUS_CONCLUSIVE, + status=STATUS_FAILED, segmentation_event=event ) @@ -45,7 +45,7 @@ class TestSegmentationFinding(IslandTestCase): SegmentationFinding.create_or_add_to_existing_finding( # !!! REVERSE ORDER subnets=[second_segment, third_segment], - status=STATUS_CONCLUSIVE, + status=STATUS_FAILED, segmentation_event=event ) diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index 7bf106194..420f1b303 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,4 +1,4 @@ { - "server_config": "testing", + "server_config": "standard", "deployment": "develop" } diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py index 790f757dd..2bd74c796 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py @@ -7,9 +7,9 @@ from monkey_island.cc.testing.IslandTestCase import IslandTestCase def save_example_findings(): # arrange - Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, []) # devices positive = 1 - Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_POSITIVE, []) # devices positive = 2 - Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_CONCLUSIVE, []) # devices conclusive = 1 + Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, []) # devices passed = 1 + Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, []) # devices passed = 2 + Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_FAILED, []) # devices failed = 1 # devices unexecuted = 1 # people inconclusive = 1 # networks inconclusive = 1 @@ -17,22 +17,22 @@ def save_example_findings(): # people inconclusive = 2 # networks inconclusive = 2 Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_INCONCLUSIVE, []) - # data conclusive 1 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) - # data conclusive 2 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) - # data conclusive 3 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) - # data conclusive 4 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) - # data conclusive 5 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_CONCLUSIVE, []) + # data failed 1 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + # data failed 2 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + # data failed 3 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + # data failed 4 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + # data failed 5 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) # data inconclusive 1 Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, []) # data inconclusive 2 Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, []) - # data positive 1 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_POSITIVE, []) + # data passed 1 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_PASSED, []) class TestZeroTrustService(IslandTestCase): @@ -44,52 +44,52 @@ class TestZeroTrustService(IslandTestCase): expected = [ { - "Conclusive": 5, - "Inconclusive": 2, - "Positive": 1, - "Unexecuted": 1, + STATUS_FAILED: 5, + STATUS_INCONCLUSIVE: 2, + STATUS_PASSED: 1, + STATUS_UNEXECUTED: 1, "pillar": "Data" }, { - "Conclusive": 0, - "Inconclusive": 2, - "Positive": 0, - "Unexecuted": 0, + STATUS_FAILED: 0, + STATUS_INCONCLUSIVE: 2, + STATUS_PASSED: 0, + STATUS_UNEXECUTED: 0, "pillar": "People" }, { - "Conclusive": 0, - "Inconclusive": 2, - "Positive": 0, - "Unexecuted": 2, + STATUS_FAILED: 0, + STATUS_INCONCLUSIVE: 2, + STATUS_PASSED: 0, + STATUS_UNEXECUTED: 2, "pillar": "Networks" }, { - "Conclusive": 1, - "Inconclusive": 0, - "Positive": 2, - "Unexecuted": 1, + STATUS_FAILED: 1, + STATUS_INCONCLUSIVE: 0, + STATUS_PASSED: 2, + STATUS_UNEXECUTED: 1, "pillar": "Devices" }, { - "Conclusive": 0, - "Inconclusive": 0, - "Positive": 0, - "Unexecuted": 0, + STATUS_FAILED: 0, + STATUS_INCONCLUSIVE: 0, + STATUS_PASSED: 0, + STATUS_UNEXECUTED: 0, "pillar": "Workloads" }, { - "Conclusive": 0, - "Inconclusive": 0, - "Positive": 0, - "Unexecuted": 1, + STATUS_FAILED: 0, + STATUS_INCONCLUSIVE: 0, + STATUS_PASSED: 0, + STATUS_UNEXECUTED: 1, "pillar": "Visibility & Analytics" }, { - "Conclusive": 0, - "Inconclusive": 0, - "Positive": 0, - "Unexecuted": 0, + STATUS_FAILED: 0, + STATUS_INCONCLUSIVE: 0, + STATUS_PASSED: 0, + STATUS_UNEXECUTED: 0, "pillar": "Automation & Orchestration" } ] @@ -109,14 +109,14 @@ class TestZeroTrustService(IslandTestCase): DATA: [ { "recommendation": RECOMMENDATIONS[RECOMMENDATION_DATA_TRANSIT], - "status": STATUS_CONCLUSIVE, + "status": STATUS_FAILED, "tests": [ { "status": STATUS_UNEXECUTED, "test": TESTS_MAP[TEST_DATA_ENDPOINT_ELASTIC][TEST_EXPLANATION_KEY] }, { - "status": STATUS_CONCLUSIVE, + "status": STATUS_FAILED, "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY] } ] @@ -125,10 +125,10 @@ class TestZeroTrustService(IslandTestCase): DEVICES: [ { "recommendation": RECOMMENDATIONS[RECOMMENDATION_ENDPOINT_SECURITY], - "status": STATUS_CONCLUSIVE, + "status": STATUS_FAILED, "tests": [ { - "status": STATUS_CONCLUSIVE, + "status": STATUS_FAILED, "test": TESTS_MAP[TEST_ENDPOINT_SECURITY_EXISTS][TEST_EXPLANATION_KEY] }, { @@ -221,12 +221,12 @@ class TestZeroTrustService(IslandTestCase): expected = { AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED, - DEVICES: STATUS_CONCLUSIVE, + DEVICES: STATUS_FAILED, NETWORKS: STATUS_INCONCLUSIVE, PEOPLE: STATUS_INCONCLUSIVE, VISIBILITY_ANALYTICS: STATUS_UNEXECUTED, WORKLOADS: STATUS_UNEXECUTED, - DATA: STATUS_CONCLUSIVE + DATA: STATUS_FAILED } self.assertEquals(ZeroTrustService.get_pillars_to_statuses(), expected) diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index 2db61cdc5..d8f6c87e9 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -16,9 +16,9 @@ class ZeroTrustService(object): all_findings = Finding.objects() pillar_grade = { "pillar": pillar, - STATUS_CONCLUSIVE: 0, + STATUS_FAILED: 0, STATUS_INCONCLUSIVE: 0, - STATUS_POSITIVE: 0, + STATUS_PASSED: 0, STATUS_UNEXECUTED: 0 } @@ -123,9 +123,9 @@ class ZeroTrustService(object): @staticmethod def get_statuses_to_pillars(): results = { - STATUS_CONCLUSIVE: [], + STATUS_FAILED: [], STATUS_INCONCLUSIVE: [], - STATUS_POSITIVE: [], + STATUS_PASSED: [], STATUS_UNEXECUTED: [] } for pillar in PILLARS: diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py index 46176c9b9..f6461dd3f 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/state.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py @@ -1,6 +1,6 @@ from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import \ - test_positive_findings_for_unreached_segments + test_passed_findings_for_unreached_segments def process_state_telemetry(telemetry_json): @@ -12,4 +12,4 @@ def process_state_telemetry(telemetry_json): NodeService.set_monkey_dead(monkey, False) if telemetry_json['data']['done']: - test_positive_findings_for_unreached_segments(telemetry_json) + test_passed_findings_for_unreached_segments(telemetry_json) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index c86838476..acfdf1643 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -1,7 +1,7 @@ import json from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, \ - STATUS_POSITIVE, STATUS_CONCLUSIVE, TEST_ENDPOINT_SECURITY_EXISTS + STATUS_PASSED, STATUS_FAILED, TEST_ENDPOINT_SECURITY_EXISTS from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.finding import Finding @@ -28,9 +28,9 @@ def test_antivirus_existence(telemetry_json): )) if len(av_processes) > 0: - test_status = STATUS_POSITIVE + test_status = STATUS_PASSED else: - test_status = STATUS_CONCLUSIVE + test_status = STATUS_FAILED Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index c7b0f5219..65d044b19 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -11,8 +11,8 @@ HTTP_SERVERS_SERVICES_NAMES = ['tcp-80'] def test_open_data_endpoints(telemetry_json): services = telemetry_json["data"]["machine"]["services"] current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) - found_http_server_status = STATUS_POSITIVE - found_elastic_search_server = STATUS_POSITIVE + found_http_server_status = STATUS_PASSED + found_elastic_search_server = STATUS_PASSED events = [ Event.create_event( @@ -32,7 +32,7 @@ def test_open_data_endpoints(telemetry_json): event_type=EVENT_TYPE_ISLAND )) if service_name in HTTP_SERVERS_SERVICES_NAMES: - found_http_server_status = STATUS_CONCLUSIVE + found_http_server_status = STATUS_FAILED events.append(Event.create_event( title="Scan telemetry analysis", message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( @@ -43,7 +43,7 @@ def test_open_data_endpoints(telemetry_json): event_type=EVENT_TYPE_ISLAND )) if service_name in 'elastic-search-9200': - found_elastic_search_server = STATUS_CONCLUSIVE + found_elastic_search_server = STATUS_FAILED events.append(Event.create_event( title="Scan telemetry analysis", message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index 3a5f78bcb..d4f8c53c1 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -18,7 +18,7 @@ def test_machine_exploited(telemetry_json): ) ] - status = STATUS_POSITIVE + status = STATUS_PASSED if telemetry_json['data']['result']: events.append( @@ -31,7 +31,7 @@ def test_machine_exploited(telemetry_json): event_type=EVENT_TYPE_MONKEY_NETWORK, timestamp=telemetry_json['timestamp']) ) - status = STATUS_CONCLUSIVE + status = STATUS_FAILED Finding.save_finding( test=TEST_MACHINE_EXPLOITED, diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index bb447d992..763c46b2f 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -1,7 +1,7 @@ import itertools from six import text_type -from common.data.zero_trust_consts import STATUS_CONCLUSIVE, EVENT_TYPE_MONKEY_NETWORK, STATUS_POSITIVE, \ +from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_PASSED, \ EVENT_TYPE_ISLAND from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst, get_ip_if_in_subnet @@ -45,7 +45,7 @@ def test_segmentation_violation(scan_telemetry_json): event = get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet) SegmentationFinding.create_or_add_to_existing_finding( subnets=[source_subnet, target_subnet], - status=STATUS_CONCLUSIVE, + status=STATUS_FAILED, segmentation_event=event ) @@ -64,7 +64,7 @@ def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, t ) -def test_positive_findings_for_unreached_segments(state_telemetry_json): +def test_passed_findings_for_unreached_segments(state_telemetry_json): flat_all_subnets = [item for sublist in get_config_network_segments_as_subnet_groups() for item in sublist] current_monkey = Monkey.get_single_monkey_by_guid(state_telemetry_json['monkey_guid']) create_or_add_findings_for_all_pairs(flat_all_subnets, current_monkey) @@ -87,7 +87,7 @@ def create_or_add_findings_for_all_pairs(all_subnets, current_monkey): for subnet_pair in all_subnets_pairs_for_this_monkey: SegmentationFinding.create_or_add_to_existing_finding( subnets=list(subnet_pair), - status=STATUS_POSITIVE, + status=STATUS_PASSED, segmentation_event=Event.create_event( "Segmentation test done", message="Monkey on {hostname} is done attempting cross-segment communications from `{src_seg}` " diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py index f345d4482..5f986e3b5 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py @@ -1,6 +1,6 @@ import uuid -from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_POSITIVE, STATUS_CONCLUSIVE, \ +from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_PASSED, STATUS_FAILED, \ EVENT_TYPE_MONKEY_NETWORK from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.event import Event @@ -32,20 +32,15 @@ class TestSegmentationTests(IslandTestCase): create_or_add_findings_for_all_pairs(all_subnets, monkey) # There are 2 subnets in which the monkey is NOT - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_POSITIVE)), 2) + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_PASSED)), 2) # This is a monkey from 2nd subnet communicated with 1st subnet. SegmentationFinding.create_or_add_to_existing_finding( [FIRST_SUBNET, SECOND_SUBNET], - STATUS_CONCLUSIVE, + STATUS_FAILED, Event.create_event(title="sdf", message="asd", event_type=EVENT_TYPE_MONKEY_NETWORK) ) - print("Printing all segmentation findings") - all_findings = Finding.objects(test=TEST_SEGMENTATION) - for f in all_findings: - print(f.to_json()) - - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_POSITIVE)), 1) - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_CONCLUSIVE)), 1) + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_PASSED)), 1) + self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_FAILED)), 1) self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 2) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 6b598357f..fd6175ee1 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -12,6 +12,7 @@ import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToP import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; import ZeroTrustReportLegend from "../report-components/zerotrust/ReportLegend"; +import {ZeroTrustStatuses} from "../report-components/zerotrust/ZeroTrustPillars"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -179,14 +180,6 @@ class ZeroTrustReportPageComponent extends AuthComponent { }); }); } - - anyIssuesFound() { - const severe = function(finding) { - return (finding.status === "Conclusive" || finding.status === "Inconclusive"); - }; - - return this.state.findings.some(severe); - } } export default ZeroTrustReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index 824885cad..7cefcab61 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -1,27 +1,11 @@ import React, {Component} from "react"; -import PillarLabel from "./PillarLabel"; import * as PropTypes from "prop-types"; import ResponsiveVennDiagram from "./venn-components/ResponsiveVennDiagram"; -const columns = [ - { - Header: 'Pillar Grading', - columns: [ - { Header: 'Pillar', id: 'Pillar', accessor: x => { - return (); - }}, - { Header: 'Conclusive', accessor: 'Conclusive'}, - { Header: 'Inconclusive', accessor: 'Inconclusive'}, - { Header: 'Unexecuted', accessor: 'Unexecuted'}, - { Header: 'Positive', accessor: 'Positive'}, - ] - } -]; - class PillarOverview extends Component { render() { return (
        - +
        ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js index e6a488a4f..d8b1a99b5 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js @@ -4,6 +4,7 @@ import AuthComponent from "../../AuthComponent"; import 'styles/ZeroTrustPillars.css' import StatusLabel from "./StatusLabel"; import * as PropTypes from "prop-types"; +import {ZeroTrustStatuses} from "./ZeroTrustPillars"; const columns = [ @@ -30,17 +31,12 @@ const columns = [ class TestsStatus extends AuthComponent { render() { - const positiveStatus = "Positive"; - const conclusiveStatus = "Conclusive"; - const inconclusiveStatus = "Inconclusive"; - const unexecutedStatus = "Unexecuted"; - return ( - {this.getFilteredTestsByStatusIfAny(conclusiveStatus)} - {this.getFilteredTestsByStatusIfAny(inconclusiveStatus)} - {this.getFilteredTestsByStatusIfAny(positiveStatus)} - {this.getFilteredTestsByStatusIfAny(unexecutedStatus)} + {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.failed)} + {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.inconclusive)} + {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.passed)} + {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.unexecuted)} ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index 0564b8364..0f731192f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -38,7 +38,7 @@ class ZeroTrustReportLegend extends Component {
        • - +
          {"\t"}The test failed; the monkeys found something wrong.
        • @@ -50,7 +50,7 @@ class ZeroTrustReportLegend extends Component {
        • - +
          {"\t"}This status means the test passed 🙂
        • diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js index 6b3243280..12c65b728 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -2,16 +2,16 @@ import React, {Component} from "react"; import * as PropTypes from "prop-types"; const statusToIcon = { - "Positive": "fa-check", + "Passed": "fa-check", "Inconclusive": "fa-exclamation-triangle", - "Conclusive": "fa-bomb", + "Failed": "fa-bomb", "Unexecuted": "fa-question", }; export const statusToLabelType = { - "Positive": "label-success", + "Passed": "label-success", "Inconclusive": "label-warning", - "Conclusive": "label-danger", + "Failed": "label-danger", "Unexecuted": "label-default", }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js index 8cdf12af5..4a597566c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js @@ -2,14 +2,15 @@ import React, {Component, Fragment} from "react"; import PillarLabel from "./PillarLabel"; import StatusLabel from "./StatusLabel"; import * as PropTypes from "prop-types"; +import {ZeroTrustStatuses} from "./ZeroTrustPillars"; export default class StatusesToPillarsSummary extends Component { render() { return (
          - {this.getStatusSummary("Conclusive")} - {this.getStatusSummary("Inconclusive")} - {this.getStatusSummary("Positive")} - {this.getStatusSummary("Unexecuted")} + {this.getStatusSummary(ZeroTrustStatuses.failed)} + {this.getStatusSummary(ZeroTrustStatuses.inconclusive)} + {this.getStatusSummary(ZeroTrustStatuses.passed)} + {this.getStatusSummary(ZeroTrustStatuses.unexecuted)}
          ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js index 6bc425be0..2165916da 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js @@ -9,9 +9,9 @@ export const ZeroTrustPillars = { }; export const ZeroTrustStatuses = { - conclusive: "Conclusive", + failed: "Failed", inconclusive: "Inconclusive", - positive: "Positive", + passed: "Passed", unexecuted: "Unexecuted" }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index c7a13d52a..32ae72deb 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -4,6 +4,7 @@ import CircularNode from './CircularNode' import ArcNode from './ArcNode' import {TypographicUtilities} from './Utility.js' import './VennDiagram.css' +import {ZeroTrustStatuses} from "../ZeroTrustPillars"; class VennDiagram extends React.Component { constructor(props_) { @@ -14,7 +15,7 @@ class VennDiagram extends React.Component { this.width = this.height = 512; this.prefix = 'vennDiagram'; - this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed']; + this.suffices = ['', '|tests are|failed', '|tests were|inconclusive', '|tests|performed']; this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, { size: Math.max(6, this.width / 52), color: 'black' @@ -65,23 +66,23 @@ class VennDiagram extends React.Component { this.rules = [ { - id: 'Rule #1', status: 'Unexecuted', hex: '#777777', f: function (d_) { - return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0; + id: 'Rule #1', status: ZeroTrustStatuses.unexecuted, hex: '#777777', f: function (d_) { + return d_[ZeroTrustStatuses.failed] + d_[ZeroTrustStatuses.inconclusive] + d_[ZeroTrustStatuses.passed] === 0; } }, { - id: 'Rule #2', status: 'Conclusive', hex: '#D9534F', f: function (d_) { - return d_['Conclusive'] > 0; + id: 'Rule #2', status: ZeroTrustStatuses.failed, hex: '#D9534F', f: function (d_) { + return d_[ZeroTrustStatuses.failed] > 0; } }, { id: 'Rule #3', status: 'Inconclusive', hex: '#F0AD4E', f: function (d_) { - return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0; + return d_[ZeroTrustStatuses.failed] === 0 && d_['Inconclusive'] > 0; } }, { - id: 'Rule #4', status: 'Positive', hex: '#5CB85C', f: function (d_) { - return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0; + id: 'Rule #4', status: ZeroTrustStatuses.passed, hex: '#5CB85C', f: function (d_) { + return d_[ZeroTrustStatuses.passed] + d_[ZeroTrustStatuses.unexecuted] >= 2 && d_[ZeroTrustStatuses.passed] * d_[ZeroTrustStatuses.unexecuted] > 0; } } From a4a9f0c4916725041d884c4439d9b9ccebe594c0 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 28 Aug 2019 14:10:30 +0300 Subject: [PATCH 105/186] Separated findings per status --- .../components/pages/ZeroTrustReportPage.js | 4 +- .../zerotrust/EventsAndButtonComponent.js | 2 +- .../zerotrust/ExportEventsButton.js | 2 +- .../zerotrust/FindingsSection.js | 38 ++++++++++++++ .../zerotrust/FindingsTable.js | 52 +++++++++---------- .../zerotrust/RecommendationsStatusTable.js | 4 +- 6 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index fd6175ee1..a901c63cf 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -3,7 +3,7 @@ import {Col, Grid, Row} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; import PillarsOverview from "../report-components/zerotrust/PillarOverview"; -import FindingsTable from "../report-components/zerotrust/FindingsTable"; +import FindingsSection from "../report-components/zerotrust/FindingsSection"; import SinglePillarRecommendationsStatus from "../report-components/zerotrust/SinglePillarRecommendationsStatus"; import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; @@ -99,7 +99,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper insight as to what exactly happened during this test.

          - +
        ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js index 0b96f8fc2..867efc049 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsAndButtonComponent.js @@ -26,7 +26,7 @@ export default class EventsAndButtonComponent extends Component {
        - { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js index b75516e2c..e5c3b1b6f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js @@ -4,7 +4,7 @@ import * as PropTypes from "prop-types"; export default class ExportEventsButton extends Component { render() { - return { @@ -41,7 +41,7 @@ export default class EventsAndButtonComponent extends Component { } -EventsAndButtonComponent.propTypes = { +EventsButtonsComponent.propTypes = { events: PropTypes.array, exportFilename: PropTypes.string, }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js index e5c3b1b6f..b75516e2c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js @@ -4,7 +4,7 @@ import * as PropTypes from "prop-types"; export default class ExportEventsButton extends Component { render() { - return - { - const content = JSON.stringify(this.props.events, null, 2); - const blob = new Blob([content], {type: "text/plain;charset=utf-8"}); - FileSaver.saveAs(blob, this.props.exportFilename + ".json"); - }}/>
        ); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js index 5512d810b..aee1fb7f2 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js @@ -1,14 +1,9 @@ import React from 'react' import PropTypes from 'prop-types'; -import { Popover, OverlayTrigger } from 'react-bootstrap'; +import {Popover, OverlayTrigger} from 'react-bootstrap'; import * as d3 from 'd3' class ArcNode extends React.Component { - - handleClick(e_) { this.props.disableHover(this.refs.overlay); } - handleOver(e_) { if(this.props.hover) { this.refs.overlay.show(); } } - handleOut(e_) { if(this.props.hover){ this.refs.overlay.hide(); } } - render() { let {prefix, index, data} = this.props; @@ -16,31 +11,51 @@ class ArcNode extends React.Component { let id = prefix + 'Node_' + index; return ( - - {data.tooltip}} rootClose> - + {data.tooltip}} rootClose> + - - - - {data.icon + '\u2000'} - {data.label} - - - + /> + + + + {data.icon + '\u2000'} + {data.label} + + + ); } + + + handleClick(e_) { + this.props.disableHover(this.refs.overlay); + } + + handleOver(e_) { + if (this.props.hover) { + this.refs.overlay.show(); + } + } + + handleOut(e_) { + if (this.props.hover) { + this.refs.overlay.hide(); + } + } } ArcNode.propTypes = { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js index a74cd74b1..5c84d95a5 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js @@ -1,40 +1,59 @@ import React from 'react' import PillarLabel from "../PillarLabel"; -import { Popover, OverlayTrigger } from 'react-bootstrap'; +import {Popover, OverlayTrigger} from 'react-bootstrap'; import PropTypes from 'prop-types'; class CircularNode extends React.Component { - - handleClick(e_) { this.props.disableHover(this.refs.overlay); } - handleOver(e_) { if(this.props.hover) { this.refs.overlay.show(); } } - handleOut(e_) { if(this.props.hover){ this.refs.overlay.hide(); } } - render() { let {prefix, index, data} = this.props; let translate = 'translate(' + data.cx + ',' + data.cy + ')'; return ( - - {data.tooltip}} rootClose> - + {data.tooltip}} rootClose> + - - - - - + /> + + + + + ); } + + + handleClick(e_) { + this.props.disableHover(this.refs.overlay); + } + + handleOver(e_) { + if (this.props.hover) { + this.refs.overlay.show(); + } + } + + handleOut(e_) { + if (this.props.hover) { + this.refs.overlay.hide(); + } + } + } CircularNode.propTypes = { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 25d36131d..c1d5d2a68 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -12,11 +12,14 @@ class VennDiagram extends React.Component { this.state = {hover: true, currentPopover: undefined}; this._disableHover = this._disableHover.bind(this); - + this.width = this.height = 512; this.prefix = 'vennDiagram'; - this.fontStyles = [{size: Math.max(9, this.width / 28), color: 'white'}, { size: Math.max(6, this.width / 38), color: 'white'}, { size: Math.max(6, this.width / 48), color: 'white'} ]; + this.fontStyles = [{size: Math.max(9, this.width / 28), color: 'white'}, { + size: Math.max(6, this.width / 38), + color: 'white' + }, {size: Math.max(6, this.width / 48), color: 'white'}]; this.offset = this.width / 16; this.thirdWidth = this.width / 3; @@ -25,14 +28,43 @@ class VennDiagram extends React.Component { this.width1By11 = this.width / 11; this.width1By28 = this.width / 28; this.arcNodesGap = 4; - + this.layout = { Data: {cx: 0, cy: 0, r: this.width11By2, offset: {x: 0, y: 0}, popover: 'top'}, - People: {cx: -this.width2By7, cy: 0, r: this.width11By2, offset: {x: this.width1By11 + this.fontStyles[1].size / 5 * 3, y: 0}, popover: 'right'}, - Networks: {cx: this.width2By7, cy: 0, r: this.width11By2, offset: {x: -this.width1By11 - this.fontStyles[1].size / 5 * 3, y: 0}, popover: 'left'}, - Devices: {cx: 0, cy: this.width2By7, r: this.width11By2, offset: {x: 0, y: -this.width1By11 + this.fontStyles[1].size / 6 * 3}, popover: 'top'}, - Workloads: {cx: 0, cy: -this.width2By7, r: this.width11By2, offset: {x: 0, y: this.width1By11}, popover: 'bottom' }, - VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, icon: '\uf070', popover: 'left'}, + People: { + cx: -this.width2By7, + cy: 0, + r: this.width11By2, + offset: {x: this.width1By11 + this.fontStyles[1].size / 5 * 3, y: 0}, + popover: 'right' + }, + Networks: { + cx: this.width2By7, + cy: 0, + r: this.width11By2, + offset: {x: -this.width1By11 - this.fontStyles[1].size / 5 * 3, y: 0}, + popover: 'left' + }, + Devices: { + cx: 0, + cy: this.width2By7, + r: this.width11By2, + offset: {x: 0, y: -this.width1By11 + this.fontStyles[1].size / 6 * 3}, + popover: 'top' + }, + Workloads: { + cx: 0, + cy: -this.width2By7, + r: this.width11By2, + offset: {x: 0, y: this.width1By11}, + popover: 'bottom' + }, + VisibilityAndAnalytics: { + inner: this.thirdWidth - this.width1By28, + outer: this.thirdWidth, + icon: '\uf070', + popover: 'right' + }, AutomationAndOrchestration: { inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap, outer: this.thirdWidth - this.width1By28 - this.arcNodesGap, @@ -85,51 +117,61 @@ class VennDiagram extends React.Component { } - componentDidMount() { this.parseData(); if(this.state.currentPopover !== undefined) { this.state.currentPopover.show(); } } - - _disableHover(ref_) { this.setState({hover: false, currentPopover: ref_, data: this.state.data }); } - + componentDidMount() { + this.parseData(); + if (this.state.currentPopover !== undefined) { + this.state.currentPopover.show(); + } + } + + _disableHover(ref_) { + this.setState({hover: false, currentPopover: ref_, data: this.state.data}); + } + _onMouseMove(e) { - + let self = this; - + let hidden = 'none'; let html = ''; let bcolor = '#DEDEDE'; - - if(this.state.currentPopover !== undefined) { this.state.currentPopover.show(); } + + if (this.state.currentPopover !== undefined) { + this.state.currentPopover.show(); + } document.querySelectorAll('circle, path').forEach((d_, i_) => { - d_.setAttribute('opacity', "0.8"); + d_.setAttribute('opacity', "0.8"); }); if (e.target.id.includes('Node')) { - e.target.setAttribute('opacity', 0.95); + e.target.setAttribute('opacity', 0.95); + + // Set highest z-index + e.target.parentNode.parentNode.appendChild(e.target.parentNode); - // Set highest z-index - e.target.parentNode.parentNode.appendChild(e.target.parentNode); - } else { - // Return z indices to default - Object.keys(this.layout).forEach(function (d_, i_) { - document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); }) + // Return z indices to default + Object.keys(this.layout).forEach(function (d_, i_) { + document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); + }) } } - + _onClick(e) { - - if (!e.target.id.includes('Node')) { - - this.state.currentPopover.hide(); - this.setState({hover: true, currentPopover: undefined, data: this.state.data }); - } + + if (!e.target.id.includes('Node')) { + + this.state.currentPopover.hide(); + this.setState({hover: true, currentPopover: undefined, data: this.state.data}); + } } - + parseData() { - + let self = this; let data = []; const omit = (prop, {[prop]: _, ...rest}) => rest; @@ -157,23 +199,25 @@ class VennDiagram extends React.Component { this.setState({hover: true, activePopover: undefined, data: data}); this.render(); } - + buildTooltipHtmlContent(object_) { - return Object.keys(object_).map((key_, i_) => { return (

        {key_}: {object_[key_]}

        ) }) + return Object.keys(object_).map((key_, i_) => { + return (

        {key_}: {object_[key_]}

        ) + }) } setLayoutElement(rule_, key_, html_, d_) { - - if(rule_ === null) { console.log(Error('The node scores are invalid, please check the data or the rules set.')); } + + if (rule_ === null) { + console.log(Error('The node scores are invalid, please check the data or the rules set.')); + } if (key_ === 'Data') { this.layout[key_].fontStyle = this.fontStyles[0]; - } - else if(this.layout[key_].hasOwnProperty('cx')){ + } else if (this.layout[key_].hasOwnProperty('cx')) { this.layout[key_].fontStyle = this.fontStyles[1]; - } - else { + } else { this.layout[key_].fontStyle = this.fontStyles[2]; } @@ -218,11 +262,12 @@ class VennDiagram extends React.Component { }); return ( -
        this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)}> - this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} + onClick={this._onClick.bind(this)}> + {nodes} - +
        ) } From 5e059f78eb7ff57ce6db49c93adc4106f726a631 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 10:51:33 +0300 Subject: [PATCH 112/186] Various UI Improvements --- .../components/pages/ZeroTrustReportPage.js | 10 ++++++++-- ...entsButtonsComponent.js => EventsButton.js} | 6 +++--- .../zerotrust/ExportEventsButton.js | 2 +- .../zerotrust/FindingsSection.js | 2 +- .../zerotrust/FindingsTable.js | 4 ++-- .../zerotrust/ReportLegend.js | 18 ++++++------------ 6 files changed, 21 insertions(+), 21 deletions(-) rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{EventsButtonsComponent.js => EventsButton.js} (87%) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 34fec0079..7ca40bd5f 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -113,12 +113,18 @@ class ZeroTrustReportPageComponent extends AuthComponent { grades={this.state.pillars.grades}/> - + - +

        What am I seeing?

        +

        + The Zero Trust eXtended framework categorizes its recommendations into 7 pillars. Infection Monkey + Zero Trust edition tests some of those recommendations. The tests that the monkey executes + produce findings. The tests, recommendations and pillars are then granted a status in accordance + with the tests results. +

        diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButtonsComponent.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js similarity index 87% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButtonsComponent.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js index 10027c888..33c6a2384 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButtonsComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js @@ -5,7 +5,7 @@ import FileSaver from "file-saver"; import * as PropTypes from "prop-types"; import ExportEventsButton from "./ExportEventsButton"; -export default class EventsButtonsComponent extends Component { +export default class EventsButton extends Component { constructor(props) { super(props); this.state = { @@ -27,7 +27,7 @@ export default class EventsButtonsComponent extends Component {
    @@ -36,7 +36,7 @@ export default class EventsButtonsComponent extends Component { } -EventsButtonsComponent.propTypes = { +EventsButton.propTypes = { events: PropTypes.array, exportFilename: PropTypes.string, }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js index b75516e2c..bb6fc6c45 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js @@ -7,7 +7,7 @@ export default class ExportEventsButton extends Component { return } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js index 3f4260f98..eed4c0f8c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js @@ -1,6 +1,6 @@ import React, {Component, Fragment} from "react"; import PillarLabel from "./PillarLabel"; -import EventsButtonsComponent from "./EventsButtonsComponent"; +import EventsButton from "./EventsButton"; import {ZeroTrustStatuses} from "./ZeroTrustPillars"; import {FindingsTable} from "./FindingsTable"; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 514e48a98..924ddf631 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -3,7 +3,7 @@ import StatusLabel from "./StatusLabel"; import PaginatedTable from "../common/PaginatedTable"; import * as PropTypes from "prop-types"; import PillarLabel from "./PillarLabel"; -import EventsButtonsComponent from "./EventsButtonsComponent"; +import EventsButton from "./EventsButton"; const columns = [ { @@ -16,7 +16,7 @@ const columns = [ { Header: 'Events', id: "events", accessor: x => { - return ; + return ; }, maxWidth: 160, }, diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index 0f731192f..143120793 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -27,41 +27,35 @@ class ZeroTrustReportLegend extends Component { getLegendContent() { return
    -

    What is this?

    -

    - The Zero Trust eXtended framework categorizes its recommendations into 7 pillars. Infection Monkey - Zero Trust edition tests some of those recommendations. The tests that the monkey executes - produce findings. The tests, recommendations and pillars are then granted a status in accordance - with the tests results. -

    Statuses

    • - {"\t"}The test failed; the monkeys found something wrong. + {"\t"}Some tests failed; the monkeys found something wrong.
    • - {"\t"}The test was executed, but manual verification is required to determine the results. + {"\t"}The test ran; manual verification is required to determine the results.
    • - {"\t"}This status means the test passed 🙂 + {"\t"}The test passed, so this is OK 🙂
    • - {"\t"}This status means the test wasn't executed. Some of the tests can be activated or deactivated using - the configuration. + {"\t"}This status means the test wasn't executed.
    +
    + Some of the tests can be activated using the configuration.
    ; } } From 1d5a4d20cee4a86713540819307b4c77174cd5af Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 11:29:04 +0300 Subject: [PATCH 113/186] Added aggregate finding --- .../cc/models/zero_trust/aggregate_finding.py | 23 ++++++++ .../cc/models/zero_trust/finding.py | 4 ++ .../zero_trust/test_aggregate_finding.py | 53 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py create mode 100644 monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py diff --git a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py new file mode 100644 index 000000000..613b9a4a2 --- /dev/null +++ b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py @@ -0,0 +1,23 @@ +from monkey_island.cc.models.zero_trust.finding import Finding + + +class AggregateFinding(Finding): + @staticmethod + def create_or_add_to_existing(test, status, events): + """ + Create a new finding or add the events to an existing one if it's the same (same meaning same status and same + test). + + :raises: Assertion error if this is used when there's more then one finding which fits the query - this is not + when this function should be used. + """ + existing_findings = Finding.objects(test=test, status=status) + assert (len(existing_findings) < 2), "More than one finding exists for {}:{}".format(test, status) + + if len(existing_findings) == 0: + Finding.save_finding(test, status, events) + else: + # Now we know for sure this is the only one + orig_finding = existing_findings[0] + orig_finding.add_events(events) + orig_finding.save() diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index 4027690c8..441d22e3a 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -54,3 +54,7 @@ class Finding(Document): finding.save() return finding + + def add_events(self, events): + # type: (list) -> None + self.events.extend(events) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py new file mode 100644 index 000000000..b32e8ad53 --- /dev/null +++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py @@ -0,0 +1,53 @@ +from common.data.zero_trust_consts import * +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.models.zero_trust.finding import Finding +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +class TestAggregateFinding(IslandTestCase): + def test_create_or_add_to_existing(self): + self.fail_if_not_testing_env() + self.clean_finding_db() + + test = TEST_MALICIOUS_ACTIVITY_TIMELINE + status = STATUS_INCONCLUSIVE + events = [Event.create_event("t", "t", EVENT_TYPE_ISLAND)] + self.assertEquals(len(Finding.objects(test=test, status=status)), 0) + + AggregateFinding.create_or_add_to_existing(test, status, events) + + self.assertEquals(len(Finding.objects(test=test, status=status)), 1) + self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1) + + AggregateFinding.create_or_add_to_existing(test, status, events) + + self.assertEquals(len(Finding.objects(test=test, status=status)), 1) + self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2) + + def test_create_or_add_to_existing_2_tests_already_exist(self): + self.fail_if_not_testing_env() + self.clean_finding_db() + + test = TEST_MALICIOUS_ACTIVITY_TIMELINE + status = STATUS_INCONCLUSIVE + event = Event.create_event("t", "t", EVENT_TYPE_ISLAND) + events = [event] + self.assertEquals(len(Finding.objects(test=test, status=status)), 0) + + Finding.save_finding(test, status, events) + + self.assertEquals(len(Finding.objects(test=test, status=status)), 1) + self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1) + + AggregateFinding.create_or_add_to_existing(test, status, events) + + self.assertEquals(len(Finding.objects(test=test, status=status)), 1) + self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2) + + Finding.save_finding(test, status, events) + + self.assertEquals(len(Finding.objects(test=test, status=status)), 2) + + with self.assertRaises(AssertionError): + AggregateFinding.create_or_add_to_existing(test, status, events) From 1fddd4abbfd9f68654c987d8ea5e547affd93bd9 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 11:44:08 +0300 Subject: [PATCH 114/186] Made some findings aggregate findings to improve readability of Findings table. --- .../telemetry/zero_trust_tests/antivirus_existence.py | 6 ++++-- .../services/telemetry/zero_trust_tests/data_endpoints.py | 8 ++++---- .../telemetry/zero_trust_tests/machine_exploited.py | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index acfdf1643..5795a2773 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -3,8 +3,8 @@ import json from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, \ STATUS_PASSED, STATUS_FAILED, TEST_ENDPOINT_SECURITY_EXISTS from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event -from monkey_island.cc.models.zero_trust.finding import Finding from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES @@ -31,7 +31,9 @@ def test_antivirus_existence(telemetry_json): test_status = STATUS_PASSED else: test_status = STATUS_FAILED - Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events) + AggregateFinding.create_or_add_to_existing( + test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events + ) def filter_av_processes(telemetry_json): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index 65d044b19..be240f150 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -2,8 +2,8 @@ import json from common.data.zero_trust_consts import * from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event -from monkey_island.cc.models.zero_trust.finding import Finding HTTP_SERVERS_SERVICES_NAMES = ['tcp-80'] @@ -54,19 +54,19 @@ def test_open_data_endpoints(telemetry_json): event_type=EVENT_TYPE_ISLAND )) - Finding.save_finding( + AggregateFinding.create_or_add_to_existing( test=TEST_DATA_ENDPOINT_HTTP, status=found_http_server_status, events=events ) - Finding.save_finding( + AggregateFinding.create_or_add_to_existing( test=TEST_DATA_ENDPOINT_ELASTIC, status=found_elastic_search_server, events=events ) - Finding.save_finding( + AggregateFinding.create_or_add_to_existing( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, status=STATUS_INCONCLUSIVE, events=events diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index d4f8c53c1..d6416c0ef 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -1,5 +1,6 @@ from common.data.zero_trust_consts import * from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.finding import Finding @@ -39,7 +40,7 @@ def test_machine_exploited(telemetry_json): events=events ) - Finding.save_finding( + AggregateFinding.create_or_add_to_existing( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, status=STATUS_INCONCLUSIVE, events=events From 3f2d5b1479b9845541e7ff6c4eeddb696da324ab Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 12:08:58 +0300 Subject: [PATCH 115/186] Aggregate passed exploit attempts tests (which means failed exploiting) --- .../zero_trust_tests/machine_exploited.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index d6416c0ef..1afe8bfe1 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -34,11 +34,19 @@ def test_machine_exploited(telemetry_json): ) status = STATUS_FAILED - Finding.save_finding( - test=TEST_MACHINE_EXPLOITED, - status=status, - events=events - ) + # aggregate only passed tests (which means exploit failed). Each successful exploit gets its own finding. + if status == STATUS_FAILED: + Finding.save_finding( + test=TEST_MACHINE_EXPLOITED, + status=status, + events=events + ) + else: + AggregateFinding.create_or_add_to_existing( + test=TEST_MACHINE_EXPLOITED, + status=status, + events=events + ) AggregateFinding.create_or_add_to_existing( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, From e7953defdcc2ffb34d8ed37bc7a54147c2b89aa5 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 12:09:26 +0300 Subject: [PATCH 116/186] Now that findings are aggregated, added events amount counter badge --- .../zerotrust/EventsButton.js | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js index 33c6a2384..66f1ae3d3 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js @@ -1,9 +1,7 @@ -import React, {Component} from "react"; +import React, {Component, Fragment} from "react"; import EventsModal from "./EventsModal"; -import {Button} from "react-bootstrap"; -import FileSaver from "file-saver"; +import {Badge, Button} from "react-bootstrap"; import * as PropTypes from "prop-types"; -import ExportEventsButton from "./ExportEventsButton"; export default class EventsButton extends Component { constructor(props) { @@ -22,16 +20,21 @@ export default class EventsButton extends Component { }; render() { - return ( -
    - + let eventsAmountBadge; + if (this.props.events.length > 10) { + eventsAmountBadge = 9+; + } else { + eventsAmountBadge = {this.props.events.length}; + } + return +
    -
    - ); + ; } } From f7d66e0ebc3d6ec8c71e1832b206ff825a9dd684 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 12:10:27 +0300 Subject: [PATCH 117/186] Realize the previous idea was stupid and aggregate all exploit attempts based on status alone --- .../zero_trust_tests/machine_exploited.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index 1afe8bfe1..ef300d82b 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -34,19 +34,11 @@ def test_machine_exploited(telemetry_json): ) status = STATUS_FAILED - # aggregate only passed tests (which means exploit failed). Each successful exploit gets its own finding. - if status == STATUS_FAILED: - Finding.save_finding( - test=TEST_MACHINE_EXPLOITED, - status=status, - events=events - ) - else: - AggregateFinding.create_or_add_to_existing( - test=TEST_MACHINE_EXPLOITED, - status=status, - events=events - ) + AggregateFinding.create_or_add_to_existing( + test=TEST_MACHINE_EXPLOITED, + status=status, + events=events + ) AggregateFinding.create_or_add_to_existing( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, From 146c87c33814c426437195bd72ed6dab09256ffc Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 12:18:42 +0300 Subject: [PATCH 118/186] Optimize import --- .../cc/services/telemetry/zero_trust_tests/machine_exploited.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index ef300d82b..57007cd09 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -2,7 +2,6 @@ from common.data.zero_trust_consts import * from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event -from monkey_island.cc.models.zero_trust.finding import Finding def test_machine_exploited(telemetry_json): From 1550742d4d46bf92b3fd5ad06ce424a4c783bcd1 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 15:40:29 +0300 Subject: [PATCH 119/186] Added tunneling zero trust test --- monkey/common/data/zero_trust_consts.py | 17 ++++++++-- .../services/telemetry/processing/tunnel.py | 5 ++- .../cc/services/telemetry/processing/utils.py | 5 +++ .../telemetry/zero_trust_tests/tunneling.py | 31 +++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 18c02e818..3cf9cda92 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -29,6 +29,7 @@ TEST_ENDPOINT_SECURITY_EXISTS = u"endpoint_security_exists" TEST_SCHEDULED_EXECUTION = u"scheduled_execution" TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline" TEST_SEGMENTATION = u"segmentation" +TEST_TUNNELING = u"tunneling" TESTS = ( TEST_SEGMENTATION, TEST_MALICIOUS_ACTIVITY_TIMELINE, @@ -36,7 +37,8 @@ TESTS = ( TEST_ENDPOINT_SECURITY_EXISTS, TEST_MACHINE_EXPLOITED, TEST_DATA_ENDPOINT_HTTP, - TEST_DATA_ENDPOINT_ELASTIC + TEST_DATA_ENDPOINT_ELASTIC, + TEST_TUNNELING ) RECOMMENDATION_DATA_TRANSIT = u"data_transit" @@ -44,12 +46,14 @@ RECOMMENDATION_ENDPOINT_SECURITY = u"endpoint_security" RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour" RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" RECOMMENDATION_SEGMENTATION = u"segmentation" +RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies" RECOMMENDATIONS = { RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", - RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it." + RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.", + RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible." } POSSIBLE_STATUSES_KEY = u"possible_statuses" @@ -127,6 +131,15 @@ TESTS_MAP = { PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, + TEST_TUNNELING: { + TEST_EXPLANATION_KEY: u"The Monkey tried to tunnel traffic using other monkeys.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_FAILED: "Monkey was tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them." + }, + RECOMMENDATION_KEY: RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES, + PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED] + }, } EVENT_TYPE_ISLAND = "island" diff --git a/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py index ed57f3c7b..1598b144a 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py @@ -1,10 +1,13 @@ from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field +from monkey_island.cc.services.telemetry.zero_trust_tests.tunneling import test_tunneling_violation def process_tunnel_telemetry(telemetry_json): + test_tunneling_violation(telemetry_json) monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"] if telemetry_json['data']['proxy'] is not None: - tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "") + tunnel_host_ip = get_tunnel_host_ip_from_proxy_field(telemetry_json) NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip) else: NodeService.unset_all_monkey_tunnels(monkey_id) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/utils.py b/monkey/monkey_island/cc/services/telemetry/processing/utils.py index 9bafb505f..466b81bf1 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/utils.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/utils.py @@ -11,3 +11,8 @@ def get_edge_by_scan_or_exploit_telemetry(telemetry_json): dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name) return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"]) + + +def get_tunnel_host_ip_from_proxy_field(telemetry_json): + tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "") + return tunnel_host_ip diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py new file mode 100644 index 000000000..2c9be5e1f --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py @@ -0,0 +1,31 @@ +from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_INCONCLUSIVE, \ + TEST_MALICIOUS_ACTIVITY_TIMELINE +from monkey_island.cc.models import Monkey +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding +from monkey_island.cc.models.zero_trust.event import Event +from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field + + +def test_tunneling_violation(tunnel_telemetry_json): + if tunnel_telemetry_json['data']['proxy'] is not None: + # Monkey is tunneling, create findings + tunnel_host_ip = get_tunnel_host_ip_from_proxy_field(tunnel_telemetry_json) + current_monkey = Monkey.get_single_monkey_by_guid(tunnel_telemetry_json['monkey_guid']) + tunneling_events = [Event.create_event( + title="Tunneling event", + message="Monkey on {hostname} tunneled traffic through {proxy}.".format( + hostname=current_monkey.hostname, proxy=tunnel_host_ip), + event_type=EVENT_TYPE_MONKEY_NETWORK, + timestamp=tunnel_telemetry_json['timestamp'] + )] + AggregateFinding.create_or_add_to_existing( + test=TEST_TUNNELING, + status=STATUS_FAILED, + events=tunneling_events + ) + + AggregateFinding.create_or_add_to_existing( + test=TEST_MALICIOUS_ACTIVITY_TIMELINE, + status=STATUS_INCONCLUSIVE, + events=tunneling_events + ) From 68c0f590ac1c335973328820462cb3f4a4d8d967 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 1 Sep 2019 16:17:22 +0300 Subject: [PATCH 120/186] Removing the refresh data interval when leaving report page --- .../cc/ui/src/components/pages/ZeroTrustReportPage.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index 7ca40bd5f..cec603f0e 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -8,11 +8,9 @@ import SinglePillarRecommendationsStatus from "../report-components/zerotrust/Si import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; -import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary"; import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; import ZeroTrustReportLegend from "../report-components/zerotrust/ReportLegend"; -import {ZeroTrustStatuses} from "../report-components/zerotrust/ZeroTrustPillars"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -27,7 +25,14 @@ class ZeroTrustReportPageComponent extends AuthComponent { componentDidMount() { this.updatePageState(); - setInterval(this.updatePageState, 8000) + const refreshInterval = setInterval(this.updatePageState, 8000) + this.setState( + {refreshDataIntervalHandler: refreshInterval} + ) + } + + componentWillUnmount() { + clearInterval(this.state.refreshDataIntervalHandler); } updateMonkeysRunning = () => { From 98764f0291d43e2450f90f1ff82013e2b7304456 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 10:07:26 +0300 Subject: [PATCH 121/186] Added post breach processing dict and extracted consts to common --- monkey/common/data/post_breach_consts.py | 2 ++ .../infection_monkey/post_breach/actions/add_user.py | 5 +++-- .../post_breach/actions/users_custom_pba.py | 3 ++- .../cc/services/telemetry/processing/post_breach.py | 10 ++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 monkey/common/data/post_breach_consts.py diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py new file mode 100644 index 000000000..8262757ca --- /dev/null +++ b/monkey/common/data/post_breach_consts.py @@ -0,0 +1,2 @@ +POST_BREACH_BACKDOOR_USER = "Backdoor user" +POST_BREACH_FILE_EXECUTION = "File execution" diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index ff7ae3a50..ce05371a6 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -1,8 +1,9 @@ import datetime + +from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER from infection_monkey.post_breach.pba import PBA from infection_monkey.config import WormConfiguration - __author__ = 'danielg' LINUX_COMMANDS = ['useradd', '-M', '--expiredate', @@ -16,6 +17,6 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add, class BackdoorUser(PBA): def __init__(self): - super(BackdoorUser, self).__init__("Backdoor user", + super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER, linux_cmd=' '.join(LINUX_COMMANDS), windows_cmd=WINDOWS_COMMANDS) diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py index a388813ab..468a2b29b 100644 --- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py +++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py @@ -1,6 +1,7 @@ import os import logging +from common.data.post_breach_consts import POST_BREACH_FILE_EXECUTION from infection_monkey.utils import is_windows_os from infection_monkey.post_breach.pba import PBA from infection_monkey.control import ControlClient @@ -27,7 +28,7 @@ class UsersPBA(PBA): Defines user's configured post breach action. """ def __init__(self): - super(UsersPBA, self).__init__("File execution") + super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION) self.filename = '' if not is_windows_os(): # Add linux commands to PBA's diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py index b086d5ff4..2515c2d30 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -1,7 +1,17 @@ from monkey_island.cc.database import mongo +from common.data.post_breach_consts import * + +POST_BREACH_TELEMETRY_PROCESSING_FUNCS = { + # `lambda *args, **kwargs: None` is a no-op. + POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None, + POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None, +} def process_post_breach_telemetry(telemetry_json): mongo.db.monkey.update( {'guid': telemetry_json['monkey_guid']}, {'$push': {'pba_results': telemetry_json['data']}}) + + if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS: + POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json) From 36ad6fc4416b8df75902ce47eb559fd58e84ca54 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 10:08:04 +0300 Subject: [PATCH 122/186] Revert "Added post breach processing dict and extracted consts to common" This reverts commit 98764f0291d43e2450f90f1ff82013e2b7304456. --- monkey/common/data/post_breach_consts.py | 2 -- .../infection_monkey/post_breach/actions/add_user.py | 5 ++--- .../post_breach/actions/users_custom_pba.py | 3 +-- .../cc/services/telemetry/processing/post_breach.py | 10 ---------- 4 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 monkey/common/data/post_breach_consts.py diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py deleted file mode 100644 index 8262757ca..000000000 --- a/monkey/common/data/post_breach_consts.py +++ /dev/null @@ -1,2 +0,0 @@ -POST_BREACH_BACKDOOR_USER = "Backdoor user" -POST_BREACH_FILE_EXECUTION = "File execution" diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index ce05371a6..ff7ae3a50 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -1,9 +1,8 @@ import datetime - -from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER from infection_monkey.post_breach.pba import PBA from infection_monkey.config import WormConfiguration + __author__ = 'danielg' LINUX_COMMANDS = ['useradd', '-M', '--expiredate', @@ -17,6 +16,6 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add, class BackdoorUser(PBA): def __init__(self): - super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER, + super(BackdoorUser, self).__init__("Backdoor user", linux_cmd=' '.join(LINUX_COMMANDS), windows_cmd=WINDOWS_COMMANDS) diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py index 468a2b29b..a388813ab 100644 --- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py +++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py @@ -1,7 +1,6 @@ import os import logging -from common.data.post_breach_consts import POST_BREACH_FILE_EXECUTION from infection_monkey.utils import is_windows_os from infection_monkey.post_breach.pba import PBA from infection_monkey.control import ControlClient @@ -28,7 +27,7 @@ class UsersPBA(PBA): Defines user's configured post breach action. """ def __init__(self): - super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION) + super(UsersPBA, self).__init__("File execution") self.filename = '' if not is_windows_os(): # Add linux commands to PBA's diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py index 2515c2d30..b086d5ff4 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -1,17 +1,7 @@ from monkey_island.cc.database import mongo -from common.data.post_breach_consts import * - -POST_BREACH_TELEMETRY_PROCESSING_FUNCS = { - # `lambda *args, **kwargs: None` is a no-op. - POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None, - POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None, -} def process_post_breach_telemetry(telemetry_json): mongo.db.monkey.update( {'guid': telemetry_json['monkey_guid']}, {'$push': {'pba_results': telemetry_json['data']}}) - - if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS: - POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json) From 30b74675a5e9d02f9b69727a44a988af28a7221b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 10:08:52 +0300 Subject: [PATCH 123/186] Revert "Revert "Added post breach processing dict and extracted consts to common"" This reverts commit 36ad6fc4416b8df75902ce47eb559fd58e84ca54. --- monkey/common/data/post_breach_consts.py | 2 ++ .../infection_monkey/post_breach/actions/add_user.py | 5 +++-- .../post_breach/actions/users_custom_pba.py | 3 ++- .../cc/services/telemetry/processing/post_breach.py | 10 ++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 monkey/common/data/post_breach_consts.py diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py new file mode 100644 index 000000000..8262757ca --- /dev/null +++ b/monkey/common/data/post_breach_consts.py @@ -0,0 +1,2 @@ +POST_BREACH_BACKDOOR_USER = "Backdoor user" +POST_BREACH_FILE_EXECUTION = "File execution" diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index ff7ae3a50..ce05371a6 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -1,8 +1,9 @@ import datetime + +from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER from infection_monkey.post_breach.pba import PBA from infection_monkey.config import WormConfiguration - __author__ = 'danielg' LINUX_COMMANDS = ['useradd', '-M', '--expiredate', @@ -16,6 +17,6 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add, class BackdoorUser(PBA): def __init__(self): - super(BackdoorUser, self).__init__("Backdoor user", + super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER, linux_cmd=' '.join(LINUX_COMMANDS), windows_cmd=WINDOWS_COMMANDS) diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py index a388813ab..468a2b29b 100644 --- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py +++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py @@ -1,6 +1,7 @@ import os import logging +from common.data.post_breach_consts import POST_BREACH_FILE_EXECUTION from infection_monkey.utils import is_windows_os from infection_monkey.post_breach.pba import PBA from infection_monkey.control import ControlClient @@ -27,7 +28,7 @@ class UsersPBA(PBA): Defines user's configured post breach action. """ def __init__(self): - super(UsersPBA, self).__init__("File execution") + super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION) self.filename = '' if not is_windows_os(): # Add linux commands to PBA's diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py index b086d5ff4..2515c2d30 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -1,7 +1,17 @@ from monkey_island.cc.database import mongo +from common.data.post_breach_consts import * + +POST_BREACH_TELEMETRY_PROCESSING_FUNCS = { + # `lambda *args, **kwargs: None` is a no-op. + POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None, + POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None, +} def process_post_breach_telemetry(telemetry_json): mongo.db.monkey.update( {'guid': telemetry_json['monkey_guid']}, {'$push': {'pba_results': telemetry_json['data']}}) + + if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS: + POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json) From faf6da15bb55f7417635dd37cb2a65276e74680c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 10:20:52 +0300 Subject: [PATCH 124/186] Improved doc, refactored names and added test case for segmentation_utils CR --- monkey/common/network/segmentation_utils.py | 9 +++++++-- .../common/network/segmentation_utils_test.py | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/monkey/common/network/segmentation_utils.py b/monkey/common/network/segmentation_utils.py index 6569d636b..aeff7f135 100644 --- a/monkey/common/network/segmentation_utils.py +++ b/monkey/common/network/segmentation_utils.py @@ -11,8 +11,13 @@ def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet): return get_ip_if_in_subnet(ip_addresses, source_subnet) -def get_ip_if_in_subnet(ip_addresses, source_subnet): +def get_ip_if_in_subnet(ip_addresses, subnet): + """ + :param ip_addresses: IP address list. + :param subnet: Subnet to check if one of ip_addresses is in there. This is common.network.network_range.NetworkRange + :return: The first IP in ip_addresses which is in the subnet. + """ for ip_address in ip_addresses: - if source_subnet.is_in_range(ip_address): + if subnet.is_in_range(ip_address): return ip_address return None diff --git a/monkey/common/network/segmentation_utils_test.py b/monkey/common/network/segmentation_utils_test.py index 7ef3e4450..56a560922 100644 --- a/monkey/common/network/segmentation_utils_test.py +++ b/monkey/common/network/segmentation_utils_test.py @@ -8,12 +8,23 @@ class TestSegmentationUtils(IslandTestCase): self.fail_if_not_testing_env() source = CidrRange("1.1.1.0/24") target = CidrRange("2.2.2.0/24") - self.assertIsNone(get_ip_in_src_and_not_in_dst( - [text_type("2.2.2.2")], source, target - )) + + # IP not in both self.assertIsNone(get_ip_in_src_and_not_in_dst( [text_type("3.3.3.3"), text_type("4.4.4.4")], source, target )) + + # IP not in source, in target + self.assertIsNone(get_ip_in_src_and_not_in_dst( + [text_type("2.2.2.2")], source, target + )) + + # IP in source, not in target self.assertIsNotNone(get_ip_in_src_and_not_in_dst( [text_type("8.8.8.8"), text_type("1.1.1.1")], source, target )) + + # IP in both subnets + self.assertIsNone(get_ip_in_src_and_not_in_dst( + [text_type("8.8.8.8"), text_type("1.1.1.1")], source, source + )) From 9fc2bf886d7f9c68b1e17eac0c432c8f2eed7efb Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 11:00:10 +0300 Subject: [PATCH 125/186] Extracted ES_SERVICE to const CR --- monkey/common/data/network_consts.py | 2 ++ monkey/infection_monkey/exploit/elasticgroovy.py | 3 ++- monkey/infection_monkey/network/elasticfinger.py | 2 +- .../cc/services/telemetry/zero_trust_tests/data_endpoints.py | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 monkey/common/data/network_consts.py diff --git a/monkey/common/data/network_consts.py b/monkey/common/data/network_consts.py new file mode 100644 index 000000000..5fc9d6d8a --- /dev/null +++ b/monkey/common/data/network_consts.py @@ -0,0 +1,2 @@ +ES_SERVICE = 'elastic-search-9200' + diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index 24a902eea..690b5e348 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -10,7 +10,8 @@ import requests from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\ DOWNLOAD_TIMEOUT -from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE +from infection_monkey.network.elasticfinger import ES_PORT +from common.data.network_consts import ES_SERVICE from infection_monkey.telemetry.attack.t1197_telem import T1197Telem from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index 31ce6e24a..aaac09be2 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -6,11 +6,11 @@ import requests from requests.exceptions import Timeout, ConnectionError import infection_monkey.config +from common.data.network_consts import ES_SERVICE from infection_monkey.model.host import VictimHost from infection_monkey.network import HostFinger ES_PORT = 9200 -ES_SERVICE = 'elastic-search-9200' ES_HTTP_TIMEOUT = 5 LOG = logging.getLogger(__name__) __author__ = 'danielg' diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index 65d044b19..a11f7694a 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -1,5 +1,6 @@ import json +from common.data.network_consts import ES_SERVICE from common.data.zero_trust_consts import * from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.event import Event @@ -42,7 +43,7 @@ def test_open_data_endpoints(telemetry_json): ), event_type=EVENT_TYPE_ISLAND )) - if service_name in 'elastic-search-9200': + if service_name == ES_SERVICE: found_elastic_search_server = STATUS_FAILED events.append(Event.create_event( title="Scan telemetry analysis", From 107ac73366670f3f5c74c2ccca8c2ae56e8ca53d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 11:00:57 +0300 Subject: [PATCH 126/186] Improved documentation of create_ir_add_to_existing_finding --- .../cc/models/zero_trust/segmentation_finding.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py index 716548453..32a450f57 100644 --- a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py @@ -15,15 +15,9 @@ class SegmentationFinding(Finding): @staticmethod def create_or_add_to_existing_finding(subnets, status, segmentation_event): """ - If you're trying to add a Failed finding: - If the finding doesn't exist at all: create failed - else: - if pass, turn to fail - add event - - If you're trying to add a Passed finding: - If the finding doesn't exist at all: create Passed - else: add event + Creates a segmentation finding. If a segmentation finding with the relevant subnets already exists, adds the + event to the existing finding, and the "worst" status is chosen (i.e. if the existing one is "Failed" it will + remain so). :param subnets: the 2 subnets of this finding. :param status: STATUS_PASSED or STATUS_FAILED From 2d7829ca4b90e4607700eba3c3001b401434df50 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 11:05:57 +0300 Subject: [PATCH 127/186] Split test_machine_exploited into 2 functions --- .../zero_trust_tests/machine_exploited.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index d4f8c53c1..7da763dd8 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -6,39 +6,44 @@ from monkey_island.cc.models.zero_trust.finding import Finding def test_machine_exploited(telemetry_json): current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + target_ip = telemetry_json['data']['machine']['ip_addr'] + exploiter = telemetry_json['data']['exploiter'] + timestamp = telemetry_json['timestamp'] + exploit_successful = telemetry_json['data']['result'] + + create_findings_from_exploit_data(current_monkey, exploit_successful, exploiter, target_ip, timestamp) + + +def create_findings_from_exploit_data(current_monkey, exploit_successful, exploiter, target_ip, timestamp): events = [ Event.create_event( title="Exploit attempt", message="Monkey on {} attempted to exploit {} using {}.".format( current_monkey.hostname, - telemetry_json['data']['machine']['ip_addr'], - telemetry_json['data']['exploiter']), + target_ip, + exploiter), event_type=EVENT_TYPE_MONKEY_NETWORK, - timestamp=telemetry_json['timestamp'] + timestamp=timestamp ) ] - status = STATUS_PASSED - - if telemetry_json['data']['result']: + if exploit_successful: events.append( Event.create_event( title="Exploit success!", message="Monkey on {} successfully exploited {} using {}.".format( current_monkey.hostname, - telemetry_json['data']['machine']['ip_addr'], - telemetry_json['data']['exploiter']), + target_ip, + exploiter), event_type=EVENT_TYPE_MONKEY_NETWORK, - timestamp=telemetry_json['timestamp']) + timestamp=timestamp) ) status = STATUS_FAILED - Finding.save_finding( test=TEST_MACHINE_EXPLOITED, status=status, events=events ) - Finding.save_finding( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, status=STATUS_INCONCLUSIVE, From 2269e788883b1d428dda4dbe31e1af2e198145d4 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 11:37:26 +0300 Subject: [PATCH 128/186] Added docs for is_segmentation_violation --- .../services/telemetry/zero_trust_tests/segmentation.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index 763c46b2f..1742bbea7 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -16,6 +16,15 @@ SEGMENTATION_VIOLATION_EVENT_TEXT = \ def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet): + # type: (Monkey, str, str, str) -> bool + """ + Checks is a specific communication is a segmentation violation. + :param current_monkey: The source monkey which originated the communication. + :param target_ip: The target with which the current monkey communicated with. + :param source_subnet: The segment the monkey belongs to. + :param target_subnet: Another segment which the monkey isn't supposed to communicate with. + :return: True if this is a violation of segmentation between source_subnet and target_subnet; Otherwise, False. + """ if source_subnet == target_subnet: return False source_subnet_range = NetworkRange.get_range_obj(source_subnet) From fec0791c7bc3f285465f6f1ced6ede3311cf5449 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 11:40:22 +0300 Subject: [PATCH 129/186] Moved JSON parsing to exploit.py --- .../cc/services/telemetry/processing/exploit.py | 11 +++++++++-- .../telemetry/zero_trust_tests/machine_exploited.py | 13 +------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 7464722f9..cf6e9b544 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -4,6 +4,7 @@ import dateutil from monkey_island.cc.database import mongo from monkey_island.cc.encryptor import encryptor +from monkey_island.cc.models import Monkey from monkey_island.cc.services.edge import EdgeService from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry @@ -11,11 +12,17 @@ from monkey_island.cc.services.telemetry.zero_trust_tests.machine_exploited impo def process_exploit_telemetry(telemetry_json): - edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json) encrypt_exploit_creds(telemetry_json) + edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json) update_edge_info_with_new_exploit(edge, telemetry_json) update_node_credentials_from_successful_attempts(edge, telemetry_json) - test_machine_exploited(telemetry_json) + + test_machine_exploited( + current_monkey=Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']), + exploit_successful=telemetry_json['data']['result'], + exploiter=telemetry_json['data']['exploiter'], + target_ip=telemetry_json['data']['machine']['ip_addr'], + timestamp=telemetry_json['timestamp']) def update_node_credentials_from_successful_attempts(edge, telemetry_json): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index 7da763dd8..dba16470f 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -1,20 +1,9 @@ from common.data.zero_trust_consts import * -from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.finding import Finding -def test_machine_exploited(telemetry_json): - current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) - target_ip = telemetry_json['data']['machine']['ip_addr'] - exploiter = telemetry_json['data']['exploiter'] - timestamp = telemetry_json['timestamp'] - exploit_successful = telemetry_json['data']['result'] - - create_findings_from_exploit_data(current_monkey, exploit_successful, exploiter, target_ip, timestamp) - - -def create_findings_from_exploit_data(current_monkey, exploit_successful, exploiter, target_ip, timestamp): +def test_machine_exploited(current_monkey, exploit_successful, exploiter, target_ip, timestamp): events = [ Event.create_event( title="Exploit attempt", From a330dc1bb7f389b76664aa0ab8e2aac76769bd23 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 11:46:42 +0300 Subject: [PATCH 130/186] Extracted json parsing to scan.py --- .../monkey_island/cc/services/telemetry/processing/scan.py | 6 +++++- .../cc/services/telemetry/zero_trust_tests/segmentation.py | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/scan.py b/monkey/monkey_island/cc/services/telemetry/processing/scan.py index 8ae386388..bea451170 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/scan.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/scan.py @@ -1,6 +1,7 @@ import copy from monkey_island.cc.database import mongo +from monkey_island.cc.models import Monkey from monkey_island.cc.services.telemetry.processing.utils import get_edge_by_scan_or_exploit_telemetry from monkey_island.cc.services.telemetry.zero_trust_tests.data_endpoints import test_open_data_endpoints from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import test_segmentation_violation @@ -9,7 +10,10 @@ from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import te def process_scan_telemetry(telemetry_json): update_edges_and_nodes_based_on_scan_telemetry(telemetry_json) test_open_data_endpoints(telemetry_json) - test_segmentation_violation(telemetry_json) + + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + target_ip = telemetry_json['data']['machine']['ip_addr'] + test_segmentation_violation(current_monkey, target_ip) def update_edges_and_nodes_based_on_scan_telemetry(telemetry_json): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index 1742bbea7..a30884ef5 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -39,11 +39,9 @@ def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_s return cross_segment_ip is not None -def test_segmentation_violation(scan_telemetry_json): +def test_segmentation_violation(current_monkey, target_ip): # TODO - lower code duplication between this and report.py. # TODO - single machine - current_monkey = Monkey.get_single_monkey_by_guid(scan_telemetry_json['monkey_guid']) - target_ip = scan_telemetry_json['data']['machine']['ip_addr'] subnet_groups = get_config_network_segments_as_subnet_groups() for subnet_group in subnet_groups: subnet_pairs = itertools.product(subnet_group, subnet_group) From 54873957979e4fce55416bf8beedaf52394fa187 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 12:32:00 +0300 Subject: [PATCH 131/186] Moved JSON parsing to state.py --- .../monkey_island/cc/services/telemetry/processing/state.py | 4 +++- .../cc/services/telemetry/zero_trust_tests/segmentation.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py index f6461dd3f..4e164e900 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/state.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py @@ -1,3 +1,4 @@ +from monkey_island.cc.models import Monkey from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import \ test_passed_findings_for_unreached_segments @@ -12,4 +13,5 @@ def process_state_telemetry(telemetry_json): NodeService.set_monkey_dead(monkey, False) if telemetry_json['data']['done']: - test_passed_findings_for_unreached_segments(telemetry_json) + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + test_passed_findings_for_unreached_segments(current_monkey) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index a30884ef5..a10b89262 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -41,7 +41,6 @@ def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_s def test_segmentation_violation(current_monkey, target_ip): # TODO - lower code duplication between this and report.py. - # TODO - single machine subnet_groups = get_config_network_segments_as_subnet_groups() for subnet_group in subnet_groups: subnet_pairs = itertools.product(subnet_group, subnet_group) @@ -71,9 +70,8 @@ def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, t ) -def test_passed_findings_for_unreached_segments(state_telemetry_json): +def test_passed_findings_for_unreached_segments(current_monkey): flat_all_subnets = [item for sublist in get_config_network_segments_as_subnet_groups() for item in sublist] - current_monkey = Monkey.get_single_monkey_by_guid(state_telemetry_json['monkey_guid']) create_or_add_findings_for_all_pairs(flat_all_subnets, current_monkey) From 02cd1ad684690a1455f0c6e33a7761c735c95fbc Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 14:43:39 +0300 Subject: [PATCH 132/186] Extracted event text and creation to function --- .../zero_trust_tests/segmentation.py | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index a10b89262..552192c23 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -10,11 +10,31 @@ from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups +SEGMENTATION_DONE_EVENT_TEXT = "Monkey on {hostname} is done attempting cross-segment communications " \ + "from `{src_seg}` segments to `{dst_seg}` segments." + SEGMENTATION_VIOLATION_EVENT_TEXT = \ "Segmentation violation! Monkey on '{hostname}', with the {source_ip} IP address (in segment {source_seg}) " \ "managed to communicate cross segment to {target_ip} (in segment {target_seg})." +def test_segmentation_violation(current_monkey, target_ip): + # TODO - lower code duplication between this and report.py. + subnet_groups = get_config_network_segments_as_subnet_groups() + for subnet_group in subnet_groups: + subnet_pairs = itertools.product(subnet_group, subnet_group) + for subnet_pair in subnet_pairs: + source_subnet = subnet_pair[0] + target_subnet = subnet_pair[1] + if is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet): + event = get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet) + SegmentationFinding.create_or_add_to_existing_finding( + subnets=[source_subnet, target_subnet], + status=STATUS_FAILED, + segmentation_event=event + ) + + def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet): # type: (Monkey, str, str, str) -> bool """ @@ -39,23 +59,6 @@ def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_s return cross_segment_ip is not None -def test_segmentation_violation(current_monkey, target_ip): - # TODO - lower code duplication between this and report.py. - subnet_groups = get_config_network_segments_as_subnet_groups() - for subnet_group in subnet_groups: - subnet_pairs = itertools.product(subnet_group, subnet_group) - for subnet_pair in subnet_pairs: - source_subnet = subnet_pair[0] - target_subnet = subnet_pair[1] - if is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet): - event = get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet) - SegmentationFinding.create_or_add_to_existing_finding( - subnets=[source_subnet, target_subnet], - status=STATUS_FAILED, - segmentation_event=event - ) - - def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet): return Event.create_event( title="Segmentation event", @@ -93,13 +96,16 @@ def create_or_add_findings_for_all_pairs(all_subnets, current_monkey): SegmentationFinding.create_or_add_to_existing_finding( subnets=list(subnet_pair), status=STATUS_PASSED, - segmentation_event=Event.create_event( - "Segmentation test done", - message="Monkey on {hostname} is done attempting cross-segment communications from `{src_seg}` " - "segments to `{dst_seg}` segments.".format( - hostname=current_monkey.hostname, - src_seg=subnet_pair[0], - dst_seg=subnet_pair[1]), - event_type=EVENT_TYPE_ISLAND - ) + segmentation_event=get_segmentation_done_event(current_monkey, subnet_pair) ) + + +def get_segmentation_done_event(current_monkey, subnet_pair): + return Event.create_event( + title="Segmentation test done", + message=SEGMENTATION_DONE_EVENT_TEXT.format( + hostname=current_monkey.hostname, + src_seg=subnet_pair[0], + dst_seg=subnet_pair[1]), + event_type=EVENT_TYPE_ISLAND + ) From 8f8f2738599942d0f6f2f7ace7249ad26163502f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 14:43:54 +0300 Subject: [PATCH 133/186] Seperated main sections to components --- .../components/pages/ZeroTrustReportPage.js | 90 +++---------------- .../zerotrust/FindingsSection.js | 10 ++- .../zerotrust/RecommendationsSection.js | 30 +++++++ .../zerotrust/SummarySection.js | 52 +++++++++++ 4 files changed, 102 insertions(+), 80 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index cec603f0e..c0d1f1bed 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -1,16 +1,14 @@ import React, {Fragment} from 'react'; -import {Col, Grid, Row} from 'react-bootstrap'; +import {Col} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; -import PillarsOverview from "../report-components/zerotrust/PillarOverview"; -import FindingsSection from "../report-components/zerotrust/FindingsSection"; -import SinglePillarRecommendationsStatus from "../report-components/zerotrust/SinglePillarRecommendationsStatus"; -import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; import ReportLoader from "../report-components/common/ReportLoader"; import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; -import ZeroTrustReportLegend from "../report-components/zerotrust/ReportLegend"; +import SummarySection from "../report-components/zerotrust/SummarySection"; +import FindingsSection from "../report-components/zerotrust/FindingsSection"; +import RecommendationsSection from "../report-components/zerotrust/RecommendationsSection"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -25,7 +23,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { componentDidMount() { this.updatePageState(); - const refreshInterval = setInterval(this.updatePageState, 8000) + const refreshInterval = setInterval(this.updatePageState, 8000); this.setState( {refreshDataIntervalHandler: refreshInterval} ) @@ -73,9 +71,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { content = ; } else { content =
    - {this.generateOverviewSection()} - {this.generateRecommendationsSection()} - {this.generateFindingsSection()} + + +
    ; } @@ -100,75 +99,10 @@ class ZeroTrustReportPageComponent extends AuthComponent { ) } - generateOverviewSection() { - return (
    -

    Summary

    - - - - -

    - Get a quick glance of the status for each of Zero Trust's seven pillars. -

    - -
    - - - - - - - - - - -

    What am I seeing?

    -

    - The Zero Trust eXtended framework categorizes its recommendations into 7 pillars. Infection Monkey - Zero Trust edition tests some of those recommendations. The tests that the monkey executes - produce findings. The tests, recommendations and pillars are then granted a status in accordance - with the tests results. -

    - -
    -
    -
    ); - } - - generateRecommendationsSection() { - return (
    -

    Recommendations

    -

    - Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results - to understand how the monkey tested your adherence to that recommendation. -

    - { - Object.keys(this.state.recommendations).map((pillar) => - - ) - } -
    ); - } - - generateFindingsSection() { - return (
    -

    Findings

    -

    - Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things - happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper - insight as to what exactly happened during this test. -

    - -
    ); - } - stillLoadingDataFromServer() { - return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" || typeof this.state.recommendations === "undefined"; + return typeof this.state.findings === "undefined" + || typeof this.state.pillars === "undefined" + || typeof this.state.recommendations === "undefined"; } getZeroTrustReportFromServer() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js index eed4c0f8c..ebbd09dda 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js @@ -8,11 +8,17 @@ import {FindingsTable} from "./FindingsTable"; class FindingsSection extends Component { render() { return ( - +
    +

    Findings

    +

    + Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things + happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper + insight as to what exactly happened during this test. +

    - +
    ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js new file mode 100644 index 000000000..28fda3f2b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js @@ -0,0 +1,30 @@ +import React, {Component} from "react"; +import SinglePillarRecommendationsStatus from "./SinglePillarRecommendationsStatus"; +import * as PropTypes from "prop-types"; + +export default class RecommendationsSection extends Component { + render() { + + return
    +

    Recommendations

    +

    + Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results + to understand how the monkey tested your adherence to that recommendation. +

    + { + Object.keys(this.props.recommendations).map((pillar) => + + ) + } +
    + } +} + +RecommendationsSection.propTypes = { + recommendations: PropTypes.any, + pillarsToStatuses: PropTypes.any +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js new file mode 100644 index 000000000..4a56a8b9e --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js @@ -0,0 +1,52 @@ +import React, {Component} from "react"; +import {Col, Grid, Row} from "react-bootstrap"; +import MonkeysStillAliveWarning from "../common/MonkeysStillAliveWarning"; +import PillarsOverview from "./PillarOverview"; +import ZeroTrustReportLegend from "./ReportLegend"; +import * as PropTypes from "prop-types"; + +export default class SummarySection extends Component { + render() { + return
    +

    Summary

    + + + + +

    + Get a quick glance of the status for each of Zero Trust's seven pillars. +

    + +
    + + + + + + + + + + +

    What am I seeing?

    +

    + The Zero + Trust eXtended framework categorizes its recommendations into 7 pillars. Infection + Monkey + Zero Trust edition tests some of those recommendations. The tests that the monkey executes + produce findings. The tests, recommendations and pillars are then granted a status in + accordance + with the tests results. +

    + +
    +
    +
    + } +} + +SummarySection.propTypes = { + allMonkeysAreDead: PropTypes.bool, + pillars: PropTypes.object +}; From f05178baebc84835e1bf45c1cdcd4fb9858e519e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 14:47:49 +0300 Subject: [PATCH 134/186] Fixed proptypes --- .../report-components/zerotrust/RecommendationsSection.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js index 28fda3f2b..c8d160cb8 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js @@ -25,6 +25,6 @@ export default class RecommendationsSection extends Component { } RecommendationsSection.propTypes = { - recommendations: PropTypes.any, - pillarsToStatuses: PropTypes.any + recommendations: PropTypes.object, + pillarsToStatuses: PropTypes.object }; From cdc72eace718bc34b548422c5ff7e289e6813c87 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 14:52:14 +0300 Subject: [PATCH 135/186] Renamed overview to section --- .../components/report-components/zerotrust/FindingsSection.js | 2 +- .../report-components/zerotrust/RecommendationsSection.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js index ebbd09dda..b90ccd6dc 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js @@ -8,7 +8,7 @@ import {FindingsTable} from "./FindingsTable"; class FindingsSection extends Component { render() { return ( -
    +

    Findings

    Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js index c8d160cb8..e83d1c4cc 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js @@ -4,8 +4,7 @@ import * as PropTypes from "prop-types"; export default class RecommendationsSection extends Component { render() { - - return

    + return

    Recommendations

    Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results From 4d50f0d8de2cb72e1fa91684e7aa678f1af5a7f8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 15:05:34 +0300 Subject: [PATCH 136/186] Map status to finding instead of calling function 3 times --- .../zerotrust/FindingsSection.js | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js index b90ccd6dc..d86f5cb06 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js @@ -1,12 +1,30 @@ import React, {Component, Fragment} from "react"; import PillarLabel from "./PillarLabel"; import EventsButton from "./EventsButton"; -import {ZeroTrustStatuses} from "./ZeroTrustPillars"; +import ZeroTrustPillars, {ZeroTrustStatuses} from "./ZeroTrustPillars"; import {FindingsTable} from "./FindingsTable"; class FindingsSection extends Component { + mapFindingsByStatus() { + const statusToFindings = {}; + for (const key in ZeroTrustStatuses) { + statusToFindings[ZeroTrustStatuses[key]] = []; + } + + this.props.findings.map((finding) => { + // Deep copy + const newFinding = JSON.parse(JSON.stringify(finding)); + newFinding.pillars = newFinding.pillars.map((pillar) => { + return {name: pillar, status: this.props.pillarsToStatuses[pillar]} + }); + statusToFindings[newFinding.status].push(newFinding); + }); + return statusToFindings; + } + render() { + const findingsByStatus = this.mapFindingsByStatus(); return (

    Findings

    @@ -15,9 +33,10 @@ class FindingsSection extends Component { happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper insight as to what exactly happened during this test.

    - - - + + + +
    ); } From d7543e111717362e480a51b22e42b0ecb85e9220 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 15:38:57 +0300 Subject: [PATCH 137/186] Extracted magic numbers to consts and deleted unused css file --- .../zerotrust/FindingsTable.js | 10 ++++--- .../zerotrust/PillarLabel.js | 1 - .../zerotrust/RecommendationsStatusTable.js | 4 +-- .../cc/ui/src/styles/ZeroTrustPillars.css | 27 ------------------- 4 files changed, 8 insertions(+), 34 deletions(-) delete mode 100644 monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index 924ddf631..acff1df89 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -5,6 +5,8 @@ import * as PropTypes from "prop-types"; import PillarLabel from "./PillarLabel"; import EventsButton from "./EventsButton"; +const EVENTS_COLUMN_MAX_WIDTH = 160; +const PILLARS_COLUMN_MAX_WIDTH = 200; const columns = [ { columns: [ @@ -18,7 +20,7 @@ const columns = [ accessor: x => { return ; }, - maxWidth: 160, + maxWidth: EVENTS_COLUMN_MAX_WIDTH, }, { @@ -30,7 +32,7 @@ const columns = [ ); return
    {pillarLabels}
    ; }, - maxWidth: 200, + maxWidth: PILLARS_COLUMN_MAX_WIDTH, style: {'whiteSpace': 'unset'} }, ] @@ -41,8 +43,8 @@ const columns = [ export class FindingsTable extends Component { render() { return -

    {
    -
    } tests' findings

    +

    { + } tests' findings

    } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js index 0724f9a13..51c5ca380 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -1,5 +1,4 @@ import React, {Component} from "react"; -import 'styles/ZeroTrustPillars.css' import {statusToLabelType} from "./StatusLabel"; import * as PropTypes from "prop-types"; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js index e2f9259de..e1ba3f814 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js @@ -1,12 +1,12 @@ import React, {Fragment} from "react"; import PaginatedTable from "../common/PaginatedTable"; import AuthComponent from "../../AuthComponent"; -import 'styles/ZeroTrustPillars.css' import StatusLabel from "./StatusLabel"; import * as PropTypes from "prop-types"; import {ZeroTrustStatuses} from "./ZeroTrustPillars"; +const MAX_WIDTH_STATUS_COLUMN = 80; const columns = [ { columns: [ @@ -14,7 +14,7 @@ const columns = [ accessor: x => { return ; }, - maxWidth: 80 + maxWidth: MAX_WIDTH_STATUS_COLUMN }, { Header: 'ZT Recommendation', accessor: 'recommendation', style: {'whiteSpace': 'unset'} // This enables word wrap diff --git a/monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css b/monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css deleted file mode 100644 index 09b5289dc..000000000 --- a/monkey/monkey_island/cc/ui/src/styles/ZeroTrustPillars.css +++ /dev/null @@ -1,27 +0,0 @@ -.label-zt-data { - background-color: #FAD02C !important; -} - -.label-zt-people { - background-color: #507581 !important; -} - -.label-zt-networks { - background-color: #746233 !important; -} - -.label-zt-devices { - background-color: #2F3B29 !important; -} - -.label-zt-workloads { - background-color: #0C1440 !important; -} - -.label-zt-analytics { - background-color: #6B8836 !important; -} - -.label-zt-automation { - background-color: #B4BC82 !important; -} From 68d185f5fd94ddc6f40dfaa79920e2b6681a19dc Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 16:00:04 +0300 Subject: [PATCH 138/186] Added new icons for timeline events types (and deleted the Island event type) --- monkey/common/data/zero_trust_consts.py | 3 +-- .../monkey_island/cc/models/zero_trust/test_event.py | 6 +++--- .../telemetry/zero_trust_tests/antivirus_existence.py | 4 ++-- .../telemetry/zero_trust_tests/data_endpoints.py | 6 +++--- .../telemetry/zero_trust_tests/segmentation.py | 5 ++--- .../report-components/zerotrust/EventsTimeline.js | 11 ++++++----- .../ui/src/images/zerotrust/im-alert-machine-icon.svg | 1 + .../ui/src/images/zerotrust/im-alert-network-icon.svg | 1 + 8 files changed, 19 insertions(+), 18 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg create mode 100644 monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 18c02e818..6742f435f 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -129,10 +129,9 @@ TESTS_MAP = { }, } -EVENT_TYPE_ISLAND = "island" EVENT_TYPE_MONKEY_NETWORK = "monkey_network" EVENT_TYPE_MONKEY_LOCAL = "monkey_local" -EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK, EVENT_TYPE_ISLAND) +EVENT_TYPES = (EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_MONKEY_NETWORK) PILLARS_TO_TESTS = { DATA: [], diff --git a/monkey/monkey_island/cc/models/zero_trust/test_event.py b/monkey/monkey_island/cc/models/zero_trust/test_event.py index 2542df8ef..c0742407d 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_event.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_event.py @@ -1,6 +1,6 @@ from mongoengine import ValidationError -from common.data.zero_trust_consts import EVENT_TYPE_ISLAND +from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.testing.IslandTestCase import IslandTestCase @@ -14,7 +14,7 @@ class TestEvent(IslandTestCase): _ = Event.create_event( title=None, # title required message="bla bla", - event_type=EVENT_TYPE_ISLAND + event_type=EVENT_TYPE_MONKEY_NETWORK ) with self.assertRaises(ValidationError): @@ -28,5 +28,5 @@ class TestEvent(IslandTestCase): _ = Event.create_event( title="skjs", message="bla bla", - event_type=EVENT_TYPE_ISLAND + event_type=EVENT_TYPE_MONKEY_NETWORK ) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index acfdf1643..588a31962 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -1,6 +1,6 @@ import json -from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, \ +from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, \ STATUS_PASSED, STATUS_FAILED, TEST_ENDPOINT_SECURITY_EXISTS from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.event import Event @@ -24,7 +24,7 @@ def test_antivirus_existence(telemetry_json): title="Found AV process", message="The process '{}' was recognized as an Anti Virus process. Process " "details: {}".format(process[1]['name'], json.dumps(process[1])), - event_type=EVENT_TYPE_ISLAND + event_type=EVENT_TYPE_MONKEY_LOCAL )) if len(av_processes) > 0: diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index a11f7694a..98f968e97 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -30,7 +30,7 @@ def test_open_data_endpoints(telemetry_json): events.append(Event.create_event( title="Scan telemetry analysis", message="Scanned service: {}.".format(service_name), - event_type=EVENT_TYPE_ISLAND + event_type=EVENT_TYPE_MONKEY_NETWORK )) if service_name in HTTP_SERVERS_SERVICES_NAMES: found_http_server_status = STATUS_FAILED @@ -41,7 +41,7 @@ def test_open_data_endpoints(telemetry_json): telemetry_json["data"]["machine"]["ip_addr"], json.dumps(service_data) ), - event_type=EVENT_TYPE_ISLAND + event_type=EVENT_TYPE_MONKEY_NETWORK )) if service_name == ES_SERVICE: found_elastic_search_server = STATUS_FAILED @@ -52,7 +52,7 @@ def test_open_data_endpoints(telemetry_json): telemetry_json["data"]["machine"]["ip_addr"], json.dumps(service_data) ), - event_type=EVENT_TYPE_ISLAND + event_type=EVENT_TYPE_MONKEY_NETWORK )) Finding.save_finding( diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index 552192c23..50e60e493 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -1,8 +1,7 @@ import itertools from six import text_type -from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_PASSED, \ - EVENT_TYPE_ISLAND +from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_PASSED from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst, get_ip_if_in_subnet from monkey_island.cc.models import Monkey @@ -107,5 +106,5 @@ def get_segmentation_done_event(current_monkey, subnet_pair): hostname=current_monkey.hostname, src_seg=subnet_pair[0], dst_seg=subnet_pair[1]), - event_type=EVENT_TYPE_ISLAND + event_type=EVENT_TYPE_MONKEY_NETWORK ) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index 9f9e1f899..a7dd1f855 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -2,11 +2,12 @@ import React, {Component} from "react"; import {Timeline, TimelineEvent} from "react-event-timeline"; import * as PropTypes from "prop-types"; +let monkeyLocalIcon = require('../../../images/zerotrust/im-alert-machine-icon.svg'); +let monkeyNetworkIcon = require('../../../images/zerotrust/im-alert-network-icon.svg'); + const eventTypeToIcon = { - "monkey_local": "fa fa-exclamation-circle fa-2x icon-warning", - "monkey_network": "fa fa-exclamation-circle fa-2x icon-warning", - "island": "fa fa-server fa-2x icon-info", - null: "fa fa-question-circle fa-2x icon-info", + "monkey_local": monkeyLocalIcon, + "monkey_network": monkeyNetworkIcon, }; export default class EventsTimeline extends Component { @@ -21,7 +22,7 @@ export default class EventsTimeline extends Component { key={index} createdAt={event_time} title={event.title} - icon={}> + icon={icon}> {event.message} ) }) diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg new file mode 100644 index 000000000..b62f48d5d --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg @@ -0,0 +1 @@ +im-alert-machine-icon \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg new file mode 100644 index 000000000..17d2fb6f6 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg @@ -0,0 +1 @@ +im-alert-network-icon \ No newline at end of file From 871e7b11d77814ebfc7e6fa6ddbd6d022fe8781b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 18:12:08 +0300 Subject: [PATCH 139/186] Updated SVGs --- .../components/report-components/zerotrust/EventsTimeline.js | 1 + .../cc/ui/src/images/zerotrust/im-alert-machine-icon.svg | 2 +- .../cc/ui/src/images/zerotrust/im-alert-network-icon.svg | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index a7dd1f855..d6723bd4d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -22,6 +22,7 @@ export default class EventsTimeline extends Component { key={index} createdAt={event_time} title={event.title} + icon={icon}> {event.message} ) diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg index b62f48d5d..507541be4 100644 --- a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg +++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg @@ -1 +1 @@ -im-alert-machine-icon \ No newline at end of file +im-alert-machine-icon \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg index 17d2fb6f6..50dcc6726 100644 --- a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg +++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg @@ -1 +1 @@ -im-alert-network-icon \ No newline at end of file +im-alert-network-icon \ No newline at end of file From 6e0c974215497e3d9de44b4181baecddb7a90e69 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 2 Sep 2019 18:19:49 +0300 Subject: [PATCH 140/186] Final CR comments, improved doc and extracted a saveJsonToFIle function --- monkey/common/network/segmentation_utils.py | 2 +- .../components/report-components/zerotrust/EventsModal.js | 8 ++++---- .../cc/ui/src/components/utils/SaveJsonToFile.js | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js diff --git a/monkey/common/network/segmentation_utils.py b/monkey/common/network/segmentation_utils.py index aeff7f135..9bbaabf1d 100644 --- a/monkey/common/network/segmentation_utils.py +++ b/monkey/common/network/segmentation_utils.py @@ -15,7 +15,7 @@ def get_ip_if_in_subnet(ip_addresses, subnet): """ :param ip_addresses: IP address list. :param subnet: Subnet to check if one of ip_addresses is in there. This is common.network.network_range.NetworkRange - :return: The first IP in ip_addresses which is in the subnet. + :return: The first IP in ip_addresses which is in the subnet if there is one, otherwise returns None. """ for ip_address in ip_addresses: if subnet.is_in_range(ip_address): diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js index 5da053242..2ce25bf20 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js @@ -2,8 +2,8 @@ import React, {Component} from "react"; import {Modal} from "react-bootstrap"; import EventsTimeline from "./EventsTimeline"; import * as PropTypes from "prop-types"; -import FileSaver from "file-saver"; import ExportEventsButton from "./ExportEventsButton"; +import saveJsonToFile from "../../utils/SaveJsonToFile"; export default class EventsModal extends Component { constructor(props) { @@ -27,9 +27,9 @@ export default class EventsModal extends Component { Close { - const content = JSON.stringify(this.props.events, null, 2); - const blob = new Blob([content], {type: "text/plain;charset=utf-8"}); - FileSaver.saveAs(blob, this.props.exportFilename + ".json"); + const dataToSave = this.props.events; + const filename = this.props.exportFilename; + saveJsonToFile(dataToSave, filename); }}/>
    diff --git a/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js b/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js new file mode 100644 index 000000000..6ad124457 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js @@ -0,0 +1,7 @@ +import FileSaver from "file-saver"; + +export default function saveJsonToFile(dataToSave, filename) { + const content = JSON.stringify(dataToSave, null, 2); + const blob = new Blob([content], {type: "text/plain;charset=utf-8"}); + FileSaver.saveAs(blob, filename + ".json"); +} From 52a95935c873b8f367e05e69a1eb4c66b2546e13 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 3 Sep 2019 21:17:13 +0300 Subject: [PATCH 141/186] Added new user communication PBA and ZT test, not working yet WIP! --- monkey/common/data/post_breach_consts.py | 1 + monkey/common/data/zero_trust_consts.py | 18 +++- .../post_breach/actions/add_user.py | 40 +++++++- .../actions/communicate_as_new_user.py | 98 +++++++++++++++++++ monkey/infection_monkey/post_breach/pba.py | 3 +- .../post_breach/post_breach_handler.py | 3 +- .../cc/services/config_schema.py | 8 ++ .../telemetry/processing/post_breach.py | 17 +++- .../communicate_as_new_user.py | 35 +++++++ 9 files changed, 214 insertions(+), 9 deletions(-) create mode 100644 monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py index 8262757ca..dee4f67d0 100644 --- a/monkey/common/data/post_breach_consts.py +++ b/monkey/common/data/post_breach_consts.py @@ -1,2 +1,3 @@ +POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user" POST_BREACH_BACKDOOR_USER = "Backdoor user" POST_BREACH_FILE_EXECUTION = "File execution" diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 3cf9cda92..9a452d706 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -30,6 +30,7 @@ TEST_SCHEDULED_EXECUTION = u"scheduled_execution" TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline" TEST_SEGMENTATION = u"segmentation" TEST_TUNNELING = u"tunneling" +TEST_COMMUNICATE_AS_NEW_USER = u"communicate_as_new_user" TESTS = ( TEST_SEGMENTATION, TEST_MALICIOUS_ACTIVITY_TIMELINE, @@ -38,7 +39,8 @@ TESTS = ( TEST_MACHINE_EXPLOITED, TEST_DATA_ENDPOINT_HTTP, TEST_DATA_ENDPOINT_ELASTIC, - TEST_TUNNELING + TEST_TUNNELING, + TEST_COMMUNICATE_AS_NEW_USER ) RECOMMENDATION_DATA_TRANSIT = u"data_transit" @@ -47,13 +49,15 @@ RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour" RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" RECOMMENDATION_SEGMENTATION = u"segmentation" RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies" +RECOMMENDATION_USERS_MAC_POLICIES = u"users_mac_policies" RECOMMENDATIONS = { RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.", - RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible." + RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.", + RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC only.", } POSSIBLE_STATUSES_KEY = u"possible_statuses" @@ -140,6 +144,16 @@ TESTS_MAP = { PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED] }, + TEST_COMMUNICATE_AS_NEW_USER: { + TEST_EXPLANATION_KEY: u"The Monkey tried create a new user and communicate with the internet from it.", + FINDING_EXPLANATION_BY_STATUS_KEY: { + STATUS_FAILED: "Monkey was able to cause a new user to access the network. Your network policies are too permissive - restrict them to MAC only.", + STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network." + }, + RECOMMENDATION_KEY: RECOMMENDATION_USERS_MAC_POLICIES, + PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS], + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] + }, } EVENT_TYPE_ISLAND = "island" diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index ce05371a6..b217196d3 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -17,6 +17,40 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add, class BackdoorUser(PBA): def __init__(self): - super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER, - linux_cmd=' '.join(LINUX_COMMANDS), - windows_cmd=WINDOWS_COMMANDS) + linux_cmds, windows_cmds = BackdoorUser.get_commands_to_add_user( + WormConfiguration.user_to_add, WormConfiguration.remote_user_pass) + super(BackdoorUser, self).__init__( + POST_BREACH_BACKDOOR_USER, + linux_cmd=' '.join(linux_cmds), + windows_cmd=windows_cmds) + + @staticmethod + def get_commands_to_add_user(username, password): + linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) + windows_cmds = BackdoorUser.get_windows_commands_to_add_user(password, username) + return linux_cmds, windows_cmds + + @staticmethod + def get_linux_commands_to_add_user(username): + linux_cmds = [ + 'useradd', + '-M', + '--expiredate', + datetime.datetime.today().strftime('%Y-%m-%d'), + '--inactive', + '0', + '-c', + 'MONKEY_USER', + username] + return linux_cmds + + @staticmethod + def get_windows_commands_to_add_user(password, username): + windows_cmds = [ + 'net', + 'user', + username, + password, + '/add', + '/ACTIVE:NO'] + return windows_cmds diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py new file mode 100644 index 000000000..63daa614c --- /dev/null +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -0,0 +1,98 @@ +import os +import random +import string +import subprocess + +import win32api +import win32con +import win32process +import win32security + +from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER +from infection_monkey.post_breach.actions.add_user import BackdoorUser +from infection_monkey.post_breach.pba import PBA +from infection_monkey.telemetry.post_breach_telem import PostBreachTelem +from infection_monkey.utils import is_windows_os + +USERNAME = "somenewuser" +PASSWORD = "N3WPa55W0rD!@12" + + +class CommunicateAsNewUser(PBA): + """ + This PBA creates a new user, and then pings google as that user. This is used for a Zero Trust test of the People + pillar. See the relevant telemetry processing to see what findings are created. + """ + + def __init__(self): + super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER) + + def run(self): + username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) + if is_windows_os(): + if not self.try_to_create_user_windows(username, PASSWORD): + return # no point to continue if failed creating the user. + + # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera + new_user_logon_token_handle = win32security.LogonUser( + username, + ".", # current domain + PASSWORD, + win32con.LOGON32_LOGON_BATCH, # logon type + win32con.LOGON32_PROVIDER_DEFAULT) # logon provider + + if new_user_logon_token_handle == 0: + PostBreachTelem( + self, + ("Can't logon as {} Last error: {}".format(username, win32api.GetLastError()), False) + ).send() + return # no point to continue if can't log on. + + # Using os.path is OK, as this is on windows for sure + ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe") + if not os.path.exists(ping_app_path): + PostBreachTelem(self, ("{} not found".format(ping_app_path), False)).send() + return # Can't continue without ping. + + # Open process as that user: + # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera + return_value_create_process = win32process.CreateProcessAsUser( + new_user_logon_token_handle, # A handle to the primary token that represents a user. + # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module + # to execute, and *lpCommandLine specifies the command line. + ping_app_path, # The name of the module to be executed. + "google.com", # The command line to be executed. + None, # Process attributes + None, # Thread attributes + True, # Should inherit handles + win32con.NORMAL_PRIORITY_CLASS, # The priority class and the creation of the process. + None, # An environment block for the new process. If this parameter is NULL, the new process + # uses the environment of the calling process. + None, # CWD. If this parameter is NULL, the new process will have the same current drive and + # directory as the calling process. + win32process.STARTUPINFO() # STARTUPINFO structure. + # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + ) + + if return_value_create_process == 0: + PostBreachTelem(self, ( + "Failed to open process as user. Last error: {}".format(win32api.GetLastError()), False)).send() + return + else: + try: + linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) + linux_cmds.extend([";", "sudo", "-", username, "-c", "'ping -c 2 google.com'"]) + subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as e: + PostBreachTelem(self, (e.output, False)).send() + return + + def try_to_create_user_windows(self, username, password): + try: + windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password) + subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True) + return True + except subprocess.CalledProcessError as e: + PostBreachTelem(self, ( + "Couldn't create the user '{}'. Error output is: '{}'".format(username, e.output), False)).send() + return False diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index 3fb8b251f..6c6c6b8b5 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -19,7 +19,8 @@ class PBA(object): def __init__(self, name="unknown", linux_cmd="", windows_cmd=""): """ :param name: Name of post breach action. - :param command: Command that will be executed on breached machine + :param linux_cmd: Command that will be executed on breached machine + :param windows_cmd: Command that will be executed on breached machine """ self.command = PBA.choose_command(linux_cmd, windows_cmd) self.name = name diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index 8522f412f..7c5bea27d 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -25,8 +25,9 @@ class PostBreach(object): Executes all post breach actions. """ for pba in self.pba_list: + LOG.debug("Executing PBA: '{}'".format(pba.name)) pba.run() - LOG.info("Post breach actions executed") + LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list))) @staticmethod def config_to_pba_list(): diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py index 3a7398663..67b5b519d 100644 --- a/monkey/monkey_island/cc/services/config_schema.py +++ b/monkey/monkey_island/cc/services/config_schema.py @@ -119,6 +119,14 @@ SCHEMA = { "title": "Back door user", "attack_techniques": [] }, + { + "type": "string", + "enum": [ + "CommunicateAsNewUser" + ], + "title": "Communicate as new user", + "attack_techniques": [] + }, ], }, "finger_classes": { diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py index 2515c2d30..c67f64f59 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -1,7 +1,18 @@ from monkey_island.cc.database import mongo from common.data.post_breach_consts import * +from monkey_island.cc.models import Monkey +from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import test_new_user_communication + + +def process_communicate_as_new_user_telemetry(telemetry_json): + current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + success = telemetry_json['data']['result'][1] + message = telemetry_json['data']['result'][0] + test_new_user_communication(current_monkey, success, message) + POST_BREACH_TELEMETRY_PROCESSING_FUNCS = { + POST_BREACH_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry, # `lambda *args, **kwargs: None` is a no-op. POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None, POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None, @@ -13,5 +24,7 @@ def process_post_breach_telemetry(telemetry_json): {'guid': telemetry_json['monkey_guid']}, {'$push': {'pba_results': telemetry_json['data']}}) - if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS: - POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json) + post_breach_action_name = telemetry_json["data"]["name"] + if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS: + POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json) + diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py new file mode 100644 index 000000000..564ce4d20 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py @@ -0,0 +1,35 @@ +from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK, STATUS_FAILED, TEST_COMMUNICATE_AS_NEW_USER, \ + STATUS_PASSED +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding +from monkey_island.cc.models.zero_trust.event import Event + + +def test_new_user_communication(current_monkey, success, message): + tried_to_communicate_event = Event.create_event( + title="Communicate as new user", + message="Monkey on {} tried to create a new user and communicate from it.".format(current_monkey.hostname), + event_type=EVENT_TYPE_MONKEY_NETWORK) + events = [tried_to_communicate_event] + + if success: + events.append( + Event.create_event( + title="Communicate as new user", + message="New user created by Monkey on {} successfully tried to communicate with the internet. " + "Details: {}".format(current_monkey.hostname, message), + event_type=EVENT_TYPE_MONKEY_NETWORK) + ) + test_status = STATUS_FAILED + else: + events.append( + Event.create_event( + title="Communicate as new user", + message="Monkey on {} couldn't communicate as new user. Details: {}".format( + current_monkey.hostname, message), + event_type=EVENT_TYPE_MONKEY_NETWORK) + ) + test_status = STATUS_PASSED + + AggregateFinding.create_or_add_to_existing( + test=TEST_COMMUNICATE_AS_NEW_USER, status=test_status, events=events + ) From 1befe35d34132ca83e9bd2759cee25e2b3231645 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 3 Sep 2019 21:42:48 +0300 Subject: [PATCH 142/186] Added some logs, and more error handling for winapis. Still not working --- .../post_breach/actions/add_user.py | 9 ++- .../actions/communicate_as_new_user.py | 74 ++++++++++--------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index b217196d3..9354ca417 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -27,7 +27,7 @@ class BackdoorUser(PBA): @staticmethod def get_commands_to_add_user(username, password): linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) - windows_cmds = BackdoorUser.get_windows_commands_to_add_user(password, username) + windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password) return linux_cmds, windows_cmds @staticmethod @@ -45,12 +45,13 @@ class BackdoorUser(PBA): return linux_cmds @staticmethod - def get_windows_commands_to_add_user(password, username): + def get_windows_commands_to_add_user(username, password, should_be_active=False): windows_cmds = [ 'net', 'user', username, password, - '/add', - '/ACTIVE:NO'] + '/add'] + if not should_be_active: + windows_cmds.append('/ACTIVE:NO') return windows_cmds diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 63daa614c..ba1620180 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -1,3 +1,4 @@ +import logging import os import random import string @@ -15,7 +16,9 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils import is_windows_os USERNAME = "somenewuser" -PASSWORD = "N3WPa55W0rD!@12" +PASSWORD = "N3WPa55W0rD!1" + +logger = logging.getLogger(__name__) class CommunicateAsNewUser(PBA): @@ -33,50 +36,50 @@ class CommunicateAsNewUser(PBA): if not self.try_to_create_user_windows(username, PASSWORD): return # no point to continue if failed creating the user. - # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera - new_user_logon_token_handle = win32security.LogonUser( - username, - ".", # current domain - PASSWORD, - win32con.LOGON32_LOGON_BATCH, # logon type - win32con.LOGON32_PROVIDER_DEFAULT) # logon provider - - if new_user_logon_token_handle == 0: + try: + # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera + new_user_logon_token_handle = win32security.LogonUser( + username, + ".", # use current domain + PASSWORD, + win32con.LOGON32_LOGON_INTERACTIVE, # logon type - interactive (normal user) + win32con.LOGON32_PROVIDER_DEFAULT) # logon provider + except Exception as e: PostBreachTelem( self, - ("Can't logon as {} Last error: {}".format(username, win32api.GetLastError()), False) + ("Can't logon as {}. Error: {}".format(username, e.message), False) ).send() return # no point to continue if can't log on. # Using os.path is OK, as this is on windows for sure ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe") if not os.path.exists(ping_app_path): - PostBreachTelem(self, ("{} not found".format(ping_app_path), False)).send() + PostBreachTelem(self, ("{} not found.".format(ping_app_path), False)).send() return # Can't continue without ping. - # Open process as that user: - # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera - return_value_create_process = win32process.CreateProcessAsUser( - new_user_logon_token_handle, # A handle to the primary token that represents a user. - # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module - # to execute, and *lpCommandLine specifies the command line. - ping_app_path, # The name of the module to be executed. - "google.com", # The command line to be executed. - None, # Process attributes - None, # Thread attributes - True, # Should inherit handles - win32con.NORMAL_PRIORITY_CLASS, # The priority class and the creation of the process. - None, # An environment block for the new process. If this parameter is NULL, the new process - # uses the environment of the calling process. - None, # CWD. If this parameter is NULL, the new process will have the same current drive and - # directory as the calling process. - win32process.STARTUPINFO() # STARTUPINFO structure. - # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa - ) - - if return_value_create_process == 0: + try: + # Open process as that user: + # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera + return_value_create_process = win32process.CreateProcessAsUser( + new_user_logon_token_handle, # A handle to the primary token that represents a user. + # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module + # to execute, and *lpCommandLine specifies the command line. + ping_app_path, # The name of the module to be executed. + "google.com", # The command line to be executed. + None, # Process attributes + None, # Thread attributes + True, # Should inherit handles + win32con.NORMAL_PRIORITY_CLASS, # The priority class and the creation of the process. + None, # An environment block for the new process. If this parameter is NULL, the new process + # uses the environment of the calling process. + None, # CWD. If this parameter is NULL, the new process will have the same current drive and + # directory as the calling process. + win32process.STARTUPINFO() # STARTUPINFO structure. + # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + ) + except Exception as e: PostBreachTelem(self, ( - "Failed to open process as user. Last error: {}".format(win32api.GetLastError()), False)).send() + "Failed to open process as user {}. Error: {}".format(username, e.message), False)).send() return else: try: @@ -89,7 +92,8 @@ class CommunicateAsNewUser(PBA): def try_to_create_user_windows(self, username, password): try: - windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password) + windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password, True) + logger.debug("Trying these commands: {}".format(str(windows_cmds))) subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True) return True except subprocess.CalledProcessError as e: From c371bf8ac53f95e95ecc7cf5763a5546d1be1dee Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 3 Sep 2019 21:52:30 +0300 Subject: [PATCH 143/186] Added 1314 error TODO --- .../post_breach/actions/communicate_as_new_user.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index ba1620180..578886d02 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -78,8 +78,11 @@ class CommunicateAsNewUser(PBA): # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa ) except Exception as e: + # TODO: if failed on 1314, try to add elevate the rights of the current user with the "Replace a + # process level token" right, using Local Security Policy editing (need to find how to do this using + # python... PostBreachTelem(self, ( - "Failed to open process as user {}. Error: {}".format(username, e.message), False)).send() + "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() return else: try: From 3469ec6996477976cdce9f5e67e20c8601d04161 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 3 Sep 2019 22:35:18 +0300 Subject: [PATCH 144/186] Still need to test linux --- .../actions/communicate_as_new_user.py | 32 ++++++++++++------- .../post_breach/post_breach_handler.py | 2 ++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 578886d02..6eaf73db5 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -4,7 +4,6 @@ import random import string import subprocess -import win32api import win32con import win32process import win32security @@ -15,6 +14,9 @@ from infection_monkey.post_breach.pba import PBA from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils import is_windows_os +CREATED_PROCESS_AS_USER_WINDOWS_FORMAT = "Created process '{}' as user '{}'." +CREATED_PROCESS_AS_USER_LINUX_FORMAT = "Created process '{}' as user '{}'. Some of the output was '{}'." + USERNAME = "somenewuser" PASSWORD = "N3WPa55W0rD!1" @@ -60,12 +62,11 @@ class CommunicateAsNewUser(PBA): try: # Open process as that user: # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera - return_value_create_process = win32process.CreateProcessAsUser( + commandline = "{} {}".format(ping_app_path, "google.com") + _ = win32process.CreateProcessAsUser( new_user_logon_token_handle, # A handle to the primary token that represents a user. - # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module - # to execute, and *lpCommandLine specifies the command line. - ping_app_path, # The name of the module to be executed. - "google.com", # The command line to be executed. + None, # The name of the module to be executed. + commandline, # The command line to be executed. None, # Process attributes None, # Thread attributes True, # Should inherit handles @@ -77,18 +78,27 @@ class CommunicateAsNewUser(PBA): win32process.STARTUPINFO() # STARTUPINFO structure. # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa ) + + PostBreachTelem(self, ( + CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send() + return except Exception as e: - # TODO: if failed on 1314, try to add elevate the rights of the current user with the "Replace a - # process level token" right, using Local Security Policy editing (need to find how to do this using - # python... + # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the "Replace a + # process level token" right, using Local Security Policy editing. Worked, but only after reboot. So: + # 1. need to decide if worth it, and then + # 2. need to find how to do this using python... PostBreachTelem(self, ( "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() return else: try: linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) - linux_cmds.extend([";", "sudo", "-", username, "-c", "'ping -c 2 google.com'"]) - subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True) + commandline = "'ping -c 2 google.com'" + linux_cmds.extend([";", "sudo", "-", username, "-c", commandline]) + output = subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True) + PostBreachTelem(self, ( + CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send() + return except subprocess.CalledProcessError as e: PostBreachTelem(self, (e.output, False)).send() return diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index 7c5bea27d..034e1c451 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -46,7 +46,9 @@ class PostBreach(object): if ((m[1].__module__ == module.__name__) and issubclass(m[1], PBA))] # Get post breach action object from class for pba_class in pba_classes: + LOG.debug("Checking if should run PBA {}".format(pba_class.__name__)) if pba_class.should_run(pba_class.__name__): pba = pba_class() pba_list.append(pba) + LOG.debug("Added PBA {} to PBA list".format(pba_class.__name__)) return pba_list From 2a78b62d00e1d067a6057847d7a14df82c032aae Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 4 Sep 2019 11:35:18 +0300 Subject: [PATCH 145/186] Moved imports to local imports --- .../post_breach/actions/communicate_as_new_user.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 6eaf73db5..2522ab1cf 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -4,10 +4,6 @@ import random import string import subprocess -import win32con -import win32process -import win32security - from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER from infection_monkey.post_breach.actions.add_user import BackdoorUser from infection_monkey.post_breach.pba import PBA @@ -39,6 +35,10 @@ class CommunicateAsNewUser(PBA): return # no point to continue if failed creating the user. try: + # Importing these only on windows, as they won't exist on linux. + import win32con + import win32process + import win32security # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera new_user_logon_token_handle = win32security.LogonUser( username, From 4f912d9d1e389374efa3fc2c688607692cd4f8f6 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 4 Sep 2019 12:30:55 +0300 Subject: [PATCH 146/186] Fixed sudo usage + added debug logs --- .../post_breach/actions/communicate_as_new_user.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 2522ab1cf..53270e8fb 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -94,7 +94,8 @@ class CommunicateAsNewUser(PBA): try: linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) commandline = "'ping -c 2 google.com'" - linux_cmds.extend([";", "sudo", "-", username, "-c", commandline]) + linux_cmds.extend([";", "sudo", "-u", username, commandline]) + logger.debug("Trying these commands: {}".format(str(linux_cmds))) output = subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True) PostBreachTelem(self, ( CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send() From 097d8831c87d6220e207905fae977e3f5fd7af2e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 4 Sep 2019 12:40:53 +0300 Subject: [PATCH 147/186] Joining commands using ,,.join() for linux --- .../post_breach/actions/add_user.py | 16 +++------------- .../actions/communicate_as_new_user.py | 5 +++-- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index 9354ca417..b82c59a66 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -4,16 +4,6 @@ from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER from infection_monkey.post_breach.pba import PBA from infection_monkey.config import WormConfiguration -__author__ = 'danielg' - -LINUX_COMMANDS = ['useradd', '-M', '--expiredate', - datetime.datetime.today().strftime('%Y-%m-%d'), '--inactive', '0', '-c', 'MONKEY_USER', - WormConfiguration.user_to_add] - -WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add, - WormConfiguration.remote_user_pass, - '/add', '/ACTIVE:NO'] - class BackdoorUser(PBA): def __init__(self): @@ -34,13 +24,13 @@ class BackdoorUser(PBA): def get_linux_commands_to_add_user(username): linux_cmds = [ 'useradd', - '-M', + '-M', # Do not create homedir '--expiredate', datetime.datetime.today().strftime('%Y-%m-%d'), '--inactive', '0', - '-c', - 'MONKEY_USER', + '-c', # Comment + 'MONKEY_USER', # Comment username] return linux_cmds diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 53270e8fb..df4688fb5 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -95,8 +95,9 @@ class CommunicateAsNewUser(PBA): linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) commandline = "'ping -c 2 google.com'" linux_cmds.extend([";", "sudo", "-u", username, commandline]) - logger.debug("Trying these commands: {}".format(str(linux_cmds))) - output = subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True) + final_command = ' '.join(linux_cmds) + logger.debug("Trying to execute these commands: {}".format(final_command)) + output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True) PostBreachTelem(self, ( CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send() return From ae414bcd13d50fb1979bede58728c2a219d5f8da Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 4 Sep 2019 12:42:46 +0300 Subject: [PATCH 148/186] Remove unnecessary apostrophes from commandline --- .../post_breach/actions/communicate_as_new_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index df4688fb5..9335c90fe 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -93,7 +93,7 @@ class CommunicateAsNewUser(PBA): else: try: linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) - commandline = "'ping -c 2 google.com'" + commandline = "ping -c 2 google.com" linux_cmds.extend([";", "sudo", "-u", username, commandline]) final_command = ' '.join(linux_cmds) logger.debug("Trying to execute these commands: {}".format(final_command)) From 86cf09419ce06285170f1251dd0273f932f588a2 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 4 Sep 2019 16:24:46 +0300 Subject: [PATCH 149/186] Moved imports to top of try --- .../post_breach/actions/communicate_as_new_user.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 9335c90fe..182acea00 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -31,14 +31,15 @@ class CommunicateAsNewUser(PBA): def run(self): username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) if is_windows_os(): + # Importing these only on windows, as they won't exist on linux. + import win32con + import win32process + import win32security + if not self.try_to_create_user_windows(username, PASSWORD): return # no point to continue if failed creating the user. try: - # Importing these only on windows, as they won't exist on linux. - import win32con - import win32process - import win32security # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera new_user_logon_token_handle = win32security.LogonUser( username, @@ -99,7 +100,7 @@ class CommunicateAsNewUser(PBA): logger.debug("Trying to execute these commands: {}".format(final_command)) output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True) PostBreachTelem(self, ( - CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send() + CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send() return except subprocess.CalledProcessError as e: PostBreachTelem(self, (e.output, False)).send() From 5a29e047ab1d6a8ed182074bb4ecb9dda88c550a Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 4 Sep 2019 17:00:28 +0300 Subject: [PATCH 150/186] Extracted events amount badge to function --- .../zerotrust/EventsButton.js | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js index 66f1ae3d3..f7a3fbbe5 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js @@ -20,23 +20,26 @@ export default class EventsButton extends Component { }; render() { + return + +
    + +
    +
    ; + } + + createEventsAmountBadge() { let eventsAmountBadge; if (this.props.events.length > 10) { eventsAmountBadge = 9+; } else { eventsAmountBadge = {this.props.events.length}; } - return - -
    - -
    -
    ; + return eventsAmountBadge; } - } EventsButton.propTypes = { From 731e3acb9035e7f8cb4699837606d5f4de06c0b7 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 5 Sep 2019 20:56:00 +0300 Subject: [PATCH 151/186] Added exception info to monkey main function. --- monkey/infection_monkey/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index 2ddf9127e..3b51c1be2 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -127,8 +127,8 @@ def main(): json.dump(json_dict, config_fo, skipkeys=True, sort_keys=True, indent=4, separators=(',', ': ')) return True - except Exception: - LOG.exception("Exception thrown from monkey's start function") + except Exception as e: + LOG.exception("Exception thrown from monkey's start function. More info: {}".format(e)) finally: monkey.cleanup() From e9cd20a3457dff37e14abd589a9d04225f0e196e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 5 Sep 2019 20:56:17 +0300 Subject: [PATCH 152/186] If one PBA fails it shouldn't stop all the rest. --- monkey/infection_monkey/post_breach/post_breach_handler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index 034e1c451..c68422d4c 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -25,8 +25,11 @@ class PostBreach(object): Executes all post breach actions. """ for pba in self.pba_list: - LOG.debug("Executing PBA: '{}'".format(pba.name)) - pba.run() + try: + LOG.debug("Executing PBA: '{}'".format(pba.name)) + pba.run() + except Exception as e: + LOG.error("PBA {} failed. Error info: {}".format(pba.name, e)) LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list))) @staticmethod From e618378c955140521e634d4024a9b9634636a0ab Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 5 Sep 2019 20:56:48 +0300 Subject: [PATCH 153/186] Vastly improved communicate as new user PBA code structure, also not leaking any more process or thread handles. --- .../infection_monkey/monkey_utils/__init__.py | 0 .../monkey_utils/windows/__init__.py | 0 .../monkey_utils/windows/new_user.py | 64 ++++++++ .../actions/communicate_as_new_user.py | 151 ++++++++---------- 4 files changed, 134 insertions(+), 81 deletions(-) create mode 100644 monkey/infection_monkey/monkey_utils/__init__.py create mode 100644 monkey/infection_monkey/monkey_utils/windows/__init__.py create mode 100644 monkey/infection_monkey/monkey_utils/windows/new_user.py diff --git a/monkey/infection_monkey/monkey_utils/__init__.py b/monkey/infection_monkey/monkey_utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/monkey_utils/windows/__init__.py b/monkey/infection_monkey/monkey_utils/windows/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py new file mode 100644 index 000000000..be6e2534d --- /dev/null +++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py @@ -0,0 +1,64 @@ +import logging +import subprocess + +from infection_monkey.post_breach.actions.add_user import BackdoorUser +from infection_monkey.telemetry.post_breach_telem import PostBreachTelem + + +logger = logging.getLogger(__name__) + + +class NewUserError(Exception): + pass + + +class NewUser(object): + """ + RAII object to use for creating and using a new user in Windows. Use with `with`. + User will be created when the instance is instantiated. + User will log on start of `with` scope. + User will log off on end of `with` scope. + + Example: + # Created # Logged on + with NewUser("user", "pass") as new_user: + ... + ... + # Logged off + ... + """ + def __init__(self, username, password): + """ + Creates a user with the username + password. + :raises: subprocess.CalledProcessError if failed to add the user. + """ + self.username = username + self.password = password + + windows_cmds = BackdoorUser.get_windows_commands_to_add_user(self.username, self.password, True) + logger.debug("Trying these commands: {}".format(str(windows_cmds))) + _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True) + + def __enter__(self): + # Importing these only on windows, as they won't exist on linux. + import win32security + import win32con + + try: + # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera + self.logon_handle = win32security.LogonUser( + self.username, + ".", # Use current domain. + self.password, + win32con.LOGON32_LOGON_INTERACTIVE, # Logon type - interactive (normal user). + win32con.LOGON32_PROVIDER_DEFAULT) # Which logon provider to use - whatever Windows offers. + except Exception as err: + raise NewUserError("Can't logon as {}. Error: {}".format(self.username, str(err))) + return self + + def get_logon_handle(self): + return self.logon_handle + + def __exit__(self, exc_type, exc_val, exc_tb): + self.logon_handle.Close() + # TODO Delete user diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 182acea00..8869a225f 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -5,6 +5,7 @@ import string import subprocess from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER +from infection_monkey.monkey_utils.windows.new_user import NewUser, NewUserError from infection_monkey.post_breach.actions.add_user import BackdoorUser from infection_monkey.post_breach.pba import PBA from infection_monkey.telemetry.post_breach_telem import PostBreachTelem @@ -31,88 +32,76 @@ class CommunicateAsNewUser(PBA): def run(self): username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) if is_windows_os(): - # Importing these only on windows, as they won't exist on linux. - import win32con - import win32process - import win32security - - if not self.try_to_create_user_windows(username, PASSWORD): - return # no point to continue if failed creating the user. - - try: - # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera - new_user_logon_token_handle = win32security.LogonUser( - username, - ".", # use current domain - PASSWORD, - win32con.LOGON32_LOGON_INTERACTIVE, # logon type - interactive (normal user) - win32con.LOGON32_PROVIDER_DEFAULT) # logon provider - except Exception as e: - PostBreachTelem( - self, - ("Can't logon as {}. Error: {}".format(username, e.message), False) - ).send() - return # no point to continue if can't log on. - - # Using os.path is OK, as this is on windows for sure - ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe") - if not os.path.exists(ping_app_path): - PostBreachTelem(self, ("{} not found.".format(ping_app_path), False)).send() - return # Can't continue without ping. - - try: - # Open process as that user: - # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera - commandline = "{} {}".format(ping_app_path, "google.com") - _ = win32process.CreateProcessAsUser( - new_user_logon_token_handle, # A handle to the primary token that represents a user. - None, # The name of the module to be executed. - commandline, # The command line to be executed. - None, # Process attributes - None, # Thread attributes - True, # Should inherit handles - win32con.NORMAL_PRIORITY_CLASS, # The priority class and the creation of the process. - None, # An environment block for the new process. If this parameter is NULL, the new process - # uses the environment of the calling process. - None, # CWD. If this parameter is NULL, the new process will have the same current drive and - # directory as the calling process. - win32process.STARTUPINFO() # STARTUPINFO structure. - # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa - ) - - PostBreachTelem(self, ( - CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send() - return - except Exception as e: - # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the "Replace a - # process level token" right, using Local Security Policy editing. Worked, but only after reboot. So: - # 1. need to decide if worth it, and then - # 2. need to find how to do this using python... - PostBreachTelem(self, ( - "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() - return + self.communicate_as_new_user_windows(username) else: - try: - linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) - commandline = "ping -c 2 google.com" - linux_cmds.extend([";", "sudo", "-u", username, commandline]) - final_command = ' '.join(linux_cmds) - logger.debug("Trying to execute these commands: {}".format(final_command)) - output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True) - PostBreachTelem(self, ( - CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send() - return - except subprocess.CalledProcessError as e: - PostBreachTelem(self, (e.output, False)).send() - return + self.communicate_as_new_user_linux(username) - def try_to_create_user_windows(self, username, password): + def communicate_as_new_user_linux(self, username): try: - windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password, True) - logger.debug("Trying these commands: {}".format(str(windows_cmds))) - subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True) - return True - except subprocess.CalledProcessError as e: + linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) + commandline = "ping -c 2 google.com" + linux_cmds.extend([";", "sudo", "-u", username, commandline]) + final_command = ' '.join(linux_cmds) + logger.debug("Trying to execute these commands: {}".format(final_command)) + output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True) PostBreachTelem(self, ( - "Couldn't create the user '{}'. Error output is: '{}'".format(username, e.output), False)).send() - return False + CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send() + except subprocess.CalledProcessError as e: + PostBreachTelem(self, (e.output, False)).send() + + def communicate_as_new_user_windows(self, username): + # Importing these only on windows, as they won't exist on linux. + import win32con + import win32process + import win32api + + try: + with NewUser(username, PASSWORD) as new_user: + # Using os.path is OK, as this is on windows for sure + ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe") + if not os.path.exists(ping_app_path): + PostBreachTelem(self, ("{} not found.".format(ping_app_path), False)).send() + return # Can't continue without ping. + + try: + # Open process as that user: + # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera + commandline = "{} {} {} {}".format(ping_app_path, "google.com", "-n", "2") + process_handle = win32process.CreateProcessAsUser( + new_user.get_logon_handle(), # A handle to the primary token that represents a user. + None, # The name of the module to be executed. + commandline, # The command line to be executed. + None, # Process attributes + None, # Thread attributes + True, # Should inherit handles + win32con.NORMAL_PRIORITY_CLASS, # The priority class and the creation of the process. + None, # An environment block for the new process. If this parameter is NULL, the new process + # uses the environment of the calling process. + None, # CWD. If this parameter is NULL, the new process will have the same current drive and + # directory as the calling process. + win32process.STARTUPINFO() # STARTUPINFO structure. + # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + ) + + PostBreachTelem(self, + (CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send() + + win32api.CloseHandle(process_handle[0]) # Process handle + win32api.CloseHandle(process_handle[1]) # Thread handle + + except Exception as e: + # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the + # "Replace a process level token" right, using Local Security Policy editing. Worked, but only + # after reboot. So: + # 1. need to decide if worth it, and then + # 2. need to find how to do this using python... + PostBreachTelem(self, ( + "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() + + # Nothing more we can do. Leak the process handle. + except subprocess.CalledProcessError as err: + PostBreachTelem(self, ( + "Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)), + False)).send() + except NewUserError as e: + PostBreachTelem(self, (str(e), False)).send() From 51117edbea687756d3b0d7e6d9b7a0ef85160a66 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 5 Sep 2019 21:32:04 +0300 Subject: [PATCH 154/186] Add deletion of users --- .../monkey_utils/windows/new_user.py | 15 ++++++++++----- .../post_breach/actions/add_user.py | 18 ++++++++++++++++-- .../actions/communicate_as_new_user.py | 5 ++++- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py index be6e2534d..14db5c1ae 100644 --- a/monkey/infection_monkey/monkey_utils/windows/new_user.py +++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py @@ -2,7 +2,6 @@ import logging import subprocess from infection_monkey.post_breach.actions.add_user import BackdoorUser -from infection_monkey.telemetry.post_breach_telem import PostBreachTelem logger = logging.getLogger(__name__) @@ -17,14 +16,14 @@ class NewUser(object): RAII object to use for creating and using a new user in Windows. Use with `with`. User will be created when the instance is instantiated. User will log on start of `with` scope. - User will log off on end of `with` scope. + User will log off and get deleted on end of `with` scope. Example: # Created # Logged on with NewUser("user", "pass") as new_user: ... ... - # Logged off + # Logged off and deleted ... """ def __init__(self, username, password): @@ -36,7 +35,6 @@ class NewUser(object): self.password = password windows_cmds = BackdoorUser.get_windows_commands_to_add_user(self.username, self.password, True) - logger.debug("Trying these commands: {}".format(str(windows_cmds))) _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True) def __enter__(self): @@ -60,5 +58,12 @@ class NewUser(object): return self.logon_handle def __exit__(self, exc_type, exc_val, exc_tb): + # Logoff self.logon_handle.Close() - # TODO Delete user + + # Try to delete user + try: + _ = subprocess.check_output( + BackdoorUser.get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True) + except Exception as err: + raise NewUserError("Can't delete user {}. Info: {}".format(self.username, err)) diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index b82c59a66..9bb8cfcba 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -22,7 +22,7 @@ class BackdoorUser(PBA): @staticmethod def get_linux_commands_to_add_user(username): - linux_cmds = [ + return [ 'useradd', '-M', # Do not create homedir '--expiredate', @@ -32,7 +32,13 @@ class BackdoorUser(PBA): '-c', # Comment 'MONKEY_USER', # Comment username] - return linux_cmds + + @staticmethod + def get_linux_commands_to_delete_user(username): + return [ + 'deluser', + username + ] @staticmethod def get_windows_commands_to_add_user(username, password, should_be_active=False): @@ -45,3 +51,11 @@ class BackdoorUser(PBA): if not should_be_active: windows_cmds.append('/ACTIVE:NO') return windows_cmds + + @staticmethod + def get_windows_commands_to_delete_user(username): + return [ + 'net', + 'user', + username, + '/delete'] diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 8869a225f..590912c0b 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -38,14 +38,17 @@ class CommunicateAsNewUser(PBA): def communicate_as_new_user_linux(self, username): try: + # add user + ping linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) commandline = "ping -c 2 google.com" linux_cmds.extend([";", "sudo", "-u", username, commandline]) final_command = ' '.join(linux_cmds) - logger.debug("Trying to execute these commands: {}".format(final_command)) output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True) PostBreachTelem(self, ( CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send() + # delete the user + _ = subprocess.check_output( + BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True) except subprocess.CalledProcessError as e: PostBreachTelem(self, (e.output, False)).send() From e520df4c34aa7750362761011d68ae6b750ed201 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 5 Sep 2019 21:40:36 +0300 Subject: [PATCH 155/186] Fixed events length check --- .../src/components/report-components/zerotrust/EventsButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js index f7a3fbbe5..ea24e7b1a 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js @@ -33,7 +33,7 @@ export default class EventsButton extends Component { createEventsAmountBadge() { let eventsAmountBadge; - if (this.props.events.length > 10) { + if (this.props.events.length > 9) { eventsAmountBadge = 9+; } else { eventsAmountBadge = {this.props.events.length}; From f78e76bdee4c9a4eccd4a1a25fc70d0bfc654909 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sat, 7 Sep 2019 18:49:59 +0300 Subject: [PATCH 156/186] Renamed process_handle to process_info and removed bad comment --- .../post_breach/actions/communicate_as_new_user.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 590912c0b..9db9bd436 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -70,7 +70,7 @@ class CommunicateAsNewUser(PBA): # Open process as that user: # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera commandline = "{} {} {} {}".format(ping_app_path, "google.com", "-n", "2") - process_handle = win32process.CreateProcessAsUser( + process_info = win32process.CreateProcessAsUser( new_user.get_logon_handle(), # A handle to the primary token that represents a user. None, # The name of the module to be executed. commandline, # The command line to be executed. @@ -89,8 +89,8 @@ class CommunicateAsNewUser(PBA): PostBreachTelem(self, (CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send() - win32api.CloseHandle(process_handle[0]) # Process handle - win32api.CloseHandle(process_handle[1]) # Thread handle + win32api.CloseHandle(process_info[0]) # Process handle + win32api.CloseHandle(process_info[1]) # Thread handle except Exception as e: # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the @@ -100,8 +100,6 @@ class CommunicateAsNewUser(PBA): # 2. need to find how to do this using python... PostBreachTelem(self, ( "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() - - # Nothing more we can do. Leak the process handle. except subprocess.CalledProcessError as err: PostBreachTelem(self, ( "Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)), From dc2686301cc85e7e593d9ae502cedb0d3f4a863d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 9 Sep 2019 10:20:23 +0300 Subject: [PATCH 157/186] Fixed notification link and updated legend texts --- monkey/monkey_island/cc/ui/src/components/Main.js | 9 ++++++--- .../report-components/zerotrust/ReportLegend.js | 7 +++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 982791782..3a51103c9 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -29,6 +29,8 @@ let infectionMonkeyImage = require('../images/infection-monkey.svg'); let guardicoreLogoImage = require('../images/guardicore-logo.png'); let notificationIcon = require('../images/notification-logo-512x512.png'); +const reportZeroTrustPath = '/report/zero_trust'; + class AppComponent extends AuthComponent { updateStatus = () => { this.auth.loggedIn() @@ -200,7 +202,7 @@ class AppComponent extends AuthComponent { {this.renderRoute('/infection/telemetry', )} {this.renderRoute('/start-over', )} {this.renderRoute('/report/security', )} - {this.renderRoute('/report/zero_trust', )} + {this.renderRoute(reportZeroTrustPath, )} {this.renderRoute('/license', )} @@ -211,8 +213,9 @@ class AppComponent extends AuthComponent { showInfectionDoneNotification() { if (this.state.completedSteps.infection_done) { - let hostname = window.location.hostname; - let url = `https://${hostname}:5000/report`; + const hostname = window.location.hostname; + const port = window.location.port; + let url = `https://${hostname}:${port}/${reportZeroTrustPath}`; console.log("Trying to show notification. URL: " + url + " | icon: " + notificationIcon); Notifier.start( diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index 143120793..34c18eb26 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -27,19 +27,18 @@ class ZeroTrustReportLegend extends Component { getLegendContent() { return
    -

    Statuses

    • - {"\t"}Some tests failed; the monkeys found something wrong. + {"\t"}At least one of the tests related to this component failed. This means that the Infection Monkey detected an unmet Zero Trust requirement.
    • - {"\t"}The test ran; manual verification is required to determine the results. + {"\t"}At least one of the tests’ results related to this component requires further manual verification.
    • @@ -55,7 +54,7 @@ class ZeroTrustReportLegend extends Component {

    - Some of the tests can be activated using the configuration. + To activate more tests, go to the Monkey configuration page.n
    ; } } From 313911fd77ede40cccb08016de320c5715ad1ba8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 9 Sep 2019 11:38:37 +0300 Subject: [PATCH 158/186] Deleted console log + fixed link in notification --- monkey/monkey_island/cc/ui/src/components/Main.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 3a51103c9..5d2f6b898 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -215,8 +215,7 @@ class AppComponent extends AuthComponent { if (this.state.completedSteps.infection_done) { const hostname = window.location.hostname; const port = window.location.port; - let url = `https://${hostname}:${port}/${reportZeroTrustPath}`; - console.log("Trying to show notification. URL: " + url + " | icon: " + notificationIcon); + let url = `https://${hostname}:${port}${reportZeroTrustPath}`; Notifier.start( "Monkey Island", From 63d76f19f866519e18e4066fbfe507742554b8a7 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 9 Sep 2019 11:47:16 +0300 Subject: [PATCH 159/186] Updated notification to only show if the island is not on the report page already --- .../cc/ui/src/components/Main.js | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 5d2f6b898..1df53fd4f 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -29,7 +29,7 @@ let infectionMonkeyImage = require('../images/infection-monkey.svg'); let guardicoreLogoImage = require('../images/guardicore-logo.png'); let notificationIcon = require('../images/notification-logo-512x512.png'); -const reportZeroTrustPath = '/report/zero_trust'; +const reportZeroTrustRoute = '/report/zero_trust'; class AppComponent extends AuthComponent { updateStatus = () => { @@ -202,7 +202,7 @@ class AppComponent extends AuthComponent { {this.renderRoute('/infection/telemetry', )} {this.renderRoute('/start-over', )} {this.renderRoute('/report/security', )} - {this.renderRoute(reportZeroTrustPath, )} + {this.renderRoute(reportZeroTrustRoute, )} {this.renderRoute('/license', )} @@ -213,15 +213,18 @@ class AppComponent extends AuthComponent { showInfectionDoneNotification() { if (this.state.completedSteps.infection_done) { - const hostname = window.location.hostname; - const port = window.location.port; - let url = `https://${hostname}:${port}${reportZeroTrustPath}`; + // No need to show the notification to redirect to the report if we're already in the report page + if (!window.location.href.includes("report")) { + const hostname = window.location.hostname; + const port = window.location.port; + let url = `https://${hostname}:${port}${reportZeroTrustRoute}`; - Notifier.start( - "Monkey Island", - "Infection is done! Click here to go to the report page.", - url, - notificationIcon); + Notifier.start( + "Monkey Island", + "Infection is done! Click here to go to the report page.", + url, + notificationIcon); + } } } } From a32012ce5212e0e7ed2e79ee8d664cfad3cfdbd4 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 9 Sep 2019 13:35:46 +0300 Subject: [PATCH 160/186] Added communicate as new user to default PBA actions --- monkey/monkey_island/cc/services/config_schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py index 16b51984d..046273912 100644 --- a/monkey/monkey_island/cc/services/config_schema.py +++ b/monkey/monkey_island/cc/services/config_schema.py @@ -337,6 +337,7 @@ SCHEMA = { "$ref": "#/definitions/post_breach_acts" }, "default": [ + "CommunicateAsNewUser" ], "description": "List of actions the Monkey will run post breach" }, From a51a6065b8c432a6cc2521e771b855aaf527b889 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 9 Sep 2019 17:27:53 +0300 Subject: [PATCH 161/186] Now looking at the exit codes of ping --- monkey/common/data/zero_trust_consts.py | 3 +- .../actions/communicate_as_new_user.py | 37 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 385f28338..780aaafa4 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -57,7 +57,8 @@ RECOMMENDATIONS = { RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.", RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.", - RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC only.", + RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory " + u"Access Control) only.", } POSSIBLE_STATUSES_KEY = u"possible_statuses" diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 9db9bd436..be2b824bb 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -3,6 +3,7 @@ import os import random import string import subprocess +import time from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER from infection_monkey.monkey_utils.windows.new_user import NewUser, NewUserError @@ -11,8 +12,12 @@ from infection_monkey.post_breach.pba import PBA from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils import is_windows_os -CREATED_PROCESS_AS_USER_WINDOWS_FORMAT = "Created process '{}' as user '{}'." -CREATED_PROCESS_AS_USER_LINUX_FORMAT = "Created process '{}' as user '{}'. Some of the output was '{}'." +PING_TEST_DOMAIN = "google.com" + +PING_WAIT_TIMEOUT_IN_SECONDS = 20 + +CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT = "Created process '{}' as user '{}', and successfully pinged." +CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT = "Created process '{}' as user '{}', but failed to ping (exit status {})." USERNAME = "somenewuser" PASSWORD = "N3WPa55W0rD!1" @@ -40,12 +45,11 @@ class CommunicateAsNewUser(PBA): try: # add user + ping linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) - commandline = "ping -c 2 google.com" + commandline = "ping -c 1 {}".format(PING_TEST_DOMAIN) linux_cmds.extend([";", "sudo", "-u", username, commandline]) final_command = ' '.join(linux_cmds) - output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True) - PostBreachTelem(self, ( - CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send() + exit_status = os.system(final_command) + self.send_ping_result_telemetry(exit_status, commandline, username) # delete the user _ = subprocess.check_output( BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True) @@ -69,7 +73,7 @@ class CommunicateAsNewUser(PBA): try: # Open process as that user: # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera - commandline = "{} {} {} {}".format(ping_app_path, "google.com", "-n", "2") + commandline = "{} {} {} {}".format(ping_app_path, PING_TEST_DOMAIN, "-n", "1") process_info = win32process.CreateProcessAsUser( new_user.get_logon_handle(), # A handle to the primary token that represents a user. None, # The name of the module to be executed. @@ -86,8 +90,15 @@ class CommunicateAsNewUser(PBA): # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa ) - PostBreachTelem(self, - (CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send() + ping_exit_code = win32process.GetExitCodeProcess(process_info[0]) + counter = 0 + while ping_exit_code == win32con.STILL_ACTIVE and counter < PING_WAIT_TIMEOUT_IN_SECONDS: + ping_exit_code = win32process.GetExitCodeProcess(process_info[0]) + counter += 1 + logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format(counter, ping_exit_code)) + time.sleep(1) + + self.send_ping_result_telemetry(ping_exit_code, commandline, username) win32api.CloseHandle(process_info[0]) # Process handle win32api.CloseHandle(process_info[1]) # Thread handle @@ -106,3 +117,11 @@ class CommunicateAsNewUser(PBA): False)).send() except NewUserError as e: PostBreachTelem(self, (str(e), False)).send() + + def send_ping_result_telemetry(self, exit_status, commandline, username): + if exit_status == 0: + PostBreachTelem(self, ( + CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT.format(commandline, username), True)).send() + else: + PostBreachTelem(self, ( + CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT.format(commandline, username, exit_status), False)).send() From 53f31ddcc984b7899d15143ff9c3d4d10ea05f8d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 9 Sep 2019 17:36:00 +0300 Subject: [PATCH 162/186] Refactored notification logic to method --- .../cc/ui/src/components/Main.js | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 1df53fd4f..9fd3d8f1c 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -212,21 +212,23 @@ class AppComponent extends AuthComponent { } showInfectionDoneNotification() { - if (this.state.completedSteps.infection_done) { - // No need to show the notification to redirect to the report if we're already in the report page - if (!window.location.href.includes("report")) { - const hostname = window.location.hostname; - const port = window.location.port; - let url = `https://${hostname}:${port}${reportZeroTrustRoute}`; + if (this.shouldShowNotification()) { + const hostname = window.location.hostname; + const port = window.location.port; + const url = `https://${hostname}:${port}${reportZeroTrustRoute}`; - Notifier.start( - "Monkey Island", - "Infection is done! Click here to go to the report page.", - url, - notificationIcon); - } + Notifier.start( + "Monkey Island", + "Infection is done! Click here to go to the report page.", + url, + notificationIcon); } } + + shouldShowNotification() { + // No need to show the notification to redirect to the report if we're already in the report page + return (this.state.completedSteps.infection_done && !window.location.pathname.startsWith("/report")); + } } AppComponent.defaultProps = {}; From 4dca735265f62c771926e3bdd9df291110726fa4 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 10 Sep 2019 14:43:48 +0300 Subject: [PATCH 163/186] Changed `check_output` to `Popen` to make user deletion async we don't care about its result --- .../post_breach/actions/communicate_as_new_user.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index be2b824bb..75acf6fe0 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -50,9 +50,10 @@ class CommunicateAsNewUser(PBA): final_command = ' '.join(linux_cmds) exit_status = os.system(final_command) self.send_ping_result_telemetry(exit_status, commandline, username) - # delete the user - _ = subprocess.check_output( + # delete the user, async in case it gets stuck. + _ = subprocess.Popen( BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True) + # Leaking the process on purpose - nothing we can do if it's stuck. except subprocess.CalledProcessError as e: PostBreachTelem(self, (e.output, False)).send() From 50f8e9053a31669d1e486555c0e5be364f09f742 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 10 Sep 2019 14:50:54 +0300 Subject: [PATCH 164/186] Changed on windows as well --- monkey/infection_monkey/monkey_utils/windows/new_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py index 14db5c1ae..fbe4bd832 100644 --- a/monkey/infection_monkey/monkey_utils/windows/new_user.py +++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py @@ -63,7 +63,7 @@ class NewUser(object): # Try to delete user try: - _ = subprocess.check_output( + _ = subprocess.Popen( BackdoorUser.get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True) except Exception as err: raise NewUserError("Can't delete user {}. Info: {}".format(self.username, err)) From 5f02ebe1e0f217bd23d6ee407d2c00033a602dff Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 10 Sep 2019 19:32:46 +0300 Subject: [PATCH 165/186] Added Guardicore processes to AV list --- .../zero_trust_tests/known_anti_viruses.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py index e10792d0c..e5d7c2355 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py @@ -65,5 +65,23 @@ ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ u"DWHWizrd.exe", u"RtvStart.exe", u"roru.exe", - u"WSCSAvNotifier" + u"WSCSAvNotifier", + # Guardicore Centra + # Linux + u"gc-agents-service", + u"gc-guest-agent", + u"gc-guardig", + u"gc-digger", + u"gc-fastpath", + u"gc-enforcement-agent", + u"gc-enforcement-channel", + u"gc-detection-agent", + # Windows + u"gc-guest-agent.exe", + u"gc-windig.exe", + u"gc-digger.exe", + u"gc-fastpath.exe", + u"gc-enforcement-channel.exe", + u"gc-enforcement-agent.exe", + u"gc-agent-ui.exe" ] From cfd0c10d59a7885a71ee4a6e2d17372135f83d34 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 10 Sep 2019 23:44:03 +0300 Subject: [PATCH 166/186] Refactoring inconclusive to verify and recommendation to principle Product writer's orders --- monkey/common/data/zero_trust_consts.py | 94 +++++++++---------- .../cc/models/zero_trust/finding.py | 4 +- .../zero_trust/test_aggregate_finding.py | 4 +- .../cc/resources/reporting/report.py | 6 +- .../reporting/test_zero_trust_service.py | 64 ++++++------- .../services/reporting/zero_trust_service.py | 32 +++---- .../zero_trust_tests/data_endpoints.py | 2 +- .../zero_trust_tests/machine_exploited.py | 2 +- .../telemetry/zero_trust_tests/tunneling.py | 4 +- .../components/pages/ZeroTrustReportPage.js | 12 +-- .../zerotrust/FindingsSection.js | 2 +- .../zerotrust/PrinciplesSection.js | 29 ++++++ ...tatusTable.js => PrinciplesStatusTable.js} | 12 +-- .../zerotrust/RecommendationsSection.js | 29 ------ .../zerotrust/ReportLegend.js | 5 +- ...tus.js => SinglePillarPrinciplesStatus.js} | 12 +-- .../zerotrust/StatusLabel.js | 4 +- .../zerotrust/StatusesToPillarsSummary.js | 2 +- .../zerotrust/SummarySection.js | 17 +--- .../zerotrust/ZeroTrustPillars.js | 2 +- .../zerotrust/venn-components/VennDiagram.js | 11 +-- 21 files changed, 167 insertions(+), 182 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{RecommendationsStatusTable.js => PrinciplesStatusTable.js} (79%) delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{SinglePillarRecommendationsStatus.js => SinglePillarPrinciplesStatus.js} (67%) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 780aaafa4..3362756d9 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -2,7 +2,7 @@ This file contains all the static data relating to Zero Trust. It is mostly used in the zero trust report generation and in creating findings. -This file contains static mappings between zero trust components such as: pillars, recommendations, tests, statuses. +This file contains static mappings between zero trust components such as: pillars, principles, tests, statuses. Some of the mappings are computed when this module is loaded. """ @@ -17,10 +17,10 @@ PILLARS = (DATA, PEOPLE, NETWORKS, DEVICES, WORKLOADS, VISIBILITY_ANALYTICS, AUT STATUS_UNEXECUTED = u"Unexecuted" STATUS_PASSED = u"Passed" -STATUS_INCONCLUSIVE = u"Inconclusive" +STATUS_VERIFY = u"Verify" STATUS_FAILED = u"Failed" # Don't change order! The statuses are ordered by importance/severity. -ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_INCONCLUSIVE, STATUS_PASSED, STATUS_UNEXECUTED] +ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED] TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic" TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http" @@ -43,27 +43,27 @@ TESTS = ( TEST_COMMUNICATE_AS_NEW_USER ) -RECOMMENDATION_DATA_TRANSIT = u"data_transit" -RECOMMENDATION_ENDPOINT_SECURITY = u"endpoint_security" -RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour" -RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" -RECOMMENDATION_SEGMENTATION = u"segmentation" -RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies" -RECOMMENDATION_USERS_MAC_POLICIES = u"users_mac_policies" -RECOMMENDATIONS = { - RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", - RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", - RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", - RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", - RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.", - RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.", - RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory " +PRINCIPLE_DATA_TRANSIT = u"data_transit" +PRINCIPLE_ENDPOINT_SECURITY = u"endpoint_security" +PRINCIPLE_USER_BEHAVIOUR = u"user_behaviour" +PRINCIPLE_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" +PRINCIPLE_SEGMENTATION = u"segmentation" +PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES = u"network_policies" +PRINCIPLE_USERS_MAC_POLICIES = u"users_mac_policies" +PRINCIPLES = { + PRINCIPLE_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", + PRINCIPLE_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", + PRINCIPLE_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", + PRINCIPLE_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", + PRINCIPLE_DATA_TRANSIT: u"Secure data at transit by encrypting it.", + PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.", + PRINCIPLE_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory " u"Access Control) only.", } POSSIBLE_STATUSES_KEY = u"possible_statuses" PILLARS_KEY = u"pillars" -RECOMMENDATION_KEY = u"recommendation_key" +PRINCIPLE_KEY = u"principle_key" FINDING_EXPLANATION_BY_STATUS_KEY = u"finding_explanation" TEST_EXPLANATION_KEY = u"explanation" TESTS_MAP = { @@ -73,18 +73,18 @@ TESTS_MAP = { STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.", STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." }, - RECOMMENDATION_KEY: RECOMMENDATION_SEGMENTATION, + PRINCIPLE_KEY: PRINCIPLE_SEGMENTATION, PILLARS_KEY: [NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED] }, TEST_MALICIOUS_ACTIVITY_TIMELINE: { TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_INCONCLUSIVE: "Monkey performed malicious actions in the network. Check SOC logs and alerts." + STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and alerts." }, - RECOMMENDATION_KEY: RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC, + PRINCIPLE_KEY: PRINCIPLE_ANALYZE_NETWORK_TRAFFIC, PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY] }, TEST_ENDPOINT_SECURITY_EXISTS: { TEST_EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.", @@ -92,7 +92,7 @@ TESTS_MAP = { STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.", STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern." }, - RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY, + PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, @@ -102,19 +102,19 @@ TESTS_MAP = { STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.", STATUS_PASSED: "Monkey didn't manage to exploit an endpoint." }, - RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY, + PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_INCONCLUSIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_VERIFY] }, TEST_SCHEDULED_EXECUTION: { TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_INCONCLUSIVE: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.", + STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.", STATUS_PASSED: "Monkey failed to execute in a scheduled manner." }, - RECOMMENDATION_KEY: RECOMMENDATION_USER_BEHAVIOUR, + PRINCIPLE_KEY: PRINCIPLE_USER_BEHAVIOUR, PILLARS_KEY: [PEOPLE, NETWORKS], - POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE] + POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY] }, TEST_DATA_ENDPOINT_ELASTIC: { TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.", @@ -122,7 +122,7 @@ TESTS_MAP = { STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.", STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them." }, - RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT, + PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, @@ -132,7 +132,7 @@ TESTS_MAP = { STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.", STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them." }, - RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT, + PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, @@ -141,7 +141,7 @@ TESTS_MAP = { FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "Monkey was tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them." }, - RECOMMENDATION_KEY: RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES, + PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES, PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED] }, @@ -151,7 +151,7 @@ TESTS_MAP = { STATUS_FAILED: "Monkey was able to cause a new user to access the network. Your network policies are too permissive - restrict them to MAC only.", STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network." }, - RECOMMENDATION_KEY: RECOMMENDATION_USERS_MAC_POLICIES, + PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES, PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, @@ -171,15 +171,15 @@ PILLARS_TO_TESTS = { AUTOMATION_ORCHESTRATION: [] } -RECOMMENDATIONS_TO_TESTS = {} +PRINCIPLES_TO_TESTS = {} -RECOMMENDATIONS_TO_PILLARS = {} +PRINCIPLES_TO_PILLARS = {} def populate_mappings(): populate_pillars_to_tests() - populate_recommendations_to_tests() - populate_recommendations_to_pillars() + populate_principles_to_tests() + populate_principles_to_pillars() def populate_pillars_to_tests(): @@ -189,17 +189,17 @@ def populate_pillars_to_tests(): PILLARS_TO_TESTS[pillar].append(test) -def populate_recommendations_to_tests(): - for single_recommendation in RECOMMENDATIONS: - RECOMMENDATIONS_TO_TESTS[single_recommendation] = [] +def populate_principles_to_tests(): + for single_principle in PRINCIPLES: + PRINCIPLES_TO_TESTS[single_principle] = [] for test, test_info in TESTS_MAP.items(): - RECOMMENDATIONS_TO_TESTS[test_info[RECOMMENDATION_KEY]].append(test) + PRINCIPLES_TO_TESTS[test_info[PRINCIPLE_KEY]].append(test) -def populate_recommendations_to_pillars(): - for recommendation, recommendation_tests in RECOMMENDATIONS_TO_TESTS.items(): - recommendations_pillars = set() - for test in recommendation_tests: +def populate_principles_to_pillars(): + for principle, principle_tests in PRINCIPLES_TO_TESTS.items(): + principles_pillars = set() + for test in principle_tests: for pillar in TESTS_MAP[test][PILLARS_KEY]: - recommendations_pillars.add(pillar) - RECOMMENDATIONS_TO_PILLARS[recommendation] = recommendations_pillars + principles_pillars.add(pillar) + PRINCIPLES_TO_PILLARS[principle] = principles_pillars diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index 441d22e3a..df4eb12f7 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -14,12 +14,12 @@ from monkey_island.cc.models.zero_trust.event import Event class Finding(Document): """ This model represents a Zero-Trust finding: A result of a test the monkey/island might perform to see if a - specific recommendation of zero trust is upheld or broken. + specific principle of zero trust is upheld or broken. Findings might have the following statuses: Failed ❌ Meaning that we are sure that something is wrong (example: segmentation issue). - Inconclusive ⁉ + Verify ⁉ Meaning that we need the user to check something himself (example: 2FA logs, AV missing). Passed ✔ Meaning that we are sure that something is correct (example: Monkey failed exploiting). diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py index b32e8ad53..4a67a21b7 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py @@ -11,7 +11,7 @@ class TestAggregateFinding(IslandTestCase): self.clean_finding_db() test = TEST_MALICIOUS_ACTIVITY_TIMELINE - status = STATUS_INCONCLUSIVE + status = STATUS_VERIFY events = [Event.create_event("t", "t", EVENT_TYPE_ISLAND)] self.assertEquals(len(Finding.objects(test=test, status=status)), 0) @@ -30,7 +30,7 @@ class TestAggregateFinding(IslandTestCase): self.clean_finding_db() test = TEST_MALICIOUS_ACTIVITY_TIMELINE - status = STATUS_INCONCLUSIVE + status = STATUS_VERIFY event = Event.create_event("t", "t", EVENT_TYPE_ISLAND) events = [event] self.assertEquals(len(Finding.objects(test=test, status=status)), 0) diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index db2f40518..8c5286fee 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -14,7 +14,7 @@ REPORT_TYPES = [SECURITY_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE] REPORT_DATA_PILLARS = "pillars" REPORT_DATA_FINDINGS = "findings" -REPORT_DATA_RECOMMENDATIONS_STATUS = "recommendations" +REPORT_DATA_PRINCIPLES_STATUS = "principles" __author__ = ["itay.mizeretz", "shay.nehmad"] @@ -33,8 +33,8 @@ class Report(flask_restful.Resource): "grades": ZeroTrustService.get_pillars_grades() } ) - elif report_data == REPORT_DATA_RECOMMENDATIONS_STATUS: - return jsonify(ZeroTrustService.get_recommendations_status()) + elif report_data == REPORT_DATA_PRINCIPLES_STATUS: + return jsonify(ZeroTrustService.get_principles_status()) elif report_data == REPORT_DATA_FINDINGS: return jsonify(ZeroTrustService.get_all_findings()) diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py index 2bd74c796..5d84a9cb0 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py @@ -11,12 +11,12 @@ def save_example_findings(): Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, []) # devices passed = 2 Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_FAILED, []) # devices failed = 1 # devices unexecuted = 1 - # people inconclusive = 1 - # networks inconclusive = 1 - Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_INCONCLUSIVE, []) - # people inconclusive = 2 - # networks inconclusive = 2 - Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_INCONCLUSIVE, []) + # people verify = 1 + # networks verify = 1 + Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, []) + # people verify = 2 + # networks verify = 2 + Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, []) # data failed 1 Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) # data failed 2 @@ -27,10 +27,10 @@ def save_example_findings(): Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) # data failed 5 Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) - # data inconclusive 1 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, []) - # data inconclusive 2 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, []) + # data verify 1 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, []) + # data verify 2 + Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, []) # data passed 1 Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_PASSED, []) @@ -45,49 +45,49 @@ class TestZeroTrustService(IslandTestCase): expected = [ { STATUS_FAILED: 5, - STATUS_INCONCLUSIVE: 2, + STATUS_VERIFY: 2, STATUS_PASSED: 1, STATUS_UNEXECUTED: 1, "pillar": "Data" }, { STATUS_FAILED: 0, - STATUS_INCONCLUSIVE: 2, + STATUS_VERIFY: 2, STATUS_PASSED: 0, STATUS_UNEXECUTED: 0, "pillar": "People" }, { STATUS_FAILED: 0, - STATUS_INCONCLUSIVE: 2, + STATUS_VERIFY: 2, STATUS_PASSED: 0, STATUS_UNEXECUTED: 2, "pillar": "Networks" }, { STATUS_FAILED: 1, - STATUS_INCONCLUSIVE: 0, + STATUS_VERIFY: 0, STATUS_PASSED: 2, STATUS_UNEXECUTED: 1, "pillar": "Devices" }, { STATUS_FAILED: 0, - STATUS_INCONCLUSIVE: 0, + STATUS_VERIFY: 0, STATUS_PASSED: 0, STATUS_UNEXECUTED: 0, "pillar": "Workloads" }, { STATUS_FAILED: 0, - STATUS_INCONCLUSIVE: 0, + STATUS_VERIFY: 0, STATUS_PASSED: 0, STATUS_UNEXECUTED: 1, "pillar": "Visibility & Analytics" }, { STATUS_FAILED: 0, - STATUS_INCONCLUSIVE: 0, + STATUS_VERIFY: 0, STATUS_PASSED: 0, STATUS_UNEXECUTED: 0, "pillar": "Automation & Orchestration" @@ -98,7 +98,7 @@ class TestZeroTrustService(IslandTestCase): self.assertEquals(result, expected) - def test_get_recommendations_status(self): + def test_get_principles_status(self): self.fail_if_not_testing_env() self.clean_finding_db() @@ -108,7 +108,7 @@ class TestZeroTrustService(IslandTestCase): AUTOMATION_ORCHESTRATION: [], DATA: [ { - "recommendation": RECOMMENDATIONS[RECOMMENDATION_DATA_TRANSIT], + "principle": PRINCIPLES[PRINCIPLE_DATA_TRANSIT], "status": STATUS_FAILED, "tests": [ { @@ -124,7 +124,7 @@ class TestZeroTrustService(IslandTestCase): ], DEVICES: [ { - "recommendation": RECOMMENDATIONS[RECOMMENDATION_ENDPOINT_SECURITY], + "principle": PRINCIPLES[PRINCIPLE_ENDPOINT_SECURITY], "status": STATUS_FAILED, "tests": [ { @@ -140,7 +140,7 @@ class TestZeroTrustService(IslandTestCase): ], NETWORKS: [ { - "recommendation": RECOMMENDATIONS[RECOMMENDATION_SEGMENTATION], + "principle": PRINCIPLES[PRINCIPLE_SEGMENTATION], "status": STATUS_UNEXECUTED, "tests": [ { @@ -150,17 +150,17 @@ class TestZeroTrustService(IslandTestCase): ] }, { - "recommendation": RECOMMENDATIONS[RECOMMENDATION_USER_BEHAVIOUR], - "status": STATUS_INCONCLUSIVE, + "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR], + "status": STATUS_VERIFY, "tests": [ { - "status": STATUS_INCONCLUSIVE, + "status": STATUS_VERIFY, "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY] } ] }, { - "recommendation": RECOMMENDATIONS[RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC], + "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], "status": STATUS_UNEXECUTED, "tests": [ { @@ -172,11 +172,11 @@ class TestZeroTrustService(IslandTestCase): ], PEOPLE: [ { - "recommendation": RECOMMENDATIONS[RECOMMENDATION_USER_BEHAVIOUR], - "status": STATUS_INCONCLUSIVE, + "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR], + "status": STATUS_VERIFY, "tests": [ { - "status": STATUS_INCONCLUSIVE, + "status": STATUS_VERIFY, "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY] } ] @@ -184,7 +184,7 @@ class TestZeroTrustService(IslandTestCase): ], "Visibility & Analytics": [ { - "recommendation": RECOMMENDATIONS[RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC], + "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], "status": STATUS_UNEXECUTED, "tests": [ { @@ -197,7 +197,7 @@ class TestZeroTrustService(IslandTestCase): "Workloads": [] } - self.assertEquals(ZeroTrustService.get_recommendations_status(), expected) + self.assertEquals(ZeroTrustService.get_principles_status(), expected) def test_get_pillars_to_statuses(self): self.fail_if_not_testing_env() @@ -222,8 +222,8 @@ class TestZeroTrustService(IslandTestCase): expected = { AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED, DEVICES: STATUS_FAILED, - NETWORKS: STATUS_INCONCLUSIVE, - PEOPLE: STATUS_INCONCLUSIVE, + NETWORKS: STATUS_VERIFY, + PEOPLE: STATUS_VERIFY, VISIBILITY_ANALYTICS: STATUS_UNEXECUTED, WORKLOADS: STATUS_UNEXECUTED, DATA: STATUS_FAILED diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index d8f6c87e9..f4b23f095 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -17,7 +17,7 @@ class ZeroTrustService(object): pillar_grade = { "pillar": pillar, STATUS_FAILED: 0, - STATUS_INCONCLUSIVE: 0, + STATUS_VERIFY: 0, STATUS_PASSED: 0, STATUS_UNEXECUTED: 0 } @@ -39,30 +39,30 @@ class ZeroTrustService(object): return pillar_grade @staticmethod - def get_recommendations_status(): - all_recommendations_statuses = {} + def get_principles_status(): + all_principles_statuses = {} # init with empty lists for pillar in PILLARS: - all_recommendations_statuses[pillar] = [] + all_principles_statuses[pillar] = [] - for recommendation, recommendation_tests in RECOMMENDATIONS_TO_TESTS.items(): - for pillar in RECOMMENDATIONS_TO_PILLARS[recommendation]: - all_recommendations_statuses[pillar].append( + for principle, principle_tests in PRINCIPLES_TO_TESTS.items(): + for pillar in PRINCIPLES_TO_PILLARS[principle]: + all_principles_statuses[pillar].append( { - "recommendation": RECOMMENDATIONS[recommendation], - "tests": ZeroTrustService.__get_tests_status(recommendation_tests), - "status": ZeroTrustService.__get_recommendation_status(recommendation_tests) + "principle": PRINCIPLES[principle], + "tests": ZeroTrustService.__get_tests_status(principle_tests), + "status": ZeroTrustService.__get_principle_status(principle_tests) } ) - return all_recommendations_statuses + return all_principles_statuses @staticmethod - def __get_recommendation_status(recommendation_tests): + def __get_principle_status(principle_tests): worst_status = STATUS_UNEXECUTED all_statuses = set() - for test in recommendation_tests: + for test in principle_tests: all_statuses |= set(Finding.objects(test=test).distinct("status")) for status in all_statuses: @@ -72,9 +72,9 @@ class ZeroTrustService(object): return worst_status @staticmethod - def __get_tests_status(recommendation_tests): + def __get_tests_status(principle_tests): results = [] - for test in recommendation_tests: + for test in principle_tests: test_findings = Finding.objects(test=test) results.append( { @@ -124,7 +124,7 @@ class ZeroTrustService(object): def get_statuses_to_pillars(): results = { STATUS_FAILED: [], - STATUS_INCONCLUSIVE: [], + STATUS_VERIFY: [], STATUS_PASSED: [], STATUS_UNEXECUTED: [] } diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index b84dd94c9..7b45b1dee 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -69,6 +69,6 @@ def test_open_data_endpoints(telemetry_json): AggregateFinding.create_or_add_to_existing( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, - status=STATUS_INCONCLUSIVE, + status=STATUS_VERIFY, events=events ) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index 88661d1aa..8198b5a3e 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -38,6 +38,6 @@ def test_machine_exploited(current_monkey, exploit_successful, exploiter, target AggregateFinding.create_or_add_to_existing( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, - status=STATUS_INCONCLUSIVE, + status=STATUS_VERIFY, events=events ) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py index 2c9be5e1f..ba55fc575 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py @@ -1,4 +1,4 @@ -from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_INCONCLUSIVE, \ +from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_VERIFY, \ TEST_MALICIOUS_ACTIVITY_TIMELINE from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding @@ -26,6 +26,6 @@ def test_tunneling_violation(tunnel_telemetry_json): AggregateFinding.create_or_add_to_existing( test=TEST_MALICIOUS_ACTIVITY_TIMELINE, - status=STATUS_INCONCLUSIVE, + status=STATUS_VERIFY, events=tunneling_events ) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js index c0d1f1bed..a0b92d9bd 100755 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js @@ -8,7 +8,7 @@ import PrintReportButton from "../report-components/common/PrintReportButton"; import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; import SummarySection from "../report-components/zerotrust/SummarySection"; import FindingsSection from "../report-components/zerotrust/FindingsSection"; -import RecommendationsSection from "../report-components/zerotrust/RecommendationsSection"; +import PrinciplesSection from "../report-components/zerotrust/PrinciplesSection"; class ZeroTrustReportPageComponent extends AuthComponent { @@ -72,8 +72,8 @@ class ZeroTrustReportPageComponent extends AuthComponent { } else { content =
    - +
    ; } @@ -102,7 +102,7 @@ class ZeroTrustReportPageComponent extends AuthComponent { stillLoadingDataFromServer() { return typeof this.state.findings === "undefined" || typeof this.state.pillars === "undefined" - || typeof this.state.recommendations === "undefined"; + || typeof this.state.principles === "undefined"; } getZeroTrustReportFromServer() { @@ -114,11 +114,11 @@ class ZeroTrustReportPageComponent extends AuthComponent { findings: res }); }); - this.authFetch('/api/report/zero_trust/recommendations') + this.authFetch('/api/report/zero_trust/principles') .then(res => res.json()) .then(res => { this.setState({ - recommendations: res + principles: res }); }); this.authFetch('/api/report/zero_trust/pillars') diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js index d86f5cb06..95b9d0389 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js @@ -35,7 +35,7 @@ class FindingsSection extends Component {

    - +
    ); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js new file mode 100644 index 000000000..44b427c11 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js @@ -0,0 +1,29 @@ +import React, {Component} from "react"; +import SinglePillarPrinciplesStatus from "./SinglePillarPrinciplesStatus"; +import * as PropTypes from "prop-types"; + +export default class PrinciplesSection extends Component { + render() { + return
    +

    Test Results

    +

    + The Zero Trust eXtended (ZTX) framework is composed of 7 pillars. Each pillar is built of + several guiding principles tested by the Infection Monkey. +

    + { + Object.keys(this.props.principles).map((pillar) => + + ) + } +
    + } +} + +PrinciplesSection.propTypes = { + principles: PropTypes.object, + pillarsToStatuses: PropTypes.object +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js similarity index 79% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js index e1ba3f814..b50ee0c28 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js @@ -16,7 +16,7 @@ const columns = [ }, maxWidth: MAX_WIDTH_STATUS_COLUMN }, - { Header: 'ZT Recommendation', accessor: 'recommendation', + { Header: 'Zero Trust Principle', accessor: 'principle', style: {'whiteSpace': 'unset'} // This enables word wrap }, { Header: 'Monkey Tests', id: 'tests', @@ -34,7 +34,7 @@ class TestsStatus extends AuthComponent { return ( {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.failed)} - {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.inconclusive)} + {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.verify)} {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.passed)} {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.unexecuted)} @@ -60,12 +60,12 @@ class TestsStatus extends AuthComponent { } } -export class RecommendationsStatusTable extends AuthComponent { +export class PrinciplesStatusTable extends AuthComponent { render() { - return ; + return ; } } -export default RecommendationsStatusTable; +export default PrinciplesStatusTable; -RecommendationsStatusTable.propTypes = {recommendationsStatus: PropTypes.array}; +PrinciplesStatusTable.propTypes = {principlesStatus: PropTypes.array}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js deleted file mode 100644 index e83d1c4cc..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js +++ /dev/null @@ -1,29 +0,0 @@ -import React, {Component} from "react"; -import SinglePillarRecommendationsStatus from "./SinglePillarRecommendationsStatus"; -import * as PropTypes from "prop-types"; - -export default class RecommendationsSection extends Component { - render() { - return
    -

    Recommendations

    -

    - Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results - to understand how the monkey tested your adherence to that recommendation. -

    - { - Object.keys(this.props.recommendations).map((pillar) => - - ) - } -
    - } -} - -RecommendationsSection.propTypes = { - recommendations: PropTypes.object, - pillarsToStatuses: PropTypes.object -}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index 34c18eb26..1881c82d2 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -36,7 +36,7 @@ class ZeroTrustReportLegend extends Component {
  • - +
    {"\t"}At least one of the tests’ results related to this component requires further manual verification.
  • @@ -50,11 +50,10 @@ class ZeroTrustReportLegend extends Component {
    - {"\t"}This status means the test wasn't executed. + {"\t"}This status means the test wasn't executed.To activate more tests, refer to the Monkey configuration page.
    - To activate more tests, go to the Monkey configuration page.n
    ; } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js similarity index 67% rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js index 1ce02afce..8e4512ac7 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js @@ -1,13 +1,13 @@ import AuthComponent from "../../AuthComponent"; import PillarLabel from "./PillarLabel"; -import RecommendationsStatusTable from "./RecommendationsStatusTable"; +import PrinciplesStatusTable from "./PrinciplesStatusTable"; import React from "react"; import * as PropTypes from "prop-types"; import {Panel} from "react-bootstrap"; -export default class SinglePillarRecommendationsStatus extends AuthComponent { +export default class SinglePillarPrinciplesStatus extends AuthComponent { render() { - if (this.props.recommendationsStatus.length === 0) { + if (this.props.principlesStatus.length === 0) { return null; } else { @@ -22,7 +22,7 @@ export default class SinglePillarRecommendationsStatus extends AuthComponent { - + @@ -31,7 +31,7 @@ export default class SinglePillarRecommendationsStatus extends AuthComponent { } } -SinglePillarRecommendationsStatus.propTypes = { - recommendationsStatus: PropTypes.array, +SinglePillarPrinciplesStatus.propTypes = { + principlesStatus: PropTypes.array, pillar: PropTypes.string, }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js index 12c65b728..028ca7d89 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -3,14 +3,14 @@ import * as PropTypes from "prop-types"; const statusToIcon = { "Passed": "fa-check", - "Inconclusive": "fa-exclamation-triangle", + "Verify": "fa-exclamation-triangle", "Failed": "fa-bomb", "Unexecuted": "fa-question", }; export const statusToLabelType = { "Passed": "label-success", - "Inconclusive": "label-warning", + "Verify": "label-warning", "Failed": "label-danger", "Unexecuted": "label-default", }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js index 4a597566c..d34a484b9 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js @@ -8,7 +8,7 @@ export default class StatusesToPillarsSummary extends Component { render() { return (
    {this.getStatusSummary(ZeroTrustStatuses.failed)} - {this.getStatusSummary(ZeroTrustStatuses.inconclusive)} + {this.getStatusSummary(ZeroTrustStatuses.verify)} {this.getStatusSummary(ZeroTrustStatuses.passed)} {this.getStatusSummary(ZeroTrustStatuses.unexecuted)}
    ); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js index 4a56a8b9e..585f22047 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js @@ -14,7 +14,8 @@ export default class SummarySection extends Component {

    - Get a quick glance of the status for each of Zero Trust's seven pillars. + Get a quick glance at how your network aligns with the Zero + Trust eXtended (ZTX) framework.

    @@ -27,20 +28,6 @@ export default class SummarySection extends Component { - - -

    What am I seeing?

    -

    - The Zero - Trust eXtended framework categorizes its recommendations into 7 pillars. Infection - Monkey - Zero Trust edition tests some of those recommendations. The tests that the monkey executes - produce findings. The tests, recommendations and pillars are then granted a status in - accordance - with the tests results. -

    - -
    } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js index 2165916da..dd2a55865 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js @@ -10,7 +10,7 @@ export const ZeroTrustPillars = { export const ZeroTrustStatuses = { failed: "Failed", - inconclusive: "Inconclusive", + verify: "Verify", passed: "Passed", unexecuted: "Unexecuted" }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index c1d5d2a68..70304daad 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -78,23 +78,22 @@ class VennDiagram extends React.Component { RULE #1: All scores have to be equal 0, except Unexecuted [U] which could be also a negative integer sum(C, I, P) has to be <=0 - RULE #2: Conclusive [C] has to be > 0, + RULE #2: Failed [C] has to be > 0, sum(C) > 0 - RULE #3: Inconclusive [I] has to be > 0 while Conclusive has to be 0, + RULE #3: Verify [I] has to be > 0 while Failed has to be 0, sum(C, I) > 0 and C * I = 0, while C has to be 0 RULE #4: By process of elimination, passed. if the P is bigger by 2 then negative U, first conditional would be true. - */ this.rules = [ { id: 'Rule #1', status: ZeroTrustStatuses.unexecuted, hex: '#777777', f: function (d_) { - return d_[ZeroTrustStatuses.failed] + d_[ZeroTrustStatuses.inconclusive] + d_[ZeroTrustStatuses.passed] === 0; + return d_[ZeroTrustStatuses.failed] + d_[ZeroTrustStatuses.verify] + d_[ZeroTrustStatuses.passed] === 0; } }, { @@ -103,8 +102,8 @@ class VennDiagram extends React.Component { } }, { - id: 'Rule #3', status: 'Inconclusive', hex: '#F0AD4E', f: function (d_) { - return d_[ZeroTrustStatuses.failed] === 0 && d_[ZeroTrustStatuses.inconclusive] > 0; + id: 'Rule #3', status: ZeroTrustStatuses.verify, hex: '#F0AD4E', f: function (d_) { + return d_[ZeroTrustStatuses.failed] === 0 && d_[ZeroTrustStatuses.verify] > 0; } }, { From 68383f069b51dc2b5dd21d706b22921bf8888fc9 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 10 Sep 2019 23:51:19 +0300 Subject: [PATCH 167/186] Final text changes --- .../report-components/zerotrust/PrinciplesSection.js | 4 +++- .../components/report-components/zerotrust/ReportLegend.js | 3 +-- .../components/report-components/zerotrust/SummarySection.js | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js index 44b427c11..bb957d42d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js @@ -7,7 +7,9 @@ export default class PrinciplesSection extends Component { return

    Test Results

    - The Zero Trust eXtended (ZTX) framework is composed of 7 pillars. Each pillar is built of + The + Zero Trust eXtended (ZTX) framework + is composed of 7 pillars. Each pillar is built of several guiding principles tested by the Infection Monkey.

    { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index 1881c82d2..5ef75f2b4 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -44,7 +44,7 @@ class ZeroTrustReportLegend extends Component {
    - {"\t"}The test passed, so this is OK 🙂 + {"\t"}All Tests related to this pillar passed. No violation of a Zero Trust guiding principle was detected.
  • @@ -53,7 +53,6 @@ class ZeroTrustReportLegend extends Component { {"\t"}This status means the test wasn't executed.To activate more tests, refer to the Monkey configuration page.
  • -
    ; } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js index 585f22047..e4012bf50 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js @@ -14,8 +14,9 @@ export default class SummarySection extends Component {

    - Get a quick glance at how your network aligns with the Zero - Trust eXtended (ZTX) framework. + Get a quick glance at how your network aligns with the + Zero Trust eXtended (ZTX) framework + .

    From 4d24d8432e23150e48cb50db650512b8c4b99262 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 11 Sep 2019 17:19:23 +0300 Subject: [PATCH 168/186] Improved the Events modal --- monkey/monkey_island/cc/ui/package.json | 3 +- .../zerotrust/EventsButton.js | 9 +---- .../zerotrust/EventsModal.js | 39 +++++++++++-------- .../zerotrust/EventsModalButtons.js | 20 ++++++++++ .../zerotrust/EventsTimeline.js | 3 +- 5 files changed, 47 insertions(+), 27 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 872a22bdc..983366c6e 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -104,6 +104,7 @@ "react-tooltip-lite": "^1.9.1", "redux": "^4.0.0", "sass-loader": "^7.1.0", - "sha3": "^2.0.0" + "sha3": "^2.0.0", + "pluralize": "latest" } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js index ea24e7b1a..761ff94a9 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js @@ -32,13 +32,8 @@ export default class EventsButton extends Component { } createEventsAmountBadge() { - let eventsAmountBadge; - if (this.props.events.length > 9) { - eventsAmountBadge = 9+; - } else { - eventsAmountBadge = {this.props.events.length}; - } - return eventsAmountBadge; + const eventsAmountBadgeContent = this.props.events.length > 9 ? "9+" : this.props.events.length; + return {eventsAmountBadgeContent}; } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js index 2ce25bf20..a7f2fe41c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js @@ -1,9 +1,11 @@ import React, {Component} from "react"; -import {Modal} from "react-bootstrap"; +import {Badge, Modal} from "react-bootstrap"; import EventsTimeline from "./EventsTimeline"; import * as PropTypes from "prop-types"; -import ExportEventsButton from "./ExportEventsButton"; import saveJsonToFile from "../../utils/SaveJsonToFile"; +import EventsModalButtons from "./EventsModalButtons"; +import Pluralize from 'pluralize' +import {statusToLabelType} from "./StatusLabel"; export default class EventsModal extends Component { constructor(props) { @@ -15,28 +17,31 @@ export default class EventsModal extends Component {
    this.props.hideCallback()}> -

    +

    Events
    -

    - + +
    +

    + There {Pluralize('is', this.props.events.length)} {

    {this.props.events.length}
    } {Pluralize('event', this.props.events.length)} associated with this finding. +

    + {this.props.events.length > 5 ? this.renderButtons() : null} - -
    - - { - const dataToSave = this.props.events; - const filename = this.props.exportFilename; - saveJsonToFile(dataToSave, filename); - }}/> -
    + {this.renderButtons()}
    ); } + + renderButtons() { + return this.props.hideCallback()} + onClickExport={() => { + const dataToSave = this.props.events; + const filename = this.props.exportFilename; + saveJsonToFile(dataToSave, filename); + }}/>; + } } EventsModal.propTypes = { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js new file mode 100644 index 000000000..962c54893 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js @@ -0,0 +1,20 @@ +import React, {Component} from "react"; +import ExportEventsButton from "./ExportEventsButton"; +import * as PropTypes from "prop-types"; + +export default class EventsModalButtons extends Component { + render() { + return
    + + +
    + } +} + +EventsModalButtons.propTypes = { + onClickClose: PropTypes.func, + onClickExport: PropTypes.func +}; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index d6723bd4d..b7fb90811 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -14,7 +14,7 @@ export default class EventsTimeline extends Component { render() { return (
    - + { this.props.events.map((event, index) => { const event_time = new Date(event.timestamp['$date']).toString(); @@ -22,7 +22,6 @@ export default class EventsTimeline extends Component { key={index} createdAt={event_time} title={event.title} - icon={icon}> {event.message} ) From 4b44fad1cd729428fd94631718eb9ebfc20b75b0 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 12:27:50 +0300 Subject: [PATCH 169/186] Fixed typos and grammer errors --- monkey/common/data/zero_trust_consts.py | 6 +++--- monkey/infection_monkey/monkey_utils/windows/new_user.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 3362756d9..4add05d04 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -139,16 +139,16 @@ TESTS_MAP = { TEST_TUNNELING: { TEST_EXPLANATION_KEY: u"The Monkey tried to tunnel traffic using other monkeys.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey was tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them." + STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them." }, PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES, PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED] }, TEST_COMMUNICATE_AS_NEW_USER: { - TEST_EXPLANATION_KEY: u"The Monkey tried create a new user and communicate with the internet from it.", + TEST_EXPLANATION_KEY: u"The Monkey tried to create a new user and communicate with the internet from it.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey was able to cause a new user to access the network. Your network policies are too permissive - restrict them to MAC only.", + STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - restrict them to MAC only.", STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network." }, PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES, diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py index fbe4bd832..87d2da3b8 100644 --- a/monkey/infection_monkey/monkey_utils/windows/new_user.py +++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py @@ -15,8 +15,8 @@ class NewUser(object): """ RAII object to use for creating and using a new user in Windows. Use with `with`. User will be created when the instance is instantiated. - User will log on start of `with` scope. - User will log off and get deleted on end of `with` scope. + User will log on at the start of the `with` scope. + User will log off and get deleted at the end of said `with` scope. Example: # Created # Logged on From edc2d49307332ccd4c4bf65c63f478d00be8fb85 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 13:00:42 +0300 Subject: [PATCH 170/186] Broke monkey_utils to utils/ and moved sambacry_runner to exploit. This commit is 100% refactoring without any new code, just deleted unused utils. --- deployment_scripts/config.ps1 | 2 +- deployment_scripts/deploy_linux.sh | 2 +- monkey/infection_monkey/exploit/mssqlexec.py | 2 +- .../sambacry_monkey_runner/build.sh | 0 .../sambacry_monkey_runner/sc_monkey_runner.c | 0 .../sambacry_monkey_runner/sc_monkey_runner.h | 0 monkey/infection_monkey/main.py | 6 +- monkey/infection_monkey/monkey.py | 11 ++-- monkey/infection_monkey/network/tools.py | 2 +- .../actions/communicate_as_new_user.py | 4 +- .../post_breach/actions/users_custom_pba.py | 4 +- monkey/infection_monkey/post_breach/pba.py | 2 +- .../post_breach/post_breach_handler.py | 2 +- monkey/infection_monkey/readme.txt | 2 +- monkey/infection_monkey/utils.py | 62 ------------------- .../{monkey_utils => utils}/__init__.py | 0 monkey/infection_monkey/utils/environment.py | 18 ++++++ monkey/infection_monkey/utils/monkey_dir.py | 29 +++++++++ .../infection_monkey/utils/monkey_log_path.py | 14 +++++ .../windows/__init__.py | 0 .../windows/new_user.py | 0 monkey/infection_monkey/windows_upgrader.py | 2 +- 22 files changed, 82 insertions(+), 82 deletions(-) rename monkey/infection_monkey/{monkey_utils => exploit}/sambacry_monkey_runner/build.sh (100%) rename monkey/infection_monkey/{monkey_utils => exploit}/sambacry_monkey_runner/sc_monkey_runner.c (100%) rename monkey/infection_monkey/{monkey_utils => exploit}/sambacry_monkey_runner/sc_monkey_runner.h (100%) delete mode 100644 monkey/infection_monkey/utils.py rename monkey/infection_monkey/{monkey_utils => utils}/__init__.py (100%) create mode 100644 monkey/infection_monkey/utils/environment.py create mode 100644 monkey/infection_monkey/utils/monkey_dir.py create mode 100644 monkey/infection_monkey/utils/monkey_log_path.py rename monkey/infection_monkey/{monkey_utils => utils}/windows/__init__.py (100%) rename monkey/infection_monkey/{monkey_utils => utils}/windows/new_user.py (100%) diff --git a/deployment_scripts/config.ps1 b/deployment_scripts/config.ps1 index 24a8d3322..07be64612 100644 --- a/deployment_scripts/config.ps1 +++ b/deployment_scripts/config.ps1 @@ -22,7 +22,7 @@ $SAMBA_64_BINARY_NAME = "sc_monkey_runner64.so" # Other directories and paths ( most likely you dont need to configure) $MONKEY_ISLAND_DIR = "\monkey\monkey_island" $MONKEY_DIR = "\monkey\infection_monkey" -$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\monkey_utils\sambacry_monkey_runner" +$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\exploit\sambacry_monkey_runner" $PYTHON_DLL = "C:\Windows\System32\python27.dll" $MK32_DLL = "mk32.dll" $MK64_DLL = "mk64.dll" diff --git a/deployment_scripts/deploy_linux.sh b/deployment_scripts/deploy_linux.sh index 5ce29ac59..4df8ba114 100644 --- a/deployment_scripts/deploy_linux.sh +++ b/deployment_scripts/deploy_linux.sh @@ -129,7 +129,7 @@ python -m pip install --user -r requirements_linux.txt || handle_error # Build samba log_message "Building samba binaries" sudo apt-get install gcc-multilib -cd ${monkey_home}/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner +cd ${monkey_home}/monkey/infection_monkey/exploit/sambacry_monkey_runner sudo chmod +x ./build.sh || handle_error ./build.sh diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index e4eaf3151..0115dfbf5 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -11,7 +11,7 @@ from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, get_target_monkey, \ build_monkey_commandline, get_monkey_depth from infection_monkey.model import DROPPER_ARG -from infection_monkey.utils import get_monkey_dir_path +from infection_monkey.utils.monkey_dir import get_monkey_dir_path LOG = logging.getLogger(__name__) diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh b/monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh similarity index 100% rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c b/monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.c similarity index 100% rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.c diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h b/monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.h similarity index 100% rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.h diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index 3b51c1be2..c20a84190 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -8,7 +8,7 @@ import os import sys import traceback -import infection_monkey.utils as utils +from infection_monkey.utils.monkey_log_path import get_dropper_log_path, get_monkey_log_path from infection_monkey.config import WormConfiguration, EXTERNAL_CONFIG_FILE from infection_monkey.dropper import MonkeyDrops from infection_monkey.model import MONKEY_ARG, DROPPER_ARG @@ -79,10 +79,10 @@ def main(): try: if MONKEY_ARG == monkey_mode: - log_path = utils.get_monkey_log_path() + log_path = get_monkey_log_path() monkey_cls = InfectionMonkey elif DROPPER_ARG == monkey_mode: - log_path = utils.get_dropper_log_path() + log_path = get_dropper_log_path() monkey_cls = MonkeyDrops else: return True diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 3cd20d9c2..b97e08dfd 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -7,7 +7,8 @@ import time from six.moves import xrange import infection_monkey.tunnel as tunnel -import infection_monkey.utils as utils +from infection_monkey.utils.monkey_dir import create_monkey_dir, get_monkey_dir_path, remove_monkey_dir +from infection_monkey.utils.monkey_log_path import get_monkey_log_path from infection_monkey.config import WormConfiguration from infection_monkey.control import ControlClient from infection_monkey.model import DELAY_DELETE_CMD @@ -90,7 +91,7 @@ class InfectionMonkey(object): self.set_default_port() # Create a dir for monkey files if there isn't one - utils.create_monkey_dir() + create_monkey_dir() if WindowsUpgrader.should_upgrade(): self._upgrading_to_64 = True @@ -244,8 +245,8 @@ class InfectionMonkey(object): @staticmethod def self_delete(): - status = ScanStatus.USED if utils.remove_monkey_dir() else ScanStatus.SCANNED - T1107Telem(status, utils.get_monkey_dir_path()).send() + status = ScanStatus.USED if remove_monkey_dir() else ScanStatus.SCANNED + T1107Telem(status, get_monkey_dir_path()).send() if WormConfiguration.self_delete_in_cleanup \ and -1 == sys.executable.find('python'): @@ -269,7 +270,7 @@ class InfectionMonkey(object): T1107Telem(status, sys.executable).send() def send_log(self): - monkey_log_path = utils.get_monkey_log_path() + monkey_log_path = get_monkey_log_path() if os.path.exists(monkey_log_path): with open(monkey_log_path, 'r') as f: log = f.read() diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py index 3a9adef57..5e448002c 100644 --- a/monkey/infection_monkey/network/tools.py +++ b/monkey/infection_monkey/network/tools.py @@ -10,7 +10,7 @@ import re from six.moves import range from infection_monkey.pyinstaller_utils import get_binary_file_path -from infection_monkey.utils import is_64bit_python +from infection_monkey.utils.environment import is_64bit_python DEFAULT_TIMEOUT = 10 BANNER_READ = 1024 diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 75acf6fe0..49c2404de 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -6,11 +6,11 @@ import subprocess import time from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER -from infection_monkey.monkey_utils.windows.new_user import NewUser, NewUserError +from infection_monkey.utils.windows.new_user import NewUser, NewUserError from infection_monkey.post_breach.actions.add_user import BackdoorUser from infection_monkey.post_breach.pba import PBA from infection_monkey.telemetry.post_breach_telem import PostBreachTelem -from infection_monkey.utils import is_windows_os +from infection_monkey.utils.environment import is_windows_os PING_TEST_DOMAIN = "google.com" diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py index 468a2b29b..89417757d 100644 --- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py +++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py @@ -2,11 +2,11 @@ import os import logging from common.data.post_breach_consts import POST_BREACH_FILE_EXECUTION -from infection_monkey.utils import is_windows_os +from infection_monkey.utils.environment import is_windows_os from infection_monkey.post_breach.pba import PBA from infection_monkey.control import ControlClient from infection_monkey.config import WormConfiguration -from infection_monkey.utils import get_monkey_dir_path +from infection_monkey.utils.monkey_dir import get_monkey_dir_path from infection_monkey.telemetry.attack.t1105_telem import T1105Telem from common.utils.attack_utils import ScanStatus from infection_monkey.exploit.tools.helpers import get_interface_to_target diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index fc074b563..22201ab7f 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -3,7 +3,7 @@ import subprocess from common.utils.attack_utils import ScanStatus from infection_monkey.telemetry.post_breach_telem import PostBreachTelem -from infection_monkey.utils import is_windows_os +from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.telemetry.attack.t1064_telem import T1064Telem diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index c68422d4c..b5dfa93c7 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -3,7 +3,7 @@ import inspect import importlib from infection_monkey.post_breach.pba import PBA from infection_monkey.post_breach.actions import get_pba_files -from infection_monkey.utils import is_windows_os +from infection_monkey.utils.environment import is_windows_os LOG = logging.getLogger(__name__) diff --git a/monkey/infection_monkey/readme.txt b/monkey/infection_monkey/readme.txt index 0b56da2f7..06bf449da 100644 --- a/monkey/infection_monkey/readme.txt +++ b/monkey/infection_monkey/readme.txt @@ -62,7 +62,7 @@ a. Build sambacry binaries yourself a.1. Install gcc-multilib if it's not installed sudo apt-get install gcc-multilib a.2. Build the binaries - cd [code location]/infection_monkey/monkey_utils/sambacry_monkey_runner + cd [code location]/infection_monkey/exploit/sambacry_monkey_runner ./build.sh b. Download our pre-built sambacry binaries diff --git a/monkey/infection_monkey/utils.py b/monkey/infection_monkey/utils.py deleted file mode 100644 index f8b5cc56a..000000000 --- a/monkey/infection_monkey/utils.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -import shutil -import struct -import sys -import tempfile - -from infection_monkey.config import WormConfiguration - - -def get_monkey_log_path(): - return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \ - else WormConfiguration.monkey_log_path_linux - - -def get_dropper_log_path(): - return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \ - else WormConfiguration.dropper_log_path_linux - - -def is_64bit_windows_os(): - """ - Checks for 64 bit Windows OS using environment variables. - """ - return 'PROGRAMFILES(X86)' in os.environ - - -def is_64bit_python(): - return struct.calcsize("P") == 8 - - -def is_windows_os(): - return sys.platform.startswith("win") - - -def utf_to_ascii(string): - # Converts utf string to ascii. Safe to use even if string is already ascii. - udata = string.decode("utf-8") - return udata.encode("ascii", "ignore") - - -def create_monkey_dir(): - """ - Creates directory for monkey and related files - """ - if not os.path.exists(get_monkey_dir_path()): - os.mkdir(get_monkey_dir_path()) - - -def remove_monkey_dir(): - """ - Removes monkey's root directory - :return True if removed without errors and False otherwise - """ - try: - shutil.rmtree(get_monkey_dir_path()) - return True - except Exception: - return False - - -def get_monkey_dir_path(): - return os.path.join(tempfile.gettempdir(), WormConfiguration.monkey_dir_name) diff --git a/monkey/infection_monkey/monkey_utils/__init__.py b/monkey/infection_monkey/utils/__init__.py similarity index 100% rename from monkey/infection_monkey/monkey_utils/__init__.py rename to monkey/infection_monkey/utils/__init__.py diff --git a/monkey/infection_monkey/utils/environment.py b/monkey/infection_monkey/utils/environment.py new file mode 100644 index 000000000..40a70ce58 --- /dev/null +++ b/monkey/infection_monkey/utils/environment.py @@ -0,0 +1,18 @@ +import os +import struct +import sys + + +def is_64bit_windows_os(): + """ + Checks for 64 bit Windows OS using environment variables. + """ + return 'PROGRAMFILES(X86)' in os.environ + + +def is_64bit_python(): + return struct.calcsize("P") == 8 + + +def is_windows_os(): + return sys.platform.startswith("win") diff --git a/monkey/infection_monkey/utils/monkey_dir.py b/monkey/infection_monkey/utils/monkey_dir.py new file mode 100644 index 000000000..bb69dae5b --- /dev/null +++ b/monkey/infection_monkey/utils/monkey_dir.py @@ -0,0 +1,29 @@ +import os +import shutil +import tempfile + +from infection_monkey.config import WormConfiguration + + +def create_monkey_dir(): + """ + Creates directory for monkey and related files + """ + if not os.path.exists(get_monkey_dir_path()): + os.mkdir(get_monkey_dir_path()) + + +def remove_monkey_dir(): + """ + Removes monkey's root directory + :return True if removed without errors and False otherwise + """ + try: + shutil.rmtree(get_monkey_dir_path()) + return True + except Exception: + return False + + +def get_monkey_dir_path(): + return os.path.join(tempfile.gettempdir(), WormConfiguration.monkey_dir_name) diff --git a/monkey/infection_monkey/utils/monkey_log_path.py b/monkey/infection_monkey/utils/monkey_log_path.py new file mode 100644 index 000000000..ad80bc73d --- /dev/null +++ b/monkey/infection_monkey/utils/monkey_log_path.py @@ -0,0 +1,14 @@ +import os +import sys + +from infection_monkey.config import WormConfiguration + + +def get_monkey_log_path(): + return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \ + else WormConfiguration.monkey_log_path_linux + + +def get_dropper_log_path(): + return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \ + else WormConfiguration.dropper_log_path_linux diff --git a/monkey/infection_monkey/monkey_utils/windows/__init__.py b/monkey/infection_monkey/utils/windows/__init__.py similarity index 100% rename from monkey/infection_monkey/monkey_utils/windows/__init__.py rename to monkey/infection_monkey/utils/windows/__init__.py diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/utils/windows/new_user.py similarity index 100% rename from monkey/infection_monkey/monkey_utils/windows/new_user.py rename to monkey/infection_monkey/utils/windows/new_user.py diff --git a/monkey/infection_monkey/windows_upgrader.py b/monkey/infection_monkey/windows_upgrader.py index 4a165940d..af904b143 100644 --- a/monkey/infection_monkey/windows_upgrader.py +++ b/monkey/infection_monkey/windows_upgrader.py @@ -10,7 +10,7 @@ from infection_monkey.config import WormConfiguration from infection_monkey.control import ControlClient from infection_monkey.exploit.tools.helpers import build_monkey_commandline_explicitly from infection_monkey.model import MONKEY_CMDLINE_WINDOWS -from infection_monkey.utils import is_windows_os, is_64bit_windows_os, is_64bit_python +from infection_monkey.utils.environment import is_windows_os, is_64bit_windows_os, is_64bit_python __author__ = 'itay.mizeretz' From 889c8a23787a971d3d6fbf6944393ec6c820cdc3 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 13:53:33 +0300 Subject: [PATCH 171/186] Moved user add+delete commands into `utils/users` --- .../post_breach/actions/add_user.py | 53 ++----------------- .../actions/communicate_as_new_user.py | 10 ++-- .../infection_monkey/utils/linux/__init__.py | 0 monkey/infection_monkey/utils/linux/users.py | 21 ++++++++ monkey/infection_monkey/utils/users.py | 10 ++++ .../windows/{new_user.py => auto_new_user.py} | 8 +-- .../infection_monkey/utils/windows/users.py | 18 +++++++ 7 files changed, 62 insertions(+), 58 deletions(-) create mode 100644 monkey/infection_monkey/utils/linux/__init__.py create mode 100644 monkey/infection_monkey/utils/linux/users.py create mode 100644 monkey/infection_monkey/utils/users.py rename monkey/infection_monkey/utils/windows/{new_user.py => auto_new_user.py} (89%) create mode 100644 monkey/infection_monkey/utils/windows/users.py diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index 9bb8cfcba..09c8d4796 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -1,61 +1,16 @@ -import datetime - from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER from infection_monkey.post_breach.pba import PBA from infection_monkey.config import WormConfiguration +from infection_monkey.utils.users import get_commands_to_add_user class BackdoorUser(PBA): def __init__(self): - linux_cmds, windows_cmds = BackdoorUser.get_commands_to_add_user( - WormConfiguration.user_to_add, WormConfiguration.remote_user_pass) + linux_cmds, windows_cmds = get_commands_to_add_user( + WormConfiguration.user_to_add, + WormConfiguration.remote_user_pass) super(BackdoorUser, self).__init__( POST_BREACH_BACKDOOR_USER, linux_cmd=' '.join(linux_cmds), windows_cmd=windows_cmds) - @staticmethod - def get_commands_to_add_user(username, password): - linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) - windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password) - return linux_cmds, windows_cmds - - @staticmethod - def get_linux_commands_to_add_user(username): - return [ - 'useradd', - '-M', # Do not create homedir - '--expiredate', - datetime.datetime.today().strftime('%Y-%m-%d'), - '--inactive', - '0', - '-c', # Comment - 'MONKEY_USER', # Comment - username] - - @staticmethod - def get_linux_commands_to_delete_user(username): - return [ - 'deluser', - username - ] - - @staticmethod - def get_windows_commands_to_add_user(username, password, should_be_active=False): - windows_cmds = [ - 'net', - 'user', - username, - password, - '/add'] - if not should_be_active: - windows_cmds.append('/ACTIVE:NO') - return windows_cmds - - @staticmethod - def get_windows_commands_to_delete_user(username): - return [ - 'net', - 'user', - username, - '/delete'] diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 49c2404de..725bf3bda 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -5,12 +5,12 @@ import string import subprocess import time +from infection_monkey.utils.windows.auto_new_user import AutoNewUser, NewUserError from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER -from infection_monkey.utils.windows.new_user import NewUser, NewUserError -from infection_monkey.post_breach.actions.add_user import BackdoorUser from infection_monkey.post_breach.pba import PBA from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils.environment import is_windows_os +from infection_monkey.utils.linux.users import get_linux_commands_to_delete_user, get_linux_commands_to_add_user PING_TEST_DOMAIN = "google.com" @@ -44,7 +44,7 @@ class CommunicateAsNewUser(PBA): def communicate_as_new_user_linux(self, username): try: # add user + ping - linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username) + linux_cmds = get_linux_commands_to_add_user(username) commandline = "ping -c 1 {}".format(PING_TEST_DOMAIN) linux_cmds.extend([";", "sudo", "-u", username, commandline]) final_command = ' '.join(linux_cmds) @@ -52,7 +52,7 @@ class CommunicateAsNewUser(PBA): self.send_ping_result_telemetry(exit_status, commandline, username) # delete the user, async in case it gets stuck. _ = subprocess.Popen( - BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True) + get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True) # Leaking the process on purpose - nothing we can do if it's stuck. except subprocess.CalledProcessError as e: PostBreachTelem(self, (e.output, False)).send() @@ -64,7 +64,7 @@ class CommunicateAsNewUser(PBA): import win32api try: - with NewUser(username, PASSWORD) as new_user: + with AutoNewUser(username, PASSWORD) as new_user: # Using os.path is OK, as this is on windows for sure ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe") if not os.path.exists(ping_app_path): diff --git a/monkey/infection_monkey/utils/linux/__init__.py b/monkey/infection_monkey/utils/linux/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/utils/linux/users.py b/monkey/infection_monkey/utils/linux/users.py new file mode 100644 index 000000000..1acc87d72 --- /dev/null +++ b/monkey/infection_monkey/utils/linux/users.py @@ -0,0 +1,21 @@ +import datetime + + +def get_linux_commands_to_add_user(username): + return [ + 'useradd', + '-M', # Do not create homedir + '--expiredate', + datetime.datetime.today().strftime('%Y-%m-%d'), + '--inactive', + '0', + '-c', # Comment + 'MONKEY_USER', # Comment + username] + + +def get_linux_commands_to_delete_user(username): + return [ + 'deluser', + username + ] diff --git a/monkey/infection_monkey/utils/users.py b/monkey/infection_monkey/utils/users.py new file mode 100644 index 000000000..68148d9e9 --- /dev/null +++ b/monkey/infection_monkey/utils/users.py @@ -0,0 +1,10 @@ +from infection_monkey.utils.linux.users import get_linux_commands_to_add_user +from infection_monkey.utils.windows.users import get_windows_commands_to_add_user + + +def get_commands_to_add_user(username, password): + linux_cmds = get_linux_commands_to_add_user(username) + windows_cmds = get_windows_commands_to_add_user(username, password) + return linux_cmds, windows_cmds + + diff --git a/monkey/infection_monkey/utils/windows/new_user.py b/monkey/infection_monkey/utils/windows/auto_new_user.py similarity index 89% rename from monkey/infection_monkey/utils/windows/new_user.py rename to monkey/infection_monkey/utils/windows/auto_new_user.py index 87d2da3b8..5cf840ad1 100644 --- a/monkey/infection_monkey/utils/windows/new_user.py +++ b/monkey/infection_monkey/utils/windows/auto_new_user.py @@ -2,7 +2,7 @@ import logging import subprocess from infection_monkey.post_breach.actions.add_user import BackdoorUser - +from infection_monkey.utils.windows.users import get_windows_commands_to_delete_user logger = logging.getLogger(__name__) @@ -11,7 +11,7 @@ class NewUserError(Exception): pass -class NewUser(object): +class AutoNewUser(object): """ RAII object to use for creating and using a new user in Windows. Use with `with`. User will be created when the instance is instantiated. @@ -20,7 +20,7 @@ class NewUser(object): Example: # Created # Logged on - with NewUser("user", "pass") as new_user: + with AutoNewUser("user", "pass") as new_user: ... ... # Logged off and deleted @@ -64,6 +64,6 @@ class NewUser(object): # Try to delete user try: _ = subprocess.Popen( - BackdoorUser.get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True) + get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True) except Exception as err: raise NewUserError("Can't delete user {}. Info: {}".format(self.username, err)) diff --git a/monkey/infection_monkey/utils/windows/users.py b/monkey/infection_monkey/utils/windows/users.py new file mode 100644 index 000000000..0e6847cff --- /dev/null +++ b/monkey/infection_monkey/utils/windows/users.py @@ -0,0 +1,18 @@ +def get_windows_commands_to_add_user(username, password, should_be_active=False): + windows_cmds = [ + 'net', + 'user', + username, + password, + '/add'] + if not should_be_active: + windows_cmds.append('/ACTIVE:NO') + return windows_cmds + + +def get_windows_commands_to_delete_user(username): + return [ + 'net', + 'user', + username, + '/delete'] From 77269fb3ce68a488ec2d64c9f713b807c8fe54ce Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 14:06:21 +0300 Subject: [PATCH 172/186] Extracted user name creation to separate function --- .../post_breach/actions/communicate_as_new_user.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 725bf3bda..165173ced 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -35,12 +35,16 @@ class CommunicateAsNewUser(PBA): super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER) def run(self): - username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) + username = self.get_random_new_user_name() if is_windows_os(): self.communicate_as_new_user_windows(username) else: self.communicate_as_new_user_linux(username) + @staticmethod + def get_random_new_user_name(): + return USERNAME + "_" + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) + def communicate_as_new_user_linux(self, username): try: # add user + ping From b8f48d354278e78f02e02e2e3a13502afbefb0cc Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 14:45:39 +0300 Subject: [PATCH 173/186] Unpacking struct from winapi --- .../post_breach/actions/communicate_as_new_user.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 165173ced..770e96b7d 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -79,7 +79,7 @@ class CommunicateAsNewUser(PBA): # Open process as that user: # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera commandline = "{} {} {} {}".format(ping_app_path, PING_TEST_DOMAIN, "-n", "1") - process_info = win32process.CreateProcessAsUser( + process_handle, thread_handle, _, _ = win32process.CreateProcessAsUser( new_user.get_logon_handle(), # A handle to the primary token that represents a user. None, # The name of the module to be executed. commandline, # The command line to be executed. @@ -95,18 +95,20 @@ class CommunicateAsNewUser(PBA): # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa ) - ping_exit_code = win32process.GetExitCodeProcess(process_info[0]) + ping_exit_code = win32process.GetExitCodeProcess(process_handle) counter = 0 while ping_exit_code == win32con.STILL_ACTIVE and counter < PING_WAIT_TIMEOUT_IN_SECONDS: - ping_exit_code = win32process.GetExitCodeProcess(process_info[0]) + ping_exit_code = win32process.GetExitCodeProcess(process_handle) counter += 1 - logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format(counter, ping_exit_code)) + logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format( + counter, + ping_exit_code)) time.sleep(1) self.send_ping_result_telemetry(ping_exit_code, commandline, username) - win32api.CloseHandle(process_info[0]) # Process handle - win32api.CloseHandle(process_info[1]) # Thread handle + win32api.CloseHandle(process_handle) # Process handle + win32api.CloseHandle(thread_handle) # Thread handle except Exception as e: # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the From bc94e5854a67f8565c249de5a66bcd16fd26e325 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 14:54:02 +0300 Subject: [PATCH 174/186] Moved handle close to finally block --- .../post_breach/actions/communicate_as_new_user.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 770e96b7d..1b577e5d8 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -106,10 +106,6 @@ class CommunicateAsNewUser(PBA): time.sleep(1) self.send_ping_result_telemetry(ping_exit_code, commandline, username) - - win32api.CloseHandle(process_handle) # Process handle - win32api.CloseHandle(thread_handle) # Thread handle - except Exception as e: # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the # "Replace a process level token" right, using Local Security Policy editing. Worked, but only @@ -118,6 +114,12 @@ class CommunicateAsNewUser(PBA): # 2. need to find how to do this using python... PostBreachTelem(self, ( "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() + finally: + try: + win32api.CloseHandle(process_handle) + win32api.CloseHandle(thread_handle) + except Exception as err: + logger.error("Close handle error: " + str(err)) except subprocess.CalledProcessError as err: PostBreachTelem(self, ( "Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)), From bb8a5bf55d1e19d6e6fe4b31ff13dedb27a6890d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 14:56:34 +0300 Subject: [PATCH 175/186] Deleted TODO --- .../post_breach/actions/communicate_as_new_user.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 1b577e5d8..5b5117681 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -107,11 +107,8 @@ class CommunicateAsNewUser(PBA): self.send_ping_result_telemetry(ping_exit_code, commandline, username) except Exception as e: - # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the - # "Replace a process level token" right, using Local Security Policy editing. Worked, but only - # after reboot. So: - # 1. need to decide if worth it, and then - # 2. need to find how to do this using python... + # If failed on 1314, it's possible to try to elevate the rights of the current user with the + # "Replace a process level token" right, using Local Security Policy editing. PostBreachTelem(self, ( "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() finally: From 4330a397255e23dec83dae5d8f79660d5769898b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 14:59:27 +0300 Subject: [PATCH 176/186] Removed unused PBA processing funcs --- .../post_breach/actions/communicate_as_new_user.py | 2 +- .../cc/services/telemetry/processing/post_breach.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 5b5117681..1c5dfcf45 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -108,7 +108,7 @@ class CommunicateAsNewUser(PBA): self.send_ping_result_telemetry(ping_exit_code, commandline, username) except Exception as e: # If failed on 1314, it's possible to try to elevate the rights of the current user with the - # "Replace a process level token" right, using Local Security Policy editing. + # "Replace a process level token" right, using Local Security Policy editing. PostBreachTelem(self, ( "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send() finally: diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py index c67f64f59..c64849905 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -6,16 +6,13 @@ from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_use def process_communicate_as_new_user_telemetry(telemetry_json): current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) - success = telemetry_json['data']['result'][1] message = telemetry_json['data']['result'][0] + success = telemetry_json['data']['result'][1] test_new_user_communication(current_monkey, success, message) POST_BREACH_TELEMETRY_PROCESSING_FUNCS = { POST_BREACH_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry, - # `lambda *args, **kwargs: None` is a no-op. - POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None, - POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None, } From dd9a4b2d101b1dcd7a7282c55f4bd6e7a35a57b8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 15:04:22 +0300 Subject: [PATCH 177/186] Refactored test_new_user_communication, mostly separated to functions --- .../communicate_as_new_user.py | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py index 564ce4d20..0c36b7b94 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py @@ -5,31 +5,35 @@ from monkey_island.cc.models.zero_trust.event import Event def test_new_user_communication(current_monkey, success, message): + AggregateFinding.create_or_add_to_existing( + test=TEST_COMMUNICATE_AS_NEW_USER, + status=STATUS_PASSED if success else STATUS_FAILED, + events=[ + get_attempt_event(current_monkey), + get_result_event(current_monkey, message, success) + ] + ) + + +def get_attempt_event(current_monkey): tried_to_communicate_event = Event.create_event( title="Communicate as new user", message="Monkey on {} tried to create a new user and communicate from it.".format(current_monkey.hostname), event_type=EVENT_TYPE_MONKEY_NETWORK) - events = [tried_to_communicate_event] + return tried_to_communicate_event + +def get_result_event(current_monkey, message, success): if success: - events.append( - Event.create_event( - title="Communicate as new user", - message="New user created by Monkey on {} successfully tried to communicate with the internet. " - "Details: {}".format(current_monkey.hostname, message), - event_type=EVENT_TYPE_MONKEY_NETWORK) - ) - test_status = STATUS_FAILED + event_to_append = Event.create_event( + title="Communicate as new user", + message="New user created by Monkey on {} successfully tried to communicate with the internet. " + "Details: {}".format(current_monkey.hostname, message), + event_type=EVENT_TYPE_MONKEY_NETWORK) else: - events.append( - Event.create_event( - title="Communicate as new user", - message="Monkey on {} couldn't communicate as new user. Details: {}".format( - current_monkey.hostname, message), - event_type=EVENT_TYPE_MONKEY_NETWORK) - ) - test_status = STATUS_PASSED - - AggregateFinding.create_or_add_to_existing( - test=TEST_COMMUNICATE_AS_NEW_USER, status=test_status, events=events - ) + event_to_append = Event.create_event( + title="Communicate as new user", + message="Monkey on {} couldn't communicate as new user. Details: {}".format( + current_monkey.hostname, message), + event_type=EVENT_TYPE_MONKEY_NETWORK) + return event_to_append From 76c642e4b334ed49fc1e372481a32ca32804d841 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 15:08:22 +0300 Subject: [PATCH 178/186] Lowered code dup in get_result_event --- .../communicate_as_new_user.py | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py index 0c36b7b94..a48c3598a 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py @@ -3,6 +3,10 @@ from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK, STATUS_FAIL from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event +COMM_AS_NEW_USER_FAILED_FORMAT = "Monkey on {} couldn't communicate as new user. Details: {}" +COMM_AS_NEW_USER_SUCCEEDED_FORMAT = \ + "New user created by Monkey on {} successfully tried to communicate with the internet. Details: {}" + def test_new_user_communication(current_monkey, success, message): AggregateFinding.create_or_add_to_existing( @@ -24,16 +28,9 @@ def get_attempt_event(current_monkey): def get_result_event(current_monkey, message, success): - if success: - event_to_append = Event.create_event( - title="Communicate as new user", - message="New user created by Monkey on {} successfully tried to communicate with the internet. " - "Details: {}".format(current_monkey.hostname, message), - event_type=EVENT_TYPE_MONKEY_NETWORK) - else: - event_to_append = Event.create_event( - title="Communicate as new user", - message="Monkey on {} couldn't communicate as new user. Details: {}".format( - current_monkey.hostname, message), - event_type=EVENT_TYPE_MONKEY_NETWORK) - return event_to_append + message_format = COMM_AS_NEW_USER_SUCCEEDED_FORMAT if success else COMM_AS_NEW_USER_FAILED_FORMAT + + return Event.create_event( + title="Communicate as new user", + message=message_format.format(current_monkey.hostname, message), + event_type=EVENT_TYPE_MONKEY_NETWORK) From 0a11c4b0076d6b70ef670cd3f5612281589317b8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 16:17:30 +0300 Subject: [PATCH 179/186] Extracted duplicate code to `add_malicious_activity_to_timeline` helper function --- .../cc/models/zero_trust/aggregate_finding.py | 9 +++++++++ .../cc/models/zero_trust/test_aggregate_finding.py | 4 ++-- .../telemetry/zero_trust_tests/data_endpoints.py | 8 ++------ .../telemetry/zero_trust_tests/machine_exploited.py | 8 ++------ .../services/telemetry/zero_trust_tests/tunneling.py | 12 ++++-------- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py index 613b9a4a2..c3ed52649 100644 --- a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py @@ -1,3 +1,4 @@ +from common.data.zero_trust_consts import TEST_MALICIOUS_ACTIVITY_TIMELINE, STATUS_VERIFY from monkey_island.cc.models.zero_trust.finding import Finding @@ -21,3 +22,11 @@ class AggregateFinding(Finding): orig_finding = existing_findings[0] orig_finding.add_events(events) orig_finding.save() + + +def add_malicious_activity_to_timeline(events): + AggregateFinding.create_or_add_to_existing( + test=TEST_MALICIOUS_ACTIVITY_TIMELINE, + status=STATUS_VERIFY, + events=events + ) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py index 4a67a21b7..c1a94166f 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py @@ -12,7 +12,7 @@ class TestAggregateFinding(IslandTestCase): test = TEST_MALICIOUS_ACTIVITY_TIMELINE status = STATUS_VERIFY - events = [Event.create_event("t", "t", EVENT_TYPE_ISLAND)] + events = [Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)] self.assertEquals(len(Finding.objects(test=test, status=status)), 0) AggregateFinding.create_or_add_to_existing(test, status, events) @@ -31,7 +31,7 @@ class TestAggregateFinding(IslandTestCase): test = TEST_MALICIOUS_ACTIVITY_TIMELINE status = STATUS_VERIFY - event = Event.create_event("t", "t", EVENT_TYPE_ISLAND) + event = Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK) events = [event] self.assertEquals(len(Finding.objects(test=test, status=status)), 0) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index 7b45b1dee..68a7f713d 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -3,7 +3,7 @@ import json from common.data.network_consts import ES_SERVICE from common.data.zero_trust_consts import * from monkey_island.cc.models import Monkey -from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline from monkey_island.cc.models.zero_trust.event import Event HTTP_SERVERS_SERVICES_NAMES = ['tcp-80'] @@ -67,8 +67,4 @@ def test_open_data_endpoints(telemetry_json): events=events ) - AggregateFinding.create_or_add_to_existing( - test=TEST_MALICIOUS_ACTIVITY_TIMELINE, - status=STATUS_VERIFY, - events=events - ) + add_malicious_activity_to_timeline(events) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index 8198b5a3e..454f3a7fe 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -1,6 +1,6 @@ from common.data.zero_trust_consts import * from monkey_island.cc.models import Monkey -from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline from monkey_island.cc.models.zero_trust.event import Event @@ -36,8 +36,4 @@ def test_machine_exploited(current_monkey, exploit_successful, exploiter, target events=events ) - AggregateFinding.create_or_add_to_existing( - test=TEST_MALICIOUS_ACTIVITY_TIMELINE, - status=STATUS_VERIFY, - events=events - ) + add_malicious_activity_to_timeline(events) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py index ba55fc575..ce34c2bb4 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py @@ -1,7 +1,6 @@ -from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_VERIFY, \ - TEST_MALICIOUS_ACTIVITY_TIMELINE +from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK from monkey_island.cc.models import Monkey -from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding +from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field @@ -18,14 +17,11 @@ def test_tunneling_violation(tunnel_telemetry_json): event_type=EVENT_TYPE_MONKEY_NETWORK, timestamp=tunnel_telemetry_json['timestamp'] )] + AggregateFinding.create_or_add_to_existing( test=TEST_TUNNELING, status=STATUS_FAILED, events=tunneling_events ) - AggregateFinding.create_or_add_to_existing( - test=TEST_MALICIOUS_ACTIVITY_TIMELINE, - status=STATUS_VERIFY, - events=tunneling_events - ) + add_malicious_activity_to_timeline(tunneling_events) From 3b06768a98925ba69e3d4f3be2ec1eb24600657d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 16:32:21 +0300 Subject: [PATCH 180/186] Replaced sleep loop for waiting on the process with WaitForSingleObject winapi. --- .../actions/communicate_as_new_user.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 1c5dfcf45..cf49dc349 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -5,6 +5,8 @@ import string import subprocess import time +import win32event + from infection_monkey.utils.windows.auto_new_user import AutoNewUser, NewUserError from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER from infection_monkey.post_breach.pba import PBA @@ -14,7 +16,7 @@ from infection_monkey.utils.linux.users import get_linux_commands_to_delete_user PING_TEST_DOMAIN = "google.com" -PING_WAIT_TIMEOUT_IN_SECONDS = 20 +PING_WAIT_TIMEOUT_IN_MILLISECONDS = 20 * 1000 CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT = "Created process '{}' as user '{}', and successfully pinged." CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT = "Created process '{}' as user '{}', but failed to ping (exit status {})." @@ -95,15 +97,16 @@ class CommunicateAsNewUser(PBA): # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa ) + logger.debug( + "Waiting for ping process to finish. Timeout: {}ms".format(PING_WAIT_TIMEOUT_IN_MILLISECONDS)) + + # Ignoring return code, as we'll use `GetExitCode` to determine the state of the process later. + _ = win32event.WaitForSingleObject( # Waits until the specified object is signaled, or time-out. + process_handle, # Ping process handle + PING_WAIT_TIMEOUT_IN_MILLISECONDS # Timeout in milliseconds + ) + ping_exit_code = win32process.GetExitCodeProcess(process_handle) - counter = 0 - while ping_exit_code == win32con.STILL_ACTIVE and counter < PING_WAIT_TIMEOUT_IN_SECONDS: - ping_exit_code = win32process.GetExitCodeProcess(process_handle) - counter += 1 - logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format( - counter, - ping_exit_code)) - time.sleep(1) self.send_ping_result_telemetry(ping_exit_code, commandline, username) except Exception as e: @@ -125,6 +128,13 @@ class CommunicateAsNewUser(PBA): PostBreachTelem(self, (str(e), False)).send() def send_ping_result_telemetry(self, exit_status, commandline, username): + """ + Parses the result of ping and sends telemetry accordingly. + + :param exit_status: In both Windows and Linux, 0 exit code from Ping indicates success. + :param commandline: Exact commandline which was executed, for reporting back. + :param username: Username from which the command was executed, for reporting back. + """ if exit_status == 0: PostBreachTelem(self, ( CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT.format(commandline, username), True)).send() From 1f56e8df61566056b0254a513e7509d6bbbe9fa8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 16:34:13 +0300 Subject: [PATCH 181/186] Use classname instead of self for static method --- .../post_breach/actions/communicate_as_new_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index cf49dc349..4522def4f 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -37,7 +37,7 @@ class CommunicateAsNewUser(PBA): super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER) def run(self): - username = self.get_random_new_user_name() + username = CommunicateAsNewUser.get_random_new_user_name() if is_windows_os(): self.communicate_as_new_user_windows(username) else: From d4947d97f3eb1bcd0524ac829d98b1b9dbadb556 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 16:37:30 +0300 Subject: [PATCH 182/186] Lock npm version for `pluralize` --- monkey/monkey_island/cc/ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 983366c6e..4da085836 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -105,6 +105,6 @@ "redux": "^4.0.0", "sass-loader": "^7.1.0", "sha3": "^2.0.0", - "pluralize": "latest" + "pluralize": "^7.0.0" } } From 9f98025d3356bec9b4324a6af3cefcd635c21040 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 16:44:16 +0300 Subject: [PATCH 183/186] Using protocol as well for cases when we are running on HTTP and not HTTPS (npm run start for example) --- monkey/monkey_island/cc/server_config.json | 2 +- monkey/monkey_island/cc/ui/src/components/Main.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index 420f1b303..7bf106194 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,4 +1,4 @@ { - "server_config": "standard", + "server_config": "testing", "deployment": "develop" } diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 9fd3d8f1c..09038292e 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -215,7 +215,8 @@ class AppComponent extends AuthComponent { if (this.shouldShowNotification()) { const hostname = window.location.hostname; const port = window.location.port; - const url = `https://${hostname}:${port}${reportZeroTrustRoute}`; + const protocol = window.location.protocol; + const url = `${protocol}//${hostname}:${port}${reportZeroTrustRoute}`; Notifier.start( "Monkey Island", From 841e54afc883a9260c7f5420590eed9337169ae5 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 17:41:26 +0300 Subject: [PATCH 184/186] Fixed UTs --- .../reporting/test_zero_trust_service.py | 85 +++++++++++++++---- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py index 5d84a9cb0..46b4fefd7 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py @@ -54,14 +54,14 @@ class TestZeroTrustService(IslandTestCase): STATUS_FAILED: 0, STATUS_VERIFY: 2, STATUS_PASSED: 0, - STATUS_UNEXECUTED: 0, + STATUS_UNEXECUTED: 1, "pillar": "People" }, { STATUS_FAILED: 0, STATUS_VERIFY: 2, STATUS_PASSED: 0, - STATUS_UNEXECUTED: 2, + STATUS_UNEXECUTED: 4, "pillar": "Networks" }, { @@ -82,7 +82,7 @@ class TestZeroTrustService(IslandTestCase): STATUS_FAILED: 0, STATUS_VERIFY: 0, STATUS_PASSED: 0, - STATUS_UNEXECUTED: 1, + STATUS_UNEXECUTED: 3, "pillar": "Visibility & Analytics" }, { @@ -102,6 +102,8 @@ class TestZeroTrustService(IslandTestCase): self.fail_if_not_testing_env() self.clean_finding_db() + self.maxDiff = None + save_example_findings() expected = { @@ -111,14 +113,14 @@ class TestZeroTrustService(IslandTestCase): "principle": PRINCIPLES[PRINCIPLE_DATA_TRANSIT], "status": STATUS_FAILED, "tests": [ + { + "status": STATUS_FAILED, + "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY] + }, { "status": STATUS_UNEXECUTED, "test": TESTS_MAP[TEST_DATA_ENDPOINT_ELASTIC][TEST_EXPLANATION_KEY] }, - { - "status": STATUS_FAILED, - "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY] - } ] } ], @@ -127,14 +129,14 @@ class TestZeroTrustService(IslandTestCase): "principle": PRINCIPLES[PRINCIPLE_ENDPOINT_SECURITY], "status": STATUS_FAILED, "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_MACHINE_EXPLOITED][TEST_EXPLANATION_KEY] + }, { "status": STATUS_FAILED, "test": TESTS_MAP[TEST_ENDPOINT_SECURITY_EXISTS][TEST_EXPLANATION_KEY] }, - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_MACHINE_EXPLOITED][TEST_EXPLANATION_KEY] - } ] } ], @@ -159,6 +161,16 @@ class TestZeroTrustService(IslandTestCase): } ] }, + { + "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES], + "status": STATUS_UNEXECUTED, + "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY] + } + ] + }, { "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], "status": STATUS_UNEXECUTED, @@ -168,7 +180,17 @@ class TestZeroTrustService(IslandTestCase): "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] } ] - } + }, + { + "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES], + "status": STATUS_UNEXECUTED, + "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY] + } + ] + }, ], PEOPLE: [ { @@ -180,9 +202,29 @@ class TestZeroTrustService(IslandTestCase): "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY] } ] + }, + { + "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES], + "status": STATUS_UNEXECUTED, + "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY] + } + ] } ], - "Visibility & Analytics": [ + VISIBILITY_ANALYTICS: [ + { + "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES], + "status": STATUS_UNEXECUTED, + "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY] + } + ] + }, { "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], "status": STATUS_UNEXECUTED, @@ -192,12 +234,23 @@ class TestZeroTrustService(IslandTestCase): "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] } ] - } + }, + { + "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES], + "status": STATUS_UNEXECUTED, + "tests": [ + { + "status": STATUS_UNEXECUTED, + "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY] + } + ] + }, ], - "Workloads": [] + WORKLOADS: [] } - self.assertEquals(ZeroTrustService.get_principles_status(), expected) + result = ZeroTrustService.get_principles_status() + self.assertEquals(result, expected) def test_get_pillars_to_statuses(self): self.fail_if_not_testing_env() From db328a3432ff8b51b72a0b467e8163d37312ac0b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 17:42:21 +0300 Subject: [PATCH 185/186] Accidentaly committed server config testing :-1: --- monkey/monkey_island/cc/server_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index 7bf106194..420f1b303 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,4 +1,4 @@ { - "server_config": "testing", + "server_config": "standard", "deployment": "develop" } From 0667aad87fd2a1b2615e2f64d12c2ea15b496a03 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 16 Sep 2019 17:57:35 +0300 Subject: [PATCH 186/186] Small fixes - reversed condition accidentaly and missed one reference to get_windows_commands_to_add_user --- .../post_breach/actions/communicate_as_new_user.py | 2 +- monkey/infection_monkey/utils/windows/auto_new_user.py | 4 ++-- .../telemetry/zero_trust_tests/communicate_as_new_user.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 4522def4f..296179d41 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -45,7 +45,7 @@ class CommunicateAsNewUser(PBA): @staticmethod def get_random_new_user_name(): - return USERNAME + "_" + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) + return USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) def communicate_as_new_user_linux(self, username): try: diff --git a/monkey/infection_monkey/utils/windows/auto_new_user.py b/monkey/infection_monkey/utils/windows/auto_new_user.py index 5cf840ad1..d95ac0bf0 100644 --- a/monkey/infection_monkey/utils/windows/auto_new_user.py +++ b/monkey/infection_monkey/utils/windows/auto_new_user.py @@ -2,7 +2,7 @@ import logging import subprocess from infection_monkey.post_breach.actions.add_user import BackdoorUser -from infection_monkey.utils.windows.users import get_windows_commands_to_delete_user +from infection_monkey.utils.windows.users import get_windows_commands_to_delete_user, get_windows_commands_to_add_user logger = logging.getLogger(__name__) @@ -34,7 +34,7 @@ class AutoNewUser(object): self.username = username self.password = password - windows_cmds = BackdoorUser.get_windows_commands_to_add_user(self.username, self.password, True) + windows_cmds = get_windows_commands_to_add_user(self.username, self.password, True) _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True) def __enter__(self): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py index a48c3598a..6c5b1154b 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py @@ -11,7 +11,8 @@ COMM_AS_NEW_USER_SUCCEEDED_FORMAT = \ def test_new_user_communication(current_monkey, success, message): AggregateFinding.create_or_add_to_existing( test=TEST_COMMUNICATE_AS_NEW_USER, - status=STATUS_PASSED if success else STATUS_FAILED, + # If the monkey succeeded to create a user, then the test failed. + status=STATUS_FAILED if success else STATUS_PASSED, events=[ get_attempt_event(current_monkey), get_result_event(current_monkey, message, success)