From e10d2246a3a4c323f6f8481ac7e0f4d91976fa54 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 28 Jul 2021 10:28:35 +0300 Subject: [PATCH 1/7] Island UI: reword the info for LateralMovement.tsx to not contain encryption part in description --- .../components/report-components/ransomware/LateralMovement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx index c5a9f9f1e..1d062c5c5 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx @@ -5,7 +5,7 @@ import pluralize from 'pluralize' const LATERAL_MOVEMENT_DESCRIPTION = 'After the initial breach, the attacker will begin the Lateral \ Movement phase of the attack. They will employ various \ techniques in order to compromise other systems in your \ - network and encrypt as many files as possible.' + network.' type PropagationStats = { num_scanned_nodes: number, From 1013347b3c4c0bea5e63ebc5b7730f75d801cd9b Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 28 Jul 2021 12:08:11 +0300 Subject: [PATCH 2/7] Island: move manual exploitation service into a separate exploitations directory under report directory --- .../cc/resources/exploitations/manual_exploitation.py | 4 +++- .../cc/services/reporting/exploitations/__init__.py | 0 .../{ => reporting}/exploitations/manual_exploitation.py | 0 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/services/reporting/exploitations/__init__.py rename monkey/monkey_island/cc/services/{ => reporting}/exploitations/manual_exploitation.py (100%) diff --git a/monkey/monkey_island/cc/resources/exploitations/manual_exploitation.py b/monkey/monkey_island/cc/resources/exploitations/manual_exploitation.py index 5754bd49f..7c5db2f75 100644 --- a/monkey/monkey_island/cc/resources/exploitations/manual_exploitation.py +++ b/monkey/monkey_island/cc/resources/exploitations/manual_exploitation.py @@ -1,7 +1,9 @@ import flask_restful from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.services.exploitations.manual_exploitation import get_manual_exploitations +from monkey_island.cc.services.reporting.exploitations.manual_exploitation import ( + get_manual_exploitations, +) class ManualExploitation(flask_restful.Resource): diff --git a/monkey/monkey_island/cc/services/reporting/exploitations/__init__.py b/monkey/monkey_island/cc/services/reporting/exploitations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/exploitations/manual_exploitation.py b/monkey/monkey_island/cc/services/reporting/exploitations/manual_exploitation.py similarity index 100% rename from monkey/monkey_island/cc/services/exploitations/manual_exploitation.py rename to monkey/monkey_island/cc/services/reporting/exploitations/manual_exploitation.py From 1448bb1850af30fd6baed4fd4665f18f09f659c6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 28 Jul 2021 12:13:37 +0300 Subject: [PATCH 3/7] Island: extract monkey exploitations into a separate service and a separate endpoint This change not only removes complexity from the huge report service, but also allows different UI components to call the API without forcing the whole report to be generated --- monkey/monkey_island/cc/app.py | 2 + .../exploitations/monkey_exploitation.py | 13 ++++ .../services/ransomware/ransomware_report.py | 10 +++- .../exploitations/monkey_exploitation.py | 60 +++++++++++++++++++ .../cc/services/reporting/report.py | 48 ++------------- .../report-components/SecurityReport.js | 2 +- .../security/BreachedServers.js | 50 ---------------- .../security/BreachedServers.tsx | 59 ++++++++++++++++++ .../ransomware/test_ransomware_report.py | 9 +-- .../exploitations/test_monkey_exploitation.py | 20 +++++++ .../cc/services/reporting/test_report.py | 11 ---- 11 files changed, 172 insertions(+), 112 deletions(-) create mode 100644 monkey/monkey_island/cc/resources/exploitations/monkey_exploitation.py create mode 100644 monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx create mode 100644 monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 67640d352..b25ae476c 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -25,6 +25,7 @@ from monkey_island.cc.resources.configuration_import import ConfigurationImport from monkey_island.cc.resources.edge import Edge from monkey_island.cc.resources.environment import Environment from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation +from monkey_island.cc.resources.exploitations.monkey_exploitation import MonkeyExploitation from monkey_island.cc.resources.island_configuration import IslandConfiguration from monkey_island.cc.resources.island_logs import IslandLog from monkey_island.cc.resources.island_mode import IslandMode @@ -156,6 +157,7 @@ def init_api_resources(api): api.add_resource(AttackReport, "/api/report/attack") api.add_resource(RansomwareReport, "/api/report/ransomware") api.add_resource(ManualExploitation, "/api/exploitations/manual") + api.add_resource(MonkeyExploitation, "/api/exploitations/monkey") api.add_resource(ZeroTrustFindingEvent, "/api/zero-trust/finding-event/") api.add_resource(TelemetryFeed, "/api/telemetry-feed", "/api/telemetry-feed/") diff --git a/monkey/monkey_island/cc/resources/exploitations/monkey_exploitation.py b/monkey/monkey_island/cc/resources/exploitations/monkey_exploitation.py new file mode 100644 index 000000000..5e00a51a0 --- /dev/null +++ b/monkey/monkey_island/cc/resources/exploitations/monkey_exploitation.py @@ -0,0 +1,13 @@ +import flask_restful + +from monkey_island.cc.resources.auth.auth import jwt_required +from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import ( + get_monkey_exploited, +) + + +class MonkeyExploitation(flask_restful.Resource): + @jwt_required + def get(self): + monkey_exploitations = [exploitation.__dict__ for exploitation in get_monkey_exploited()] + return {"monkey_exploitations": monkey_exploitations} diff --git a/monkey/monkey_island/cc/services/ransomware/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware/ransomware_report.py index 0e0f1c299..5dd384511 100644 --- a/monkey/monkey_island/cc/services/ransomware/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware/ransomware_report.py @@ -1,11 +1,15 @@ from typing import Dict, List +from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import ( + MonkeyExploitation, + get_monkey_exploited, +) from monkey_island.cc.services.reporting.report import ReportService def get_propagation_stats() -> Dict: scanned = ReportService.get_scanned() - exploited = ReportService.get_exploited() + exploited = get_monkey_exploited() return { "num_scanned_nodes": len(scanned), @@ -14,11 +18,11 @@ def get_propagation_stats() -> Dict: } -def _get_exploit_counts(exploited: List[Dict]) -> Dict: +def _get_exploit_counts(exploited: List[MonkeyExploitation]) -> Dict: exploit_counts = {} for node in exploited: - for exploit in node["exploits"]: + for exploit in node.exploits: exploit_counts[exploit] = exploit_counts.get(exploit, 0) + 1 return exploit_counts diff --git a/monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py b/monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py new file mode 100644 index 000000000..ac400d8cc --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py @@ -0,0 +1,60 @@ +import logging +from dataclasses import dataclass +from typing import List + +from monkey_island.cc.database import mongo +from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_descriptor_enum import ( # noqa: E501 + ExploiterDescriptorEnum, +) + +logger = logging.getLogger(__name__) + + +@dataclass +class MonkeyExploitation: + label: str + ip_addresses: List[str] + domain_name: str + exploits: List[str] + + +def get_monkey_exploited() -> List[MonkeyExploitation]: + exploited_with_monkeys = [ + NodeService.get_displayed_node_by_id(monkey["_id"], True) + for monkey in mongo.db.monkey.find({}, {"_id": 1}) + if not NodeService.get_monkey_manual_run(NodeService.get_monkey_by_id(monkey["_id"])) + ] + + exploited_without_monkeys = [ + NodeService.get_displayed_node_by_id(node["_id"], True) + for node in mongo.db.node.find({"exploited": True}, {"_id": 1}) + ] + + exploited = exploited_with_monkeys + exploited_without_monkeys + + exploited = [ + MonkeyExploitation( + label=exploited_node["label"], + ip_addresses=exploited_node["ip_addresses"], + domain_name=exploited_node["domain_name"], + exploits=get_exploits_used_on_node(exploited_node), + ) + for exploited_node in exploited + ] + + logger.info("Exploited nodes generated for reporting") + + return exploited + + +def get_exploits_used_on_node(node: dict) -> List[str]: + return list( + set( + [ + ExploiterDescriptorEnum.get_by_class_name(exploit["exploiter"]).display_name + for exploit in node["exploits"] + if exploit["result"] + ] + ) + ) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 20574c54f..6b9265435 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -21,8 +21,11 @@ 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.exploitations.manual_exploitation import get_manual_monkeys from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.reporting.exploitations.manual_exploitation import get_manual_monkeys +from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import ( + get_monkey_exploited, +) from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_descriptor_enum import ( # noqa: E501 ExploiterDescriptorEnum, ) @@ -150,47 +153,6 @@ class ReportService: nodes = nodes_without_monkeys + nodes_with_monkeys return nodes - @staticmethod - def get_exploited(): - exploited_with_monkeys = [ - NodeService.get_displayed_node_by_id(monkey["_id"], True) - for monkey in mongo.db.monkey.find({}, {"_id": 1}) - if not NodeService.get_monkey_manual_run(NodeService.get_monkey_by_id(monkey["_id"])) - ] - - exploited_without_monkeys = [ - NodeService.get_displayed_node_by_id(node["_id"], True) - for node in mongo.db.node.find({"exploited": True}, {"_id": 1}) - ] - - exploited = exploited_with_monkeys + exploited_without_monkeys - - exploited = [ - { - "label": exploited_node["label"], - "ip_addresses": exploited_node["ip_addresses"], - "domain_name": exploited_node["domain_name"], - "exploits": ReportService.get_exploits_used_on_node(exploited_node), - } - for exploited_node in exploited - ] - - logger.info("Exploited nodes generated for reporting") - - return exploited - - @staticmethod - def get_exploits_used_on_node(node: dict) -> List[str]: - return list( - set( - [ - ExploiterDescriptorEnum.get_by_class_name(exploit["exploiter"]).display_name - for exploit in node["exploits"] - if exploit["result"] - ] - ) - ) - @staticmethod def get_stolen_creds(): creds = [] @@ -648,7 +610,7 @@ class ReportService: monkey_latest_modify_time = Monkey.get_latest_modifytime() scanned_nodes = ReportService.get_scanned() - exploited_nodes = ReportService.get_exploited() + exploited_nodes = get_monkey_exploited() report = { "overview": { "manual_monkeys": ReportService.get_manual_monkey_hostnames(), diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js index c9fdd2c52..f7cffd277 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -566,7 +566,7 @@ class ReportPageComponent extends AuthComponent {
- +
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js deleted file mode 100644 index 827549c1a..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import ReactTable from 'react-table'; -import Pluralize from 'pluralize'; -import {renderArray, renderIpAddresses} from '../common/RenderArrays'; - - -const columns = [ - { - Header: 'Breached Servers', - columns: [ - {Header: 'Machine', accessor: 'label'}, - { - Header: 'IP Addresses', id: 'ip_addresses', - accessor: x => renderIpAddresses(x) - }, - {Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)} - ] - } -]; - -const pageSize = 10; - -class BreachedServersComponent extends React.Component { - constructor(props) { - super(props); - } - - render() { - let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length; - let showPagination = this.props.data.length > pageSize; - return ( - <> -

- The Monkey successfully breached {this.props.data.length} {Pluralize('machine', this.props.data.length)}: -

-
- -
- - ); - } -} - -export default BreachedServersComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx new file mode 100644 index 000000000..f2f774d61 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx @@ -0,0 +1,59 @@ +import React, {useEffect, useState} from 'react'; +import ReactTable from 'react-table'; +import Pluralize from 'pluralize'; +import {renderArray, renderIpAddresses} from '../common/RenderArrays'; +import LoadingIcon from '../../ui-components/LoadingIcon'; +import IslandHttpClient from '../../IslandHttpClient'; + + +const columns = [ + { + Header: 'Breached Servers', + columns: [ + {Header: 'Machine', accessor: 'label'}, + { + Header: 'IP Addresses', id: 'ip_addresses', + accessor: x => renderIpAddresses(x) + }, + {Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)} + ] + } +]; + +const pageSize = 10; + +function BreachedServersComponent() { + + const [exploitations, setExploitations] = useState(null); + + useEffect(() => { + IslandHttpClient.get('/api/exploitations/monkey') + .then(res => setExploitations(res.body['monkey_exploitations'])) + }, []); + + if(exploitations === null){ + return + } + + let defaultPageSize = exploitations.length > pageSize ? pageSize : exploitations.length; + let showPagination = exploitations.length > pageSize; + return ( + <> +

+ The Monkey successfully breached {exploitations.length} {Pluralize('machine', exploitations.length)}: +

+
+ +
+ + ); + +} + +export default BreachedServersComponent; diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py index 4c586aa81..a12b2aa9c 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py @@ -1,6 +1,7 @@ import pytest from monkey_island.cc.services.ransomware import ransomware_report +from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import MonkeyExploitation from monkey_island.cc.services.reporting.report import ReportService @@ -8,13 +9,13 @@ from monkey_island.cc.services.reporting.report import ReportService def patch_report_service_for_stats(monkeypatch): TEST_SCANNED_RESULTS = [{}, {}, {}, {}] TEST_EXPLOITED_RESULTS = [ - {"exploits": ["SSH Exploiter"]}, - {"exploits": ["SSH Exploiter", "SMB Exploiter"]}, - {"exploits": ["WMI Exploiter"]}, + MonkeyExploitation("", [], "", exploits=["SSH Exploiter"]), + MonkeyExploitation("", [], "", exploits=["SSH Exploiter", "SMB Exploiter"]), + MonkeyExploitation("", [], "", exploits=["WMI Exploiter"]), ] monkeypatch.setattr(ReportService, "get_scanned", lambda: TEST_SCANNED_RESULTS) - monkeypatch.setattr(ReportService, "get_exploited", lambda: TEST_EXPLOITED_RESULTS) + monkeypatch.setattr(ransomware_report, "get_monkey_exploited", lambda: TEST_EXPLOITED_RESULTS) def test_get_propagation_stats__num_scanned(patch_report_service_for_stats): diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py new file mode 100644 index 000000000..cdee46ba1 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py @@ -0,0 +1,20 @@ +from tests.unit_tests.monkey_island.cc.services.reporting.test_report import ( + NODE_DICT, + NODE_DICT_DUPLICATE_EXPLOITS, + NODE_DICT_FAILED_EXPLOITS, +) + +from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import ( + get_exploits_used_on_node, +) + + +def test_get_exploits_used_on_node(): + exploits = get_exploits_used_on_node(NODE_DICT) + assert sorted(exploits) == sorted(["Elastic Groovy Exploiter", "Drupal Server Exploiter"]) + + exploits = get_exploits_used_on_node(NODE_DICT_DUPLICATE_EXPLOITS) + assert exploits == ["Drupal Server Exploiter"] + + exploits = get_exploits_used_on_node(NODE_DICT_FAILED_EXPLOITS) + assert exploits == [] diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py index 989c46eed..0093e4235 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_report.py @@ -164,14 +164,3 @@ def test_get_stolen_creds_no_creds(fake_mongo): expected_stolen_creds_no_creds = [] assert expected_stolen_creds_no_creds == stolen_creds_no_creds - - -def test_get_exploits_used_on_node(): - exploits = ReportService.get_exploits_used_on_node(NODE_DICT) - assert sorted(exploits) == sorted(["Elastic Groovy Exploiter", "Drupal Server Exploiter"]) - - exploits = ReportService.get_exploits_used_on_node(NODE_DICT_DUPLICATE_EXPLOITS) - assert exploits == ["Drupal Server Exploiter"] - - exploits = ReportService.get_exploits_used_on_node(NODE_DICT_FAILED_EXPLOITS) - assert exploits == [] From 15fad503935701e0169d8f249951f7a33f57804b Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 28 Jul 2021 12:24:20 +0300 Subject: [PATCH 4/7] Island UI: change ransomware report to contain a table of exploitations in lateral movement section --- .../report-components/ransomware/LateralMovement.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx index 1d062c5c5..b7026af5e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ransomware/LateralMovement.tsx @@ -1,6 +1,7 @@ import React, {ReactElement} from 'react'; import NumberedReportSection from './NumberedReportSection'; import pluralize from 'pluralize' +import BreachedServersComponent from '../security/BreachedServers'; const LATERAL_MOVEMENT_DESCRIPTION = 'After the initial breach, the attacker will begin the Lateral \ Movement phase of the attack. They will employ various \ @@ -18,6 +19,8 @@ function LateralMovement({propagationStats}: {propagationStats: PropagationStats <> {getScannedVsExploitedStats(propagationStats.num_scanned_nodes, propagationStats.num_exploited_nodes)} {getExploitationStatsPerExploit(propagationStats.num_exploited_per_exploit)} +
+ ) From 4c1747d2e612dbbed28eb40d4e96e7ea7fe31756 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 28 Jul 2021 15:58:17 +0300 Subject: [PATCH 5/7] Island: change island report to only send exploited machine count to UI instead of whole machine list --- monkey/monkey_island/cc/services/reporting/report.py | 4 ++-- .../ui/src/components/report-components/SecurityReport.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 6b9265435..3bc56f737 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -610,7 +610,7 @@ class ReportService: monkey_latest_modify_time = Monkey.get_latest_modifytime() scanned_nodes = ReportService.get_scanned() - exploited_nodes = get_monkey_exploited() + exploited_cnt = len(get_monkey_exploited()) report = { "overview": { "manual_monkeys": ReportService.get_manual_monkey_hostnames(), @@ -628,7 +628,7 @@ class ReportService: }, "glance": { "scanned": scanned_nodes, - "exploited": exploited_nodes, + "exploited_cnt": exploited_cnt, "stolen_creds": ReportService.get_stolen_creds(), "azure_passwords": ReportService.get_azure_creds(), "ssh_keys": ReportService.get_ssh_keys(), diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js index f7cffd277..6825fb1d4 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -288,9 +288,9 @@ class ReportPageComponent extends AuthComponent {

Overview

- 0}/> + 0}/> { - this.state.report.glance.exploited.length > 0 ? + this.state.report.glance.exploited_cnt > 0 ? '' :

@@ -524,7 +524,7 @@ class ReportPageComponent extends AuthComponent { generateReportGlanceSection() { let exploitPercentage = - (100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length; + (100 * this.state.report.glance.exploited_cnt) / this.state.report.glance.scanned.length; return (

@@ -535,7 +535,7 @@ class ReportPageComponent extends AuthComponent { The Monkey discovered {this.state.report.glance.scanned.length} machines and successfully breached {this.state.report.glance.exploited.length} of them. + className='badge badge-danger'>{this.state.report.glance.exploited_cnt} of them.

Date: Wed, 28 Jul 2021 16:57:06 +0300 Subject: [PATCH 6/7] Island: remove the redundant line with total amount of exploited machines from ransomware report + small improvements in monkey_exploitation.py --- .../reporting/exploitations/monkey_exploitation.py | 8 +++++--- .../ui/src/components/report-components/SecurityReport.js | 7 +++++++ .../report-components/security/BreachedServers.tsx | 5 ----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py b/monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py index ac400d8cc..f06d23274 100644 --- a/monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py +++ b/monkey/monkey_island/cc/services/reporting/exploitations/monkey_exploitation.py @@ -20,18 +20,20 @@ class MonkeyExploitation: def get_monkey_exploited() -> List[MonkeyExploitation]: - exploited_with_monkeys = [ + exploited_nodes_monkeys_launched = [ NodeService.get_displayed_node_by_id(monkey["_id"], True) for monkey in mongo.db.monkey.find({}, {"_id": 1}) if not NodeService.get_monkey_manual_run(NodeService.get_monkey_by_id(monkey["_id"])) ] - exploited_without_monkeys = [ + # The node got exploited, but no monkeys got launched. + # For example the exploited machine was too old. + exploited_nodes_monkeys_failed = [ NodeService.get_displayed_node_by_id(node["_id"], True) for node in mongo.db.node.find({"exploited": True}, {"_id": 1}) ] - exploited = exploited_with_monkeys + exploited_without_monkeys + exploited = exploited_nodes_monkeys_launched + exploited_nodes_monkeys_failed exploited = [ MonkeyExploitation( diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js index 6825fb1d4..4f8af8c62 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -1,4 +1,5 @@ import React, {Fragment} from 'react'; +import Pluralize from 'pluralize'; import BreachedServers from 'components/report-components/security/BreachedServers'; import ScannedServers from 'components/report-components/security/ScannedServers'; import PostBreach from 'components/report-components/security/PostBreach'; @@ -566,6 +567,12 @@ class ReportPageComponent extends AuthComponent {
+

+ The Monkey successfully breached  + + {this.state.report.glance.exploited_cnt} + {Pluralize('machine', this.state.report.glance.exploited_cnt)}: +

diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx index f2f774d61..d9145242e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.tsx @@ -1,6 +1,5 @@ import React, {useEffect, useState} from 'react'; import ReactTable from 'react-table'; -import Pluralize from 'pluralize'; import {renderArray, renderIpAddresses} from '../common/RenderArrays'; import LoadingIcon from '../../ui-components/LoadingIcon'; import IslandHttpClient from '../../IslandHttpClient'; @@ -39,10 +38,6 @@ function BreachedServersComponent() { let showPagination = exploitations.length > pageSize; return ( <> -

- The Monkey successfully breached {exploitations.length} {Pluralize('machine', exploitations.length)}: -

Date: Wed, 28 Jul 2021 12:17:47 -0400 Subject: [PATCH 7/7] Tests: Split up test_get_exploits_used_on_node() --- .../reporting/exploitations/test_monkey_exploitation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py index cdee46ba1..f40e09c62 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/exploitations/test_monkey_exploitation.py @@ -9,12 +9,16 @@ from monkey_island.cc.services.reporting.exploitations.monkey_exploitation impor ) -def test_get_exploits_used_on_node(): +def test_get_exploits_used_on_node__2_exploits(): exploits = get_exploits_used_on_node(NODE_DICT) assert sorted(exploits) == sorted(["Elastic Groovy Exploiter", "Drupal Server Exploiter"]) + +def test_get_exploits_used_on_node__duplicate_exploits(): exploits = get_exploits_used_on_node(NODE_DICT_DUPLICATE_EXPLOITS) assert exploits == ["Drupal Server Exploiter"] + +def test_get_exploits_used_on_node__failed(): exploits = get_exploits_used_on_node(NODE_DICT_FAILED_EXPLOITS) assert exploits == []